Compare commits
114
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7f882f2e71
|
||
|
|
cb76cbb9b1
|
||
|
|
1f32387f63
|
||
|
|
be93c75fe7
|
||
|
|
38d18873b8
|
||
|
|
5053d37388
|
||
|
|
c00be9a49d
|
||
|
|
881280fa3e
|
||
|
|
40eb8b5335
|
||
|
|
789e9e199f
|
||
|
|
be521a42ee
|
||
|
|
65424c2944
|
||
|
|
490e48a87a
|
||
|
|
b958a2f5b3
|
||
|
|
2da0ccb09b
|
||
|
|
584fcaba39
|
||
|
|
b5dc96f98b
|
||
|
|
4dbbb1321c
|
||
|
|
0a7cfdbb8e
|
||
|
|
751c1895ca
|
||
|
|
3fd43b6e41
|
||
|
|
ae3554c365
|
||
|
|
5526a55145
|
||
|
|
3e6514a2e5
|
||
|
|
8566641459
|
||
|
|
14f7a5b9b5
|
||
|
|
6fb951a01e
|
||
|
|
c7c2df89c5
|
||
|
|
67114211ec
|
||
|
|
f2d99c4f0f
|
||
|
|
2ed01df331
|
||
|
|
f89bed25da
|
||
|
|
d707c20ab6
|
||
|
|
49d72f1d48
|
||
|
|
eabf17cb77
|
||
|
|
7b45069904
|
||
|
|
49df206ddc
|
||
|
|
e0ea18a6d0
|
||
|
|
20a850f011
|
||
|
|
157f530ad5
|
||
|
|
0b261b6272
|
||
|
|
882b326aca
|
||
|
|
49fe85dcde
|
||
|
|
a021aff92c
|
||
|
|
6abe93e3b5
|
||
|
|
7e84e6b4ba
|
||
|
|
76ed977375
|
||
|
|
fea403fe5a
|
||
|
|
1d96fd0cb9
|
||
|
|
7437e19de5
|
||
|
|
47567c9b75
|
||
|
|
ef16e70235
|
||
|
|
91d064947e
|
||
|
|
9a6e95673e
|
||
|
|
d24c3bb1cb
|
||
|
|
2ad637e55f
|
||
|
|
ac09342c9d
|
||
|
|
3a1054e1df
|
||
|
|
0f02c4b5be
|
||
|
|
be0a15d02d
|
||
|
|
12065b0525
|
||
|
|
0fc21cc461
|
||
|
|
7d63de4403
|
||
|
|
c520c256ee
|
||
|
|
2f9b97d1a4
|
||
|
|
ad18c7cb25
|
||
|
|
a2fb166b55
|
||
|
|
f0a7b34212
|
||
|
|
dccb34971b
|
||
|
|
36f767ea9b
|
||
|
|
c0739eeddf
|
||
|
|
cac30c9203
|
||
|
|
89b0ce768b
|
||
|
|
656ce219ef
|
||
|
|
fd30911dd3
|
||
|
|
b4cfa7aab2
|
||
|
|
617ae903ae
|
||
|
|
a7997429b7
|
||
|
|
4d5e982370
|
||
|
|
46ea01efe1
|
||
|
|
4161abe098
|
||
|
|
8777d931a4
|
||
|
|
d2ef9bb18a
|
||
|
|
3ec7fc11fa
|
||
|
|
9fd8453f63
|
||
|
|
a7d3942d1f
|
||
|
|
41933ec510
|
||
|
|
e98bb28faf
|
||
|
|
34eeb10c27
|
||
|
|
70e13f70c0
|
||
|
|
a89182b64f
|
||
|
|
45a22c2dd4
|
||
|
|
c6d33753c8
|
||
|
|
b32092f1cd
|
||
|
|
b5cb59d3ee
|
||
|
|
0b8d7b9cda
|
||
|
|
4f4cc43e90
|
||
|
|
b066ef22b4
|
||
|
|
08767d1d59
|
||
|
|
46404a9d7b
|
||
|
|
8a4c5403cb
|
||
|
|
15a58e8ace
|
||
|
|
d763c4344c
|
||
|
|
4dc08dffc9
|
||
|
|
87b811f716
|
||
|
|
eb4ebb9582
|
||
|
|
6af7813e3d
|
||
|
|
31ff9b287d
|
||
|
|
c9a06e5ada
|
||
|
|
4f007765f4
|
||
|
|
97780eb079
|
||
|
|
e487a59816
|
||
|
|
a7790319eb
|
||
|
|
ed137b38de
|
No files matched your search
@@ -15,9 +15,13 @@ $dotenv->load();
|
|||||||
class Container
|
class Container
|
||||||
{
|
{
|
||||||
static SokoWeb\Interfaces\Database\IConnection $dbConnection;
|
static SokoWeb\Interfaces\Database\IConnection $dbConnection;
|
||||||
static SokoWeb\Routing\RouteCollection $routeCollection;
|
static SokoWeb\Interfaces\Database\IAuditLogger $auditLogger;
|
||||||
static SokoWeb\Interfaces\Session\ISessionHandler $sessionHandler;
|
static SokoWeb\Interfaces\PersistentData\IPersistentDataManager $persistentDataManager;
|
||||||
static SokoWeb\Interfaces\Request\IRequest $request;
|
static ?SokoWeb\Interfaces\Routing\IRouteCollection $routeCollection = null;
|
||||||
|
static ?SokoWeb\Interfaces\Session\ISessionHandler $sessionHandler = null;
|
||||||
|
static ?SokoWeb\Interfaces\Request\IRequest $request = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Container::$dbConnection = new SokoWeb\Database\Mysql\Connection($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $_ENV['DB_NAME']);
|
Container::$dbConnection = new SokoWeb\Database\Mysql\Connection($_ENV['DB_HOST'], $_ENV['DB_USER'], $_ENV['DB_PASSWORD'], $_ENV['DB_NAME']);
|
||||||
|
Container::$auditLogger = new RVR\Database\AuditLogger(Container::$dbConnection, 'audit_log');
|
||||||
|
Container::$persistentDataManager = new SokoWeb\PersistentData\PersistentDataManager(Container::$dbConnection, Container::$auditLogger);
|
||||||
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"esoko/soko-web": "0.1",
|
"esoko/soko-web": "0.8",
|
||||||
"firebase/php-jwt": "^6.4"
|
"firebase/php-jwt": "^6.4"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
|||||||
Generated
+4
-4
@@ -4,15 +4,15 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "f2dcf297a4a619bc5edfe7b4fac0836e",
|
"content-hash": "a89a42e04596ab159fc41abbd9390068",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "esoko/soko-web",
|
"name": "esoko/soko-web",
|
||||||
"version": "v0.1",
|
"version": "v0.8",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://git.esoko.eu/esoko/soko-web.git",
|
"url": "https://git.esoko.eu/esoko/soko-web.git",
|
||||||
"reference": "a17a88e9d44e42cef3d996fc66b4dd1134b85def"
|
"reference": "219b42f995b8e34432da4dde77e53e24b75d78dd"
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
"phpmailer/phpmailer": "^6.8",
|
"phpmailer/phpmailer": "^6.8",
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
"GNU GPL 3.0"
|
"GNU GPL 3.0"
|
||||||
],
|
],
|
||||||
"description": "Lightweight web framework",
|
"description": "Lightweight web framework",
|
||||||
"time": "2023-04-07T17:32:15+00:00"
|
"time": "2023-05-01T17:08:22+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firebase/php-jwt",
|
"name": "firebase/php-jwt",
|
||||||
|
|||||||
@@ -0,0 +1,19 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use RVR\PersistentData\Model\Community;
|
||||||
|
use RVR\PersistentData\Model\Currency;
|
||||||
|
use SokoWeb\Database\Query\Select;
|
||||||
|
|
||||||
|
$select = new Select(Container::$dbConnection);
|
||||||
|
$communities = Container::$persistentDataManager->selectMultipleFromDb($select, Community::class);
|
||||||
|
|
||||||
|
foreach ($communities as $community) {
|
||||||
|
$mainCurrency = new Currency();
|
||||||
|
$mainCurrency->setCommunity($community);
|
||||||
|
$mainCurrency->setCode($community->getCurrency());
|
||||||
|
$mainCurrency->setRoundDigits(0);
|
||||||
|
Container::$persistentDataManager->saveToDb($mainCurrency);
|
||||||
|
|
||||||
|
$community->setMainCurrency($mainCurrency);
|
||||||
|
Container::$persistentDataManager->saveToDb($community);
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
CREATE TABLE `oauth_clients` (
|
||||||
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`client_id` varchar(16) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||||
|
`client_secret` varchar(40) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
|
||||||
|
`redirect_uris` text NOT NULL,
|
||||||
|
`preapproved` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
`created` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `client_id` (`client_id`)
|
||||||
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `oauth_tokens`
|
||||||
|
ADD `audience` varchar(255) NOT NULL DEFAULT '';
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `oauth_tokens`
|
||||||
|
DROP `audience`;
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE `oauth_clients`
|
||||||
|
MODIFY `client_id` varchar(255) CHARACTER SET ascii COLLATE ascii_bin NOT NULL;
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
CREATE TABLE `communities` (
|
||||||
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(255) NOT NULL,
|
||||||
|
`currency` varchar(3) NOT NULL,
|
||||||
|
`created` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||||
|
|
||||||
|
CREATE TABLE `community_members` (
|
||||||
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`community_id` int(10) unsigned NOT NULL,
|
||||||
|
`user_id` int(10) unsigned DEFAULT NULL,
|
||||||
|
`owner` tinyint(1) NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `unique_user_for_community` (`community_id`, `user_id`),
|
||||||
|
KEY `community_id` (`community_id`),
|
||||||
|
KEY `user_id` (`user_id`),
|
||||||
|
CONSTRAINT `community_members_community_id` FOREIGN KEY (`community_id`) REFERENCES `communities` (`id`),
|
||||||
|
CONSTRAINT `community_members_user_id` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`)
|
||||||
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
CREATE TABLE `audit_log` (
|
||||||
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`local_table` varchar(255) NOT NULL,
|
||||||
|
`local_id` int(10) unsigned NOT NULL,
|
||||||
|
`type` enum('insert','update','delete') NOT NULL,
|
||||||
|
`date` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||||
|
`modifier_id` int(10) unsigned NULL,
|
||||||
|
`column` varchar(255) NULL,
|
||||||
|
`old` text NULL,
|
||||||
|
`new` text NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
CREATE TABLE `currencies` (
|
||||||
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`community_id` int(10) unsigned NOT NULL,
|
||||||
|
`code` varchar(3) NOT NULL,
|
||||||
|
`round_digits` tinyint(1) unsigned NOT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
UNIQUE KEY `unique_currency_for_community` (`community_id`, `code`),
|
||||||
|
KEY `community_id` (`community_id`),
|
||||||
|
CONSTRAINT `currencies_community_id` FOREIGN KEY (`community_id`) REFERENCES `communities` (`id`)
|
||||||
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
CREATE TABLE `currency_exchange_rates` (
|
||||||
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`currency_id` int(10) unsigned NOT NULL,
|
||||||
|
`exchange_rate` decimal(19,9) unsigned NOT NULL,
|
||||||
|
`valid_from` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `currency_id` (`currency_id`),
|
||||||
|
INDEX `valid_from` (`valid_from`),
|
||||||
|
CONSTRAINT `currency_exchange_rates_currency_id` FOREIGN KEY (`currency_id`) REFERENCES `currencies` (`id`)
|
||||||
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
ALTER TABLE `communities`
|
||||||
|
ADD `main_currency_id` int(10) unsigned DEFAULT NULL,
|
||||||
|
ADD KEY `main_currency_id` (`main_currency_id`),
|
||||||
|
ADD CONSTRAINT `communities_main_currency_id` FOREIGN KEY (`main_currency_id`) REFERENCES `currencies` (`id`);
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
CREATE TABLE `transactions` (
|
||||||
|
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`community_id` int(10) unsigned NOT NULL,
|
||||||
|
`currency_id` int(10) unsigned NOT NULL,
|
||||||
|
`payer_user_id` int(10) unsigned NOT NULL,
|
||||||
|
`payee_user_id` int(10) unsigned NULL,
|
||||||
|
`description` varchar(255) NOT NULL,
|
||||||
|
`sum` decimal(19,9) NOT NULL,
|
||||||
|
`time` timestamp NOT NULL DEFAULT current_timestamp(),
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `community_id` (`community_id`),
|
||||||
|
KEY `currency_id` (`currency_id`),
|
||||||
|
KEY `payer_user_id` (`payer_user_id`),
|
||||||
|
KEY `payee_user_id` (`payee_user_id`),
|
||||||
|
CONSTRAINT `transactions_community_id` FOREIGN KEY (`community_id`) REFERENCES `communities` (`id`),
|
||||||
|
CONSTRAINT `transactions_currency_id` FOREIGN KEY (`currency_id`) REFERENCES `currencies` (`id`),
|
||||||
|
CONSTRAINT `transactions_payer_user_id` FOREIGN KEY (`payer_user_id`) REFERENCES `users` (`id`),
|
||||||
|
CONSTRAINT `transactions_payee_user_id` FOREIGN KEY (`payee_user_id`) REFERENCES `users` (`id`)
|
||||||
|
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
|
||||||
@@ -19,6 +19,12 @@ services:
|
|||||||
MYSQL_DATABASE: 'rvr'
|
MYSQL_DATABASE: 'rvr'
|
||||||
MYSQL_USER: 'rvr'
|
MYSQL_USER: 'rvr'
|
||||||
MYSQL_PASSWORD: 'rvr'
|
MYSQL_PASSWORD: 'rvr'
|
||||||
|
adminer:
|
||||||
|
image: adminer:4.8.1-standalone
|
||||||
|
ports:
|
||||||
|
- 9090:8080
|
||||||
|
environment:
|
||||||
|
- ADMINER_DEFAULT_SERVER=mariadb
|
||||||
mail:
|
mail:
|
||||||
image: marcopas/docker-mailslurper:latest
|
image: marcopas/docker-mailslurper:latest
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -1,58 +1,3 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use SokoWeb\Interfaces\Response\IRedirect;
|
|
||||||
use SokoWeb\Response\Redirect;
|
|
||||||
|
|
||||||
require '../web.php';
|
require '../web.php';
|
||||||
|
|
||||||
$method = strtolower($_SERVER['REQUEST_METHOD']);
|
|
||||||
$url = substr($_SERVER['REQUEST_URI'], strlen('/'));
|
|
||||||
if (($pos = strpos($url, '?')) !== false) {
|
|
||||||
$url = substr($url, 0, $pos);
|
|
||||||
}
|
|
||||||
$url = rawurldecode($url);
|
|
||||||
|
|
||||||
$match = Container::$routeCollection->match($method, $url == '' ? [] : explode('/', $url));
|
|
||||||
|
|
||||||
if ($match !== null) {
|
|
||||||
list($route, $params) = $match;
|
|
||||||
|
|
||||||
Container::$request->setParsedRouteParams($params);
|
|
||||||
|
|
||||||
$handler = $route->getHandler();
|
|
||||||
$controller = new $handler[0](Container::$request);
|
|
||||||
|
|
||||||
if ($controller instanceof SokoWeb\Interfaces\Authorization\ISecured) {
|
|
||||||
$authorized = $controller->authorize();
|
|
||||||
} else {
|
|
||||||
$authorized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$authorized) {
|
|
||||||
Container::$request->session()->set('redirect_after_login', substr($_SERVER['REQUEST_URI'], strlen('/')));
|
|
||||||
$response = new Redirect(Container::$routeCollection->getRoute('login')->generateLink(), IRedirect::TEMPORARY);
|
|
||||||
header('Location: ' . $response->getUrl(), true, $response->getHttpCode());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($method === 'post' && !in_array($url, $antiCsrfTokenExceptions) && Container::$request->post('anti_csrf_token') !== Container::$request->session()->get('anti_csrf_token')) {
|
|
||||||
$content = new SokoWeb\Response\JsonContent(['error' => 'no_valid_anti_csrf_token']);
|
|
||||||
header('Content-Type: text/html; charset=UTF-8', true, 403);
|
|
||||||
$content->render();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$response = call_user_func([$controller, $handler[1]]);
|
|
||||||
if ($response instanceof SokoWeb\Interfaces\Response\IContent) {
|
|
||||||
header('Content-Type: ' . $response->getContentType() . '; charset=UTF-8');
|
|
||||||
$response->render();
|
|
||||||
return;
|
|
||||||
} elseif ($response instanceof SokoWeb\Interfaces\Response\IRedirect) {
|
|
||||||
header('Location: ' . $response->getUrl(), true, $response->getHttpCode());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$content = new SokoWeb\Response\HtmlContent('error/404');
|
|
||||||
header('Content-Type: text/html; charset=UTF-8', true, 404);
|
|
||||||
$content->render();
|
|
||||||
+118
-8
@@ -31,6 +31,10 @@ main {
|
|||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
::placeholder, select > option[value=""], .gray {
|
||||||
|
color: #8e8e8e;
|
||||||
|
}
|
||||||
|
|
||||||
p, h1, h2, h3, input, textarea, select, button, a, table, label {
|
p, h1, h2, h3, input, textarea, select, button, a, table, label {
|
||||||
font-family: 'Oxygen', sans-serif;
|
font-family: 'Oxygen', sans-serif;
|
||||||
}
|
}
|
||||||
@@ -63,7 +67,7 @@ p, h2, h3 {
|
|||||||
line-height: 150%;
|
line-height: 150%;
|
||||||
}
|
}
|
||||||
|
|
||||||
p {
|
p, th, td {
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
}
|
}
|
||||||
@@ -88,18 +92,35 @@ sub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hr {
|
hr {
|
||||||
border: solid #bbbbbb 1px;
|
border: solid #bbbbcc 1px;
|
||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.normal {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
.bold {
|
.bold {
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
p.small, span.small {
|
p.small, span.small, td.small {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.red {
|
||||||
|
color: #a80908;
|
||||||
|
}
|
||||||
|
|
||||||
|
.green {
|
||||||
|
color: #008000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mono {
|
||||||
|
font-family: 'Oxygen Mono', mono;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
.justify {
|
.justify {
|
||||||
text-align: justify;
|
text-align: justify;
|
||||||
}
|
}
|
||||||
@@ -109,7 +130,7 @@ p.small, span.small {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.marginLeft {
|
.marginLeft {
|
||||||
margin-left: 10px;
|
margin-left: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.marginBottom {
|
.marginBottom {
|
||||||
@@ -117,7 +138,7 @@ p.small, span.small {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.marginRight {
|
.marginRight {
|
||||||
margin-right: 10px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.center {
|
.center {
|
||||||
@@ -146,6 +167,12 @@ a:hover, a:focus {
|
|||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
a.block {
|
||||||
|
color: initial;
|
||||||
|
font-weight: initial;
|
||||||
|
text-decoration: initial;
|
||||||
|
}
|
||||||
|
|
||||||
button, a.button {
|
button, a.button {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
@@ -211,6 +238,7 @@ button.noRightRadius, a.button.noRightRadius {
|
|||||||
|
|
||||||
button.gray, a.button.gray {
|
button.gray, a.button.gray {
|
||||||
background-color: #808080;
|
background-color: #808080;
|
||||||
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.gray:enabled:hover, button.gray:enabled:focus, a.button.gray:hover, a.button.gray:focus {
|
button.gray:enabled:hover, button.gray:enabled:focus, a.button.gray:hover, a.button.gray:focus {
|
||||||
@@ -219,6 +247,7 @@ button.gray:enabled:hover, button.gray:enabled:focus, a.button.gray:hover, a.but
|
|||||||
|
|
||||||
button.red, a.button.red {
|
button.red, a.button.red {
|
||||||
background-color: #aa5e5e;
|
background-color: #aa5e5e;
|
||||||
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
button.red:enabled:hover, button.red:enabled:focus, a.button.red:hover, a.button.red:focus {
|
button.red:enabled:hover, button.red:enabled:focus, a.button.red:hover, a.button.red:focus {
|
||||||
@@ -417,14 +446,94 @@ div.buttonContainer>button {
|
|||||||
}
|
}
|
||||||
|
|
||||||
div.box {
|
div.box {
|
||||||
width: 576px;
|
background-color: #eeeef4;
|
||||||
background-color: #eeeeee;
|
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
margin: 10px auto;
|
margin: 10px auto;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.compactBox {
|
||||||
|
width: 576px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.transaction {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: auto auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.gridContainer {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(400px, 1fr));
|
||||||
|
grid-gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
div.gridContainer > div {
|
||||||
|
background-color: #eeeef4;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: separate;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
table.fullWidth {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th {
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th, table td {
|
||||||
|
padding: 3px 0;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th:not(:first-child), table td:not(:first-child) {
|
||||||
|
padding-left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th:not(:last-child), table td:not(:last-child) {
|
||||||
|
padding-right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.paginateContainer {
|
||||||
|
font-size: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.paginateContainer > * {
|
||||||
|
font-size: initial;
|
||||||
|
background-color: #5e77aa;
|
||||||
|
border: solid #5e77aa 1px;
|
||||||
|
color: #ffffff;
|
||||||
|
font-weight: 700;
|
||||||
|
padding: 3px 6px;
|
||||||
|
text-align: center;
|
||||||
|
display: inline-block;
|
||||||
|
height: 25px;
|
||||||
|
line-height: 25px;
|
||||||
|
width: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.paginateContainer > a:hover, p.paginateContainer > .selected {
|
||||||
|
background-color: #3b5998;
|
||||||
|
border: solid #29457f 1px;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.paginateContainer > *:not(:last-child) {
|
||||||
|
border-right: solid #869ab9 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 424px) {
|
||||||
|
div.gridContainer {
|
||||||
|
grid-template-columns: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (max-width: 599px) {
|
@media screen and (max-width: 599px) {
|
||||||
header h1 span {
|
header h1 span {
|
||||||
display: none;
|
display: none;
|
||||||
@@ -433,6 +542,7 @@ div.box {
|
|||||||
margin-top: 4px;
|
margin-top: 4px;
|
||||||
}
|
}
|
||||||
button, a.button {
|
button, a.button {
|
||||||
|
margin: 3px 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
@@ -448,7 +558,7 @@ div.box {
|
|||||||
padding-left: 15px;
|
padding-left: 15px;
|
||||||
padding-right: 15px;
|
padding-right: 15px;
|
||||||
}
|
}
|
||||||
div.box {
|
div.compactBox {
|
||||||
width: initial;
|
width: initial;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ var Account = {
|
|||||||
countdown: null,
|
countdown: null,
|
||||||
|
|
||||||
openGoogleAuthenticate: function () {
|
openGoogleAuthenticate: function () {
|
||||||
window.open('/account/googleAuthenticate', 'googleAuthenticate', 'height=600,width=600')
|
window.open(googleAuthenticateUrl, 'googleAuthenticate', 'height=600,width=600')
|
||||||
},
|
},
|
||||||
|
|
||||||
authenticatedWithGoogleCallback: function (authenticatedWithGoogleUntil) {
|
authenticatedWithGoogleCallback: function (authenticatedWithGoogleUntil) {
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
(function () {
|
||||||
|
const element = document.getElementById('newMember').elements['user_id'];
|
||||||
|
const select = new TomSelect(element, {
|
||||||
|
placeholder: 'User',
|
||||||
|
valueField: 'value',
|
||||||
|
labelField: 'label',
|
||||||
|
searchField: 'label',
|
||||||
|
loadThrottle: 300,
|
||||||
|
load: function (query, callback) {
|
||||||
|
var self = this;
|
||||||
|
RVR.httpRequest('GET', searchUserUrl.replace('QUERY', encodeURIComponent(query)), function () {
|
||||||
|
self.clearOptions();
|
||||||
|
callback(this.response.results);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
select.on('change', function (value) {
|
||||||
|
this.clearOptions();
|
||||||
|
});
|
||||||
|
|
||||||
|
select.on('blur', function (value) {
|
||||||
|
this.clearOptions();
|
||||||
|
});
|
||||||
|
|
||||||
|
select.on('type', function (value) {
|
||||||
|
if (value === '') {
|
||||||
|
this.clearOptions();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
})();
|
||||||
+45
-5
@@ -49,7 +49,7 @@ var RVR = {
|
|||||||
document.getElementById('loading').style.visibility = 'visible';
|
document.getElementById('loading').style.visibility = 'visible';
|
||||||
|
|
||||||
var formData = new FormData(form);
|
var formData = new FormData(form);
|
||||||
var formError = form.getElementsByClassName('formError')[0];
|
var formError = document.getElementsByClassName('formError')[0];
|
||||||
var pageLeaveOnSuccess = form.dataset.redirectOnSuccess || form.dataset.reloadOnSuccess;
|
var pageLeaveOnSuccess = form.dataset.redirectOnSuccess || form.dataset.reloadOnSuccess;
|
||||||
|
|
||||||
RVR.httpRequest('POST', form.action, function () {
|
RVR.httpRequest('POST', form.action, function () {
|
||||||
@@ -88,6 +88,28 @@ var RVR = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
setOnclickForFormConfirmation: function (button) {
|
||||||
|
button.onclick = function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
RVR.showModalWithContent('Confirmation', this.dataset.confirmation, [
|
||||||
|
{
|
||||||
|
type: 'button',
|
||||||
|
text: self.textContent,
|
||||||
|
classNames: ['red'],
|
||||||
|
onclick: function() {
|
||||||
|
var event = new Event('submit', {'bubbles': true, 'cancelable': true});
|
||||||
|
self.form.dispatchEvent(event);
|
||||||
|
|
||||||
|
RVR.hideModal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
showModal: function (id) {
|
showModal: function (id) {
|
||||||
document.getElementById(id).style.visibility = 'visible';
|
document.getElementById(id).style.visibility = 'visible';
|
||||||
document.getElementById('cover').style.visibility = 'visible';
|
document.getElementById('cover').style.visibility = 'visible';
|
||||||
@@ -163,12 +185,18 @@ var RVR = {
|
|||||||
|
|
||||||
for (var i = 0; i < observedInputs.length; i++) {
|
for (var i = 0; i < observedInputs.length; i++) {
|
||||||
var input = form.elements[observedInputs[i]];
|
var input = form.elements[observedInputs[i]];
|
||||||
if (input.defaultValue !== input.value) {
|
if (input.type === 'checkbox') {
|
||||||
anyChanged = true;
|
if (input.defaultChecked !== input.checked) {
|
||||||
|
anyChanged = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (input.defaultValue !== input.value) {
|
||||||
|
anyChanged = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
form.elements.submit.disabled = !anyChanged;
|
form.elements['submit_button'].disabled = !anyChanged;
|
||||||
},
|
},
|
||||||
|
|
||||||
observeInputsInForm: function (form, observedInputs) {
|
observeInputsInForm: function (form, observedInputs) {
|
||||||
@@ -191,8 +219,16 @@ var RVR = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
form.onreset = function () {
|
form.onreset = function () {
|
||||||
form.elements.submit.disabled = true;
|
form.elements['submit_button'].disabled = true;
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
debounce: function(func, timeout = 300) {
|
||||||
|
let timer;
|
||||||
|
return (...args) => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
timer = setTimeout(() => { func.apply(this, args); }, timeout);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -220,6 +256,10 @@ var RVR = {
|
|||||||
if (form.dataset.observeInputs) {
|
if (form.dataset.observeInputs) {
|
||||||
RVR.observeInputsInForm(form, form.dataset.observeInputs.split(','));
|
RVR.observeInputsInForm(form, form.dataset.observeInputs.split(','));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (form.elements['submit_button'] && form.elements['submit_button'].dataset.confirmation) {
|
||||||
|
RVR.setOnclickForFormConfirmation(form.elements['submit_button']);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('cover').onclick = function () {
|
document.getElementById('cover').onclick = function () {
|
||||||
|
|||||||
Generated
+38
@@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"requires": true,
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"dependencies": {
|
||||||
|
"@orchidjs/sifter": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@orchidjs/sifter/-/sifter-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-zCZbwKegHytfsPm8Amcfh7v/4vHqTAaOu6xFswBYcn8nznBOuseu6COB2ON7ez0tFV0mKL0nRNnCiZZA+lU9/g==",
|
||||||
|
"requires": {
|
||||||
|
"@orchidjs/unicode-variants": "^1.0.4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@orchidjs/unicode-variants": {
|
||||||
|
"version": "1.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@orchidjs/unicode-variants/-/unicode-variants-1.0.4.tgz",
|
||||||
|
"integrity": "sha512-NvVBRnZNE+dugiXERFsET1JlKZfM5lJDEpSMilKW4bToYJ7pxf0Zne78xyXB2ny2c2aHfJ6WLnz1AaTNHAmQeQ=="
|
||||||
|
},
|
||||||
|
"leaflet": {
|
||||||
|
"version": "1.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.3.tgz",
|
||||||
|
"integrity": "sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ=="
|
||||||
|
},
|
||||||
|
"leaflet.markercluster": {
|
||||||
|
"version": "1.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/leaflet.markercluster/-/leaflet.markercluster-1.5.3.tgz",
|
||||||
|
"integrity": "sha512-vPTw/Bndq7eQHjLBVlWpnGeLa3t+3zGiuM7fJwCkiMFq+nmRuG3RI3f7f4N4TDX7T4NpbAXpR2+NTRSEGfCSeA=="
|
||||||
|
},
|
||||||
|
"tom-select": {
|
||||||
|
"version": "2.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/tom-select/-/tom-select-2.2.2.tgz",
|
||||||
|
"integrity": "sha512-igGah1yY6yhrnN2h/Ky8I5muw/nE/YQxIsEZoYu5qaA4bsRibvKto3s8QZZosKpOd0uO8fNYhRfAwgHB4IAYew==",
|
||||||
|
"requires": {
|
||||||
|
"@orchidjs/sifter": "^1.0.3",
|
||||||
|
"@orchidjs/unicode-variants": "^1.0.4"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"leaflet": "^1.6.0",
|
"leaflet": "^1.6.0",
|
||||||
"leaflet.markercluster": "^1.4.1"
|
"leaflet.markercluster": "^1.4.1",
|
||||||
|
"tom-select": "^2.2.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9,5 +9,8 @@ $app->add(new RVR\Cli\MigrateDatabaseCommand());
|
|||||||
$app->add(new RVR\Cli\AddUserCommand());
|
$app->add(new RVR\Cli\AddUserCommand());
|
||||||
$app->add(new RVR\Cli\LinkViewCommand());
|
$app->add(new RVR\Cli\LinkViewCommand());
|
||||||
$app->add(new RVR\Cli\MaintainDatabaseCommand());
|
$app->add(new RVR\Cli\MaintainDatabaseCommand());
|
||||||
|
$app->add(new RVR\Cli\AddOAuthClientCommand());
|
||||||
|
$app->add(new RVR\Cli\AddOAuthRedirectUriCommand());
|
||||||
|
$app->add(new RVR\Cli\RemoveOAuthRedirectUriCommand());
|
||||||
|
|
||||||
$app->run();
|
$app->run();
|
||||||
@@ -7,7 +7,7 @@ if [[ "${BRANCH_NAME}" =~ $BRANCH_PATTERN ]]; then
|
|||||||
TICKET_ID=$(echo $BRANCH_NAME | sed -E "s@$BRANCH_PATTERN@\\2@")
|
TICKET_ID=$(echo $BRANCH_NAME | sed -E "s@$BRANCH_PATTERN@\\2@")
|
||||||
|
|
||||||
COMMIT_MESSAGE=$(head -n 1 $1)
|
COMMIT_MESSAGE=$(head -n 1 $1)
|
||||||
COMMIT_MESSAGE_REGEX="^$TICKET_ID .*"
|
COMMIT_MESSAGE_REGEX="^(fixup! )?$TICKET_ID .*"
|
||||||
|
|
||||||
if [[ ! "${COMMIT_MESSAGE}" =~ $COMMIT_MESSAGE_REGEX ]]; then
|
if [[ ! "${COMMIT_MESSAGE}" =~ $COMMIT_MESSAGE_REGEX ]]; then
|
||||||
sed -i.bak -e "1s/^/$TICKET_ID /" $1
|
sed -i.bak -e "1s/^/$TICKET_ID /" $1
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?php namespace RVR\Cli;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use RVR\PersistentData\Model\OAuthClient;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
class AddOAuthClientCommand extends Command
|
||||||
|
{
|
||||||
|
public function configure(): void
|
||||||
|
{
|
||||||
|
$this->setName('oauth:add-client')
|
||||||
|
->setDescription('Adding of OAuth client.')
|
||||||
|
->addArgument('client-id', InputArgument::OPTIONAL, 'Client ID')
|
||||||
|
->addArgument('preapproved', InputArgument::OPTIONAL, 'Preapproved');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$clientId = $input->getArgument('client-id') ? $input->getArgument('client-id') : bin2hex(random_bytes(8));
|
||||||
|
$clientSecret = bin2hex(random_bytes(20));
|
||||||
|
|
||||||
|
$oAuthClient = new OAuthClient();
|
||||||
|
$oAuthClient->setClientId($clientId);
|
||||||
|
$oAuthClient->setClientSecret($clientSecret);
|
||||||
|
$oAuthClient->setCreatedDate(new DateTime());
|
||||||
|
|
||||||
|
if ($input->getArgument('preapproved')) {
|
||||||
|
$oAuthClient->setPreapproved($input->getArgument('preapproved'));
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
\Container::$persistentDataManager->saveToDb($oAuthClient);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$output->writeln('<error>Adding OAuth client failed!</error>');
|
||||||
|
$output->writeln('');
|
||||||
|
|
||||||
|
$output->writeln((string) $e);
|
||||||
|
$output->writeln('');
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$output->writeln('<info>OAuth client was successfully added!</info>');
|
||||||
|
$output->writeln('<info>Client ID: ' . $clientId . '</info>');
|
||||||
|
$output->writeln('<info>Client secret: ' . $clientSecret . '</info>');
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?php namespace RVR\Cli;
|
||||||
|
|
||||||
|
use RVR\Repository\OAuthClientRepository;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
class AddOAuthRedirectUriCommand extends Command
|
||||||
|
{
|
||||||
|
public function configure(): void
|
||||||
|
{
|
||||||
|
$this->setName('oauth:add-redirect-uri')
|
||||||
|
->setDescription('Adding of redirect URI for OAuth client.')
|
||||||
|
->addArgument('client_id', InputArgument::REQUIRED, 'The OAuth client ID')
|
||||||
|
->addArgument('redirect_uris', InputArgument::IS_ARRAY, 'Redirect URIs to add');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$oAuthClientRepository = new OAuthClientRepository();
|
||||||
|
$oAuthClient = $oAuthClientRepository->getByClientId($input->getArgument('client_id'));
|
||||||
|
|
||||||
|
if ($oAuthClient === null) {
|
||||||
|
$output->writeln('<error>OAuth client does not exist!</error>');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$redirectUris = array_unique(array_merge($oAuthClient->getRedirectUrisArray(), $input->getArgument('redirect_uris')));
|
||||||
|
|
||||||
|
$oAuthClient->setRedirectUrisArray($redirectUris);
|
||||||
|
|
||||||
|
try {
|
||||||
|
\Container::$persistentDataManager->saveToDb($oAuthClient);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$output->writeln('<error>Adding redirect URI failed!</error>');
|
||||||
|
$output->writeln('');
|
||||||
|
|
||||||
|
$output->writeln((string) $e);
|
||||||
|
$output->writeln('');
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$redirectUrisToPrint = [];
|
||||||
|
foreach ($redirectUris as $redirectUri) $redirectUrisToPrint[] = '* ' . $redirectUri;
|
||||||
|
|
||||||
|
$output->writeln('<info>Redirect URIS were successfully added! Current URIs:' . "\n" . implode("\n", $redirectUrisToPrint) . '</info>');
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
<?php namespace RVR\Cli;
|
<?php namespace RVR\Cli;
|
||||||
|
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use SokoWeb\PersistentData\PersistentDataManager;
|
|
||||||
use RVR\PersistentData\Model\User;
|
use RVR\PersistentData\Model\User;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\Console\Input\InputArgument;
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
@@ -36,8 +35,7 @@ class AddUserCommand extends Command
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$pdm = new PersistentDataManager();
|
\Container::$persistentDataManager->saveToDb($user);
|
||||||
$pdm->saveToDb($user);
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$output->writeln('<error>Adding user failed!</error>');
|
$output->writeln('<error>Adding user failed!</error>');
|
||||||
$output->writeln('');
|
$output->writeln('');
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ use DateTime;
|
|||||||
use SokoWeb\Database\Query\Modify;
|
use SokoWeb\Database\Query\Modify;
|
||||||
use SokoWeb\Database\Query\Select;
|
use SokoWeb\Database\Query\Select;
|
||||||
use SokoWeb\Interfaces\Database\IResultSet;
|
use SokoWeb\Interfaces\Database\IResultSet;
|
||||||
use SokoWeb\PersistentData\PersistentDataManager;
|
|
||||||
use RVR\Repository\UserPasswordResetterRepository;
|
use RVR\Repository\UserPasswordResetterRepository;
|
||||||
use Symfony\Component\Console\Command\Command;
|
use Symfony\Component\Console\Command\Command;
|
||||||
use Symfony\Component\Console\Input\InputInterface;
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
@@ -12,15 +11,12 @@ use Symfony\Component\Console\Output\OutputInterface;
|
|||||||
|
|
||||||
class MaintainDatabaseCommand extends Command
|
class MaintainDatabaseCommand extends Command
|
||||||
{
|
{
|
||||||
private PersistentDataManager $pdm;
|
|
||||||
|
|
||||||
private UserPasswordResetterRepository $userPasswordResetterRepository;
|
private UserPasswordResetterRepository $userPasswordResetterRepository;
|
||||||
|
|
||||||
public function __construct()
|
public function __construct()
|
||||||
{
|
{
|
||||||
parent::__construct();
|
parent::__construct();
|
||||||
|
|
||||||
$this->pdm = new PersistentDataManager();
|
|
||||||
$this->userPasswordResetterRepository = new UserPasswordResetterRepository();
|
$this->userPasswordResetterRepository = new UserPasswordResetterRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,7 +50,7 @@ class MaintainDatabaseCommand extends Command
|
|||||||
private function deleteExpiredPasswordResetters(): void
|
private function deleteExpiredPasswordResetters(): void
|
||||||
{
|
{
|
||||||
foreach ($this->userPasswordResetterRepository->getAllExpired() as $passwordResetter) {
|
foreach ($this->userPasswordResetterRepository->getAllExpired() as $passwordResetter) {
|
||||||
$this->pdm->deleteFromDb($passwordResetter);
|
\Container::$persistentDataManager->deleteFromDb($passwordResetter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?php namespace RVR\Cli;
|
||||||
|
|
||||||
|
use RVR\Repository\OAuthClientRepository;
|
||||||
|
use Symfony\Component\Console\Command\Command;
|
||||||
|
use Symfony\Component\Console\Input\InputArgument;
|
||||||
|
use Symfony\Component\Console\Input\InputInterface;
|
||||||
|
use Symfony\Component\Console\Output\OutputInterface;
|
||||||
|
|
||||||
|
class RemoveOAuthRedirectUriCommand extends Command
|
||||||
|
{
|
||||||
|
public function configure(): void
|
||||||
|
{
|
||||||
|
$this->setName('oauth:remove-redirect-uri')
|
||||||
|
->setDescription('Removing of redirect URI for OAuth client.')
|
||||||
|
->addArgument('client_id', InputArgument::REQUIRED, 'The OAuth client ID')
|
||||||
|
->addArgument('redirect_uris', InputArgument::IS_ARRAY, 'Redirect URIs to remove');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(InputInterface $input, OutputInterface $output): int
|
||||||
|
{
|
||||||
|
$oAuthClientRepository = new OAuthClientRepository();
|
||||||
|
$oAuthClient = $oAuthClientRepository->getByClientId($input->getArgument('client_id'));
|
||||||
|
|
||||||
|
if ($oAuthClient === null) {
|
||||||
|
$output->writeln('<error>OAuth client does not exist!</error>');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$redirectUris = array_diff($oAuthClient->getRedirectUrisArray(), $input->getArgument('redirect_uris'));
|
||||||
|
|
||||||
|
$oAuthClient->setRedirectUrisArray($redirectUris);
|
||||||
|
|
||||||
|
try {
|
||||||
|
\Container::$persistentDataManager->saveToDb($oAuthClient);
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$output->writeln('<error>Removing redirect URI failed!</error>');
|
||||||
|
$output->writeln('');
|
||||||
|
|
||||||
|
$output->writeln((string) $e);
|
||||||
|
$output->writeln('');
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
$redirectUrisToPrint = [];
|
||||||
|
foreach ($redirectUris as $redirectUri) $redirectUrisToPrint[] = '* ' . $redirectUri;
|
||||||
|
|
||||||
|
$output->writeln('<info>Redirect URIS were successfully removed! Current URIs:' . "\n" . implode("\n", $redirectUrisToPrint) . '</info>');
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,440 @@
|
|||||||
|
<?php namespace RVR\Controller;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use RVR\Finance\BalanceCalculator;
|
||||||
|
use RVR\PersistentData\Model\Community;
|
||||||
|
use RVR\PersistentData\Model\CommunityMember;
|
||||||
|
use RVR\PersistentData\Model\Currency;
|
||||||
|
use RVR\PersistentData\Model\CurrencyExchangeRate;
|
||||||
|
use RVR\PersistentData\Model\User;
|
||||||
|
use RVR\Repository\CommunityRepository;
|
||||||
|
use RVR\Repository\CommunityMemberRepository;
|
||||||
|
use RVR\Repository\CurrencyExchangeRateRepository;
|
||||||
|
use RVR\Repository\CurrencyRepository;
|
||||||
|
use RVR\Repository\TransactionRepository;
|
||||||
|
use RVR\Repository\UserRepository;
|
||||||
|
use SokoWeb\Interfaces\Authentication\IAuthenticationRequired;
|
||||||
|
use SokoWeb\Interfaces\Response\IContent;
|
||||||
|
use SokoWeb\Response\HtmlContent;
|
||||||
|
use SokoWeb\Response\JsonContent;
|
||||||
|
|
||||||
|
class CommunityController implements IAuthenticationRequired
|
||||||
|
{
|
||||||
|
private UserRepository $userRepository;
|
||||||
|
|
||||||
|
private CommunityRepository $communityRepository;
|
||||||
|
|
||||||
|
private CommunityMemberRepository $communityMemberRepository;
|
||||||
|
|
||||||
|
private CurrencyRepository $currencyRepository;
|
||||||
|
|
||||||
|
private CurrencyExchangeRateRepository $currencyExchangeRatesRepository;
|
||||||
|
|
||||||
|
private TransactionRepository $transactionRepository;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userRepository = new UserRepository();
|
||||||
|
$this->communityRepository = new CommunityRepository();
|
||||||
|
$this->communityMemberRepository = new CommunityMemberRepository();
|
||||||
|
$this->currencyRepository = new CurrencyRepository();
|
||||||
|
$this->currencyExchangeRatesRepository = new CurrencyExchangeRateRepository();
|
||||||
|
$this->transactionRepository = new TransactionRepository();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommunityHome(): ?IContent
|
||||||
|
{
|
||||||
|
if (!$this->checkPermission(\Container::$request->query('communityId'), false, $community, $ownCommunityMember)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
\Container::$persistentDataManager->loadRelationsFromDb($community, false, ['main_currency']);
|
||||||
|
|
||||||
|
$balanceCalculator = new BalanceCalculator($community);
|
||||||
|
$debts = $balanceCalculator->calculate();
|
||||||
|
$debtItems = [];
|
||||||
|
$debtBalance = 0.0;
|
||||||
|
$outstandingItems = [];
|
||||||
|
$outstandingBalance = 0.0;
|
||||||
|
foreach ($debts as $debt) {
|
||||||
|
if ($debt['payer']->getId() === \Container::$request->user()->getUniqueId()) {
|
||||||
|
$debtBalance += $debt['amount'];
|
||||||
|
$debtItems[] = $debt;
|
||||||
|
}
|
||||||
|
if ($debt['payee']->getId() === \Container::$request->user()->getUniqueId()) {
|
||||||
|
$outstandingBalance += $debt['amount'];
|
||||||
|
$outstandingItems[] = $debt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$balance = $outstandingBalance - $debtBalance;
|
||||||
|
|
||||||
|
return new HtmlContent('communities/community', [
|
||||||
|
'community' => $community,
|
||||||
|
'upcomingEvents' => [],
|
||||||
|
'debtItems' => $debtItems,
|
||||||
|
'debtBalance' => $debtBalance,
|
||||||
|
'outstandingItems' => $outstandingItems,
|
||||||
|
'outstandingBalance' => $outstandingBalance,
|
||||||
|
'balance' => $balance,
|
||||||
|
'editPermission' => $ownCommunityMember->getOwner()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommunitySettings(): ?IContent
|
||||||
|
{
|
||||||
|
if (!$this->checkPermission(\Container::$request->query('communityId'), false, $community, $ownCommunityMember)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HtmlContent('communities/community_settings', [
|
||||||
|
'community' => $community,
|
||||||
|
'members' => $this->getMembers($community),
|
||||||
|
'currencies' => $this->getCurrencies($community),
|
||||||
|
'editPermission' => $ownCommunityMember->getOwner()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommunityNew(): IContent
|
||||||
|
{
|
||||||
|
return new HtmlContent('communities/community_edit');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommunityEdit(): ?IContent
|
||||||
|
{
|
||||||
|
if (!$this->checkPermission(\Container::$request->query('communityId'), true, $community, $ownCommunityMember)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HtmlContent('communities/community_edit', [
|
||||||
|
'community' => $community
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveCommunity(): ?IContent
|
||||||
|
{
|
||||||
|
$name = \Container::$request->post('name');
|
||||||
|
if (strlen($name) === 0) {
|
||||||
|
return new JsonContent([
|
||||||
|
'error' => ['errorText' => 'Please fill all required fields!']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$communityId = \Container::$request->query('communityId');
|
||||||
|
if ($communityId){
|
||||||
|
if (!$this->checkPermission($communityId, true, $community, $ownCommunityMember)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$mainCurrencyCode = \Container::$request->post('main_currency_code');
|
||||||
|
$mainCurrencyRoundDigits = \Container::$request->post('main_currency_round_digits');
|
||||||
|
if (strlen($mainCurrencyCode) === 0 || strlen($mainCurrencyCode) > 3 || $mainCurrencyRoundDigits < 0 || $mainCurrencyRoundDigits > 9) {
|
||||||
|
return new JsonContent([
|
||||||
|
'error' => ['errorText' => 'Please fill all required fields!']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$community = new Community();
|
||||||
|
$community->setCreatedDate(new DateTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
$community->setName($name);
|
||||||
|
\Container::$persistentDataManager->saveToDb($community);
|
||||||
|
|
||||||
|
if (!$communityId) {
|
||||||
|
/**
|
||||||
|
* @var User $user
|
||||||
|
*/
|
||||||
|
$user = \Container::$request->user();
|
||||||
|
|
||||||
|
$communityMember = new CommunityMember();
|
||||||
|
$communityMember->setCommunity($community);
|
||||||
|
$communityMember->setUser($user);
|
||||||
|
$communityMember->setOwner(true);
|
||||||
|
\Container::$persistentDataManager->saveToDb($communityMember);
|
||||||
|
|
||||||
|
$mainCurrency = new Currency();
|
||||||
|
$mainCurrency->setCommunity($community);
|
||||||
|
$mainCurrency->setCode($mainCurrencyCode);
|
||||||
|
$mainCurrency->setRoundDigits($mainCurrencyRoundDigits);
|
||||||
|
\Container::$persistentDataManager->saveToDb($mainCurrency);
|
||||||
|
|
||||||
|
$community->setMainCurrency($mainCurrency);
|
||||||
|
\Container::$persistentDataManager->saveToDb($community);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonContent([
|
||||||
|
'redirect' => ['target' => \Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()])]
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMembersEdit(): ?IContent
|
||||||
|
{
|
||||||
|
if (!$this->checkPermission(\Container::$request->query('communityId'), true, $community, $ownCommunityMember)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HtmlContent('communities/community_members', [
|
||||||
|
'community' => $community,
|
||||||
|
'members' => $this->getMembers($community)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveMember(): ?IContent
|
||||||
|
{
|
||||||
|
if (!$this->checkPermission(\Container::$request->query('communityId'), true, $community, $ownCommunityMember)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$communityMemberId = \Container::$request->query('community_member_id');
|
||||||
|
if ($communityMemberId) {
|
||||||
|
$communityMember = $this->communityMemberRepository->getById($communityMemberId);
|
||||||
|
if ($communityMember->getUserId() === $ownCommunityMember->getUserId()) {
|
||||||
|
return new JsonContent([
|
||||||
|
'error' => ['errorText' => 'Own user cannot be edited.']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($this->transactionRepository->isAnyCommon()) {
|
||||||
|
return new JsonContent([
|
||||||
|
'error' => ['errorText' => 'There are transactions with common payee!']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = $this->userRepository->getById(\Container::$request->post('user_id'));
|
||||||
|
if ($this->communityMemberRepository->getByCommunityAndUser($community, $user) !== null) {
|
||||||
|
return new JsonContent([
|
||||||
|
'error' => ['errorText' => 'This user is already a member of this community.']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$communityMember = new CommunityMember();
|
||||||
|
$communityMember->setCommunity($community);
|
||||||
|
$communityMember->setUser($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
$communityMember->setOwner((bool)\Container::$request->post('owner'));
|
||||||
|
\Container::$persistentDataManager->saveToDb($communityMember);
|
||||||
|
|
||||||
|
return new JsonContent(['success' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteMember(): ?IContent
|
||||||
|
{
|
||||||
|
if (!$this->checkPermission(\Container::$request->query('communityId'), true, $community, $ownCommunityMember)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$communityMember = $this->communityMemberRepository->getById(\Container::$request->query('community_member_id'));
|
||||||
|
if ($communityMember->getUserId() === \Container::$request->user()->getUniqueId()) {
|
||||||
|
return new JsonContent([
|
||||||
|
'error' => ['errorText' => 'Own user cannot be deleted.']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
\Container::$persistentDataManager->loadRelationsFromDb($communityMember, false, ['user']);
|
||||||
|
if ($this->transactionRepository->isAnyForUser($communityMember->getUser())) {
|
||||||
|
return new JsonContent([
|
||||||
|
'error' => ['errorText' => 'There are transactions where the member is payer or payee!']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
\Container::$persistentDataManager->deleteFromDb($communityMember);
|
||||||
|
|
||||||
|
return new JsonContent(['success' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCurrenciesEdit(): ?IContent
|
||||||
|
{
|
||||||
|
if (!$this->checkPermission(\Container::$request->query('communityId'), true, $community, $ownCommunityMember)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HtmlContent('communities/community_currencies', [
|
||||||
|
'community' => $community,
|
||||||
|
'currencies' => $this->getCurrencies($community)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveCurrency(): ?IContent
|
||||||
|
{
|
||||||
|
if (!$this->checkPermission(\Container::$request->query('communityId'), true, $community, $ownCommunityMember)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$code = \Container::$request->post('code');
|
||||||
|
$roundDigits = (int)\Container::$request->post('round_digits');
|
||||||
|
if (strlen($code) === 0 || strlen($code) > 3 || $roundDigits < 0 || $roundDigits > 9) {
|
||||||
|
return new JsonContent([
|
||||||
|
'error' => ['errorText' => 'Please fill all required fields!']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$currencyId = \Container::$request->query('currency_id');
|
||||||
|
if ($currencyId){
|
||||||
|
$currency = $this->currencyRepository->getById($currencyId);
|
||||||
|
} else {
|
||||||
|
$currency = new Currency();
|
||||||
|
$currency->setCommunity($community);
|
||||||
|
}
|
||||||
|
|
||||||
|
$existingCurrency = $this->currencyRepository->getByCommunityAndCurrencyCode($community, $code);
|
||||||
|
if ($existingCurrency !== null && $currency->getId() !== $existingCurrency->getId()) {
|
||||||
|
return new JsonContent([
|
||||||
|
'error' => ['errorText' => 'A currency with the same code exists for this community.']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$currency->setCode($code);
|
||||||
|
$currency->setRoundDigits($roundDigits);
|
||||||
|
\Container::$persistentDataManager->saveToDb($currency);
|
||||||
|
|
||||||
|
return new JsonContent(['success' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteCurrency(): ?IContent
|
||||||
|
{
|
||||||
|
if (!$this->checkPermission(\Container::$request->query('communityId'), true, $community, $ownCommunityMember)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$currency = $this->currencyRepository->getById(\Container::$request->query('currency_id'));
|
||||||
|
if ($currency->getId() === $community->getMainCurrencyId()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->transactionRepository->isAnyForCurrency($currency)) {
|
||||||
|
return new JsonContent([
|
||||||
|
'error' => ['errorText' => 'There are transactions with this currency!']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($this->currencyExchangeRatesRepository->getAllByCurrency($currency) as $currencyExchangeRate) {
|
||||||
|
\Container::$persistentDataManager->deleteFromDb($currencyExchangeRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
\Container::$persistentDataManager->deleteFromDb($currency);
|
||||||
|
|
||||||
|
return new JsonContent(['success' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCurrencyExchangeRates(): ?IContent
|
||||||
|
{
|
||||||
|
if (!$this->checkPermission(\Container::$request->query('communityId'), true, $community, $ownCommunityMember)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$currency = $this->currencyRepository->getByCommunityAndCurrencyCode($community, \Container::$request->query('code'));
|
||||||
|
if ($currency === null || $currency->getId() === $community->getMainCurrencyId()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$currencyExchangeRates = $this->currencyExchangeRatesRepository->getAllByCurrency($currency);
|
||||||
|
|
||||||
|
return new HtmlContent('communities/currency_exchange_rates', [
|
||||||
|
'community' => $community,
|
||||||
|
'currency' => $currency,
|
||||||
|
'currencyExchangeRates' => $currencyExchangeRates,
|
||||||
|
'editPermission' => $ownCommunityMember->getOwner()
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveCurrencyExchangeRate(): ?IContent
|
||||||
|
{
|
||||||
|
if (!$this->checkPermission(\Container::$request->query('communityId'), true, $community, $ownCommunityMember)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$currency = $this->currencyRepository->getByCommunityAndCurrencyCode($community, \Container::$request->query('code'));
|
||||||
|
if ($currency === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$exchangeRate = (float)\Container::$request->post('exchange_rate');
|
||||||
|
if ($exchangeRate < 0) {
|
||||||
|
return new JsonContent([
|
||||||
|
'error' => ['errorText' => 'Please fill all required fields!']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$currencyExchangeRateId = \Container::$request->query('currency_exchange_rate_id');
|
||||||
|
if ($currencyExchangeRateId){
|
||||||
|
$currencyExchangeRate = $this->currencyExchangeRatesRepository->getById($currencyExchangeRateId);
|
||||||
|
} else {
|
||||||
|
$currencyExchangeRate = new CurrencyExchangeRate();
|
||||||
|
$currencyExchangeRate->setCurrency($currency);
|
||||||
|
}
|
||||||
|
|
||||||
|
$currencyExchangeRate->setExchangeRate($exchangeRate);
|
||||||
|
$currencyExchangeRate->setValidFromDate(new DateTime(\Container::$request->post('valid_from')));
|
||||||
|
\Container::$persistentDataManager->saveToDb($currencyExchangeRate);
|
||||||
|
|
||||||
|
return new JsonContent(['success' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteCurrencyExchangeRate(): ?IContent
|
||||||
|
{
|
||||||
|
if (!$this->checkPermission(\Container::$request->query('communityId'), true, $community, $ownCommunityMember)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$currency = $this->currencyRepository->getByCommunityAndCurrencyCode($community, \Container::$request->query('code'));
|
||||||
|
if ($currency === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$currencyExchangeRate = $this->currencyExchangeRatesRepository->getById(\Container::$request->query('currency_exchange_rate_id'));
|
||||||
|
\Container::$persistentDataManager->deleteFromDb($currencyExchangeRate);
|
||||||
|
|
||||||
|
return new JsonContent(['success' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMembers(Community $community): array
|
||||||
|
{
|
||||||
|
$members = iterator_to_array($this->communityMemberRepository->getAllByCommunity($community, true, ['user']));
|
||||||
|
usort($members, function($a, $b) {
|
||||||
|
return strnatcmp($a->getUser()->getDisplayName(), $b->getUser()->getDisplayName());
|
||||||
|
});
|
||||||
|
return $members;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getCurrencies(Community $community): array
|
||||||
|
{
|
||||||
|
$currencies = iterator_to_array($this->currencyRepository->getAllByCommunity($community));
|
||||||
|
usort($currencies, function($a, $b) {
|
||||||
|
return strnatcmp($a->getCode(), $b->getCode());
|
||||||
|
});
|
||||||
|
usort($currencies, function($a, $b) use ($community) {
|
||||||
|
return (int)($b->getId() === $community->getMainCurrencyId()) - (int)($a->getId() === $community->getMainCurrencyId());
|
||||||
|
});
|
||||||
|
return $currencies;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function checkPermission(
|
||||||
|
int $communityId,
|
||||||
|
bool $needToBeOwner,
|
||||||
|
?Community &$community,
|
||||||
|
?CommunityMember &$ownCommunityMember): bool
|
||||||
|
{
|
||||||
|
$community = $this->communityRepository->getById($communityId);
|
||||||
|
if ($community === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var User $user
|
||||||
|
*/
|
||||||
|
$user = \Container::$request->user();
|
||||||
|
|
||||||
|
$ownCommunityMember = $this->communityMemberRepository->getByCommunityAndUser($community, $user);
|
||||||
|
if ($ownCommunityMember === null || ($needToBeOwner && !$ownCommunityMember->getOwner())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,27 +1,45 @@
|
|||||||
<?php namespace RVR\Controller;
|
<?php namespace RVR\Controller;
|
||||||
|
|
||||||
use SokoWeb\Interfaces\Authorization\ISecured;
|
use RVR\PersistentData\Model\Community;
|
||||||
use SokoWeb\Interfaces\Request\IRequest;
|
use RVR\PersistentData\Model\User;
|
||||||
|
use RVR\Repository\CommunityMemberRepository;
|
||||||
|
use SokoWeb\Interfaces\Authentication\IAuthenticationRequired;
|
||||||
use SokoWeb\Interfaces\Response\IContent;
|
use SokoWeb\Interfaces\Response\IContent;
|
||||||
use SokoWeb\Response\HtmlContent;
|
use SokoWeb\Response\HtmlContent;
|
||||||
use SokoWeb\Response\JsonContent;
|
|
||||||
|
|
||||||
class HomeController implements ISecured
|
class HomeController implements IAuthenticationRequired
|
||||||
{
|
{
|
||||||
private IRequest $request;
|
private CommunityMemberRepository $communityMemberRepository;
|
||||||
|
|
||||||
public function __construct(IRequest $request)
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
$this->communityMemberRepository = new CommunityMemberRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function authorize(): bool
|
public function isAuthenticationRequired(): bool
|
||||||
{
|
{
|
||||||
return $this->request->user() !== null;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getIndex(): IContent
|
public function getHome(): IContent
|
||||||
{
|
{
|
||||||
return new HtmlContent('index');
|
/**
|
||||||
|
* @var User $user
|
||||||
|
*/
|
||||||
|
$user = \Container::$request->user();
|
||||||
|
|
||||||
|
$ownCommunityMembers = $this->communityMemberRepository->getAllByUser($user, true, ['community']);
|
||||||
|
$communities = [];
|
||||||
|
foreach ($ownCommunityMembers as $ownCommunityMember) {
|
||||||
|
$communities[] = $ownCommunityMember->getCommunity();
|
||||||
|
}
|
||||||
|
usort($communities, function($a, $b) {
|
||||||
|
return strnatcmp($a->getName(), $b->getName());
|
||||||
|
});
|
||||||
|
|
||||||
|
return new HtmlContent('home', [
|
||||||
|
'communities' => $communities,
|
||||||
|
'upcomingEvents' => []
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2,13 +2,11 @@
|
|||||||
|
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use SokoWeb\Http\Request;
|
use SokoWeb\Http\Request;
|
||||||
use SokoWeb\Interfaces\Request\IRequest;
|
|
||||||
use SokoWeb\Interfaces\Response\IContent;
|
use SokoWeb\Interfaces\Response\IContent;
|
||||||
use SokoWeb\Interfaces\Response\IRedirect;
|
use SokoWeb\Interfaces\Response\IRedirect;
|
||||||
use SokoWeb\Mailing\Mail;
|
use SokoWeb\Mailing\Mail;
|
||||||
use SokoWeb\OAuth\GoogleOAuth;
|
use SokoWeb\OAuth\GoogleOAuth;
|
||||||
use RVR\PersistentData\Model\UserPasswordResetter;
|
use RVR\PersistentData\Model\UserPasswordResetter;
|
||||||
use SokoWeb\PersistentData\PersistentDataManager;
|
|
||||||
use RVR\Repository\UserPasswordResetterRepository;
|
use RVR\Repository\UserPasswordResetterRepository;
|
||||||
use RVR\Repository\UserRepository;
|
use RVR\Repository\UserRepository;
|
||||||
use SokoWeb\Response\HtmlContent;
|
use SokoWeb\Response\HtmlContent;
|
||||||
@@ -19,35 +17,29 @@ use SokoWeb\Util\JwtParser;
|
|||||||
|
|
||||||
class LoginController
|
class LoginController
|
||||||
{
|
{
|
||||||
private IRequest $request;
|
|
||||||
|
|
||||||
private PersistentDataManager $pdm;
|
|
||||||
|
|
||||||
private UserRepository $userRepository;
|
private UserRepository $userRepository;
|
||||||
|
|
||||||
private UserPasswordResetterRepository $userPasswordResetterRepository;
|
private UserPasswordResetterRepository $userPasswordResetterRepository;
|
||||||
|
|
||||||
private string $redirectUrl;
|
private string $redirectUrl;
|
||||||
|
|
||||||
public function __construct(IRequest $request)
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
|
||||||
$this->pdm = new PersistentDataManager();
|
|
||||||
$this->userRepository = new UserRepository();
|
$this->userRepository = new UserRepository();
|
||||||
$this->userPasswordResetterRepository = new UserPasswordResetterRepository();
|
$this->userPasswordResetterRepository = new UserPasswordResetterRepository();
|
||||||
$this->redirectUrl = $this->request->session()->has('redirect_after_login') ?
|
$this->redirectUrl = \Container::$request->session()->has('redirect_after_login') ?
|
||||||
$this->request->session()->get('redirect_after_login') :
|
\Container::$request->session()->get('redirect_after_login') :
|
||||||
\Container::$routeCollection->getRoute('index')->generateLink();
|
\Container::$routeCollection->getRoute('home')->generateLink();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getLoginForm()
|
public function getLoginForm()
|
||||||
{
|
{
|
||||||
if ($this->request->user() !== null) {
|
if (\Container::$request->user() !== null) {
|
||||||
$this->deleteRedirectUrl();
|
$this->deleteRedirectUrl();
|
||||||
return new Redirect($this->redirectUrl, IRedirect::TEMPORARY);
|
return new Redirect($this->redirectUrl, IRedirect::TEMPORARY);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HtmlContent('login/login', ['redirectUrl' => '/' . $this->redirectUrl]);
|
return new HtmlContent('login/login', ['redirectUrl' => $this->redirectUrl]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getGoogleLoginRedirect(): IRedirect
|
public function getGoogleLoginRedirect(): IRedirect
|
||||||
@@ -55,13 +47,13 @@ class LoginController
|
|||||||
$state = bin2hex(random_bytes(16));
|
$state = bin2hex(random_bytes(16));
|
||||||
$nonce = bin2hex(random_bytes(16));
|
$nonce = bin2hex(random_bytes(16));
|
||||||
|
|
||||||
$this->request->session()->set('oauth_state', $state);
|
\Container::$request->session()->set('oauth_state', $state);
|
||||||
$this->request->session()->set('oauth_nonce', $nonce);
|
\Container::$request->session()->set('oauth_nonce', $nonce);
|
||||||
|
|
||||||
$oAuth = new GoogleOAuth(new Request());
|
$oAuth = new GoogleOAuth(new Request());
|
||||||
$url = $oAuth->getDialogUrl(
|
$url = $oAuth->getDialogUrl(
|
||||||
$state,
|
$state,
|
||||||
$this->request->getBase() . '/' . \Container::$routeCollection->getRoute('login-google-action')->generateLink(),
|
\Container::$request->getBase() . \Container::$routeCollection->getRoute('login.google-action')->generateLink(),
|
||||||
$nonce
|
$nonce
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -70,12 +62,12 @@ class LoginController
|
|||||||
|
|
||||||
public function getRequestPasswordResetForm()
|
public function getRequestPasswordResetForm()
|
||||||
{
|
{
|
||||||
if ($this->request->user() !== null) {
|
if (\Container::$request->user() !== null) {
|
||||||
$this->deleteRedirectUrl();
|
$this->deleteRedirectUrl();
|
||||||
return new Redirect($this->redirectUrl, IRedirect::TEMPORARY);
|
return new Redirect($this->redirectUrl, IRedirect::TEMPORARY);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new HtmlContent('login/password_reset_request', ['email' => $this->request->query('email')]);
|
return new HtmlContent('login/password_reset_request', ['email' => \Container::$request->query('email')]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRequestPasswordResetSuccess(): IContent
|
public function getRequestPasswordResetSuccess(): IContent
|
||||||
@@ -85,12 +77,12 @@ class LoginController
|
|||||||
|
|
||||||
public function getResetPasswordForm()
|
public function getResetPasswordForm()
|
||||||
{
|
{
|
||||||
if ($this->request->user() !== null) {
|
if (\Container::$request->user() !== null) {
|
||||||
$this->deleteRedirectUrl();
|
$this->deleteRedirectUrl();
|
||||||
return new Redirect($this->redirectUrl, IRedirect::TEMPORARY);
|
return new Redirect($this->redirectUrl, IRedirect::TEMPORARY);
|
||||||
}
|
}
|
||||||
|
|
||||||
$token = $this->request->query('token');
|
$token = \Container::$request->query('token');
|
||||||
$resetter = $this->userPasswordResetterRepository->getByToken($token);
|
$resetter = $this->userPasswordResetterRepository->getByToken($token);
|
||||||
|
|
||||||
if ($resetter === null || $resetter->getExpiresDate() < new DateTime()) {
|
if ($resetter === null || $resetter->getExpiresDate() < new DateTime()) {
|
||||||
@@ -99,27 +91,27 @@ class LoginController
|
|||||||
|
|
||||||
$user = $this->userRepository->getById($resetter->getUserId());
|
$user = $this->userRepository->getById($resetter->getUserId());
|
||||||
|
|
||||||
return new HtmlContent('login/reset_password', ['success' => true, 'token' => $token, 'email' => $user->getEmail(), 'redirectUrl' => '/' . $this->redirectUrl]);
|
return new HtmlContent('login/reset_password', ['success' => true, 'token' => $token, 'email' => $user->getEmail(), 'redirectUrl' => $this->redirectUrl]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function login(): IContent
|
public function login(): IContent
|
||||||
{
|
{
|
||||||
if ($this->request->user() !== null) {
|
if (\Container::$request->user() !== null) {
|
||||||
$this->deleteRedirectUrl();
|
$this->deleteRedirectUrl();
|
||||||
return new JsonContent(['success' => true]);
|
return new JsonContent(['success' => true]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->userRepository->getByEmailOrUsername($this->request->post('email'));
|
$user = $this->userRepository->getByEmailOrUsername(\Container::$request->post('email'));
|
||||||
if ($user === null || !$user->checkPassword($this->request->post('password'))) {
|
if ($user === null || !$user->checkPassword(\Container::$request->post('password'))) {
|
||||||
return new JsonContent([
|
return new JsonContent([
|
||||||
'error' => [
|
'error' => [
|
||||||
'errorText' => 'No user found with the given email address / username or the given password is wrong. You can <a href="/password/requestReset?email=' .
|
'errorText' => 'No user found with the given email address / username or the given password is wrong. You can <a href="' .
|
||||||
urlencode($this->request->post('email')) . '" title="Request password reset">request password reset</a>!'
|
\Container::$routeCollection->getRoute('password.requestReset')->generateLink(['email' => \Container::$request->post('email')]) . '" title="Request password reset">request password reset</a>!'
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->request->setUser($user);
|
\Container::$request->setUser($user);
|
||||||
|
|
||||||
$this->deleteRedirectUrl();
|
$this->deleteRedirectUrl();
|
||||||
return new JsonContent(['success' => true]);
|
return new JsonContent(['success' => true]);
|
||||||
@@ -127,21 +119,21 @@ class LoginController
|
|||||||
|
|
||||||
public function loginWithGoogle()
|
public function loginWithGoogle()
|
||||||
{
|
{
|
||||||
$defaultError = 'Authentication with Google failed. Please <a href="/login/google" title="Login with Google">try again</a>!';
|
$defaultError = 'Authentication with Google failed. Please <a href="' . \Container::$routeCollection->getRoute('login.google')->generateLink() . '" title="Login with Google">try again</a>!';
|
||||||
|
|
||||||
if ($this->request->user() !== null) {
|
if (\Container::$request->user() !== null) {
|
||||||
$this->deleteRedirectUrl();
|
$this->deleteRedirectUrl();
|
||||||
return new Redirect($this->redirectUrl, IRedirect::TEMPORARY);
|
return new Redirect($this->redirectUrl, IRedirect::TEMPORARY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->request->query('state') !== $this->request->session()->get('oauth_state')) {
|
if (\Container::$request->query('state') !== \Container::$request->session()->get('oauth_state')) {
|
||||||
return new HtmlContent('login/google_login_error', ['error' => $defaultError]);
|
return new HtmlContent('login/google_login_error', ['error' => $defaultError]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$oAuth = new GoogleOAuth(new Request());
|
$oAuth = new GoogleOAuth(new Request());
|
||||||
$tokenData = $oAuth->getToken(
|
$tokenData = $oAuth->getToken(
|
||||||
$this->request->query('code'),
|
\Container::$request->query('code'),
|
||||||
$this->request->getBase() . '/' . \Container::$routeCollection->getRoute('login-google-action')->generateLink()
|
\Container::$request->getBase() . \Container::$routeCollection->getRoute('login.google-action')->generateLink()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isset($tokenData['id_token'])) {
|
if (!isset($tokenData['id_token'])) {
|
||||||
@@ -151,7 +143,7 @@ class LoginController
|
|||||||
$jwtParser = new JwtParser($tokenData['id_token']);
|
$jwtParser = new JwtParser($tokenData['id_token']);
|
||||||
$idToken = $jwtParser->getPayload();
|
$idToken = $jwtParser->getPayload();
|
||||||
|
|
||||||
if ($idToken['nonce'] !== $this->request->session()->get('oauth_nonce')) {
|
if ($idToken['nonce'] !== \Container::$request->session()->get('oauth_nonce')) {
|
||||||
return new HtmlContent('login/google_login_error', ['error' => $defaultError]);
|
return new HtmlContent('login/google_login_error', ['error' => $defaultError]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,7 +156,7 @@ class LoginController
|
|||||||
return new HtmlContent('login/google_login_error', ['error' => 'No user found for this Google account.']);
|
return new HtmlContent('login/google_login_error', ['error' => 'No user found for this Google account.']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->request->setUser($user);
|
\Container::$request->setUser($user);
|
||||||
|
|
||||||
$this->deleteRedirectUrl();
|
$this->deleteRedirectUrl();
|
||||||
return new Redirect($this->redirectUrl, IRedirect::TEMPORARY);
|
return new Redirect($this->redirectUrl, IRedirect::TEMPORARY);
|
||||||
@@ -172,35 +164,35 @@ class LoginController
|
|||||||
|
|
||||||
public function logout(): IRedirect
|
public function logout(): IRedirect
|
||||||
{
|
{
|
||||||
$this->request->setUser(null);
|
\Container::$request->setUser(null);
|
||||||
|
|
||||||
return new Redirect(\Container::$routeCollection->getRoute('index')->generateLink(), IRedirect::TEMPORARY);
|
return new Redirect(\Container::$routeCollection->getRoute('home')->generateLink(), IRedirect::TEMPORARY);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function requestPasswordReset(): IContent
|
public function requestPasswordReset(): IContent
|
||||||
{
|
{
|
||||||
if ($this->request->user() !== null) {
|
if (\Container::$request->user() !== null) {
|
||||||
$this->deleteRedirectUrl();
|
$this->deleteRedirectUrl();
|
||||||
return new JsonContent([
|
return new JsonContent([
|
||||||
'redirect' => [
|
'redirect' => [
|
||||||
'target' => '/' . $this->redirectUrl
|
'target' => $this->redirectUrl
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!empty($_ENV['RECAPTCHA_SITEKEY'])) {
|
if (!empty($_ENV['RECAPTCHA_SITEKEY'])) {
|
||||||
if (!$this->request->post('g-recaptcha-response')) {
|
if (!\Container::$request->post('g-recaptcha-response')) {
|
||||||
return new JsonContent(['error' => ['errorText' => 'Please check "I\'m not a robot" in the reCAPTCHA box!']]);
|
return new JsonContent(['error' => ['errorText' => 'Please check "I\'m not a robot" in the reCAPTCHA box!']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$captchaValidator = new CaptchaValidator();
|
$captchaValidator = new CaptchaValidator();
|
||||||
$captchaResponse = $captchaValidator->validate($this->request->post('g-recaptcha-response'));
|
$captchaResponse = $captchaValidator->validate(\Container::$request->post('g-recaptcha-response'));
|
||||||
if (!$captchaResponse['success']) {
|
if (!$captchaResponse['success']) {
|
||||||
return new JsonContent(['error' => ['errorText' => 'reCAPTCHA challenge failed. Please try again!']]);
|
return new JsonContent(['error' => ['errorText' => 'reCAPTCHA challenge failed. Please try again!']]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$user = $this->userRepository->getByEmailOrUsername($this->request->post('email'));
|
$user = $this->userRepository->getByEmailOrUsername(\Container::$request->post('email'));
|
||||||
if ($user === null) {
|
if ($user === null) {
|
||||||
return new JsonContent([
|
return new JsonContent([
|
||||||
'error' => [
|
'error' => [
|
||||||
@@ -226,15 +218,11 @@ class LoginController
|
|||||||
$passwordResetter->setToken($token);
|
$passwordResetter->setToken($token);
|
||||||
$passwordResetter->setExpiresDate($expires);
|
$passwordResetter->setExpiresDate($expires);
|
||||||
|
|
||||||
\Container::$dbConnection->startTransaction();
|
|
||||||
|
|
||||||
if ($existingResetter !== null) {
|
if ($existingResetter !== null) {
|
||||||
$this->pdm->deleteFromDb($existingResetter);
|
\Container::$persistentDataManager->deleteFromDb($existingResetter);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->pdm->saveToDb($passwordResetter);
|
\Container::$persistentDataManager->saveToDb($passwordResetter);
|
||||||
|
|
||||||
\Container::$dbConnection->commit();
|
|
||||||
|
|
||||||
$this->sendPasswordResetEmail($user->getEmail(), $token, $expires);
|
$this->sendPasswordResetEmail($user->getEmail(), $token, $expires);
|
||||||
|
|
||||||
@@ -243,27 +231,27 @@ class LoginController
|
|||||||
|
|
||||||
public function resetPassword(): IContent
|
public function resetPassword(): IContent
|
||||||
{
|
{
|
||||||
if ($this->request->user() !== null) {
|
if (\Container::$request->user() !== null) {
|
||||||
$this->deleteRedirectUrl();
|
$this->deleteRedirectUrl();
|
||||||
return new JsonContent([
|
return new JsonContent([
|
||||||
'redirect' => [
|
'redirect' => [
|
||||||
'target' => '/' . $this->redirectUrl
|
'target' => $this->redirectUrl
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$token = $this->request->query('token');
|
$token = \Container::$request->query('token');
|
||||||
$resetter = $this->userPasswordResetterRepository->getByToken($token);
|
$resetter = $this->userPasswordResetterRepository->getByToken($token);
|
||||||
|
|
||||||
if ($resetter === null || $resetter->getExpiresDate() < new DateTime()) {
|
if ($resetter === null || $resetter->getExpiresDate() < new DateTime()) {
|
||||||
return new JsonContent([
|
return new JsonContent([
|
||||||
'redirect' => [
|
'redirect' => [
|
||||||
'target' => '/' . \Container::$routeCollection->getRoute('password-reset')->generateLink(['token' => $token])
|
'target' => \Container::$routeCollection->getRoute('password.reset')->generateLink(['token' => $token])
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen($this->request->post('password')) < 6) {
|
if (strlen(\Container::$request->post('password')) < 6) {
|
||||||
return new JsonContent([
|
return new JsonContent([
|
||||||
'error' => [
|
'error' => [
|
||||||
'errorText' => 'The given password is too short. Please choose a password that is at least 6 characters long!'
|
'errorText' => 'The given password is too short. Please choose a password that is at least 6 characters long!'
|
||||||
@@ -271,22 +259,18 @@ class LoginController
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->request->post('password') !== $this->request->post('password_confirm')) {
|
if (\Container::$request->post('password') !== \Container::$request->post('password_confirm')) {
|
||||||
return new JsonContent(['error' => ['errorText' => 'The given passwords do not match.']]);
|
return new JsonContent(['error' => ['errorText' => 'The given passwords do not match.']]);
|
||||||
}
|
}
|
||||||
|
|
||||||
\Container::$dbConnection->startTransaction();
|
\Container::$persistentDataManager->deleteFromDb($resetter);
|
||||||
|
|
||||||
$this->pdm->deleteFromDb($resetter);
|
|
||||||
|
|
||||||
$user = $this->userRepository->getById($resetter->getUserId());
|
$user = $this->userRepository->getById($resetter->getUserId());
|
||||||
$user->setPlainPassword($this->request->post('password'));
|
$user->setPlainPassword(\Container::$request->post('password'));
|
||||||
|
|
||||||
$this->pdm->saveToDb($user);
|
\Container::$persistentDataManager->saveToDb($user);
|
||||||
|
|
||||||
\Container::$dbConnection->commit();
|
\Container::$request->setUser($user);
|
||||||
|
|
||||||
$this->request->setUser($user);
|
|
||||||
|
|
||||||
$this->deleteRedirectUrl();
|
$this->deleteRedirectUrl();
|
||||||
return new JsonContent(['success' => true]);
|
return new JsonContent(['success' => true]);
|
||||||
@@ -297,10 +281,10 @@ class LoginController
|
|||||||
$mail = new Mail();
|
$mail = new Mail();
|
||||||
$mail->addRecipient($email);
|
$mail->addRecipient($email);
|
||||||
$mail->setSubject($_ENV['APP_NAME'] . ' - Password reset');
|
$mail->setSubject($_ENV['APP_NAME'] . ' - Password reset');
|
||||||
$mail->setBodyFromTemplate('password-reset', [
|
$mail->setBodyFromTemplate('password.reset', [
|
||||||
'EMAIL' => $email,
|
'EMAIL' => $email,
|
||||||
'RESET_LINK' => $this->request->getBase() . '/' .
|
'RESET_LINK' => \Container::$request->getBase() .
|
||||||
\Container::$routeCollection->getRoute('password-reset')->generateLink(['token' => $token]),
|
\Container::$routeCollection->getRoute('password.reset')->generateLink(['token' => $token]),
|
||||||
'EXPIRES' => $expires->format('Y-m-d H:i T')
|
'EXPIRES' => $expires->format('Y-m-d H:i T')
|
||||||
]);
|
]);
|
||||||
$mail->send();
|
$mail->send();
|
||||||
@@ -308,6 +292,6 @@ class LoginController
|
|||||||
|
|
||||||
private function deleteRedirectUrl(): void
|
private function deleteRedirectUrl(): void
|
||||||
{
|
{
|
||||||
$this->request->session()->delete('redirect_after_login');
|
\Container::$request->session()->delete('redirect_after_login');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -3,47 +3,58 @@
|
|||||||
use DateTime;
|
use DateTime;
|
||||||
use RVR\PersistentData\Model\OAuthToken;
|
use RVR\PersistentData\Model\OAuthToken;
|
||||||
use RVR\PersistentData\Model\User;
|
use RVR\PersistentData\Model\User;
|
||||||
use SokoWeb\Interfaces\Authorization\ISecured;
|
use RVR\Repository\OAuthClientRepository;
|
||||||
use SokoWeb\Interfaces\Request\IRequest;
|
use SokoWeb\Interfaces\Authentication\IAuthenticationRequired;
|
||||||
use SokoWeb\Interfaces\Response\IRedirect;
|
use SokoWeb\Interfaces\Response\IRedirect;
|
||||||
use SokoWeb\Response\Redirect;
|
use SokoWeb\Response\Redirect;
|
||||||
use SokoWeb\PersistentData\PersistentDataManager;
|
|
||||||
use SokoWeb\Response\HtmlContent;
|
use SokoWeb\Response\HtmlContent;
|
||||||
|
|
||||||
class OAuthAuthController implements ISecured
|
class OAuthAuthController implements IAuthenticationRequired
|
||||||
{
|
{
|
||||||
private IRequest $request;
|
private OAuthClientRepository $oAuthClientRepository;
|
||||||
|
|
||||||
private PersistentDataManager $pdm;
|
public function __construct()
|
||||||
|
|
||||||
public function __construct(IRequest $request)
|
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
$this->oAuthClientRepository = new OAuthClientRepository();
|
||||||
$this->pdm = new PersistentDataManager();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function authorize(): bool
|
public function isAuthenticationRequired(): bool
|
||||||
{
|
{
|
||||||
return $this->request->user() !== null;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function auth()
|
public function auth()
|
||||||
{
|
{
|
||||||
$redirectUri = $this->request->query('redirect_uri');
|
$redirectUri = \Container::$request->query('redirect_uri');
|
||||||
$scope = $this->request->query('scope') ? $this->request->query('scope'): '';
|
$clientId = \Container::$request->query('client_id');
|
||||||
$state = $this->request->query('state');
|
$scope = \Container::$request->query('scope') ? \Container::$request->query('scope'): '';
|
||||||
$nonce = $this->request->query('nonce') ? $this->request->query('nonce'): '';
|
$state = \Container::$request->query('state');
|
||||||
|
$nonce = \Container::$request->query('nonce') ? \Container::$request->query('nonce'): '';
|
||||||
|
|
||||||
if (!$redirectUri || !$state) {
|
if (!$clientId || !$redirectUri || !$state) {
|
||||||
return new HtmlContent('oauth/oauth_error', ['error' => 'An invalid request was made. Please start authentication again.']);
|
return new HtmlContent('oauth/oauth_error', ['error' => 'An invalid request was made. Please start authentication again.']);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->request->session()->delete('oauth_payload');
|
$client = $this->oAuthClientRepository->getByClientId($clientId);
|
||||||
|
if ($client === null) {
|
||||||
|
return new HtmlContent('oauth/oauth_error', ['error' => 'Client is not authorized.']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$redirectUriParsed = parse_url($redirectUri);
|
||||||
|
$redirectUriBase = $redirectUriParsed['scheme'] . '://' . $redirectUriParsed['host'] . $redirectUriParsed['path'];
|
||||||
|
$redirectUriQuery = [];
|
||||||
|
if (isset($redirectUriParsed['query'])) {
|
||||||
|
parse_str($redirectUriParsed['query'], $redirectUriQuery);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!in_array($redirectUriBase, $client->getRedirectUrisArray())) {
|
||||||
|
return new HtmlContent('oauth/oauth_error', ['error' => 'Redirect URI \'' . $redirectUriBase .'\' is not allowed for this client.']);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @var ?User $user
|
* @var ?User $user
|
||||||
*/
|
*/
|
||||||
$user = $this->request->user();
|
$user = \Container::$request->user();
|
||||||
$code = bin2hex(random_bytes(16));
|
$code = bin2hex(random_bytes(16));
|
||||||
$accessToken = bin2hex(random_bytes(16));
|
$accessToken = bin2hex(random_bytes(16));
|
||||||
|
|
||||||
@@ -55,15 +66,13 @@ class OAuthAuthController implements ISecured
|
|||||||
$token->setAccessToken($accessToken);
|
$token->setAccessToken($accessToken);
|
||||||
$token->setCreatedDate(new DateTime());
|
$token->setCreatedDate(new DateTime());
|
||||||
$token->setExpiresDate(new DateTime('+5 minutes'));
|
$token->setExpiresDate(new DateTime('+5 minutes'));
|
||||||
$this->pdm->saveToDb($token);
|
\Container::$persistentDataManager->saveToDb($token);
|
||||||
|
|
||||||
$redirectUri = $redirectUri;
|
$redirectUriQuery = array_merge($redirectUriQuery, [
|
||||||
$additionalUriParams = [
|
|
||||||
'state' => $state,
|
'state' => $state,
|
||||||
'code' => $code
|
'code' => $code
|
||||||
];
|
]);
|
||||||
$and = (strpos($redirectUri, '?') !== false) ? '&' : '?';
|
$finalRedirectUri = $redirectUriBase . '?' . http_build_query($redirectUriQuery);
|
||||||
$finalRedirectUri = $redirectUri . $and . http_build_query($additionalUriParams);
|
|
||||||
|
|
||||||
return new Redirect($finalRedirectUri, IRedirect::TEMPORARY);
|
return new Redirect($finalRedirectUri, IRedirect::TEMPORARY);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,29 +5,45 @@ use Firebase\JWT\JWT;
|
|||||||
use RVR\Repository\OAuthTokenRepository;
|
use RVR\Repository\OAuthTokenRepository;
|
||||||
use RVR\Repository\UserRepository;
|
use RVR\Repository\UserRepository;
|
||||||
use RVR\PersistentData\Model\User;
|
use RVR\PersistentData\Model\User;
|
||||||
use SokoWeb\Interfaces\Request\IRequest;
|
use RVR\Repository\OAuthClientRepository;
|
||||||
use SokoWeb\Interfaces\Response\IContent;
|
use SokoWeb\Interfaces\Response\IContent;
|
||||||
use SokoWeb\Response\JsonContent;
|
use SokoWeb\Response\JsonContent;
|
||||||
|
|
||||||
class OAuthController
|
class OAuthController
|
||||||
{
|
{
|
||||||
private IRequest $request;
|
private OAuthClientRepository $oAuthClientRepository;
|
||||||
|
|
||||||
private OAuthTokenRepository $oAuthTokenRepository;
|
private OAuthTokenRepository $oAuthTokenRepository;
|
||||||
|
|
||||||
private UserRepository $userRepository;
|
private UserRepository $userRepository;
|
||||||
|
|
||||||
public function __construct(IRequest $request)
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
$this->oAuthClientRepository = new OAuthClientRepository();
|
||||||
$this->oAuthTokenRepository = new OAuthTokenRepository();
|
$this->oAuthTokenRepository = new OAuthTokenRepository();
|
||||||
$this->userRepository = new UserRepository();
|
$this->userRepository = new UserRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getToken(): ?IContent
|
public function getToken(): ?IContent
|
||||||
{
|
{
|
||||||
$token = $this->oAuthTokenRepository->getByCode($this->request->post('code'));
|
$clientId = \Container::$request->post('client_id');
|
||||||
|
$clientSecret = \Container::$request->post('client_secret');
|
||||||
|
$code = \Container::$request->post('code');
|
||||||
|
|
||||||
|
if (!$clientId || !$clientSecret || !$code) {
|
||||||
|
return new JsonContent([
|
||||||
|
'error' => 'An invalid request was made.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$client = $this->oAuthClientRepository->getByClientId($clientId);
|
||||||
|
if ($client === null || $client->getClientSecret() !== $clientSecret) {
|
||||||
|
return new JsonContent([
|
||||||
|
'error' => 'Client is not authorized.'
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$token = $this->oAuthTokenRepository->getByCode($code);
|
||||||
if ($token === null || $token->getExpiresDate() < new DateTime()) {
|
if ($token === null || $token->getExpiresDate() < new DateTime()) {
|
||||||
return new JsonContent([
|
return new JsonContent([
|
||||||
'error' => 'The provided code is invalid.'
|
'error' => 'The provided code is invalid.'
|
||||||
@@ -39,6 +55,7 @@ class OAuthController
|
|||||||
'iat' => (int)$token->getCreatedDate()->getTimestamp(),
|
'iat' => (int)$token->getCreatedDate()->getTimestamp(),
|
||||||
'nbf' => (int)$token->getCreatedDate()->getTimestamp(),
|
'nbf' => (int)$token->getCreatedDate()->getTimestamp(),
|
||||||
'exp' => (int)$token->getExpiresDate()->getTimestamp(),
|
'exp' => (int)$token->getExpiresDate()->getTimestamp(),
|
||||||
|
'aud' => $clientId,
|
||||||
'nonce' => $token->getNonce()
|
'nonce' => $token->getNonce()
|
||||||
], $this->getUserInfoInternal(
|
], $this->getUserInfoInternal(
|
||||||
$this->userRepository->getById($token->getUserId()),
|
$this->userRepository->getById($token->getUserId()),
|
||||||
@@ -59,16 +76,14 @@ class OAuthController
|
|||||||
|
|
||||||
public function getUserInfo() : IContent
|
public function getUserInfo() : IContent
|
||||||
{
|
{
|
||||||
//TODO: headers should be set by soko-web
|
$authorization = \Container::$request->header('Authorization');
|
||||||
$headers = getallheaders();
|
if ($authorization === null) {
|
||||||
|
|
||||||
if (!isset($headers['Authorization'])) {
|
|
||||||
return new JsonContent([
|
return new JsonContent([
|
||||||
'error' => 'No Authorization header was sent.'
|
'error' => 'No Authorization header was sent.'
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$accessToken = substr($headers['Authorization'], strlen('Bearer '));
|
$accessToken = substr($authorization, strlen('Bearer '));
|
||||||
$token = $this->oAuthTokenRepository->getByAccessToken($accessToken);
|
$token = $this->oAuthTokenRepository->getByAccessToken($accessToken);
|
||||||
|
|
||||||
if ($token === null || $token->getExpiresDate() < new DateTime()) {
|
if ($token === null || $token->getExpiresDate() < new DateTime()) {
|
||||||
@@ -89,10 +104,10 @@ class OAuthController
|
|||||||
{
|
{
|
||||||
return new JsonContent([
|
return new JsonContent([
|
||||||
'issuer' => $_ENV['APP_URL'],
|
'issuer' => $_ENV['APP_URL'],
|
||||||
'authorization_endpoint' => $this->request->getBase() . '/oauth/auth',
|
'authorization_endpoint' => \Container::$request->getBase() . \Container::$routeCollection->getRoute('oauth.auth')->generateLink(),
|
||||||
'token_endpoint' => $this->request->getBase() . '/oauth/token',
|
'token_endpoint' => \Container::$request->getBase() . \Container::$routeCollection->getRoute('oauth.token')->generateLink(),
|
||||||
'userinfo_endpoint' => $this->request->getBase() . '/oauth/userinfo',
|
'userinfo_endpoint' => \Container::$request->getBase() . \Container::$routeCollection->getRoute('oauth.userinfo')->generateLink(),
|
||||||
'jwks_uri' => $this->request->getBase() . '/oauth/certs',
|
'jwks_uri' => \Container::$request->getBase() . \Container::$routeCollection->getRoute('oauth.certs')->generateLink(),
|
||||||
'response_types_supported' =>
|
'response_types_supported' =>
|
||||||
[
|
[
|
||||||
'code',
|
'code',
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
<?php namespace RVR\Controller;
|
||||||
|
|
||||||
|
use Container;
|
||||||
|
use DateTime;
|
||||||
|
use RVR\Finance\ExchangeRateCalculator;
|
||||||
|
use RVR\PersistentData\Model\Community;
|
||||||
|
use RVR\PersistentData\Model\CommunityMember;
|
||||||
|
use RVR\PersistentData\Model\Transaction;
|
||||||
|
use RVR\PersistentData\Model\User;
|
||||||
|
use RVR\Repository\CommunityMemberRepository;
|
||||||
|
use RVR\Repository\CommunityRepository;
|
||||||
|
use RVR\Repository\CurrencyRepository;
|
||||||
|
use RVR\Repository\TransactionRepository;
|
||||||
|
use SokoWeb\Interfaces\Authentication\IAuthenticationRequired;
|
||||||
|
use SokoWeb\Interfaces\Authorization\ISecured;
|
||||||
|
use SokoWeb\Interfaces\Response\IContent;
|
||||||
|
use SokoWeb\Response\HtmlContent;
|
||||||
|
use SokoWeb\Response\JsonContent;
|
||||||
|
|
||||||
|
class TransactionController implements IAuthenticationRequired, ISecured
|
||||||
|
{
|
||||||
|
private CommunityRepository $communityRepository;
|
||||||
|
|
||||||
|
private CommunityMemberRepository $communityMemberRepository;
|
||||||
|
|
||||||
|
private CurrencyRepository $currencyRepository;
|
||||||
|
|
||||||
|
private TransactionRepository $transactionRepository;
|
||||||
|
|
||||||
|
private Community $community;
|
||||||
|
|
||||||
|
private CommunityMember $ownCommunityMember;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->communityRepository = new CommunityRepository();
|
||||||
|
$this->communityMemberRepository = new CommunityMemberRepository();
|
||||||
|
$this->currencyRepository = new CurrencyRepository();
|
||||||
|
$this->transactionRepository = new TransactionRepository();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
$communityId = \Container::$request->query('communityId');
|
||||||
|
$this->community = $this->communityRepository->getById($communityId);
|
||||||
|
if ($this->community === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var User $user
|
||||||
|
*/
|
||||||
|
$user = \Container::$request->user();
|
||||||
|
$this->ownCommunityMember = $this->communityMemberRepository->getByCommunityAndUser($this->community, $user);
|
||||||
|
if ($this->ownCommunityMember === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTransactions(): IContent
|
||||||
|
{
|
||||||
|
Container::$persistentDataManager->loadRelationsFromDb($this->community, true, ['main_currency']);
|
||||||
|
$exchangeRateCalculator = new ExchangeRateCalculator($this->community->getMainCurrency());
|
||||||
|
|
||||||
|
$itemsPerPage = 50;
|
||||||
|
$numberOfTransactions = $this->transactionRepository->countAllByCommunity($this->community);
|
||||||
|
$pages = ceil($numberOfTransactions / $itemsPerPage);
|
||||||
|
$currentPage = Container::$request->query('page') ?: 0;
|
||||||
|
$transactions = $this->transactionRepository->getPagedByCommunity(
|
||||||
|
$this->community,
|
||||||
|
$currentPage * $itemsPerPage,
|
||||||
|
$itemsPerPage,
|
||||||
|
true,
|
||||||
|
['currency', 'payer_user', 'payee_user']
|
||||||
|
);
|
||||||
|
|
||||||
|
return new HtmlContent('communities/transactions', [
|
||||||
|
'community' => $this->community,
|
||||||
|
'exchangeRateCalculator' => $exchangeRateCalculator,
|
||||||
|
'pages' => $pages,
|
||||||
|
'currentPage' => $currentPage,
|
||||||
|
'numberOfTransactions' => $numberOfTransactions,
|
||||||
|
'transactions' => $transactions
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTransactionEdit(): ?IContent
|
||||||
|
{
|
||||||
|
$transactionId = Container::$request->query('transactionId');
|
||||||
|
if ($transactionId) {
|
||||||
|
$transaction = $this->transactionRepository->getById($transactionId);
|
||||||
|
if ($transaction === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$transaction = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new HtmlContent('communities/transaction_edit', [
|
||||||
|
'community' => $this->community,
|
||||||
|
'transaction' => $transaction,
|
||||||
|
'members' => $this->getMembers($this->community),
|
||||||
|
'currencies' => $this->getCurrencies($this->community)
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveTransaction(): ?IContent
|
||||||
|
{
|
||||||
|
$transactionId = Container::$request->query('transactionId');
|
||||||
|
if ($transactionId) {
|
||||||
|
$transaction = $this->transactionRepository->getById($transactionId);
|
||||||
|
} else {
|
||||||
|
$transaction = new Transaction();
|
||||||
|
$transaction->setCommunity($this->community);
|
||||||
|
}
|
||||||
|
|
||||||
|
$transaction->setCurrencyId(Container::$request->post('currency_id'));
|
||||||
|
$transaction->setPayerUserId(Container::$request->post('payer_user_id'));
|
||||||
|
$transaction->setPayeeUserId(Container::$request->post('payee_user_id') ?: null);
|
||||||
|
$transaction->setDescription(Container::$request->post('description'));
|
||||||
|
$transaction->setSum(Container::$request->post('sum'));
|
||||||
|
$transaction->setTimeDate(new DateTime(Container::$request->post('time')));
|
||||||
|
Container::$persistentDataManager->saveToDb($transaction);
|
||||||
|
|
||||||
|
return new JsonContent(['success' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteTransaction(): IContent
|
||||||
|
{
|
||||||
|
$transaction = $this->transactionRepository->getById(Container::$request->query('transactionId'));
|
||||||
|
Container::$persistentDataManager->deleteFromDb($transaction);
|
||||||
|
|
||||||
|
return new JsonContent(['success' => true]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMembers(Community $community): array
|
||||||
|
{
|
||||||
|
$members = iterator_to_array($this->communityMemberRepository->getAllByCommunity($community, true, ['user']));
|
||||||
|
usort($members, function($a, $b) {
|
||||||
|
return strnatcmp($a->getUser()->getDisplayName(), $b->getUser()->getDisplayName());
|
||||||
|
});
|
||||||
|
return $members;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getCurrencies(Community $community): array
|
||||||
|
{
|
||||||
|
$currencies = iterator_to_array($this->currencyRepository->getAllByCommunity($community));
|
||||||
|
usort($currencies, function($a, $b) {
|
||||||
|
return strnatcmp($a->getCode(), $b->getCode());
|
||||||
|
});
|
||||||
|
usort($currencies, function($a, $b) use ($community) {
|
||||||
|
return (int)($b->getId() === $community->getMainCurrencyId()) - (int)($a->getId() === $community->getMainCurrencyId());
|
||||||
|
});
|
||||||
|
return $currencies;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,12 +2,10 @@
|
|||||||
|
|
||||||
use DateTime;
|
use DateTime;
|
||||||
use SokoWeb\Http\Request;
|
use SokoWeb\Http\Request;
|
||||||
use SokoWeb\Interfaces\Authorization\ISecured;
|
use SokoWeb\Interfaces\Authentication\IAuthenticationRequired;
|
||||||
use SokoWeb\Interfaces\Request\IRequest;
|
|
||||||
use SokoWeb\Interfaces\Response\IContent;
|
use SokoWeb\Interfaces\Response\IContent;
|
||||||
use SokoWeb\Interfaces\Response\IRedirect;
|
use SokoWeb\Interfaces\Response\IRedirect;
|
||||||
use SokoWeb\OAuth\GoogleOAuth;
|
use SokoWeb\OAuth\GoogleOAuth;
|
||||||
use SokoWeb\PersistentData\PersistentDataManager;
|
|
||||||
use RVR\PersistentData\Model\User;
|
use RVR\PersistentData\Model\User;
|
||||||
use SokoWeb\Response\HtmlContent;
|
use SokoWeb\Response\HtmlContent;
|
||||||
use SokoWeb\Response\JsonContent;
|
use SokoWeb\Response\JsonContent;
|
||||||
@@ -15,24 +13,18 @@ use SokoWeb\Response\Redirect;
|
|||||||
use SokoWeb\Util\JwtParser;
|
use SokoWeb\Util\JwtParser;
|
||||||
use RVR\Repository\UserRepository;
|
use RVR\Repository\UserRepository;
|
||||||
|
|
||||||
class UserController implements ISecured
|
class UserController implements IAuthenticationRequired
|
||||||
{
|
{
|
||||||
private IRequest $request;
|
|
||||||
|
|
||||||
private PersistentDataManager $pdm;
|
|
||||||
|
|
||||||
private UserRepository $userRepository;
|
private UserRepository $userRepository;
|
||||||
|
|
||||||
public function __construct(IRequest $request)
|
public function __construct()
|
||||||
{
|
{
|
||||||
$this->request = $request;
|
|
||||||
$this->pdm = new PersistentDataManager();
|
|
||||||
$this->userRepository = new UserRepository();
|
$this->userRepository = new UserRepository();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function authorize(): bool
|
public function isAuthenticationRequired(): bool
|
||||||
{
|
{
|
||||||
return $this->request->user() !== null;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAccount(): IContent
|
public function getAccount(): IContent
|
||||||
@@ -40,7 +32,7 @@ class UserController implements ISecured
|
|||||||
/**
|
/**
|
||||||
* @var User $user
|
* @var User $user
|
||||||
*/
|
*/
|
||||||
$user = $this->request->user();
|
$user = \Container::$request->user();
|
||||||
|
|
||||||
return new HtmlContent('account/account', ['user' => $user->toArray()]);
|
return new HtmlContent('account/account', ['user' => $user->toArray()]);
|
||||||
}
|
}
|
||||||
@@ -50,19 +42,19 @@ class UserController implements ISecured
|
|||||||
/**
|
/**
|
||||||
* @var User $user
|
* @var User $user
|
||||||
*/
|
*/
|
||||||
$user = $this->request->user();
|
$user = \Container::$request->user();
|
||||||
|
|
||||||
$state = bin2hex(random_bytes(16));
|
$state = bin2hex(random_bytes(16));
|
||||||
$nonce = bin2hex(random_bytes(16));
|
$nonce = bin2hex(random_bytes(16));
|
||||||
|
|
||||||
$this->request->session()->set('oauth_state', $state);
|
\Container::$request->session()->set('oauth_state', $state);
|
||||||
$this->request->session()->set('oauth_nonce', $nonce);
|
\Container::$request->session()->set('oauth_nonce', $nonce);
|
||||||
|
|
||||||
$oAuth = new GoogleOAuth(new Request());
|
$oAuth = new GoogleOAuth(new Request());
|
||||||
|
|
||||||
$url = $oAuth->getDialogUrl(
|
$url = $oAuth->getDialogUrl(
|
||||||
$state,
|
$state,
|
||||||
$this->request->getBase() . '/' . \Container::$routeCollection->getRoute('account.googleAuthenticate-action')->generateLink(),
|
\Container::$request->getBase() . \Container::$routeCollection->getRoute('account.googleAuthenticate-action')->generateLink(),
|
||||||
$nonce,
|
$nonce,
|
||||||
$user->getEmail()
|
$user->getEmail()
|
||||||
);
|
);
|
||||||
@@ -75,16 +67,16 @@ class UserController implements ISecured
|
|||||||
/**
|
/**
|
||||||
* @var User $user
|
* @var User $user
|
||||||
*/
|
*/
|
||||||
$user = $this->request->user();
|
$user = \Container::$request->user();
|
||||||
|
|
||||||
if ($this->request->query('state') !== $this->request->session()->get('oauth_state')) {
|
if (\Container::$request->query('state') !== \Container::$request->session()->get('oauth_state')) {
|
||||||
return new HtmlContent('account/google_authenticate', ['success' => false]);
|
return new HtmlContent('account/google_authenticate', ['success' => false]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$oAuth = new GoogleOAuth(new Request());
|
$oAuth = new GoogleOAuth(new Request());
|
||||||
$tokenData = $oAuth->getToken(
|
$tokenData = $oAuth->getToken(
|
||||||
$this->request->query('code'),
|
\Container::$request->query('code'),
|
||||||
$this->request->getBase() . '/' . \Container::$routeCollection->getRoute('account.googleAuthenticate-action')->generateLink()
|
\Container::$request->getBase() . \Container::$routeCollection->getRoute('account.googleAuthenticate-action')->generateLink()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isset($tokenData['id_token'])) {
|
if (!isset($tokenData['id_token'])) {
|
||||||
@@ -94,7 +86,7 @@ class UserController implements ISecured
|
|||||||
$jwtParser = new JwtParser($tokenData['id_token']);
|
$jwtParser = new JwtParser($tokenData['id_token']);
|
||||||
$idToken = $jwtParser->getPayload();
|
$idToken = $jwtParser->getPayload();
|
||||||
|
|
||||||
if ($idToken['nonce'] !== $this->request->session()->get('oauth_nonce')) {
|
if ($idToken['nonce'] !== \Container::$request->session()->get('oauth_nonce')) {
|
||||||
return new HtmlContent('account/google_authenticate', ['success' => false]);
|
return new HtmlContent('account/google_authenticate', ['success' => false]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,7 +98,7 @@ class UserController implements ISecured
|
|||||||
}
|
}
|
||||||
|
|
||||||
$authenticatedWithGoogleUntil = new DateTime('+45 seconds');
|
$authenticatedWithGoogleUntil = new DateTime('+45 seconds');
|
||||||
$this->request->session()->set('authenticated_with_google_until', $authenticatedWithGoogleUntil);
|
\Container::$request->session()->set('authenticated_with_google_until', $authenticatedWithGoogleUntil);
|
||||||
|
|
||||||
return new HtmlContent('account/google_authenticate', [
|
return new HtmlContent('account/google_authenticate', [
|
||||||
'success' => true,
|
'success' => true,
|
||||||
@@ -119,18 +111,18 @@ class UserController implements ISecured
|
|||||||
/**
|
/**
|
||||||
* @var User $user
|
* @var User $user
|
||||||
*/
|
*/
|
||||||
$user = $this->request->user();
|
$user = \Container::$request->user();
|
||||||
|
|
||||||
if (!$this->confirmUserIdentity(
|
if (!$this->confirmUserIdentity(
|
||||||
$user,
|
$user,
|
||||||
$this->request->session()->get('authenticated_with_google_until'),
|
\Container::$request->session()->get('authenticated_with_google_until'),
|
||||||
$this->request->post('password'),
|
\Container::$request->post('password'),
|
||||||
$error
|
$error
|
||||||
)) {
|
)) {
|
||||||
return new JsonContent(['error' => ['errorText' => $error]]);
|
return new JsonContent(['error' => ['errorText' => $error]]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$newEmail = $this->request->post('email');
|
$newEmail = \Container::$request->post('email');
|
||||||
if ($newEmail !== $user->getEmail()) {
|
if ($newEmail !== $user->getEmail()) {
|
||||||
if (!filter_var($newEmail, FILTER_VALIDATE_EMAIL)) {
|
if (!filter_var($newEmail, FILTER_VALIDATE_EMAIL)) {
|
||||||
return new JsonContent(['error' => ['errorText' => 'Please provide a valid email address.']]);
|
return new JsonContent(['error' => ['errorText' => 'Please provide a valid email address.']]);
|
||||||
@@ -143,7 +135,7 @@ class UserController implements ISecured
|
|||||||
$user->setEmail($newEmail);
|
$user->setEmail($newEmail);
|
||||||
}
|
}
|
||||||
|
|
||||||
$newUsername = $this->request->post('username');
|
$newUsername = \Container::$request->post('username');
|
||||||
if ($newUsername !== $user->getUsername()) {
|
if ($newUsername !== $user->getUsername()) {
|
||||||
if (strlen($newUsername) > 0) {
|
if (strlen($newUsername) > 0) {
|
||||||
if (filter_var($newUsername, FILTER_VALIDATE_EMAIL)) {
|
if (filter_var($newUsername, FILTER_VALIDATE_EMAIL)) {
|
||||||
@@ -160,7 +152,7 @@ class UserController implements ISecured
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$newPassword = $this->request->post('password_new');
|
$newPassword = \Container::$request->post('password_new');
|
||||||
if (strlen($newPassword) > 0) {
|
if (strlen($newPassword) > 0) {
|
||||||
if (strlen($newPassword) < 6) {
|
if (strlen($newPassword) < 6) {
|
||||||
return new JsonContent([
|
return new JsonContent([
|
||||||
@@ -170,7 +162,7 @@ class UserController implements ISecured
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($newPassword !== $this->request->post('password_new_confirm')) {
|
if ($newPassword !== \Container::$request->post('password_new_confirm')) {
|
||||||
return new JsonContent([
|
return new JsonContent([
|
||||||
'error' => [
|
'error' => [
|
||||||
'errorText' => 'The given new passwords do not match.'
|
'errorText' => 'The given new passwords do not match.'
|
||||||
@@ -181,13 +173,13 @@ class UserController implements ISecured
|
|||||||
$user->setPlainPassword($newPassword);
|
$user->setPlainPassword($newPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
$user->setNickname($this->request->post('nickname'));
|
$user->setNickname(\Container::$request->post('nickname'));
|
||||||
$user->setPhone($this->request->post('phone'));
|
$user->setPhone(\Container::$request->post('phone'));
|
||||||
$user->setIdNumber($this->request->post('id_number'));
|
$user->setIdNumber(\Container::$request->post('id_number'));
|
||||||
|
|
||||||
$this->pdm->saveToDb($user);
|
\Container::$persistentDataManager->saveToDb($user);
|
||||||
|
|
||||||
$this->request->session()->delete('authenticated_with_google_until');
|
\Container::$request->session()->delete('authenticated_with_google_until');
|
||||||
|
|
||||||
return new JsonContent(['success' => true]);
|
return new JsonContent(['success' => true]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php namespace RVR\Controller;
|
||||||
|
|
||||||
|
use RVR\Repository\UserRepository;
|
||||||
|
use SokoWeb\Interfaces\Authentication\IAuthenticationRequired;
|
||||||
|
use SokoWeb\Interfaces\Response\IContent;
|
||||||
|
use SokoWeb\Response\JsonContent;
|
||||||
|
|
||||||
|
class UserSearchController implements IAuthenticationRequired
|
||||||
|
{
|
||||||
|
private UserRepository $userRepository;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->userRepository = new UserRepository();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAuthenticationRequired(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchUser(): IContent
|
||||||
|
{
|
||||||
|
$users = iterator_to_array($this->userRepository->searchByName(\Container::$request->query('q')));
|
||||||
|
usort($users, function($a, $b) {
|
||||||
|
return strnatcmp($a->getDisplayName(), $b->getDisplayName());
|
||||||
|
});
|
||||||
|
|
||||||
|
$results = [];
|
||||||
|
foreach ($users as $user) {
|
||||||
|
$results[] = ['value' => $user->getId(), 'label' => $user->getFullDisplayName()];
|
||||||
|
}
|
||||||
|
|
||||||
|
return new JsonContent([
|
||||||
|
'results' => $results
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?php namespace RVR\Database;
|
||||||
|
|
||||||
|
use SokoWeb\Database\AuditLoggerBase;
|
||||||
|
|
||||||
|
class AuditLogger extends AuditLoggerBase
|
||||||
|
{
|
||||||
|
protected function getModifierId()
|
||||||
|
{
|
||||||
|
if (\Container::$request === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$user = \Container::$request->user();
|
||||||
|
if ($user === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return $user->getUniqueId();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,90 @@
|
|||||||
|
<?php namespace RVR\Finance;
|
||||||
|
|
||||||
|
use RVR\PersistentData\Model\Community;
|
||||||
|
use RVR\Repository\CommunityMemberRepository;
|
||||||
|
use RVR\Repository\TransactionRepository;
|
||||||
|
|
||||||
|
class BalanceCalculator
|
||||||
|
{
|
||||||
|
private Community $community;
|
||||||
|
|
||||||
|
private TransactionRepository $transactionRepository;
|
||||||
|
|
||||||
|
private CommunityMemberRepository $communityMemberRepository;
|
||||||
|
|
||||||
|
private ExchangeRateCalculator $exchangeRateCalculator;
|
||||||
|
|
||||||
|
private array $members;
|
||||||
|
|
||||||
|
private array $payments;
|
||||||
|
|
||||||
|
public function __construct(Community $community)
|
||||||
|
{
|
||||||
|
$this->community = $community;
|
||||||
|
$this->transactionRepository = new TransactionRepository();
|
||||||
|
$this->communityMemberRepository = new CommunityMemberRepository();
|
||||||
|
$this->exchangeRateCalculator = new ExchangeRateCalculator($this->community->getMainCurrency());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function calculate(): array
|
||||||
|
{
|
||||||
|
$this->collectMembers();
|
||||||
|
$this->createPaymentsMatrix();
|
||||||
|
$this->sumTransactions();
|
||||||
|
return $this->calculateActualDebts();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function collectMembers(): void
|
||||||
|
{
|
||||||
|
$this->members = [];
|
||||||
|
foreach ($this->communityMemberRepository->getAllByCommunity($this->community, true, ['user']) as $member) {
|
||||||
|
$this->members[$member->getUserId()] = $member;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createPaymentsMatrix(): void
|
||||||
|
{
|
||||||
|
$this->payments = [];
|
||||||
|
foreach ($this->members as $payerUserId => $member) {
|
||||||
|
$this->payments[$payerUserId] = [];
|
||||||
|
foreach ($this->members as $payeeUserId => $member) {
|
||||||
|
$this->payments[$payerUserId][$payeeUserId] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function sumTransactions(): void
|
||||||
|
{
|
||||||
|
$membersCount = count($this->members);
|
||||||
|
$transactions = $this->transactionRepository->getAllByCommunity($this->community, true, ['currency']);
|
||||||
|
|
||||||
|
foreach ($transactions as $transaction) {
|
||||||
|
$sum = $this->exchangeRateCalculator->calculate($transaction->getSum(), $transaction->getCurrency(), $transaction->getTimeDate());
|
||||||
|
|
||||||
|
if ($transaction->getPayeeUserId()) {
|
||||||
|
$this->payments[$transaction->getPayerUserId()][$transaction->getPayeeUserId()] += $sum;
|
||||||
|
} else {
|
||||||
|
foreach ($this->members as $payeeUserId => $member) {
|
||||||
|
$this->payments[$transaction->getPayerUserId()][$payeeUserId] += $sum / $membersCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function calculateActualDebts(): array
|
||||||
|
{
|
||||||
|
$actualDebts = [];
|
||||||
|
|
||||||
|
foreach ($this->payments as $payerUserId => $paymentsOfPayer) {
|
||||||
|
foreach ($paymentsOfPayer as $payeeUserId => $sum) {
|
||||||
|
$actualDebt = $this->payments[$payeeUserId][$payerUserId] - $sum;
|
||||||
|
|
||||||
|
if (round($actualDebt, $this->community->getMainCurrency()->getRoundDigits()) > 0.0) {
|
||||||
|
$actualDebts[] = ['payer' => $this->members[$payerUserId], 'payee' => $this->members[$payeeUserId], 'amount' => $actualDebt];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $actualDebts;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
<?php namespace RVR\Finance;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use RVR\PersistentData\Model\Currency;
|
||||||
|
use RVR\PersistentData\Model\CurrencyExchangeRate;
|
||||||
|
use RVR\Repository\CurrencyExchangeRateRepository;
|
||||||
|
|
||||||
|
class ExchangeRateCalculator
|
||||||
|
{
|
||||||
|
private Currency $mainCurrency;
|
||||||
|
|
||||||
|
private CurrencyExchangeRateRepository $currencyExchangeRateRepository;
|
||||||
|
|
||||||
|
private array $exchangeRates = [];
|
||||||
|
|
||||||
|
public function __construct(Currency $mainCurrency)
|
||||||
|
{
|
||||||
|
$this->mainCurrency = $mainCurrency;
|
||||||
|
$this->currencyExchangeRateRepository = new CurrencyExchangeRateRepository();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function calculate(float $sumInCurrency, Currency $currency, DateTime $time): float
|
||||||
|
{
|
||||||
|
if ($currency->getId() === $this->mainCurrency->getId()) {
|
||||||
|
return $sumInCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentExchangeRate = $this->getCurrentExchangeRate($currency, $time);
|
||||||
|
if ($currentExchangeRate === null) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $sumInCurrency * $currentExchangeRate->getExchangeRate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getCurrentExchangeRate(Currency $currency, DateTime $time): ?CurrencyExchangeRate
|
||||||
|
{
|
||||||
|
if (!isset($this->exchangeRates[$currency->getId()])) {
|
||||||
|
$this->exchangeRates[$currency->getId()] = iterator_to_array($this->currencyExchangeRateRepository->getAllByCurrency($currency));
|
||||||
|
}
|
||||||
|
|
||||||
|
$currentExchangeRate = null;
|
||||||
|
foreach ($this->exchangeRates[$currency->getId()] as $exchangeRate) {
|
||||||
|
if ($exchangeRate->getValidFrom() > $time) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$currentExchangeRate = $exchangeRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $currentExchangeRate;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
<?php namespace RVR\PersistentData\Model;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use SokoWeb\PersistentData\Model\Model;
|
||||||
|
|
||||||
|
class Community extends Model
|
||||||
|
{
|
||||||
|
protected static string $table = 'communities';
|
||||||
|
|
||||||
|
protected static array $fields = ['name', 'currency', 'main_currency_id', 'created'];
|
||||||
|
|
||||||
|
protected static array $relations = ['main_currency' => Currency::class];
|
||||||
|
|
||||||
|
private string $name = '';
|
||||||
|
|
||||||
|
private string $currency = '';
|
||||||
|
|
||||||
|
private ?Currency $mainCurrency = null;
|
||||||
|
|
||||||
|
private ?int $mainCurrencyId = null;
|
||||||
|
|
||||||
|
private DateTime $created;
|
||||||
|
|
||||||
|
public function setName(string $name): void
|
||||||
|
{
|
||||||
|
$this->name = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCurrency(string $currency): void
|
||||||
|
{
|
||||||
|
$this->currency = $currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMainCurrency(Currency $mainCurrency): void
|
||||||
|
{
|
||||||
|
$this->mainCurrency = $mainCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setMainCurrencyId(int $mainCurrencyId): void
|
||||||
|
{
|
||||||
|
$this->mainCurrencyId = $mainCurrencyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCreatedDate(DateTime $created): void
|
||||||
|
{
|
||||||
|
$this->created = $created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCreated(string $created): void
|
||||||
|
{
|
||||||
|
$this->created = new DateTime($created);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getName(): string
|
||||||
|
{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCurrency(): string
|
||||||
|
{
|
||||||
|
return $this->currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMainCurrency(): ?Currency
|
||||||
|
{
|
||||||
|
return $this->mainCurrency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMainCurrencyId(): ?int
|
||||||
|
{
|
||||||
|
return $this->mainCurrencyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreatedDate(): DateTime
|
||||||
|
{
|
||||||
|
return $this->created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreated(): string
|
||||||
|
{
|
||||||
|
return $this->created->format('Y-m-d H:i:s');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
<?php namespace RVR\PersistentData\Model;
|
||||||
|
|
||||||
|
use SokoWeb\PersistentData\Model\Model;
|
||||||
|
|
||||||
|
class CommunityMember extends Model
|
||||||
|
{
|
||||||
|
protected static string $table = 'community_members';
|
||||||
|
|
||||||
|
protected static array $fields = ['community_id', 'user_id', 'owner'];
|
||||||
|
|
||||||
|
protected static array $relations = ['community' => Community::class, 'user' => User::class];
|
||||||
|
|
||||||
|
private ?Community $community = null;
|
||||||
|
|
||||||
|
private ?int $communityId = null;
|
||||||
|
|
||||||
|
private ?User $user = null;
|
||||||
|
|
||||||
|
private ?int $userId = null;
|
||||||
|
|
||||||
|
private bool $owner = false;
|
||||||
|
|
||||||
|
public function setCommunity(Community $community): void
|
||||||
|
{
|
||||||
|
$this->community = $community;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCommunityId(int $communityId): void
|
||||||
|
{
|
||||||
|
$this->communityId = $communityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUser(User $user): void
|
||||||
|
{
|
||||||
|
$this->user = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setUserId(int $userId): void
|
||||||
|
{
|
||||||
|
$this->userId = $userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setOwner(bool $owner): void
|
||||||
|
{
|
||||||
|
$this->owner = $owner;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommunity(): ?Community
|
||||||
|
{
|
||||||
|
return $this->community;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommunityId(): ?int
|
||||||
|
{
|
||||||
|
return $this->communityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUser(): ?User
|
||||||
|
{
|
||||||
|
return $this->user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUserId(): ?int
|
||||||
|
{
|
||||||
|
return $this->userId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOwner(): bool
|
||||||
|
{
|
||||||
|
return $this->owner;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
<?php namespace RVR\PersistentData\Model;
|
||||||
|
|
||||||
|
use SokoWeb\PersistentData\Model\Model;
|
||||||
|
|
||||||
|
class Currency extends Model
|
||||||
|
{
|
||||||
|
protected static string $table = 'currencies';
|
||||||
|
|
||||||
|
protected static array $fields = ['community_id', 'code', 'round_digits'];
|
||||||
|
|
||||||
|
protected static array $relations = ['community' => Community::class];
|
||||||
|
|
||||||
|
private ?Community $community = null;
|
||||||
|
|
||||||
|
private ?int $communityId = null;
|
||||||
|
|
||||||
|
private string $code = '';
|
||||||
|
|
||||||
|
private int $roundDigits = 0;
|
||||||
|
|
||||||
|
public function setCommunity(Community $community): void
|
||||||
|
{
|
||||||
|
$this->community = $community;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCommunityId(int $communityId): void
|
||||||
|
{
|
||||||
|
$this->communityId = $communityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCode(string $code): void
|
||||||
|
{
|
||||||
|
$this->code = $code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRoundDigits(int $roundDigits): void
|
||||||
|
{
|
||||||
|
$this->roundDigits = $roundDigits;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommunity(): ?Community
|
||||||
|
{
|
||||||
|
return $this->community;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommunityId(): ?int
|
||||||
|
{
|
||||||
|
return $this->communityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCode(): string
|
||||||
|
{
|
||||||
|
return $this->code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRoundDigits(): int
|
||||||
|
{
|
||||||
|
return $this->roundDigits;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
<?php namespace RVR\PersistentData\Model;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use SokoWeb\PersistentData\Model\Model;
|
||||||
|
|
||||||
|
class CurrencyExchangeRate extends Model
|
||||||
|
{
|
||||||
|
protected static string $table = 'currency_exchange_rates';
|
||||||
|
|
||||||
|
protected static array $fields = ['currency_id', 'exchange_rate', 'valid_from'];
|
||||||
|
|
||||||
|
protected static array $relations = ['currency' => Currency::class];
|
||||||
|
|
||||||
|
private ?Currency $currency = null;
|
||||||
|
|
||||||
|
private ?int $currencyId = null;
|
||||||
|
|
||||||
|
private float $exchangeRate = 0.0;
|
||||||
|
|
||||||
|
private DateTime $validFrom;
|
||||||
|
|
||||||
|
public function setCurrency(Currency $currency): void
|
||||||
|
{
|
||||||
|
$this->currency = $currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCurrencyId(int $currencyId): void
|
||||||
|
{
|
||||||
|
$this->currencyId = $currencyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setExchangeRate(float $exchangeRate): void
|
||||||
|
{
|
||||||
|
$this->exchangeRate = $exchangeRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setValidFromDate(DateTime $validFrom): void
|
||||||
|
{
|
||||||
|
$this->validFrom = $validFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setValidFrom(string $validFrom): void
|
||||||
|
{
|
||||||
|
$this->validFrom = new DateTime($validFrom);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCurrency(): ?Currency
|
||||||
|
{
|
||||||
|
return $this->currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCurrencyId(): ?int
|
||||||
|
{
|
||||||
|
return $this->currencyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getExchangeRate(): float
|
||||||
|
{
|
||||||
|
return $this->exchangeRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValidFromDate(): DateTime
|
||||||
|
{
|
||||||
|
return $this->validFrom;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getValidFrom(): string
|
||||||
|
{
|
||||||
|
return $this->validFrom->format('Y-m-d H:i:s');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
<?php namespace RVR\PersistentData\Model;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use SokoWeb\PersistentData\Model\Model;
|
||||||
|
|
||||||
|
class OAuthClient extends Model
|
||||||
|
{
|
||||||
|
protected static string $table = 'oauth_clients';
|
||||||
|
|
||||||
|
protected static array $fields = ['client_id', 'client_secret', 'redirect_uris', 'preapproved', 'created'];
|
||||||
|
|
||||||
|
private string $clientId = '';
|
||||||
|
|
||||||
|
private string $clientSecret = '';
|
||||||
|
|
||||||
|
private array $redirectUris = [];
|
||||||
|
|
||||||
|
private bool $preapproved = false;
|
||||||
|
|
||||||
|
private DateTime $created;
|
||||||
|
|
||||||
|
public function setClientId(string $clientId): void
|
||||||
|
{
|
||||||
|
$this->clientId = $clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setClientSecret(string $clientSecret): void
|
||||||
|
{
|
||||||
|
$this->clientSecret = $clientSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRedirectUrisArray(array $redirectUris): void
|
||||||
|
{
|
||||||
|
$this->redirectUris = $redirectUris;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setRedirectUris(string $redirectUris): void
|
||||||
|
{
|
||||||
|
$this->redirectUris = json_decode($redirectUris, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPreapproved(bool $preapproved): void
|
||||||
|
{
|
||||||
|
$this->preapproved = $preapproved;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCreatedDate(DateTime $created): void
|
||||||
|
{
|
||||||
|
$this->created = $created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCreated(string $created): void
|
||||||
|
{
|
||||||
|
$this->created = new DateTime($created);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClientId(): string
|
||||||
|
{
|
||||||
|
return $this->clientId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getClientSecret(): string
|
||||||
|
{
|
||||||
|
return $this->clientSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRedirectUrisArray(): array
|
||||||
|
{
|
||||||
|
return $this->redirectUris;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRedirectUris(): string
|
||||||
|
{
|
||||||
|
return json_encode($this->redirectUris);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPreapproved(): bool
|
||||||
|
{
|
||||||
|
return $this->preapproved;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreatedDate(): DateTime
|
||||||
|
{
|
||||||
|
return $this->created;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCreated(): string
|
||||||
|
{
|
||||||
|
return $this->created->format('Y-m-d H:i:s');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
<?php namespace RVR\PersistentData\Model;
|
||||||
|
|
||||||
|
use DateTime;
|
||||||
|
use SokoWeb\PersistentData\Model\Model;
|
||||||
|
|
||||||
|
class Transaction extends Model
|
||||||
|
{
|
||||||
|
protected static string $table = 'transactions';
|
||||||
|
|
||||||
|
protected static array $fields = ['community_id', 'currency_id', 'payer_user_id', 'payee_user_id', 'description', 'sum', 'time'];
|
||||||
|
|
||||||
|
protected static array $relations = [
|
||||||
|
'community' => Community::class,
|
||||||
|
'currency' => Currency::class,
|
||||||
|
'payer_user' => User::class,
|
||||||
|
'payee_user' => User::class
|
||||||
|
];
|
||||||
|
|
||||||
|
private ?Community $community = null;
|
||||||
|
|
||||||
|
private int $communityId;
|
||||||
|
|
||||||
|
private ?Currency $currency = null;
|
||||||
|
|
||||||
|
private int $currencyId;
|
||||||
|
|
||||||
|
private ?User $payerUser = null;
|
||||||
|
|
||||||
|
private int $payerUserId;
|
||||||
|
|
||||||
|
private ?User $payeeUser = null;
|
||||||
|
|
||||||
|
private ?int $payeeUserId = null;
|
||||||
|
|
||||||
|
private string $description = '';
|
||||||
|
|
||||||
|
private float $sum = 0.0;
|
||||||
|
|
||||||
|
private DateTime $time;
|
||||||
|
|
||||||
|
public function setCommunity(Community $community): void
|
||||||
|
{
|
||||||
|
$this->community = $community;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCommunityId(int $communityId): void
|
||||||
|
{
|
||||||
|
$this->communityId = $communityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCurrency(Currency $currency): void
|
||||||
|
{
|
||||||
|
$this->currency = $currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setCurrencyId(int $currencyId): void
|
||||||
|
{
|
||||||
|
$this->currencyId = $currencyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPayerUser(User $payerUser): void
|
||||||
|
{
|
||||||
|
$this->payerUser = $payerUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPayerUserId(int $payerUserId): void
|
||||||
|
{
|
||||||
|
$this->payerUserId = $payerUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPayeeUser(?User $payeeUser): void
|
||||||
|
{
|
||||||
|
$this->payeeUser = $payeeUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setPayeeUserId(?int $payeeUserId): void
|
||||||
|
{
|
||||||
|
$this->payeeUserId = $payeeUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setDescription(string $description): void
|
||||||
|
{
|
||||||
|
$this->description = $description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setSum(float $sum): void
|
||||||
|
{
|
||||||
|
$this->sum = $sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTimeDate(DateTime $time): void
|
||||||
|
{
|
||||||
|
$this->time = $time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setTime(string $time): void
|
||||||
|
{
|
||||||
|
$this->time = new DateTime($time);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommunity(): ?Community
|
||||||
|
{
|
||||||
|
return $this->community;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCommunityId(): int
|
||||||
|
{
|
||||||
|
return $this->communityId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCurrency(): ?Currency
|
||||||
|
{
|
||||||
|
return $this->currency;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCurrencyId(): int
|
||||||
|
{
|
||||||
|
return $this->currencyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPayerUser(): ?User
|
||||||
|
{
|
||||||
|
return $this->payerUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPayerUserId(): int
|
||||||
|
{
|
||||||
|
return $this->payerUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPayeeUser(): ?User
|
||||||
|
{
|
||||||
|
return $this->payeeUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPayeeUserId(): ?int
|
||||||
|
{
|
||||||
|
return $this->payeeUserId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDescription(): string
|
||||||
|
{
|
||||||
|
return $this->description;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSum(): float
|
||||||
|
{
|
||||||
|
return $this->sum;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTimeDate(): DateTime
|
||||||
|
{
|
||||||
|
return $this->time;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTime(): string
|
||||||
|
{
|
||||||
|
return $this->time->format('Y-m-d H:i:s');
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -171,6 +171,11 @@ class User extends Model implements IUser
|
|||||||
return $this->nickname ?: $this->fullName;
|
return $this->nickname ?: $this->fullName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getFullDisplayName(): string
|
||||||
|
{
|
||||||
|
return $this->nickname ? $this->nickname . ' (' . $this->fullName . ')' : $this->fullName;
|
||||||
|
}
|
||||||
|
|
||||||
public function checkPassword(string $password): bool
|
public function checkPassword(string $password): bool
|
||||||
{
|
{
|
||||||
if ($this->password === null) {
|
if ($this->password === null) {
|
||||||
|
|||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<?php namespace RVR\Repository;
|
||||||
|
|
||||||
|
use Generator;
|
||||||
|
use RVR\PersistentData\Model\Community;
|
||||||
|
use RVR\PersistentData\Model\CommunityMember;
|
||||||
|
use RVR\PersistentData\Model\User;
|
||||||
|
use SokoWeb\Database\Query\Select;
|
||||||
|
|
||||||
|
class CommunityMemberRepository
|
||||||
|
{
|
||||||
|
public function getById(int $id): ?CommunityMember
|
||||||
|
{
|
||||||
|
return \Container::$persistentDataManager->selectFromDbById($id, CommunityMember::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByCommunity(Community $community, bool $useRelations = false, array $withRelations = []): Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('community_id', '=', $community->getId());
|
||||||
|
|
||||||
|
yield from \Container::$persistentDataManager->selectMultipleFromDb($select, CommunityMember::class, $useRelations, $withRelations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByUser(User $user, bool $useRelations = false, array $withRelations = []): Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('user_id', '=', $user->getId());
|
||||||
|
|
||||||
|
yield from \Container::$persistentDataManager->selectMultipleFromDb($select, CommunityMember::class, $useRelations, $withRelations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getByCommunityAndUser(Community $community, User $user) : ?CommunityMember
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('community_id', '=', $community->getId());
|
||||||
|
$select->where('user_id', '=', $user->getId());
|
||||||
|
|
||||||
|
return \Container::$persistentDataManager->selectFromDb($select, CommunityMember::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?php namespace RVR\Repository;
|
||||||
|
|
||||||
|
use RVR\PersistentData\Model\Community;
|
||||||
|
|
||||||
|
class CommunityRepository
|
||||||
|
{
|
||||||
|
public function getById(int $id): ?Community
|
||||||
|
{
|
||||||
|
return \Container::$persistentDataManager->selectFromDbById($id, Community::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<?php namespace RVR\Repository;
|
||||||
|
|
||||||
|
use Generator;
|
||||||
|
use Container;
|
||||||
|
use RVR\PersistentData\Model\Currency;
|
||||||
|
use RVR\PersistentData\Model\CurrencyExchangeRate;
|
||||||
|
use SokoWeb\Database\Query\Select;
|
||||||
|
|
||||||
|
class CurrencyExchangeRateRepository
|
||||||
|
{
|
||||||
|
public function getById(int $id): ?CurrencyExchangeRate
|
||||||
|
{
|
||||||
|
return \Container::$persistentDataManager->selectFromDbById($id, CurrencyExchangeRate::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByCurrency(Currency $currency): Generator
|
||||||
|
{
|
||||||
|
$select = new Select(Container::$dbConnection);
|
||||||
|
$select->where('currency_id', '=', $currency->getId());
|
||||||
|
$select->orderBy('valid_from');
|
||||||
|
|
||||||
|
yield from Container::$persistentDataManager->selectMultipleFromDb($select, CurrencyExchangeRate::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php namespace RVR\Repository;
|
||||||
|
|
||||||
|
use Generator;
|
||||||
|
use Container;
|
||||||
|
use RVR\PersistentData\Model\Community;
|
||||||
|
use RVR\PersistentData\Model\Currency;
|
||||||
|
use SokoWeb\Database\Query\Select;
|
||||||
|
|
||||||
|
class CurrencyRepository
|
||||||
|
{
|
||||||
|
public function getById(int $id): ?Currency
|
||||||
|
{
|
||||||
|
return \Container::$persistentDataManager->selectFromDbById($id, Currency::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByCommunity(Community $community, bool $useRelations = false): Generator
|
||||||
|
{
|
||||||
|
$select = new Select(Container::$dbConnection);
|
||||||
|
$select->where('community_id', '=', $community->getId());
|
||||||
|
|
||||||
|
yield from Container::$persistentDataManager->selectMultipleFromDb($select, Currency::class, $useRelations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getByCommunityAndCurrencyCode(Community $community, string $code): ?Currency
|
||||||
|
{
|
||||||
|
$select = new Select(Container::$dbConnection);
|
||||||
|
$select->where('community_id', '=', $community->getId());
|
||||||
|
$select->where('code', '=', $code);
|
||||||
|
|
||||||
|
return Container::$persistentDataManager->selectFromDb($select, Currency::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
<?php namespace RVR\Repository;
|
||||||
|
|
||||||
|
use SokoWeb\Database\Query\Select;
|
||||||
|
use RVR\PersistentData\Model\OAuthClient;
|
||||||
|
|
||||||
|
class OAuthClientRepository
|
||||||
|
{
|
||||||
|
public function getById(int $id): ?OAuthClient
|
||||||
|
{
|
||||||
|
return \Container::$persistentDataManager->selectFromDbById($id, OAuthClient::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getByClientId(string $clientId): ?OAuthClient
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('client_id', '=', $clientId);
|
||||||
|
|
||||||
|
return \Container::$persistentDataManager->selectFromDb($select, OAuthClient::class);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,20 +4,12 @@ use DateTime;
|
|||||||
use Generator;
|
use Generator;
|
||||||
use SokoWeb\Database\Query\Select;
|
use SokoWeb\Database\Query\Select;
|
||||||
use RVR\PersistentData\Model\OAuthToken;
|
use RVR\PersistentData\Model\OAuthToken;
|
||||||
use SokoWeb\PersistentData\PersistentDataManager;
|
|
||||||
|
|
||||||
class OAuthTokenRepository
|
class OAuthTokenRepository
|
||||||
{
|
{
|
||||||
private PersistentDataManager $pdm;
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->pdm = new PersistentDataManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getById(int $id): ?OAuthToken
|
public function getById(int $id): ?OAuthToken
|
||||||
{
|
{
|
||||||
return $this->pdm->selectFromDbById($id, OAuthToken::class);
|
return \Container::$persistentDataManager->selectFromDbById($id, OAuthToken::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getByCode(string $code): ?OAuthToken
|
public function getByCode(string $code): ?OAuthToken
|
||||||
@@ -25,7 +17,7 @@ class OAuthTokenRepository
|
|||||||
$select = new Select(\Container::$dbConnection);
|
$select = new Select(\Container::$dbConnection);
|
||||||
$select->where('code', '=', $code);
|
$select->where('code', '=', $code);
|
||||||
|
|
||||||
return $this->pdm->selectFromDb($select, OAuthToken::class);
|
return \Container::$persistentDataManager->selectFromDb($select, OAuthToken::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getByAccessToken(string $accessToken): ?OAuthToken
|
public function getByAccessToken(string $accessToken): ?OAuthToken
|
||||||
@@ -33,7 +25,7 @@ class OAuthTokenRepository
|
|||||||
$select = new Select(\Container::$dbConnection);
|
$select = new Select(\Container::$dbConnection);
|
||||||
$select->where('access_token', '=', $accessToken);
|
$select->where('access_token', '=', $accessToken);
|
||||||
|
|
||||||
return $this->pdm->selectFromDb($select, OAuthToken::class);
|
return \Container::$persistentDataManager->selectFromDb($select, OAuthToken::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAllExpired(): Generator
|
public function getAllExpired(): Generator
|
||||||
@@ -41,6 +33,6 @@ class OAuthTokenRepository
|
|||||||
$select = new Select(\Container::$dbConnection);
|
$select = new Select(\Container::$dbConnection);
|
||||||
$select->where('expires', '<', (new DateTime())->format('Y-m-d H:i:s'));
|
$select->where('expires', '<', (new DateTime())->format('Y-m-d H:i:s'));
|
||||||
|
|
||||||
yield from $this->pdm->selectMultipleFromDb($select, OAuthToken::class);
|
yield from \Container::$persistentDataManager->selectMultipleFromDb($select, OAuthToken::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
<?php namespace RVR\Repository;
|
||||||
|
|
||||||
|
use Container;
|
||||||
|
use Generator;
|
||||||
|
use RVR\PersistentData\Model\Community;
|
||||||
|
use RVR\PersistentData\Model\Currency;
|
||||||
|
use RVR\PersistentData\Model\Transaction;
|
||||||
|
use RVR\PersistentData\Model\User;
|
||||||
|
use SokoWeb\Database\Query\Select;
|
||||||
|
|
||||||
|
class TransactionRepository
|
||||||
|
{
|
||||||
|
public function getById(int $id): ?Transaction
|
||||||
|
{
|
||||||
|
return Container::$persistentDataManager->selectFromDbById($id, Transaction::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAllByCommunity(Community $community, bool $useRelations = false, array $withRelations = []): Generator
|
||||||
|
{
|
||||||
|
$select = $this->selectAllByCommunity($community);
|
||||||
|
|
||||||
|
yield from Container::$persistentDataManager->selectMultipleFromDb($select, Transaction::class, $useRelations, $withRelations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function countAllByCommunity(Community $community): int
|
||||||
|
{
|
||||||
|
return $this->selectAllByCommunity($community)->count();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAnyForUser(User $user): bool
|
||||||
|
{
|
||||||
|
$select = new Select(Container::$dbConnection, Transaction::getTable());
|
||||||
|
$select->where('payer_user_id', '=', $user->getId());
|
||||||
|
$select->orWhere('payee_user_id', '=', $user->getId());
|
||||||
|
$select->orWhere('payee_user_id', '=', null);
|
||||||
|
|
||||||
|
return $select->count() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAnyCommon(): bool
|
||||||
|
{
|
||||||
|
$select = new Select(Container::$dbConnection, Transaction::getTable());
|
||||||
|
$select->where('payee_user_id', '=', null);
|
||||||
|
|
||||||
|
return $select->count() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isAnyForCurrency(Currency $currency): bool
|
||||||
|
{
|
||||||
|
$select = new Select(Container::$dbConnection, Transaction::getTable());
|
||||||
|
$select->where('currency_id', '=', $currency->getId());
|
||||||
|
|
||||||
|
return $select->count() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPagedByCommunity(Community $community, int $start, int $limit, bool $useRelations = false, array $withRelations = []): Generator
|
||||||
|
{
|
||||||
|
$select = new Select(Container::$dbConnection);
|
||||||
|
$select->where('community_id', '=', $community->getId());
|
||||||
|
$select->orderBy('time', 'DESC');
|
||||||
|
$select->limit($limit, $start);
|
||||||
|
|
||||||
|
yield from Container::$persistentDataManager->selectMultipleFromDb($select, Transaction::class, $useRelations, $withRelations);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function selectAllByCommunity(Community $community)
|
||||||
|
{
|
||||||
|
$select = new Select(Container::$dbConnection, Transaction::getTable());
|
||||||
|
$select->where('community_id', '=', $community->getId());
|
||||||
|
return $select;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,20 +5,12 @@ use Generator;
|
|||||||
use SokoWeb\Database\Query\Select;
|
use SokoWeb\Database\Query\Select;
|
||||||
use RVR\PersistentData\Model\User;
|
use RVR\PersistentData\Model\User;
|
||||||
use RVR\PersistentData\Model\UserPasswordResetter;
|
use RVR\PersistentData\Model\UserPasswordResetter;
|
||||||
use SokoWeb\PersistentData\PersistentDataManager;
|
|
||||||
|
|
||||||
class UserPasswordResetterRepository
|
class UserPasswordResetterRepository
|
||||||
{
|
{
|
||||||
private PersistentDataManager $pdm;
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->pdm = new PersistentDataManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getById(int $userConfirmationId): ?UserPasswordResetter
|
public function getById(int $userConfirmationId): ?UserPasswordResetter
|
||||||
{
|
{
|
||||||
return $this->pdm->selectFromDbById($userConfirmationId, UserPasswordResetter::class);
|
return \Container::$persistentDataManager->selectFromDbById($userConfirmationId, UserPasswordResetter::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getByToken(string $token): ?UserPasswordResetter
|
public function getByToken(string $token): ?UserPasswordResetter
|
||||||
@@ -26,7 +18,7 @@ class UserPasswordResetterRepository
|
|||||||
$select = new Select(\Container::$dbConnection);
|
$select = new Select(\Container::$dbConnection);
|
||||||
$select->where('token', '=', $token);
|
$select->where('token', '=', $token);
|
||||||
|
|
||||||
return $this->pdm->selectFromDb($select, UserPasswordResetter::class);
|
return \Container::$persistentDataManager->selectFromDb($select, UserPasswordResetter::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getByUser(User $user): ?UserPasswordResetter
|
public function getByUser(User $user): ?UserPasswordResetter
|
||||||
@@ -34,7 +26,7 @@ class UserPasswordResetterRepository
|
|||||||
$select = new Select(\Container::$dbConnection);
|
$select = new Select(\Container::$dbConnection);
|
||||||
$select->where('user_id', '=', $user->getId());
|
$select->where('user_id', '=', $user->getId());
|
||||||
|
|
||||||
return $this->pdm->selectFromDb($select, UserPasswordResetter::class);
|
return \Container::$persistentDataManager->selectFromDb($select, UserPasswordResetter::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getAllExpired(): Generator
|
public function getAllExpired(): Generator
|
||||||
@@ -42,6 +34,6 @@ class UserPasswordResetterRepository
|
|||||||
$select = new Select(\Container::$dbConnection);
|
$select = new Select(\Container::$dbConnection);
|
||||||
$select->where('expires', '<', (new DateTime())->format('Y-m-d H:i:s'));
|
$select->where('expires', '<', (new DateTime())->format('Y-m-d H:i:s'));
|
||||||
|
|
||||||
yield from $this->pdm->selectMultipleFromDb($select, UserPasswordResetter::class);
|
yield from \Container::$persistentDataManager->selectMultipleFromDb($select, UserPasswordResetter::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,22 +1,15 @@
|
|||||||
<?php namespace RVR\Repository;
|
<?php namespace RVR\Repository;
|
||||||
|
|
||||||
|
use Generator;
|
||||||
use SokoWeb\Interfaces\Repository\IUserRepository;
|
use SokoWeb\Interfaces\Repository\IUserRepository;
|
||||||
use SokoWeb\Database\Query\Select;
|
use SokoWeb\Database\Query\Select;
|
||||||
use RVR\PersistentData\Model\User;
|
use RVR\PersistentData\Model\User;
|
||||||
use SokoWeb\PersistentData\PersistentDataManager;
|
|
||||||
|
|
||||||
class UserRepository implements IUserRepository
|
class UserRepository implements IUserRepository
|
||||||
{
|
{
|
||||||
private PersistentDataManager $pdm;
|
|
||||||
|
|
||||||
public function __construct()
|
|
||||||
{
|
|
||||||
$this->pdm = new PersistentDataManager();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getById(int $userId): ?User
|
public function getById(int $userId): ?User
|
||||||
{
|
{
|
||||||
return $this->pdm->selectFromDbById($userId, User::class);
|
return \Container::$persistentDataManager->selectFromDbById($userId, User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getByEmail(string $email): ?User
|
public function getByEmail(string $email): ?User
|
||||||
@@ -24,7 +17,7 @@ class UserRepository implements IUserRepository
|
|||||||
$select = new Select(\Container::$dbConnection);
|
$select = new Select(\Container::$dbConnection);
|
||||||
$select->where('email', '=', $email);
|
$select->where('email', '=', $email);
|
||||||
|
|
||||||
return $this->pdm->selectFromDb($select, User::class);
|
return \Container::$persistentDataManager->selectFromDb($select, User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getByUsername(string $username): ?User
|
public function getByUsername(string $username): ?User
|
||||||
@@ -32,7 +25,7 @@ class UserRepository implements IUserRepository
|
|||||||
$select = new Select(\Container::$dbConnection);
|
$select = new Select(\Container::$dbConnection);
|
||||||
$select->where('username', '=', $username);
|
$select->where('username', '=', $username);
|
||||||
|
|
||||||
return $this->pdm->selectFromDb($select, User::class);
|
return \Container::$persistentDataManager->selectFromDb($select, User::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getByEmailOrUsername(string $emailOrUsername): ?User
|
public function getByEmailOrUsername(string $emailOrUsername): ?User
|
||||||
@@ -49,6 +42,16 @@ class UserRepository implements IUserRepository
|
|||||||
$select = new Select(\Container::$dbConnection);
|
$select = new Select(\Container::$dbConnection);
|
||||||
$select->where('google_sub', '=', $sub);
|
$select->where('google_sub', '=', $sub);
|
||||||
|
|
||||||
return $this->pdm->selectFromDb($select, User::class);
|
return \Container::$persistentDataManager->selectFromDb($select, User::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function searchByName(string $name): Generator
|
||||||
|
{
|
||||||
|
$select = new Select(\Container::$dbConnection);
|
||||||
|
$select->where('full_name', 'LIKE', '%' . $name . '%');
|
||||||
|
$select->orWhere('nickname', 'LIKE', '%' . $name . '%');
|
||||||
|
$select->limit(10);
|
||||||
|
|
||||||
|
yield from \Container::$persistentDataManager->selectMultipleFromDb($select, User::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
@section(main)
|
@section(main)
|
||||||
<h2>Account</h2>
|
<h2>Account</h2>
|
||||||
<div class="box">
|
<div class="box compactBox">
|
||||||
<form id="accountForm" action="/account" method="post" data-reload-on-success="true" data-observe-inputs="email,username,password_new,password_new_confirm,nickname,phone,id_number">
|
<form id="accountForm" action="<?= Container::$routeCollection->getRoute('account-action')->generateLink() ?>" method="post" data-reload-on-success="true" data-observe-inputs="email,username,password_new,password_new_confirm,nickname,phone,id_number">
|
||||||
<?php if ($user['password'] !== null && $user['google_sub'] !== null): ?>
|
<?php if ($user['password'] !== null && $user['google_sub'] !== null): ?>
|
||||||
<p class="justify small">Please confirm your identity with your password or with Google to modify your account.</p>
|
<p class="justify small">Please confirm your identity with your password or with Google to modify your account.</p>
|
||||||
<div class="inputWithButton">
|
<div class="inputWithButton">
|
||||||
@@ -34,8 +34,14 @@
|
|||||||
<input type="text" class="text big fullWidth marginTop" name="id_number" placeholder="ID number" value="<?= $user['id_number'] ?>">
|
<input type="text" class="text big fullWidth marginTop" name="id_number" placeholder="ID number" value="<?= $user['id_number'] ?>">
|
||||||
<p id="accountFormError" class="formError justify marginTop"></p>
|
<p id="accountFormError" class="formError justify marginTop"></p>
|
||||||
<div class="right marginTop">
|
<div class="right marginTop">
|
||||||
<button type="submit" name="submit" disabled>Save</button>
|
<button type="submit" name="submit_button" disabled>Save</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
|
@section(pageScript)
|
||||||
|
<script>
|
||||||
|
var googleAuthenticateUrl = '<?= Container::$routeCollection->getRoute('account.googleAuthenticate')->generateLink() ?>';
|
||||||
|
</script>
|
||||||
|
@endsection
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
@section(main)
|
@section(main)
|
||||||
<h2>Authenticate with Google</h2>
|
<h2>Authenticate with Google</h2>
|
||||||
<?php if (!$success): ?>
|
<?php if (!$success): ?>
|
||||||
<div class="box">
|
<div class="box compactBox">
|
||||||
<p class="error justify">
|
<p class="error justify">
|
||||||
<?php if (isset($errorText)): ?>
|
<?php if (isset($errorText)): ?>
|
||||||
<?= $errorText ?>
|
<?= $errorText ?>
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
@extends(templates/layout_normal)
|
||||||
|
|
||||||
|
@section(main)
|
||||||
|
<h2>
|
||||||
|
<?= $community->getName() ?>
|
||||||
|
<?php if ($editPermission): ?>
|
||||||
|
<span class="small">[<a href="<?= Container::$routeCollection->getRoute('community.edit')->generateLink(['communityId' => $community->getId()]) ?>">edit</a>]</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p class="marginTop"><a href="<?= Container::$routeCollection->getRoute('community.settings')->generateLink(['communityId' => $community->getId()]) ?>">Settings</a></p>
|
||||||
|
|
||||||
|
<div class="gridContainer marginTop">
|
||||||
|
<div>
|
||||||
|
<h3 class="marginBottom">Upcoming events</h3>
|
||||||
|
<?php if (count($upcomingEvents) > 0): ?>
|
||||||
|
<?php foreach ($upcomingEvents as $event): ?>
|
||||||
|
<!-- todo -->
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<p>There is no upcoming event.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<?php
|
||||||
|
$mainCurrencyCode = $community->getMainCurrency()->getCode();
|
||||||
|
$mainCurrencyRoundDigits = $community->getMainCurrency()->getRoundDigits();
|
||||||
|
?>
|
||||||
|
<h3 class="marginBottom">Finances</h3>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId()]) ?>">Transactions</a>
|
||||||
|
<table class="fullWidth marginTop">
|
||||||
|
<tr>
|
||||||
|
<td class="bold">You owe</td>
|
||||||
|
<td class="mono red" style="text-align: right;"><?= number_format($debtBalance, $mainCurrencyRoundDigits) ?> <?= $mainCurrencyCode ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php foreach ($debtItems as $item): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="small"><?= $item['payee']->getUser()->getDisplayName() ?></td>
|
||||||
|
<td class="small mono red" style="text-align: right;"><?= number_format($item['amount'], $mainCurrencyRoundDigits) ?> <?= $mainCurrencyCode ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<tr>
|
||||||
|
<td class="bold">You're owed</td>
|
||||||
|
<td class="mono green" style="text-align: right;"><?= number_format($outstandingBalance, $mainCurrencyRoundDigits) ?> <?= $mainCurrencyCode ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php foreach ($outstandingItems as $item): ?>
|
||||||
|
<tr>
|
||||||
|
<td class="small"><?= $item['payer']->getUser()->getDisplayName() ?></td>
|
||||||
|
<td class="small mono green" style="text-align: right;"><?= number_format($item['amount'], $mainCurrencyRoundDigits) ?> <?= $mainCurrencyCode ?></td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<tr>
|
||||||
|
<td class="bold">Your balance</td>
|
||||||
|
<td class="mono <?= $balance < 0 ? 'red' : ($balance > 0 ? 'green' : '') ?>" style="text-align: right;;"><?= number_format($balance, $mainCurrencyRoundDigits) ?> <?= $mainCurrencyCode ?></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
@extends(templates/layout_normal)
|
||||||
|
|
||||||
|
@section(main)
|
||||||
|
<h2>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> »
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community.settings')->generateLink(['communityId' => $community->getId()]) ?>">Settings</a> »
|
||||||
|
Edit currencies
|
||||||
|
</h2>
|
||||||
|
<div class="box compactBox">
|
||||||
|
<table class="fullWidth">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: calc(100% / 3); text-align: left;">Currency</th>
|
||||||
|
<th style="width: calc(100% / 3); text-align: left;">Round digits</th>
|
||||||
|
<th style="width: calc(100% / 3);"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<?php foreach ($currencies as $currency): ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<form id="editCurrency_<?= $currency->getId() ?>" action="<?= Container::$routeCollection->getRoute('community.currencies.edit-action')->generateLink(['communityId' => $community->getId(), 'currency_id' => $currency->getId()]) ?>" method="post" data-reload-on-success="true" data-observe-inputs="code,round_digits"></form>
|
||||||
|
<form id="deleteCurrency_<?= $currency->getId() ?>" action="<?= Container::$routeCollection->getRoute('community.currencies.delete-action')->generateLink(['communityId' => $community->getId(), 'currency_id' => $currency->getId()]) ?>" method="post" data-reload-on-success="true"></form>
|
||||||
|
<input type="text" form="editCurrency_<?= $currency->getId() ?>" class="text fullWidth" name="code" value="<?= $currency->getCode() ?>" maxlength="3" required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="number" form="editCurrency_<?= $currency->getId() ?>" class="text fullWidth" name="round_digits" value="<?= $currency->getRoundDigits() ?>" min="0" max="9" required>
|
||||||
|
</td>
|
||||||
|
<td style="text-align: right; font-size: 0;">
|
||||||
|
<button type="submit" form="editCurrency_<?= $currency->getId() ?>" name="submit_button" class="small" disabled>Save</button>
|
||||||
|
<?php if ($currency->getId() !== $community->getMainCurrencyId()): ?>
|
||||||
|
<button type="submit" form="deleteCurrency_<?= $currency->getId() ?>" name="submit_button" data-confirmation="Are you sure you want to delete this currency?" class="small red marginLeft">Delete</button>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<form id="newCurrency" action="<?= Container::$routeCollection->getRoute('community.currencies.new-action')->generateLink(['communityId' => $community->getId()]) ?>" method="post" data-reload-on-success="true" data-observe-inputs="code,round_digits"></form>
|
||||||
|
<input type="text" form="newCurrency" class="text fullWidth" name="code" placeholder="Currency" maxlength="3" required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="number" form="newCurrency" class="text fullWidth" name="round_digits" placeholder="Round digits" min="0" max="9" required>
|
||||||
|
</td>
|
||||||
|
<td style="text-align: right;">
|
||||||
|
<button type="submit" form="newCurrency" name="submit_button" class="small" disabled>Add</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<p class="formError justify marginTop"></p>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
@extends(templates/layout_normal)
|
||||||
|
|
||||||
|
@section(main)
|
||||||
|
<h2>
|
||||||
|
<?php if (isset($community)): ?>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> »
|
||||||
|
Edit
|
||||||
|
<?php else: ?>
|
||||||
|
New community
|
||||||
|
<?php endif; ?>
|
||||||
|
</h2>
|
||||||
|
<div class="box compactBox">
|
||||||
|
<?php
|
||||||
|
$formAction = isset($community) ?
|
||||||
|
Container::$routeCollection->getRoute('community.edit-action')->generateLink(['communityId' => $community->getId()]) :
|
||||||
|
Container::$routeCollection->getRoute('community.new-action')->generateLink();
|
||||||
|
?>
|
||||||
|
<form id="communityForm" action="<?= $formAction ?>" method="post" data-redirect-on-success="true">
|
||||||
|
<input type="text" class="text big fullWidth" name="name" placeholder="Name" value="<?= isset($community) ? $community->getName() : '' ?>" required>
|
||||||
|
<?php if (!isset($community)): ?>
|
||||||
|
<input type="text" class="text big fullWidth marginTop" name="main_currency_code" placeholder="Main currency" maxlength="3" required>
|
||||||
|
<input type="number" class="text big fullWidth marginTop" name="main_currency_round_digits" placeholder="Main currency round digits" min="0" max="9" required>
|
||||||
|
<?php endif; ?>
|
||||||
|
<p id="communityFormError" class="formError justify marginTop"></p>
|
||||||
|
<div class="right marginTop">
|
||||||
|
<button type="submit" name="submit_button"><?= isset($community) ? 'Save' : 'Create' ?></button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
@css(/static/node_modules/tom-select/dist/css/tom-select.min.css)
|
||||||
|
@js(/static/node_modules/tom-select/dist/js/tom-select.base.min.js)
|
||||||
|
@js(js/communities/community_members.js)
|
||||||
|
|
||||||
|
@extends(templates/layout_normal)
|
||||||
|
|
||||||
|
@section(main)
|
||||||
|
<h2>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> »
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community.settings')->generateLink(['communityId' => $community->getId()]) ?>">Settings</a> »
|
||||||
|
Edit members
|
||||||
|
</h2>
|
||||||
|
<div class="box compactBox">
|
||||||
|
<table class="fullWidth">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 50%;"></th>
|
||||||
|
<th style="width: 25%; text-align: center;">Owner</th>
|
||||||
|
<th style="width: 25%;"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<?php foreach ($members as $member): ?>
|
||||||
|
<?php $editable = $member->getUserId() !== Container::$request->user()->getUniqueId(); ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<form id="editMember_<?= $member->getId() ?>" action="<?= Container::$routeCollection->getRoute('community.members.edit-action')->generateLink(['communityId' => $community->getId(), 'community_member_id' => $member->getId()]) ?>" method="post" data-reload-on-success="true" data-observe-inputs="owner"></form>
|
||||||
|
<form id="deleteMember_<?= $member->getId() ?>" action="<?= Container::$routeCollection->getRoute('community.members.delete-action')->generateLink(['communityId' => $community->getId(), 'community_member_id' => $member->getId()]) ?>" method="post" data-reload-on-success="true"></form>
|
||||||
|
<?= $member->getUser()->getDisplayName() ?>
|
||||||
|
</td>
|
||||||
|
<td style="text-align: center;">
|
||||||
|
<input type="checkbox" form="editMember_<?= $member->getId() ?>" name="owner" <?= $member->getOwner() ? 'checked' : '' ?> <?= !$editable ? 'disabled' : '' ?> />
|
||||||
|
</td>
|
||||||
|
<td style="text-align: right;">
|
||||||
|
<?php if ($editable): ?>
|
||||||
|
<button type="submit" form="editMember_<?= $member->getId() ?>" name="submit_button" class="small marginRight" disabled>Save</button><!--
|
||||||
|
--><button type="submit" form="deleteMember_<?= $member->getId() ?>" name="submit_button" data-confirmation="Are you sure you want to delete this member?" class="small red">Delete</button>
|
||||||
|
<?php endif; ?>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<form id="newMember" action="<?= Container::$routeCollection->getRoute('community.members.new-action')->generateLink(['communityId' => $community->getId()]) ?>" method="post" data-reload-on-success="true" data-observe-inputs="user_id"></form>
|
||||||
|
<select type="text" form="newMember" name="user_id">
|
||||||
|
</td>
|
||||||
|
<td style="text-align: center;">
|
||||||
|
<input type="checkbox" form="newMember" name="owner" />
|
||||||
|
</td>
|
||||||
|
<td style="text-align: right;">
|
||||||
|
<button type="submit" form="newMember" name="submit_button" class="small" disabled>Add</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
<p class="formError justify marginTop"></p>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
|
|
||||||
|
@section(pageScript)
|
||||||
|
<script>
|
||||||
|
var searchUserUrl = '<?= Container::$routeCollection->getRoute('searchUser')->generateLink(['q' => 'QUERY']) ?>';
|
||||||
|
</script>
|
||||||
|
@endsection
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
@extends(templates/layout_normal)
|
||||||
|
|
||||||
|
@section(main)
|
||||||
|
<h2>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> »
|
||||||
|
Settings
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<div class="gridContainer marginTop">
|
||||||
|
<div>
|
||||||
|
<h3 class="marginBottom">
|
||||||
|
Members
|
||||||
|
<?php if ($editPermission): ?>
|
||||||
|
<span class="small">[<a href="<?= Container::$routeCollection->getRoute('community.members')->generateLink(['communityId' => $community->getId()]) ?>">edit</a>]</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</h3>
|
||||||
|
<?php foreach ($members as $member): ?>
|
||||||
|
<p><?= $member->getUser()->getDisplayName() ?></p>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="marginBottom">
|
||||||
|
Currencies
|
||||||
|
<?php if ($editPermission): ?>
|
||||||
|
<span class="small">[<a href="<?= Container::$routeCollection->getRoute('community.currencies')->generateLink(['communityId' => $community->getId()]) ?>">edit</a>]</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</h3>
|
||||||
|
<?php foreach ($currencies as $currency): ?>
|
||||||
|
<p>
|
||||||
|
<?php if ($currency->getId() === $community->getMainCurrencyId()): ?>
|
||||||
|
<b><?= $currency->getCode() ?></b>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community.currencyExchangeRates')->generateLink(['communityId' => $community->getId(), 'code' => $currency->getCode()]) ?>"><?= $currency->getCode() ?></a>
|
||||||
|
<?php endif; ?>
|
||||||
|
</p>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -0,0 +1,61 @@
|
|||||||
|
@extends(templates/layout_normal)
|
||||||
|
|
||||||
|
@section(main)
|
||||||
|
<h2>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> »
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community.settings')->generateLink(['communityId' => $community->getId()]) ?>">Settings</a> »
|
||||||
|
Exchange rates for <?= $currency->getCode() ?>
|
||||||
|
</h2>
|
||||||
|
<div class="box compactBox">
|
||||||
|
<table class="fullWidth">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<?php if ($editPermission): ?>
|
||||||
|
<th style="width: 25%; text-align: left;">Exchange rate</th>
|
||||||
|
<th style="width: 50%; text-align: left;">Valid from</th>
|
||||||
|
<th style="width: 25%;"></th>
|
||||||
|
<?php else: ?>
|
||||||
|
<th style="width: 35%; text-align: left;">Exchange rate</th>
|
||||||
|
<th style="width: 65%; text-align: left;">Valid from</th>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<?php foreach ($currencyExchangeRates as $currencyExchangeRate): ?>
|
||||||
|
<tr>
|
||||||
|
<?php if ($editPermission): ?>
|
||||||
|
<td>
|
||||||
|
<form id="editExchangeRate_<?= $currencyExchangeRate->getId() ?>" action="<?= Container::$routeCollection->getRoute('community.currencyExchangeRates.edit-action')->generateLink(['communityId' => $community->getId(), 'code' => $currency->getCode(), 'currency_exchange_rate_id' => $currencyExchangeRate->getId()]) ?>" method="post" data-reload-on-success="true" data-observe-inputs="exchange_rate,valid_from"></form>
|
||||||
|
<form id="deleteCurrency_<?= $currencyExchangeRate->getId() ?>" action="<?= Container::$routeCollection->getRoute('community.currencyExchangeRates.delete-action')->generateLink(['communityId' => $community->getId(), 'code' => $currency->getCode(), 'currency_exchange_rate_id' => $currencyExchangeRate->getId()]) ?>" method="post" data-reload-on-success="true"></form>
|
||||||
|
<input type="number" form="editExchangeRate_<?= $currencyExchangeRate->getId() ?>" class="text fullWidth" name="exchange_rate" value="<?= $currencyExchangeRate->getExchangeRate() ?>" min="0" step="0.000000001" required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="datetime-local" form="editExchangeRate_<?= $currencyExchangeRate->getId() ?>" class="text fullWidth" name="valid_from" value="<?= $currencyExchangeRate->getValidFromDate()->format('Y-m-d\TH:i') ?>" required>
|
||||||
|
</td>
|
||||||
|
<td style="text-align: right;">
|
||||||
|
<button type="submit" form="editExchangeRate_<?= $currencyExchangeRate->getId() ?>" name="submit_button" class="small marginRight" disabled>Save</button><!--
|
||||||
|
--><button type="submit" form="deleteCurrency_<?= $currencyExchangeRate->getId() ?>" name="submit_button" data-confirmation="Are you sure you want to delete this exchange rate?" class="small red">Delete</button>
|
||||||
|
</td>
|
||||||
|
<?php else: ?>
|
||||||
|
<td><?= $currencyExchangeRate->getExchangeRate() ?></td>
|
||||||
|
<td><?= $currencyExchangeRate->getValidFromDate()->format('Y-m-d H:i') ?></td>
|
||||||
|
<?php endif; ?>
|
||||||
|
</tr>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php if ($editPermission): ?>
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<form id="newExchangeRate" action="<?= Container::$routeCollection->getRoute('community.currencyExchangeRates.new-action')->generateLink(['communityId' => $community->getId(), 'code' => $currency->getCode()]) ?>" method="post" data-reload-on-success="true" data-observe-inputs="exchange_rate,valid_from"></form>
|
||||||
|
<input type="number" form="newExchangeRate" class="text fullWidth" name="exchange_rate" placeholder="Exchange rate" min="0" step="0.000000001" required>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<input type="datetime-local" form="newExchangeRate" class="text fullWidth" name="valid_from" placeholder="Valid from" required>
|
||||||
|
</td>
|
||||||
|
<td style="text-align: right;">
|
||||||
|
<button type="submit" form="newExchangeRate" name="submit_button" class="small" disabled>Add</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<?php endif; ?>
|
||||||
|
</table>
|
||||||
|
<p class="formError justify marginTop"></p>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
@extends(templates/layout_normal)
|
||||||
|
|
||||||
|
@section(main)
|
||||||
|
<h2>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> »
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId()]) ?>">Transactions</a> »
|
||||||
|
<?php if (isset($transaction)): ?>
|
||||||
|
Edit transaction
|
||||||
|
<?php else: ?>
|
||||||
|
New transaction
|
||||||
|
<?php endif; ?>
|
||||||
|
</h2>
|
||||||
|
<div class="box compactBox">
|
||||||
|
<?php
|
||||||
|
$formAction = isset($transaction) ?
|
||||||
|
Container::$routeCollection->getRoute('community.transactions.edit-action')->generateLink(['communityId' => $community->getId(), 'transactionId' => $transaction->getId()]) :
|
||||||
|
Container::$routeCollection->getRoute('community.transactions.new-action')->generateLink(['communityId' => $community->getId()]);
|
||||||
|
?>
|
||||||
|
<form id="transactionForm" action="<?= $formAction ?>" method="post" data-redirect-on-success="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId()]) ?>">
|
||||||
|
<select class="big fullWidth" name="payer_user_id" required>
|
||||||
|
<option value="" hidden>[Payer]</option>
|
||||||
|
<?php foreach ($members as $member): ?>
|
||||||
|
<option value="<?= $member->getUser()->getId() ?>" <?= isset($transaction) && $transaction->getPayerUserId() === $member->getUser()->getId() ? 'selected' : '' ?>><?= $member->getUser()->getDisplayName() ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<select class="big fullWidth marginTop" name="payee_user_id">
|
||||||
|
<option value="">[Payee]</option>
|
||||||
|
<?php foreach ($members as $member): ?>
|
||||||
|
<option value="<?= $member->getUser()->getId() ?>" <?= isset($transaction) && $transaction->getPayeeUserId() === $member->getUser()->getId() ? 'selected' : '' ?>><?= $member->getUser()->getDisplayName() ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<input type="text" class="text big fullWidth marginTop" name="description" placeholder="Description" value="<?= isset($transaction) ? $transaction->getDescription() : '' ?>" required>
|
||||||
|
<input type="number" class="text big fullWidth marginTop" name="sum" placeholder="Sum" value="<?= isset($transaction) ? $transaction->getSum() : '' ?>" min="0" step="0.000000001" required>
|
||||||
|
<select class="big fullWidth marginTop" name="currency_id" required>
|
||||||
|
<option value="" hidden>[Currency]</option>
|
||||||
|
<?php foreach ($currencies as $currency): ?>
|
||||||
|
<option value="<?= $currency->getId() ?>" <?= isset($transaction) && $transaction->getCurrencyId() === $currency->getId() ? 'selected' : '' ?>><?= $currency->getCode() ?></option>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</select>
|
||||||
|
<input type="datetime-local" class="text big fullWidth marginTop" name="time" placeholder="Time" value="<?= isset($transaction) ? $transaction->getTimeDate()->format('Y-m-d\TH:i') : (new DateTime())->format('Y-m-d\TH:i') ?>" required>
|
||||||
|
<p class="formError justify marginTop"></p>
|
||||||
|
<div class="right marginTop" style="font-size: 0;">
|
||||||
|
<?php if (isset($transaction)): ?>
|
||||||
|
<button type="submit" form="deleteTransaction" name="submit_button" data-confirmation="Are you sure you want to delete this transaction?" class="red marginRight">Delete</button>
|
||||||
|
<?php endif; ?>
|
||||||
|
<button type="submit" name="submit_button"><?= isset($transaction) ? 'Save' : 'Create' ?></button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<?php if (isset($transaction)): ?>
|
||||||
|
<form id="deleteTransaction" action="<?= Container::$routeCollection->getRoute('community.transactions.delete-action')->generateLink(['communityId' => $community->getId(), 'transactionId' => $transaction->getId()]) ?>" method="post" data-redirect-on-success="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId()]) ?>"></form>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
@extends(templates/layout_normal)
|
||||||
|
|
||||||
|
@section(main)
|
||||||
|
<h2>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a> »
|
||||||
|
Transactions
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<p class="marginTop"><a href="<?= Container::$routeCollection->getRoute('community.transactions.new')->generateLink(['communityId' => $community->getId()]) ?>">New transaction</a></p>
|
||||||
|
|
||||||
|
<?php if ($numberOfTransactions > 0): ?>
|
||||||
|
<?php if ($pages > 1): ?>
|
||||||
|
<p class="paginateContainer marginTop">
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => 0]) ?>">«</a>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => max(0, $currentPage - 1)]) ?>">‹</a>
|
||||||
|
<?php for ($i = 0; $i < $pages; $i++): ?>
|
||||||
|
<?php if ($currentPage == $i): ?>
|
||||||
|
<span class="selected"><?= $i + 1 ?></span>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => $i]) ?>"><?= $i + 1 ?></a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endfor; ?>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => min($pages - 1, $currentPage + 1)]) ?>">›</a>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => $pages - 1]) ?>">»</a>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php foreach ($transactions as $transaction): ?>
|
||||||
|
<a class="block" href="<?= Container::$routeCollection->getRoute('community.transactions.edit')->generateLink(['communityId' => $community->getId(), 'transactionId' => $transaction->getId()]) ?>">
|
||||||
|
<div class="box transaction">
|
||||||
|
<div>
|
||||||
|
<p style="font-weight: bold;"><?= $transaction->getDescription() ?></p>
|
||||||
|
<p class="small"><?= $transaction->getPayerUser()->getDisplayName() ?> ► <?= $transaction->getPayeeUser() ? $transaction->getPayeeUser()->getDisplayName() : '[common]' ?></p>
|
||||||
|
<p class="small"><?= $transaction->getTimeDate()->format('Y-m-d H:i') ?></p>
|
||||||
|
</div>
|
||||||
|
<div style="text-align: right;">
|
||||||
|
<h3><?= number_format($exchangeRateCalculator->calculate($transaction->getSum(), $transaction->getCurrency(), $transaction->getTimeDate()), $community->getMainCurrency()->getRoundDigits()) ?> <?= $community->getMainCurrency()->getCode() ?></h3>
|
||||||
|
<?php if ($community->getMainCurrencyId() !== $transaction->getCurrencyId()): ?>
|
||||||
|
<p style="color: #8e8e8e; font-weight: bold;"><?= number_format($transaction->getSum(), $transaction->getCurrency()->getRoundDigits()) ?> <?= $transaction->getCurrency()->getCode() ?></p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
|
||||||
|
<?php if ($pages > 1): ?>
|
||||||
|
<p class="paginateContainer marginTop">
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => 0]) ?>">«</a>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => max(0, $currentPage - 1)]) ?>">‹</a>
|
||||||
|
<?php for ($i = 0; $i < $pages; $i++): ?>
|
||||||
|
<?php if ($currentPage == $i): ?>
|
||||||
|
<span class="selected"><?= $i + 1 ?></span>
|
||||||
|
<?php else: ?>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => $i]) ?>"><?= $i + 1 ?></a>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?php endfor; ?>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => min($pages - 1, $currentPage + 1)]) ?>">›</a>
|
||||||
|
<a href="<?= Container::$routeCollection->getRoute('community.transactions')->generateLink(['communityId' => $community->getId(), 'page' => $pages - 1]) ?>">»</a>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<p class="marginTop"><a href="<?= Container::$routeCollection->getRoute('community.transactions.new')->generateLink(['communityId' => $community->getId()]) ?>">New transaction</a></p>
|
||||||
|
<?php else: ?>
|
||||||
|
<div class="box">
|
||||||
|
<p>There are no transactions yet.</p>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
@endsection
|
||||||
+1
-1
@@ -2,5 +2,5 @@
|
|||||||
|
|
||||||
@section(main)
|
@section(main)
|
||||||
<h2>404 | Page not found</h2>
|
<h2>404 | Page not found</h2>
|
||||||
<p>The requested URL was not found on this server. <a href="/" title="<?= $_ENV['APP_NAME'] ?>">Back to start.</a></p>
|
<p>The requested URL was not found on this server. <a href="<?= Container::$routeCollection->getRoute('home')->generateLink() ?>" title="<?= $_ENV['APP_NAME'] ?>">Back to start.</a></p>
|
||||||
@endsection
|
@endsection
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
@extends(templates/layout_normal)
|
||||||
|
|
||||||
|
@section(main)
|
||||||
|
<h2>500 | Internal server error</h2>
|
||||||
|
<p>An error occured during processing your request. <a href="<?= Container::$routeCollection->getRoute('home')->generateLink() ?>" title="<?= $_ENV['APP_NAME'] ?>">Back to start.</a></p>
|
||||||
|
<?php if (isset($exceptionToPrint)): ?>
|
||||||
|
<pre class="marginTop">
|
||||||
|
<?= $exceptionToPrint ?>
|
||||||
|
</pre>
|
||||||
|
<?php endif; ?>
|
||||||
|
@endsection
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
@extends(templates/layout_normal)
|
||||||
|
|
||||||
|
@section(main)
|
||||||
|
<div class="gridContainer">
|
||||||
|
<div>
|
||||||
|
<h3 class="marginBottom">
|
||||||
|
Communities
|
||||||
|
<span class="small">[<a href="<?= Container::$routeCollection->getRoute('community.new')->generateLink() ?>">new</a>]</span>
|
||||||
|
</h3>
|
||||||
|
<?php if (count($communities) > 0): ?>
|
||||||
|
<?php foreach ($communities as $community): ?>
|
||||||
|
<p><a href="<?= Container::$routeCollection->getRoute('community')->generateLink(['communityId' => $community->getId()]) ?>"><?= $community->getName() ?></a></p>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<p>You have no community.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h3 class="marginBottom">Upcoming events</h3>
|
||||||
|
<?php if (count($upcomingEvents) > 0): ?>
|
||||||
|
<?php foreach ($upcomingEvents as $event): ?>
|
||||||
|
<!-- todo -->
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<p>There is no upcoming event.</p>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endsection
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
@extends(templates/layout_normal)
|
|
||||||
|
|
||||||
@section(main)
|
|
||||||
<h2><?= $_ENV['APP_NAME'] ?></h2>
|
|
||||||
<div class="box">
|
|
||||||
<p>This is the new <?= $_ENV['APP_NAME'] ?>.</p>
|
|
||||||
</div>
|
|
||||||
@endsection
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
@section(main)
|
@section(main)
|
||||||
<h2>Login up with Google</h2>
|
<h2>Login up with Google</h2>
|
||||||
<div class="box">
|
<div class="box compactBox">
|
||||||
<p class="error justify"><?= $error ?></p>
|
<p class="error justify"><?= $error ?></p>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
@@ -2,18 +2,18 @@
|
|||||||
|
|
||||||
@section(main)
|
@section(main)
|
||||||
<h2>Login</h2>
|
<h2>Login</h2>
|
||||||
<div class="box">
|
<div class="box compactBox">
|
||||||
<form id="loginForm" action="/login" method="post" data-redirect-on-success="<?= $redirectUrl ?>">
|
<form id="loginForm" action="<?= Container::$routeCollection->getRoute('login-action')->generateLink() ?>" method="post" data-redirect-on-success="<?= $redirectUrl ?>">
|
||||||
<input type="text" class="text big fullWidth" name="email" placeholder="Email address / Username" required autofocus>
|
<input type="text" class="text big fullWidth" name="email" placeholder="Email address / Username" required autofocus>
|
||||||
<input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password" required minlength="6">
|
<input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password" required minlength="6">
|
||||||
<p id="loginFormError" class="formError justify marginTop"></p>
|
<p id="loginFormError" class="formError justify marginTop"></p>
|
||||||
<div class="right marginTop">
|
<div class="right marginTop">
|
||||||
<button type="submit">Login</button>
|
<button type="submit">Login</button>
|
||||||
</div>
|
</div>
|
||||||
<p class="center marginTop"><a href="/password/requestReset" title="Request password reset">Forgot your password?</a></p>
|
<p class="center marginTop"><a href="<?= Container::$routeCollection->getRoute('password.requestReset')->generateLink() ?>" title="Request password reset">Forgot your password?</a></p>
|
||||||
<hr>
|
<hr>
|
||||||
<div class="center">
|
<div class="center">
|
||||||
<a class="button yellow" href="/login/google" title="Login with Google">Login with Google</a>
|
<a class="button yellow" href="<?= Container::$routeCollection->getRoute('login.google')->generateLink() ?>" title="Login with Google">Login with Google</a>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -4,8 +4,8 @@
|
|||||||
|
|
||||||
@section(main)
|
@section(main)
|
||||||
<h2>Request password reset</h2>
|
<h2>Request password reset</h2>
|
||||||
<div class="box">
|
<div class="box compactBox">
|
||||||
<form id="passwordResetForm" action="/password/requestReset" method="post" data-redirect-on-success="/password/requestReset/success">
|
<form id="passwordResetForm" action="<?= Container::$routeCollection->getRoute('password.requestReset-action')->generateLink() ?>" method="post" data-redirect-on-success="<?= Container::$routeCollection->getRoute('password.requestReset.success')->generateLink() ?>">
|
||||||
<input type="email" class="text big fullWidth" name="email" placeholder="Email address" value="<?= isset($email) ? $email : '' ?>" required autofocus>
|
<input type="email" class="text big fullWidth" name="email" placeholder="Email address" value="<?= isset($email) ? $email : '' ?>" required autofocus>
|
||||||
<?php if (!empty($_ENV['RECAPTCHA_SITEKEY'])): ?>
|
<?php if (!empty($_ENV['RECAPTCHA_SITEKEY'])): ?>
|
||||||
<div class="marginTop">
|
<div class="marginTop">
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
@section(main)
|
@section(main)
|
||||||
<h2>Request password reset</h2>
|
<h2>Request password reset</h2>
|
||||||
<div class="box">
|
<div class="box compactBox">
|
||||||
<p class="justify">Password reset was successfully requested. Please check your email and click on the link to reset your password!</p>
|
<p class="justify">Password reset was successfully requested. Please check your email and click on the link to reset your password!</p>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
@@ -2,9 +2,9 @@
|
|||||||
|
|
||||||
@section(main)
|
@section(main)
|
||||||
<h2>Reset password</h2>
|
<h2>Reset password</h2>
|
||||||
<div class="box">
|
<div class="box compactBox">
|
||||||
<?php if ($success) : ?>
|
<?php if ($success) : ?>
|
||||||
<form id="resetPasswordForm" action="/password/reset/<?= $token ?>" method="post" data-redirect-on-success="<?= $redirectUrl ?>">
|
<form id="resetPasswordForm" action="<?= Container::$routeCollection->getRoute('password.reset-action')->generateLink(['token' => $token]) ?>"" method="post" data-redirect-on-success="<?= $redirectUrl ?>">
|
||||||
<input type="email" class="text big fullWidth" name="email" placeholder="Email address" value="<?= $email ?>" disabled>
|
<input type="email" class="text big fullWidth" name="email" placeholder="Email address" value="<?= $email ?>" disabled>
|
||||||
<input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password" required minlength="6" autofocus>
|
<input type="password" class="text big fullWidth marginTop" name="password" placeholder="Password" required minlength="6" autofocus>
|
||||||
<input type="password" class="text big fullWidth marginTop" name="password_confirm" placeholder="Password confirmation" required minlength="6">
|
<input type="password" class="text big fullWidth marginTop" name="password_confirm" placeholder="Password confirmation" required minlength="6">
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
<p class="error justify">Confirming your identity failed. Please check the link you entered, or retry <a href="/password/requestReset" title="Request password reset">requesting password reset</a>!</p>
|
<p class="error justify">Confirming your identity failed. Please check the link you entered, or retry <a href="<?= Container::$routeCollection->getRoute('password.requestReset')->generateLink() ?>" title="Request password reset">requesting password reset</a>!</p>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
@section(main)
|
@section(main)
|
||||||
<h2>OAuth error</h2>
|
<h2>OAuth error</h2>
|
||||||
<div class="box">
|
<div class="box compactBox">
|
||||||
<p class="error justify"><?= $error ?></p>
|
<p class="error justify"><?= $error ?></p>
|
||||||
</div>
|
</div>
|
||||||
@endsection
|
@endsection
|
||||||
@@ -3,21 +3,21 @@
|
|||||||
@section(content)
|
@section(content)
|
||||||
<header>
|
<header>
|
||||||
<h1>
|
<h1>
|
||||||
<a href="/" title="<?= $_ENV['APP_NAME'] ?>">
|
<a href="<?= Container::$routeCollection->getRoute('home')->generateLink() ?>" title="<?= $_ENV['APP_NAME'] ?>">
|
||||||
<img class="inline" width="1em" height="1em" src="<?= $_ENV['STATIC_ROOT'] ?>/img/icon.svg?rev=<?= REVISION ?>" alt="<?= $_ENV['APP_NAME'] ?>"><!--
|
<img class="inline" width="1em" height="1em" src="<?= $_ENV['STATIC_ROOT'] ?>/img/icon.svg?rev=<?= REVISION ?>" alt="<?= $_ENV['APP_NAME'] ?>"><!--
|
||||||
--><span><?= $_ENV['APP_NAME'] ?></span>
|
--><span><?= $_ENV['APP_NAME'] ?></span>
|
||||||
</a>
|
</a>
|
||||||
</h1>
|
</h1>
|
||||||
<p>
|
<p>
|
||||||
<?php if (Container::$request->user()) : ?>
|
<?php if (Container::$request->user()) : ?>
|
||||||
<span><a href="/account" title="Account">
|
<span><a href="<?= Container::$routeCollection->getRoute('account')->generateLink() ?>" title="Account">
|
||||||
<?php /* Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. */ ?>
|
<?php /* Copyright (c) 2019 The Bootstrap Authors. License can be found in 'USED_SOFTWARE' in section 'Bootstrap Icons'. */ ?>
|
||||||
<svg class="inline" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
<svg class="inline" width="1em" height="1em" viewBox="0 0 16 16" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
|
||||||
<path fill-rule="evenodd" d="M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1H3zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/>
|
<path fill-rule="evenodd" d="M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1H3zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6z"/>
|
||||||
</svg><!--
|
</svg><!--
|
||||||
--><?= Container::$request->user()->getDisplayName() ?><!--
|
--><?= Container::$request->user()->getDisplayName() ?><!--
|
||||||
--></a></span><!--
|
--></a></span><!--
|
||||||
--><span><a href="/logout" title="Logout">Logout</a></span>
|
--><span><a href="<?= Container::$routeCollection->getRoute('logout')->generateLink() ?>" title="Logout">Logout</a></span>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</p>
|
</p>
|
||||||
</header>
|
</header>
|
||||||
@@ -25,8 +25,8 @@
|
|||||||
@yields('main')
|
@yields('main')
|
||||||
</main>
|
</main>
|
||||||
<footer>
|
<footer>
|
||||||
<p><?= round($__debug_runtime, 0) ?> ms</p>
|
<p><?= round($__debug_runtime, 0) ?> ms</p><!--
|
||||||
<p><span class="bold"><?= $_ENV['APP_NAME'] ?></span> <?= str_replace('Release_', '', VERSION) ?></p><!--
|
--><p><span class="bold"><?= $_ENV['APP_NAME'] ?></span> <?= str_replace('Release_', '', VERSION) ?></p><!--
|
||||||
--><p>© The RVR Contributors <?= (new DateTime(REVISION_DATE))->format('Y') ?></p>
|
--><p>© The RVR Contributors <?= (new DateTime(REVISION_DATE))->format('Y') ?></p>
|
||||||
</footer>
|
</footer>
|
||||||
@endsection
|
@endsection
|
||||||
@@ -9,13 +9,14 @@
|
|||||||
<?php if (preg_match('/^(http(s)?:)?\/\//', $_ENV['STATIC_ROOT']) === 1): ?>
|
<?php if (preg_match('/^(http(s)?:)?\/\//', $_ENV['STATIC_ROOT']) === 1): ?>
|
||||||
<link href="<?= $_ENV['STATIC_ROOT'] ?>" rel="preconnect">
|
<link href="<?= $_ENV['STATIC_ROOT'] ?>" rel="preconnect">
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<link href="https://fonts.gstatic.com" rel="preconnect">
|
<link href="https://fonts.googleapis.com" rel="preconnect">
|
||||||
|
<link href="https://fonts.gstatic.com" rel="preconnect" crossorigin>
|
||||||
<?php if (!empty($_ENV['GOOGLE_ANALITICS_ID'])): ?>
|
<?php if (!empty($_ENV['GOOGLE_ANALITICS_ID'])): ?>
|
||||||
<link href="https://www.googletagmanager.com" rel="preconnect">
|
<link href="https://www.googletagmanager.com" rel="preconnect">
|
||||||
<link href="https://www.google-analytics.com" rel="preconnect">
|
<link href="https://www.google-analytics.com" rel="preconnect">
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Oxygen:wght@400;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Oxygen:wght@400;700&display=block" rel="stylesheet">
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Oxygen+Mono:wght@400&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Oxygen+Mono:wght@400&display=block" rel="stylesheet">
|
||||||
<link href="<?= $_ENV['STATIC_ROOT'] ?>/css/rvr.css?rev=<?= REVISION ?>" rel="stylesheet">
|
<link href="<?= $_ENV['STATIC_ROOT'] ?>/css/rvr.css?rev=<?= REVISION ?>" rel="stylesheet">
|
||||||
@yields('externalCss')
|
@yields('externalCss')
|
||||||
@yields('inlineCss')
|
@yields('inlineCss')
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use SokoWeb\Response\HttpResponse;
|
||||||
|
use SokoWeb\Routing\RouteCollection;
|
||||||
|
use SokoWeb\Session\DatabaseSessionHandler;
|
||||||
|
use SokoWeb\Request\Request;
|
||||||
|
use RVR\Controller\HomeController;
|
||||||
|
use RVR\Controller\LoginController;
|
||||||
|
use RVR\Controller\OAuthAuthController;
|
||||||
|
use RVR\Controller\OAuthController;
|
||||||
|
use RVR\Controller\UserController;
|
||||||
|
use RVR\Controller\UserSearchController;
|
||||||
|
use RVR\Controller\CommunityController;
|
||||||
|
use RVR\Controller\TransactionController;
|
||||||
|
use RVR\Repository\UserRepository;
|
||||||
|
|
||||||
require 'app.php';
|
require 'app.php';
|
||||||
|
|
||||||
if (!empty($_ENV['DEV'])) {
|
if (!empty($_ENV['DEV'])) {
|
||||||
@@ -10,39 +24,71 @@ if (!empty($_ENV['DEV'])) {
|
|||||||
ini_set('display_errors', '0');
|
ini_set('display_errors', '0');
|
||||||
}
|
}
|
||||||
|
|
||||||
Container::$routeCollection = new SokoWeb\Routing\RouteCollection();
|
Container::$routeCollection = new RouteCollection();
|
||||||
|
|
||||||
Container::$routeCollection->get('index', '', [RVR\Controller\HomeController::class, 'getIndex']);
|
Container::$routeCollection->get('home', '', [HomeController::class, 'getHome']);
|
||||||
Container::$routeCollection->get('startSession', 'startSession.json', [RVR\Controller\HomeController::class, 'startSession']);
|
Container::$routeCollection->group('login', function (RouteCollection $routeCollection) {
|
||||||
Container::$routeCollection->group('login', function (SokoWeb\Routing\RouteCollection $routeCollection) {
|
$routeCollection->get('login', '', [LoginController::class, 'getLoginForm']);
|
||||||
$routeCollection->get('login', '', [RVR\Controller\LoginController::class, 'getLoginForm']);
|
$routeCollection->post('login-action', '', [LoginController::class, 'login']);
|
||||||
$routeCollection->post('login-action', '', [RVR\Controller\LoginController::class, 'login']);
|
$routeCollection->get('login.google', 'google', [LoginController::class, 'getGoogleLoginRedirect']);
|
||||||
$routeCollection->get('login-google', 'google', [RVR\Controller\LoginController::class, 'getGoogleLoginRedirect']);
|
$routeCollection->get('login.google-action', 'google/code', [LoginController::class, 'loginWithGoogle']);
|
||||||
$routeCollection->get('login-google-action', 'google/code', [RVR\Controller\LoginController::class, 'loginWithGoogle']);
|
|
||||||
});
|
});
|
||||||
Container::$routeCollection->group('oauth', function (SokoWeb\Routing\RouteCollection $routeCollection) {
|
Container::$routeCollection->group('oauth', function (RouteCollection $routeCollection) {
|
||||||
$routeCollection->get('oauth-auth', 'auth', [RVR\Controller\OAuthAuthController::class, 'auth']);
|
$routeCollection->get('oauth.auth', 'auth', [OAuthAuthController::class, 'auth']);
|
||||||
$routeCollection->post('oauth-token', 'token', [RVR\Controller\OAuthController::class, 'getToken']);
|
$routeCollection->post('oauth.token', 'token', [OAuthController::class, 'getToken']);
|
||||||
$routeCollection->get('oauth-userinfo', 'userinfo', [RVR\Controller\OAuthController::class, 'getUserInfo']);
|
$routeCollection->get('oauth.userinfo', 'userinfo', [OAuthController::class, 'getUserInfo']);
|
||||||
$routeCollection->get('oauth-config', '.well-known/openid-configuration', [RVR\Controller\OAuthController::class, 'getConfig']);
|
$routeCollection->get('oauth.config', '.well-known/openid-configuration', [OAuthController::class, 'getConfig']);
|
||||||
$routeCollection->get('oauth-certs', 'certs', [RVR\Controller\OAuthController::class, 'getCerts']);
|
$routeCollection->get('oauth.certs', 'certs', [OAuthController::class, 'getCerts']);
|
||||||
});
|
});
|
||||||
Container::$routeCollection->group('password', function (SokoWeb\Routing\RouteCollection $routeCollection) {
|
Container::$routeCollection->group('password', function (RouteCollection $routeCollection) {
|
||||||
$routeCollection->get('password-requestReset', 'requestReset', [RVR\Controller\LoginController::class, 'getRequestPasswordResetForm']);
|
$routeCollection->get('password.requestReset', 'requestReset', [LoginController::class, 'getRequestPasswordResetForm']);
|
||||||
$routeCollection->post('password-requestReset-action', 'requestReset', [RVR\Controller\LoginController::class, 'requestPasswordReset']);
|
$routeCollection->post('password.requestReset-action', 'requestReset', [LoginController::class, 'requestPasswordReset']);
|
||||||
$routeCollection->get('password-requestReset.success', 'requestReset/success', [RVR\Controller\LoginController::class, 'getRequestPasswordResetSuccess']);
|
$routeCollection->get('password.requestReset.success', 'requestReset/success', [LoginController::class, 'getRequestPasswordResetSuccess']);
|
||||||
$routeCollection->get('password-reset', 'reset/{token}', [RVR\Controller\LoginController::class, 'getResetPasswordForm']);
|
$routeCollection->get('password.reset', 'reset/{token}', [LoginController::class, 'getResetPasswordForm']);
|
||||||
$routeCollection->post('password-reset.action', 'reset/{token}', [RVR\Controller\LoginController::class, 'resetPassword']);
|
$routeCollection->post('password.reset-action', 'reset/{token}', [LoginController::class, 'resetPassword']);
|
||||||
});
|
});
|
||||||
Container::$routeCollection->get('logout', 'logout', [RVR\Controller\LoginController::class, 'logout']);
|
Container::$routeCollection->get('logout', 'logout', [LoginController::class, 'logout']);
|
||||||
Container::$routeCollection->group('account', function (SokoWeb\Routing\RouteCollection $routeCollection) {
|
Container::$routeCollection->group('account', function (RouteCollection $routeCollection) {
|
||||||
$routeCollection->get('account', '', [RVR\Controller\UserController::class, 'getAccount']);
|
$routeCollection->get('account', '', [UserController::class, 'getAccount']);
|
||||||
$routeCollection->post('account-action', '', [RVR\Controller\UserController::class, 'saveAccount']);
|
$routeCollection->post('account-action', '', [UserController::class, 'saveAccount']);
|
||||||
$routeCollection->get('account.googleAuthenticate', 'googleAuthenticate', [RVR\Controller\UserController::class, 'getGoogleAuthenticateRedirect']);
|
$routeCollection->get('account.googleAuthenticate', 'googleAuthenticate', [UserController::class, 'getGoogleAuthenticateRedirect']);
|
||||||
$routeCollection->get('account.googleAuthenticate-action', 'googleAuthenticate/code', [RVR\Controller\UserController::class, 'authenticateWithGoogle']);
|
$routeCollection->get('account.googleAuthenticate-action', 'googleAuthenticate/code', [UserController::class, 'authenticateWithGoogle']);
|
||||||
|
});
|
||||||
|
Container::$routeCollection->get('searchUser', 'searchUser', [UserSearchController::class, 'searchUser']);
|
||||||
|
Container::$routeCollection->group('communities', function (RouteCollection $routeCollection) {
|
||||||
|
$routeCollection->get('community.new', 'new', [CommunityController::class, 'getCommunityNew']);
|
||||||
|
$routeCollection->post('community.new-action', 'new', [CommunityController::class, 'saveCommunity']);
|
||||||
|
$routeCollection->group('{communityId}', function (RouteCollection $routeCollection) {
|
||||||
|
$routeCollection->get('community', '', [CommunityController::class, 'getCommunityHome']);
|
||||||
|
$routeCollection->get('community.settings', 'settings', [CommunityController::class, 'getCommunitySettings']);
|
||||||
|
$routeCollection->get('community.edit', 'edit', [CommunityController::class, 'getCommunityEdit']);
|
||||||
|
$routeCollection->post('community.edit-action', 'edit', [CommunityController::class, 'saveCommunity']);
|
||||||
|
$routeCollection->get('community.members', 'members', [CommunityController::class, 'getMembersEdit']);
|
||||||
|
$routeCollection->post('community.members.new-action', 'members/new', [CommunityController::class, 'saveMember']);
|
||||||
|
$routeCollection->post('community.members.edit-action', 'members/edit', [CommunityController::class, 'saveMember']);
|
||||||
|
$routeCollection->post('community.members.delete-action', 'members/delete', [CommunityController::class, 'deleteMember']);
|
||||||
|
$routeCollection->get('community.currencies', 'currencies', [CommunityController::class, 'getCurrenciesEdit']);
|
||||||
|
$routeCollection->post('community.currencies.new-action', 'currencies/new', [CommunityController::class, 'saveCurrency']);
|
||||||
|
$routeCollection->post('community.currencies.edit-action', 'currencies/edit', [CommunityController::class, 'saveCurrency']);
|
||||||
|
$routeCollection->post('community.currencies.delete-action', 'currencies/delete', [CommunityController::class, 'deleteCurrency']);
|
||||||
|
$routeCollection->group('currencyExchangeRates', function (RouteCollection $routeCollection) {
|
||||||
|
$routeCollection->get('community.currencyExchangeRates', '{code}', [CommunityController::class, 'getCurrencyExchangeRates']);
|
||||||
|
$routeCollection->post('community.currencyExchangeRates.new-action', '{code}/new', [CommunityController::class, 'saveCurrencyExchangeRate']);
|
||||||
|
$routeCollection->post('community.currencyExchangeRates.edit-action', '{code}/edit', [CommunityController::class, 'saveCurrencyExchangeRate']);
|
||||||
|
$routeCollection->post('community.currencyExchangeRates.delete-action', '{code}/delete', [CommunityController::class, 'deleteCurrencyExchangeRate']);
|
||||||
|
});
|
||||||
|
$routeCollection->group('transactions', function (RouteCollection $routeCollection) {
|
||||||
|
$routeCollection->get('community.transactions', '', [TransactionController::class, 'getTransactions']);
|
||||||
|
$routeCollection->get('community.transactions.new', 'new', [TransactionController::class, 'getTransactionEdit']);
|
||||||
|
$routeCollection->post('community.transactions.new-action', 'new', [TransactionController::class, 'saveTransaction']);
|
||||||
|
$routeCollection->get('community.transactions.edit', '{transactionId}', [TransactionController::class, 'getTransactionEdit']);
|
||||||
|
$routeCollection->post('community.transactions.edit-action', '{transactionId}', [TransactionController::class, 'saveTransaction']);
|
||||||
|
$routeCollection->post('community.transactions.delete-action', '{transactionId}/delete', [TransactionController::class, 'deleteTransaction']);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
Container::$sessionHandler = new SokoWeb\Session\DatabaseSessionHandler();
|
Container::$sessionHandler = new DatabaseSessionHandler(Container::$dbConnection);
|
||||||
|
|
||||||
session_set_save_handler(Container::$sessionHandler, true);
|
session_set_save_handler(Container::$sessionHandler, true);
|
||||||
session_start([
|
session_start([
|
||||||
@@ -63,22 +109,34 @@ if (isset($_COOKIE[session_name()])) {
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// this is needed to handle old type of session IDs
|
Container::$request = new Request(
|
||||||
if (!Container::$sessionHandler->validateId(session_id())) {
|
|
||||||
session_regenerate_id(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Container::$request = new SokoWeb\Request\Request(
|
|
||||||
$_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'],
|
$_SERVER['REQUEST_SCHEME'] . '://' . $_SERVER['HTTP_HOST'],
|
||||||
$_GET,
|
$_GET,
|
||||||
$_POST,
|
$_POST,
|
||||||
|
getallheaders(),
|
||||||
$_SESSION,
|
$_SESSION,
|
||||||
new RVR\Repository\UserRepository()
|
new UserRepository()
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!Container::$request->session()->has('anti_csrf_token')) {
|
if (!Container::$request->session()->has('anti_csrf_token')) {
|
||||||
Container::$request->session()->set('anti_csrf_token', bin2hex(random_bytes(16)));
|
Container::$request->session()->set('anti_csrf_token', bin2hex(random_bytes(16)));
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: make a nicer logic
|
$appConfig = [
|
||||||
$antiCsrfTokenExceptions = ['oauth/token'];
|
'antiCsrfTokenName' => 'anti_csrf_token',
|
||||||
|
'antiCsrfTokenErrorResponse' => ['error' => 'no_valid_anti_csrf_token'],
|
||||||
|
'antiCsrfTokenExceptions' => ['/oauth/token'],
|
||||||
|
'loginRouteId' => 'login',
|
||||||
|
'error404View' => 'error/404',
|
||||||
|
'error500View' => 'error/500'
|
||||||
|
];
|
||||||
|
|
||||||
|
$httpReponse = new HttpResponse(
|
||||||
|
Container::$request,
|
||||||
|
Container::$dbConnection,
|
||||||
|
Container::$routeCollection,
|
||||||
|
$appConfig,
|
||||||
|
$_SERVER['REQUEST_METHOD'],
|
||||||
|
$_SERVER['REQUEST_URI']
|
||||||
|
);
|
||||||
|
$httpReponse->render();
|
||||||
Reference in new issue
Block a user