diff --git a/community_server/Dockerfile b/community_server/Dockerfile
index 2f3c0412a..cb4f67d27 100644
--- a/community_server/Dockerfile
+++ b/community_server/Dockerfile
@@ -9,5 +9,9 @@ WORKDIR /var/www/cakephp
RUN mkdir logs && mkdir tmp && chmod 777 logs && chmod 777 tmp
COPY ./community_server/ .
COPY ./configs/community_server/app.php ./config/
-RUN composer update
+
+RUN composer update
+RUN composer dump-autoload
+
+
diff --git a/community_server/composer.json b/community_server/composer.json
index 7eeb26887..784c6d123 100644
--- a/community_server/composer.json
+++ b/community_server/composer.json
@@ -30,7 +30,9 @@
"autoload": {
"psr-4": {
"App\\": "src/",
- "" : "src/"
+ "" : "src/",
+ "GPBMetadata\\": "src/GPBMetadata",
+ "Model\\Messages\\Gradido\\" : "src/Model/Messages/Gradido"
}
},
"autoload-dev": {
diff --git a/community_server/config/bootstrap.php b/community_server/config/bootstrap.php
index c57f81374..55918ba20 100644
--- a/community_server/config/bootstrap.php
+++ b/community_server/config/bootstrap.php
@@ -193,6 +193,13 @@ Type::build('datetime')
->useImmutable();
Type::build('timestamp')
->useImmutable();
+
+header('Access-Control-Allow-Origin: *');
+header('Access-Control-Allow-Methods: POST, GET, PUT, PATCH, DELETE, OPTIONS');
+header('Access-Control-Allow-Headers: *');
+if ($_SERVER['REQUEST_METHOD'] == 'OPTIONS') {
+ exit(0);
+}
/*
* Custom Inflector rules, can be set to correctly pluralize or singularize
diff --git a/community_server/config/routes.php b/community_server/config/routes.php
index 27d998f2c..ec3b5fd27 100644
--- a/community_server/config/routes.php
+++ b/community_server/config/routes.php
@@ -58,6 +58,7 @@ Router::scope('/', function (RouteBuilder $routes) {
// Skip token check for API URLs.
//die($request->getParam('controller'));
$whitelist = ['JsonRequestHandler', 'ElopageWebhook'];
+ $ajaxWhitelist = ['TransactionSendCoins', 'TransactionCreations'];
foreach($whitelist as $entry) {
if($request->getParam('controller') === $entry) {
@@ -68,15 +69,29 @@ Router::scope('/', function (RouteBuilder $routes) {
return true;
}
$allowedCaller = Configure::read('API.allowedCaller');
+ $ipPerHost = [];
if($allowedCaller && count($allowedCaller) > 0) {
$callerIp = $request->clientIp();
foreach($allowedCaller as $allowed) {
$ip = gethostbyname($allowed);
+ $ipPerHost[$allowed] = $ip;
if($ip === $callerIp) return true;
}
+ //die("caller ip: $callerIp
");
}
+ //var_dump(['caller_ip' => $callerIp, 'ips' => $ipPerHost]);
+ die(json_encode(['state' => 'error', 'details' => ['caller_ip' => $callerIp, 'ips' => $ipPerHost]]));
}
}
+ // disable csfr for all ajax requests in ajax whitelisted controller
+ foreach($ajaxWhitelist as $entry) {
+ if($request->getParam('controller') === $entry) {
+ $action = $request->getParam('action');
+ if(preg_match('/^ajax/', $action)) {
+ return true;
+ }
+ }
+ }
});
// Register scoped middleware for in scopes.
diff --git a/community_server/copy_to_www.sh b/community_server/copy_to_www.sh
new file mode 100755
index 000000000..f6519919f
--- /dev/null
+++ b/community_server/copy_to_www.sh
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+[! -z "${FOLDER_NAME}"] && FOLDER_NAME=community_server
+
+COLOR_GREEN="\033[0;32m"
+COLOR_YELLOW="\e[33m"
+COLOR_NONE="\033[0m"
+
+SCRIPT=`realpath -s $0`
+SCRIPTPATH=`dirname $SCRIPT`
+#echo -e "script: $SCRIPT, Path: $SCRIPTPATH "
+
+cd /var/www/html
+if [ ! -d "$FOLDER_NAME" ] ; then
+ mkdir $FOLDER_NAME
+else
+ chmod -R 0755 $FOLDER_NAME
+fi
+cd $FOLDER_NAME
+cp -r $SCRIPTPATH/src .
+cp -r $SCRIPTPATH/config .
+cp -r $SCRIPTPATH/composer.json .
+cp -r $SCRIPTPATH/webroot .
+composer install
+if [ ! -d "tmp" ] ; then
+ mkdir tmp
+ chown -R www-data:www-data ./tmp
+fi
+if [ ! -d "logs" ] ; then
+ mkdir logs
+ chown -R www-data:www-data ./logs
+fi
+
+cd ..
+chown -R www-data:www-data $FOLDER_NAME
+chmod -R 0755 $FOLDER_NAME/src
+chmod -R 0755 $FOLDER_NAME/config
+chmod -R 0755 $FOLDER_NAME/webroot
diff --git a/community_server/parse_proto.php b/community_server/parse_proto.sh
similarity index 100%
rename from community_server/parse_proto.php
rename to community_server/parse_proto.sh
diff --git a/community_server/skeema/gradido_community/state_balances.sql b/community_server/skeema/gradido_community/state_balances.sql
index c8b013d44..5f79ce819 100644
--- a/community_server/skeema/gradido_community/state_balances.sql
+++ b/community_server/skeema/gradido_community/state_balances.sql
@@ -2,6 +2,7 @@ CREATE TABLE `state_balances` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`state_user_id` int(10) unsigned NOT NULL,
`modified` datetime NOT NULL,
+ `record_date`datetime NULL,
`amount` bigint(20) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
diff --git a/community_server/skeema/gradido_community/state_user_transactions.sql b/community_server/skeema/gradido_community/state_user_transactions.sql
new file mode 100644
index 000000000..272552845
--- /dev/null
+++ b/community_server/skeema/gradido_community/state_user_transactions.sql
@@ -0,0 +1,9 @@
+CREATE TABLE `state_user_transactions` (
+ `id` int UNSIGNED NOT NULL AUTO_INCREMENT,
+ `state_user_id` int UNSIGNED NOT NULL,
+ `transaction_id` int UNSIGNED NOT NULL,
+ `transaction_type_id` int UNSIGNED NOT NULL,
+ `balance` bigint(20) DEFAULT 0,
+ `balance_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
diff --git a/community_server/src/Controller/AppController.php b/community_server/src/Controller/AppController.php
index 2c59a17e0..fb0577034 100644
--- a/community_server/src/Controller/AppController.php
+++ b/community_server/src/Controller/AppController.php
@@ -139,7 +139,7 @@ class AppController extends Controller
}
}
- protected function requestLogin($session_id = 0)
+ protected function requestLogin($session_id = 0, $redirect = true)
{
$session = $this->getRequest()->getSession();
// check login
@@ -159,9 +159,9 @@ class AppController extends Controller
if ($session_id != 0) {
$userStored = $session->read('StateUser');
-
$transactionPendings = $session->read('Transaction.pending');
- $transactionExecutings = $session->read('Transaction.executing');
+ $transactionExecutings = $session->read('Transaction.executing');
+
if ($session->read('session_id') != $session_id ||
( $userStored && (!isset($userStored['id']) || !$userStored['email_checked'])) ||
intval($transactionPendings) > 0 ||
@@ -182,6 +182,8 @@ class AppController extends Controller
$session->destroy();
}
foreach ($json['user'] as $key => $value) {
+ // we don't need the id of user in login server db
+ if($key == 'id') continue;
$session->write('StateUser.' . $key, $value);
}
//var_dump($json);
@@ -238,12 +240,18 @@ class AppController extends Controller
//echo $newStateUser->id;
}
} else {
+ if(!$redirect) {
+ return ['state' => 'error', 'msg' => 'no pubkey'];
+ }
// we haven't get a pubkey? something seems to gone wrong on the login-server
$this->Flash->error(__('no pubkey'));
//var_dump($json);
return $this->redirect($this->loginServerUrl . 'account/error500/noPubkey', 303);
}
} else {
+ if(!$redirect) {
+ return ['state' => 'not found', 'msg' => 'invalid session'];
+ }
if ($json['state'] === 'not found') {
$this->Flash->error(__('invalid session'));
} else {
@@ -255,6 +263,9 @@ class AppController extends Controller
}
} catch (\Exception $e) {
$msg = $e->getMessage();
+ if(!$redirect) {
+ return ['state' => 'error', 'msg' => 'login-server http request error', 'details' => $msg];
+ }
$this->Flash->error(__('error http request: ') . $msg);
return $this->redirect(['controller' => 'Dashboard', 'action' => 'errorHttpRequest']);
//continue;
@@ -263,6 +274,9 @@ class AppController extends Controller
} else {
// no login
//die("no login");
+ if(!$redirect) {
+ return ['state' => 'error', 'msg' => 'not logged in'];
+ }
if (isset($loginServer['path'])) {
return $this->redirect($loginServer['path'], 303);
} else {
diff --git a/community_server/src/Controller/Component/JsonRequestClientComponent.php b/community_server/src/Controller/Component/JsonRequestClientComponent.php
index 75d2c5a77..8a071b70b 100644
--- a/community_server/src/Controller/Component/JsonRequestClientComponent.php
+++ b/community_server/src/Controller/Component/JsonRequestClientComponent.php
@@ -15,7 +15,7 @@ use Cake\Core\Configure;
class JsonRequestClientComponent extends Component
{
- public function sendTransaction($session_id, $base64Message, $user_balance = 0) {
+ public function sendTransaction($session_id, $base64Message, $user_balance = 0, $auto_sign = false) {
if(!is_numeric($session_id)) {
return ['state' => 'error', 'type' => 'parameter error', 'msg' => 'session_id isn\'t numeric'];
}
@@ -35,7 +35,8 @@ class JsonRequestClientComponent extends Component
return $this->sendRequest(json_encode([
'session_id' => $session_id,
'transaction_base64' => $base64Message,
- 'balance' => $user_balance
+ 'balance' => $user_balance,
+ 'auto_sign' => $auto_sign
]), '/checkTransaction');
}
diff --git a/community_server/src/Controller/StateBalancesController.php b/community_server/src/Controller/StateBalancesController.php
index 5164dce4b..bddf35125 100644
--- a/community_server/src/Controller/StateBalancesController.php
+++ b/community_server/src/Controller/StateBalancesController.php
@@ -21,7 +21,7 @@ class StateBalancesController extends AppController
{
parent::initialize();
//$this->Auth->allow(['add', 'edit']);
- $this->Auth->allow(['overview', 'overviewGdt', 'ajaxGetBalance']);
+ $this->Auth->allow(['overview', 'overviewGdt', 'ajaxGetBalance', 'ajaxListTransactions']);
$this->loadComponent('JsonRequestClient');
}
/**
@@ -39,7 +39,33 @@ class StateBalancesController extends AppController
$this->set(compact('stateBalances'));
}
-
+ private function updateBalances($state_user_id)
+ {
+ $state_balances = $this->StateBalances->find('all')->where(['state_user_id' => $state_user_id]);
+ if($state_balances->count() == 1) {
+ $stateUserTransactionsTable = TableRegistry::getTableLocator()->get('StateUserTransactions');
+ $state_user_transactions = $stateUserTransactionsTable
+ ->find('all')
+ ->where(['state_user_id' => $state_user_id])
+ ->order(['transaction_id ASC'])
+ ->contain(['']);
+ if($state_user_transactions->count() == 0){
+ return;
+ }
+ $last_state_user_transaction = $state_user_transactions->last();
+ $last_transaction = $this->StateBalance->newEntity();
+ $last_transaction->amount = $last_state_user_transaction->balance;
+ $last_transaction->record_date = $last_state_user_transaction->balance_date;
+ // if entrys are nearly the same, we don't need doing anything
+ if(abs($last_transaction->decay - $state_balances->decay) < 100) {
+ return;
+ }
+ foreach($state_user_transactions as $state_user_transaction) {
+
+ }
+
+ }
+ }
public function overview()
{
@@ -177,51 +203,73 @@ class StateBalancesController extends AppController
$this->set('gdtSum', $gdtSum);
}
- public function ajaxGetBalance($session_id)
+ public function ajaxGetBalance($session_id = 0)
{
- if(!isset($session_id) || !$session_id) {
- return $this->returnJson(['state' => 'error', 'msg' => 'invalid session']);
+ if(!$session_id) {
+ return $this->returnJson(['state' => 'error', 'msg' => 'invalid session id']);
+ }
+ $login_result = $this->requestLogin($session_id, false);
+ if($login_result !== true) {
+ return $this->returnJson($login_result);
}
- $startTime = microtime(true);
$session = $this->getRequest()->getSession();
- $result = $this->requestLogin($session_id);
- if ($result !== true) {
- return $this->returnJson(['state' => 'error', 'msg' => 'session not found']);
- }
$user = $session->read('StateUser');
- //var_dump($user);
- return $this->returnJson(['state' => 'success', 'balance' => $user['balance']]);
+ $public_key_bin = hex2bin($user['public_hex']);
+ $stateUserQuery = $this->StateBalances->StateUsers
+ ->find('all')
+ ->where(['public_key' => $public_key_bin])
+ ->contain(['StateBalances']);
+
+ $result_user_count = $stateUserQuery->count();
+ if($result_user_count < 1) {
+ return $this->returnJson(['state' => 'success', 'balance' => 0]);
+ }
+ else if($result_user_count > 1) {
+ return $this->returnJson([
+ 'state' => 'error',
+ 'msg' => 'multiple entrys found',
+ 'details' => ['public_key' => $user['public_hex'], 'entry_count' => $result_count]
+ ]);
+ }
+ $state_balances = $stateUserQuery->first()->state_balances;
+ $state_balances_count = count($state_balances);
+ if($state_balances_count > 1) {
+ return $this->returnJson(['state' => 'error', 'msg' => 'state balances count isn\'t as expected, expect 1 or 0', 'details' => $state_balances_count]);
+ }
+ if(!$state_balances_count) {
+ return $this->returnJson(['state' => 'success', 'balance' => 0]);
+ }
+
+ return $this->returnJson(['state' => 'success', 'balance' => $state_balances[0]->amount]);
}
- public function ajaxListTransactions($session_id, $page, $count)
+ public function ajaxListTransactions($session_id = 0, $sort = 'ASC')
{
- if(!isset($session_id) || !$session_id) {
- return $this->returnJson(['state' => 'error', 'msg' => 'invalid session']);
+ if(!$session_id) {
+ return $this->returnJson(['state' => 'error', 'msg' => 'invalid session id']);
}
+
$startTime = microtime(true);
- $session = $this->getRequest()->getSession();
- $result = $this->requestLogin($session_id);
- if ($result !== true) {
- return $this->returnJson(['state' => 'error', 'msg' => 'session not found']);
+ $login_result = $this->requestLogin($session_id, false);
+ if($login_result !== true) {
+ return $this->returnJson($login_result);
}
+ $session = $this->getRequest()->getSession();
$user = $session->read('StateUser');
-
- $gdtSum = 0;
-
+
+ $gdtSum = 0;
$gdtEntries = $this->JsonRequestClient->sendRequestGDT(['email' => $user['email']], 'GdtEntries' . DS . 'sumPerEmailApi');
if('success' == $gdtEntries['state'] && 'success' == $gdtEntries['data']['state']) {
$gdtSum = intval($gdtEntries['data']['sum']);
} else {
- if($user) {
-
+ if($user) {
$this->addAdminError('StateBalancesController', 'overview', $gdtEntries, $user['id']);
} else {
$this->addAdminError('StateBalancesController', 'overview', $gdtEntries, 0);
}
}
-
$creationsTable = TableRegistry::getTableLocator()->get('TransactionCreations');
$creationTransactions = $creationsTable
@@ -316,6 +364,9 @@ class StateBalancesController extends AppController
]);
}
uasort($transactions, array($this, 'sortTransactions'));
+ if($sort == 'DESC') {
+ $transactions = array_reverse($transactions);
+ }
return $this->returnJson([
'state' => 'success',
'transactions' => $transactions,
diff --git a/community_server/src/Controller/StateUserTransactionsController.php b/community_server/src/Controller/StateUserTransactionsController.php
new file mode 100644
index 000000000..8c252218c
--- /dev/null
+++ b/community_server/src/Controller/StateUserTransactionsController.php
@@ -0,0 +1,290 @@
+Auth->allow(['add', 'edit']);
+ $this->Auth->allow(['ajaxListTransactions']);
+ //$this->loadComponent('JsonRequestClient');
+ }
+ /**
+ * Index method
+ *
+ * @return \Cake\Http\Response|null
+ */
+ public function index()
+ {
+ $this->paginate = [
+ 'contain' => ['StateUsers', 'Transactions', 'TransactionTypes'],
+ ];
+ $stateUserTransactions = $this->paginate($this->StateUserTransactions);
+
+ $this->set(compact('stateUserTransactions'));
+ }
+
+ public function sortTransactions($a, $b)
+ {
+ if ($a['date'] == $b['date']) {
+ return 0;
+ }
+ return ($a['date'] > $b['date']) ? -1 : 1;
+ }
+
+
+ public function ajaxListTransactions($page = 1, $count = 20)
+ {
+ $startTime = microtime(true);
+ $session = $this->getRequest()->getSession();
+ $user = $session->read('StateUser');
+ if(!$user) {
+ return $this->returnJson(['state' => 'error', 'msg' => 'user not found', 'details' => 'exist a valid session cookie?']);
+ }
+
+ $paged_state_user_transactions = $this->StateUserTransactions
+ ->find('all')
+ ->where(['state_user_id' => $user['id'], 'transaction_type_id IN' => [1,2]])
+ ->limit($count)
+ ->page($page)
+ ->order(['transaction_id'])
+ ;
+ $all_user_transactions_count = $this->StateUserTransactions
+ ->find('all')
+ ->where(['state_user_id' => $user['id'], 'transaction_type_id IN' => [1,2]])
+ ->count()
+ ;
+ $creationTransaction_ids = [];
+ $transferTransaction_ids = [];
+ $allTransaction_ids = [];
+ foreach($paged_state_user_transactions as $state_user_transaction) {
+ $allTransaction_ids[] = $state_user_transaction->transaction_id;
+ switch($state_user_transaction->transaction_type_id) {
+ case 1: $creationTransaction_ids[] = $state_user_transaction->transaction_id; break;
+ case 2: $transferTransaction_ids[] = $state_user_transaction->transaction_id; break;
+ }
+ }
+ $transactionsTable = TableRegistry::getTableLocator()->get('Transactions');
+ $transactionCreationsTable = TableRegistry::getTableLocator()->get('TransactionCreations');
+ $transactionSendCoinsTable = TableRegistry::getTableLocator()->get('TransactionSendCoins');
+ $stateUsersTable = TableRegistry::getTableLocator()->get('StateUsers');
+ if(count($allTransaction_ids) > 0) {
+ $transactionEntries = $transactionsTable->find('all')->where(['id IN' => $allTransaction_ids])->order(['id'])->toArray();
+ }
+ if(count($creationTransaction_ids) > 0) {
+ $transactionCreations = $transactionCreationsTable->find('all')->where(['transaction_id IN' => $creationTransaction_ids]);
+ }
+ if(count($transferTransaction_ids)) {
+ $transactionTransfers = $transactionSendCoinsTable->find('all')->where(['transaction_id IN' => $transferTransaction_ids]);
+ }
+ //var_dump($transactions->all());
+
+ $transactions = [];
+ // creations
+ if(isset($transactionCreations)) {
+ foreach ($transactionCreations as $creation) {
+ //var_dump($creation);
+ $transaction_entries_index = array_search($creation->transaction_id, $allTransaction_ids);
+ if(FALSE === $transaction_entries_index) {
+ return $this->returnJson(['state' => 'error', 'msg' => 'code error', 'details' => 'creation, transaction_entries_index is FALSE, shouldn\'t occure']);
+ }
+ $transaction = $transactionEntries[$transaction_entries_index];
+ array_push($transactions, [
+ 'name' => 'Gradido Akademie',
+ 'type' => 'creation',
+ 'transaction_id' => $creation->transaction_id,
+ 'date' => $transaction->received,
+ 'balance' => $creation->amount,
+ 'memo' => $transaction->memo
+ ]);
+ }
+ }
+
+ // involved users
+ if(isset($transactionTransfers)) {
+ $involvedUserIds = [];
+
+ foreach ($transactionTransfers as $transfer) {
+ //var_dump($sendCoins);
+ if ($transfer->state_user_id != $user['id']) {
+ array_push($involvedUserIds, intval($transfer->state_user_id));
+ } elseif ($transfer->receiver_user_id != $user['id']) {
+ array_push($involvedUserIds, intval($transfer->receiver_user_id));
+ }
+ }
+
+ // exchange key with values and drop duplicates
+ $involvedUser_temp = array_flip($involvedUserIds);
+ // exchange back
+ $involvedUserIds = array_flip($involvedUser_temp);
+
+ $involvedUser = $stateUsersTable->find('all', [
+ 'contain' => false,
+ 'where' => ['id IN' => $involvedUserIds],
+ 'fields' => ['id', 'first_name', 'last_name', 'email']
+ ]);
+ //var_dump($involvedUser->toArray());
+ $involvedUserIndices = [];
+ foreach ($involvedUser as $involvedUser) {
+ $involvedUserIndices[$involvedUser->id] = $involvedUser;
+ }
+
+ // transfers - send coins
+ foreach($transactionTransfers as $transfer)
+ {
+ $transaction_entries_index = array_search($transfer->transaction_id, $allTransaction_ids);
+ if(FALSE === $transaction_entries_index) {
+
+ return $this->returnJson([
+ 'state' => 'error',
+ 'msg' => 'code error',
+ 'details' => 'transfer, transaction_entries_index is FALSE, shouldn\'t occure',
+ 'data' => ['haystack' => $allTransaction_ids, 'needle' => $transfer->transaction_id]
+ ]);
+ }
+ $transaction = $transactionEntries[$transaction_entries_index];
+ $type = '';
+ $otherUser = null;
+ $other_user_public = '';
+
+ if ($transfer->state_user_id == $user['id']) {
+ $type = 'send';
+
+ if(isset($involvedUserIndices[$transfer->receiver_user_id])) {
+ $otherUser = $involvedUserIndices[$transfer->receiver_user_id];
+ }
+ $other_user_public = bin2hex(stream_get_contents($transfer->receiver_public_key));
+ } else if ($transfer->receiver_user_id == $user['id']) {
+ $type = 'receive';
+ if(isset($involvedUserIndices[$transfer->state_user_id])) {
+ $otherUser = $involvedUserIndices[$transfer->state_user_id];
+ }
+ if($transfer->sender_public_key) {
+ $other_user_public = bin2hex(stream_get_contents($transfer->sender_public_key));
+ }
+ }
+ if(null == $otherUser) {
+ $otherUser = $stateUsersTable->newEntity();
+ }
+ array_push($transactions, [
+ 'name' => $otherUser->first_name . ' ' . $otherUser->last_name,
+ 'email' => $otherUser->email,
+ 'type' => $type,
+ 'transaction_id' => $transfer->transaction_id,
+ 'date' => $transaction->received,
+ 'balance' => $transfer->amount,
+ 'memo' => $transaction->memo,
+ 'pubkey' => $other_user_public
+ ]);
+ //*/
+
+ }
+ }
+ uasort($transactions, array($this, 'sortTransactions'));
+
+ return $this->returnJson([
+ 'state' => 'success',
+ 'transactions' => $transactions,
+ 'transactionExecutingCount' => $session->read('Transaction.executing'),
+ 'count' => $all_user_transactions_count,
+ 'timeUsed' => microtime(true) - $startTime
+ ]);
+ }
+
+ /**
+ * View method
+ *
+ * @param string|null $id State User Transaction id.
+ * @return \Cake\Http\Response|null
+ * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
+ */
+ public function view($id = null)
+ {
+ $stateUserTransaction = $this->StateUserTransactions->get($id, [
+ 'contain' => ['StateUsers', 'Transactions', 'TransactionTypes'],
+ ]);
+
+ $this->set('stateUserTransaction', $stateUserTransaction);
+ }
+
+ /**
+ * Add method
+ *
+ * @return \Cake\Http\Response|null Redirects on successful add, renders view otherwise.
+ */
+ public function add()
+ {
+ $stateUserTransaction = $this->StateUserTransactions->newEntity();
+ if ($this->request->is('post')) {
+ $stateUserTransaction = $this->StateUserTransactions->patchEntity($stateUserTransaction, $this->request->getData());
+ if ($this->StateUserTransactions->save($stateUserTransaction)) {
+ $this->Flash->success(__('The state user transaction has been saved.'));
+
+ return $this->redirect(['action' => 'index']);
+ }
+ $this->Flash->error(__('The state user transaction could not be saved. Please, try again.'));
+ }
+ $stateUsers = $this->StateUserTransactions->StateUsers->find('list', ['limit' => 200]);
+ $transactions = $this->StateUserTransactions->Transactions->find('list', ['limit' => 200]);
+ $transactionTypes = $this->StateUserTransactions->TransactionTypes->find('list', ['limit' => 200]);
+ $this->set(compact('stateUserTransaction', 'stateUsers', 'transactions', 'transactionTypes'));
+ }
+
+ /**
+ * Edit method
+ *
+ * @param string|null $id State User Transaction id.
+ * @return \Cake\Http\Response|null Redirects on successful edit, renders view otherwise.
+ * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
+ */
+ public function edit($id = null)
+ {
+ $stateUserTransaction = $this->StateUserTransactions->get($id, [
+ 'contain' => [],
+ ]);
+ if ($this->request->is(['patch', 'post', 'put'])) {
+ $stateUserTransaction = $this->StateUserTransactions->patchEntity($stateUserTransaction, $this->request->getData());
+ if ($this->StateUserTransactions->save($stateUserTransaction)) {
+ $this->Flash->success(__('The state user transaction has been saved.'));
+
+ return $this->redirect(['action' => 'index']);
+ }
+ $this->Flash->error(__('The state user transaction could not be saved. Please, try again.'));
+ }
+ $stateUsers = $this->StateUserTransactions->StateUsers->find('list', ['limit' => 200]);
+ $transactions = $this->StateUserTransactions->Transactions->find('list', ['limit' => 200]);
+ $transactionTypes = $this->StateUserTransactions->TransactionTypes->find('list', ['limit' => 200]);
+ $this->set(compact('stateUserTransaction', 'stateUsers', 'transactions', 'transactionTypes'));
+ }
+
+ /**
+ * Delete method
+ *
+ * @param string|null $id State User Transaction id.
+ * @return \Cake\Http\Response|null Redirects to index.
+ * @throws \Cake\Datasource\Exception\RecordNotFoundException When record not found.
+ */
+ public function delete($id = null)
+ {
+ $this->request->allowMethod(['post', 'delete']);
+ $stateUserTransaction = $this->StateUserTransactions->get($id);
+ if ($this->StateUserTransactions->delete($stateUserTransaction)) {
+ $this->Flash->success(__('The state user transaction has been deleted.'));
+ } else {
+ $this->Flash->error(__('The state user transaction could not be deleted. Please, try again.'));
+ }
+
+ return $this->redirect(['action' => 'index']);
+ }
+}
diff --git a/community_server/src/Controller/TransactionCreationsController.php b/community_server/src/Controller/TransactionCreationsController.php
index 9b05c1782..5e29a7997 100644
--- a/community_server/src/Controller/TransactionCreationsController.php
+++ b/community_server/src/Controller/TransactionCreationsController.php
@@ -35,6 +35,7 @@ class TransactionCreationsController extends AppController
$this->loadComponent('JsonRequestClient');
//$this->Auth->allow(['add', 'edit']);
//$this->Auth->allow('create');
+ $this->Auth->allow('ajaxCreate');
$this->set(
'naviHierarchy',
(new NaviHierarchy())->
@@ -446,6 +447,121 @@ class TransactionCreationsController extends AppController
}
}
}
+
+ public function ajaxCreate()
+ {
+ if ($this->request->is('post')) {
+ $startTime = microtime(true);
+ $jsonData = $this->request->input('json_decode', true);
+ $session_id = $jsonData['session_id'];
+ if(!isset($jsonData['session_id']) || intval($jsonData['session_id']) == 0) {
+ return $this->returnJson(['state' => 'parameter missing', 'msg' => 'invalid session id']);
+ }
+
+ $login_result = $this->requestLogin($session_id, false);
+ if($login_result !== true) {
+ return $this->returnJson($login_result);
+ }
+ $session = $this->getRequest()->getSession();
+ $user = $session->read('StateUser');
+
+ $memo = '';
+ if(isset($jsonData['memo'])) {
+ $memo = $jsonData['memo'];
+ }
+ $auto_sign = true;
+ if(isset($jsonData['auto_sign'])) {
+ $auto_sign = $jsonData['auto_sign'];
+ }
+ if(!isset($jsonData['amount']) || intval($jsonData['amount']) <= 0) {
+ return $this->returnJson(['state' => 'parameter missing', 'msg' => 'amount not set or <= 0']);
+ }
+ if(!isset($jsonData['email'])) {
+ return $this->returnJson(['state' => 'parameter missing', 'msg' => 'no receiver email set']);
+ }
+ $amount = intval($jsonData['amount']);
+ if($amount > 10000000) {
+ return $this->returnJson(['state' => 'error', 'msg' => 'amount is to big']);
+ }
+ if($amount <= 0) {
+ return $this->returnJson(['state' => 'error', 'msg' => 'amount must be > 0']);
+ }
+ if(!isset($jsonData['target_date'])) {
+ return $this->returnJson(['state' => 'parameter missing', 'msg' => 'target_date not found']);
+ }
+ //$targetDate = $requestData['target_date'];
+ $stateUserTable = TableRegistry::getTableLocator()->get('StateUsers');
+ $requestAnswear = $this->JsonRequestClient->sendRequest(json_encode([
+ 'session_id' => $session_id,
+ 'email' => $jsonData['email'],
+ 'ask' => ['user.pubkeyhex', 'user.disabled', 'user.identHash']
+ ]), '/getUserInfos');
+ $receiverPubKeyHex = '';
+ if('success' == $requestAnswear['state'] && 'success' == $requestAnswear['data']['state']) {
+ // will be allways 64 byte long, even if it is empty
+ $receiverPubKeyHex = $requestAnswear['data']['userData']['pubkeyhex'];
+ } else {
+ return $this->returnJson([
+ 'state' => 'error',
+ 'msg' => 'receiver email not found on login-server',
+ 'details' => $requestAnswear,
+ 'timeUsed' => microtime(true) - $startTime
+ ]);
+ }
+ if($requestAnswear['data']['userData']['disabled']) {
+ return $this->returnJson([
+ 'state' => 'error',
+ 'msg' => 'receiver is currently disabled, he cannot receive creations',
+ 'timeUsed' => microtime(true) - $startTime
+ ]);
+ }
+
+ $builderResult = TransactionCreation::build(
+ $amount,
+ $memo,
+ $receiverPubKeyHex,
+ $requestAnswear['data']['userData']['identHash'],
+ new FrozenDate($jsonData['target_date'])
+ );
+ $transaction_base64 = '';
+ if ($builderResult['state'] == 'success') {
+ // todo: maybe use sodium base 64 encoder to make sure it can be readed from login-server
+ $transaction_base64 = base64_encode($builderResult['transactionBody']->serializeToString());
+ }
+
+ $requestResult = $this->JsonRequestClient->sendTransaction(
+ $session_id,
+ $transaction_base64,
+ $user['balance'],
+ $auto_sign
+ );
+ if ($requestResult['state'] != 'success') {
+ $msg = 'error returned from login server';
+ if ($requestResult['type'] === 'request error') {
+ $msg = 'login server couldn\'t reached';
+ }
+ //$this->Flash->error(__('Error, please wait for the admin to fix it'));
+ return $this->returnJson([
+ 'state' => 'request error',
+ 'msg' => $msg,
+ 'details' => $requestResult,
+ 'timeUsed' => microtime(true) - $startTime
+ ]);
+ } else {
+ $json = $requestResult['data'];
+ if ($json['state'] != 'success') {
+ if ($json['msg'] == 'session not found') {
+ $session->destroy();
+ return $this->returnJson(['state' => 'error', 'msg' => 'session not found', 'timeUsed' => microtime(true) - $startTime]);
+ } else {
+ return $this->returnJson(['state' => 'error', 'msg' => 'login server error', 'details' => $json, 'timeUsed' => microtime(true) - $startTime]);
+ }
+ } else {
+ return $this->returnJson(['state' => 'success', 'timeUsed' => microtime(true) - $startTime]);
+ }
+ }
+ }
+ }
/**
* Add method
diff --git a/community_server/src/Controller/TransactionSendCoinsController.php b/community_server/src/Controller/TransactionSendCoinsController.php
index 40cccb90e..2f48de063 100644
--- a/community_server/src/Controller/TransactionSendCoinsController.php
+++ b/community_server/src/Controller/TransactionSendCoinsController.php
@@ -37,6 +37,7 @@ class TransactionSendCoinsController extends AppController
//$this->Auth->allow(['add', 'edit']);
$this->Auth->allow('create');
$this->Auth->allow('createRaw');
+ $this->Auth->allow('ajaxCreate');
$this->set(
'naviHierarchy',
(new NaviHierarchy())->
@@ -288,6 +289,143 @@ class TransactionSendCoinsController extends AppController
$this->set('timeUsed', microtime(true) - $startTime);
}
+
+ public function ajaxCreate()
+ {
+ if ($this->request->is('post')) {
+ $startTime = microtime(true);
+ $jsonData = $this->request->input('json_decode', true);
+ $session_id = $jsonData['session_id'];
+ if(!$session_id) {
+ return $this->returnJson(['state' => 'error', 'msg' => 'invalid session id']);
+ }
+
+ $login_result = $this->requestLogin($session_id, false);
+ if($login_result !== true) {
+ return $this->returnJson($login_result);
+ }
+ $session = $this->getRequest()->getSession();
+ $user = $session->read('StateUser');
+
+ $receiverPubKeyHex = '';
+ $senderPubKeyHex = $user['public_hex'];
+
+ if(!isset($jsonData['amount']) || !isset($jsonData['email'])) {
+ return $this->returnJson(['state' => 'parameter missing', 'msg' => 'amount and/or email not set']);
+ }
+ $amount = intval($jsonData['amount']);
+ if($amount < 0) {
+ return $this->returnJson(['state' => 'error', 'msg' => 'amout must be > 0 and int']);
+ }
+
+ if(!isset($user['balance']) || $jsonData['amount'] > $user['balance']) {
+ return $this->returnJson(['state' => 'error', 'msg' => 'not enough GDD']);
+ }
+ $memo = '';
+ if(isset($jsonData['memo'])) {
+ $memo = $jsonData['memo'];
+ }
+
+ $receiverEmail = $jsonData['email'];
+ if($receiverEmail === $user['email']) {
+ return $this->returnJson(['state' => 'error', 'msg' => 'sender and receiver email are the same']);
+ }
+
+ $requestAnswear = $this->JsonRequestClient->sendRequest(json_encode([
+ 'session_id' => $session_id,
+ 'email' => $receiverEmail,
+ 'ask' => ['user.pubkeyhex', 'user.disabled']
+ ]), '/getUserInfos');
+ if('success' == $requestAnswear['state'] && 'success' == $requestAnswear['data']['state']) {
+ // will be allways 64 byte long, even if it is empty
+ $receiverPubKeyHex = $requestAnswear['data']['userData']['pubkeyhex'];
+ } else {
+ return $this->returnJson([
+ 'state' => 'error',
+ 'msg' => 'receiver email not found on login-server',
+ 'details' => $requestAnswear,
+ 'timeUsed' => microtime(true) - $startTime
+ ]);
+ }
+ if($requestAnswear['data']['userData']['disabled']) {
+ return $this->returnJson([
+ 'state' => 'error',
+ 'msg' => 'receiver is currently disabled, he cannot receive payments',
+ 'timeUsed' => microtime(true) - $startTime
+ ]);
+ }
+
+
+ //var_dump($sessionStateUser);
+
+ $builderResult = TransactionTransfer::build(
+ $amount,
+ $memo,
+ $receiverPubKeyHex,
+ $senderPubKeyHex
+ );
+ $auto_sign = true;
+ if(isset($jsonData['auto_sign'])) {
+ $auto_sign = $jsonData['auto_sign'];
+ }
+ if($builderResult['state'] === 'success') {
+
+ $http = new Client();
+ try {
+ $loginServer = Configure::read('LoginServer');
+ $url = $loginServer['host'] . ':' . $loginServer['port'];
+
+ $response = $http->post($url . '/checkTransaction', json_encode([
+ 'session_id' => $session_id,
+ 'transaction_base64' => base64_encode($builderResult['transactionBody']->serializeToString()),
+ 'auto_sign' => $auto_sign,
+ 'balance' => $user['balance']
+ ]), ['type' => 'json']);
+ $json = $response->getJson();
+ if($json['state'] != 'success') {
+ if($json['msg'] == 'session not found') {
+ $session->destroy();
+ return $this->returnJson([
+ 'state' => 'error',
+ 'msg' => 'session not found',
+ 'details' => $session_id,
+ 'timeUsed' => microtime(true) - $startTime
+ ]);
+ //$this->Flash->error(__('session not found, please login again'));
+ } else {
+ return $this->returnJson([
+ 'state' => 'error',
+ 'msg' => 'login server return error',
+ 'details' => $json,
+ 'timeUsed' => microtime(true) - $startTime
+ ]);
+ }
+ } else {
+ return $this->returnJson(['state' => 'success', 'timeUsed' => microtime(true) - $startTime]);
+ }
+
+ } catch(\Exception $e) {
+ $msg = $e->getMessage();
+ //$this->Flash->error(__('error http request: ') . $msg);
+ return $this->returnJson([
+ 'state' => 'error',
+ 'msg' => 'error http request',
+ 'details' => $msg,
+ 'timeUsed' => microtime(true) - $startTime
+ ]);
+ }
+
+ } else {
+ return $this->returnJson([
+ 'state' => 'error',
+ 'msg' => 'no valid receiver public key given',
+ 'details' => $receiverPubKeyHex,
+ 'timeUsed' => microtime(true) - $startTime
+ ]);
+ }
+ }
+ return $this->returnJson(['state' => 'error', 'msg' => 'no post request']);
+ }
public function createRaw()
{
diff --git a/community_server/src/Controller/TransactionsController.php b/community_server/src/Controller/TransactionsController.php
index 2f0bc2747..4f8e04c2b 100644
--- a/community_server/src/Controller/TransactionsController.php
+++ b/community_server/src/Controller/TransactionsController.php
@@ -7,6 +7,7 @@ use Model\Transactions\Transaction;
use Model\Transactions\TransactionBody;
use Cake\Core\Configure;
+use Cake\ORM\TableRegistry;
/**
* Transactions Controller
@@ -40,6 +41,118 @@ class TransactionsController extends AppController
$this->set(compact('transactions'));
}
+
+ public function synchronizeWithStateUserTransactions()
+ {
+ $startTime = microtime(true);
+ $missing_transaction_ids = [];
+ $transaction_ids = $this->Transactions
+ ->find('all')
+ ->select(['id', 'transaction_type_id'])
+ ->order(['id'])
+ ->all()
+ ;
+ $state_user_transaction_ids = $this->Transactions->StateUserTransactions
+ ->find('all')
+ ->select(['transaction_id'])
+ ->group(['transaction_id'])
+ ->order(['transaction_id'])
+ ->toArray()
+ ;
+ $i2 = 0;
+ $count1 = count($transaction_ids);
+ $count2 = count($state_user_transaction_ids);
+ foreach($transaction_ids as $i1 => $tr_id) {
+ //echo "$i1: ";
+ if($i2 >= $count2) {
+ $missing_transaction_ids[] = $tr_id;
+ //echo "adding to missing: $tr_id, continue
";
+ continue;
+ }
+ $stu_id = $state_user_transaction_ids[$i2];
+ if($tr_id->id == $stu_id->transaction_id) {
+ $i2++;
+ //echo "after i2++: $i2
";
+ } else if($tr_id->id < $stu_id->transaction_id) {
+ $missing_transaction_ids[] = $tr_id;
+ //echo "adding to missing: $tr_id
";
+ }
+ }
+
+ if($this->request->is('POST')) {
+ $tablesForType = [
+ 1 => $this->Transactions->TransactionCreations,
+ 2 => $this->Transactions->TransactionSendCoins,
+ 3 => $this->Transactions->TransactionGroupCreates,
+ 4 => $this->Transactions->TransactionGroupAddaddress,
+ 5 => $this->Transactions->TransactionGroupAddaddress
+ ];
+ $idsForType = [];
+ foreach($missing_transaction_ids as $i => $transaction) {
+ if(!isset($idsForType[$transaction->transaction_type_id])) {
+ $idsForType[$transaction->transaction_type_id] = [];
+ }
+ $idsForType[$transaction->transaction_type_id][] = $transaction->id;
+ if($i > 200) break;
+ }
+ $entities = [];
+ $state_user_ids = [];
+ foreach($idsForType as $type_id => $transaction_ids) {
+ $specific_transactions = $tablesForType[$type_id]->find('all')->where(['transaction_id IN' => $transaction_ids])->toArray();
+ $keys = $tablesForType[$type_id]->getSchema()->columns();
+ //var_dump($keys);
+ foreach($specific_transactions as $specific) {
+
+ foreach($keys as $key) {
+ if(preg_match('/_user_id/', $key)) {
+ $entity = $this->Transactions->StateUserTransactions->newEntity();
+ $entity->transaction_id = $specific['transaction_id'];
+ $entity->transaction_type_id = $type_id;
+ $entity->state_user_id = $specific[$key];
+ if(!in_array($entity->state_user_id, $state_user_ids)) {
+ array_push($state_user_ids, $entity->state_user_id);
+ }
+ $entities[] = $entity;
+ }
+ }
+ }
+ }
+ //var_dump($entities);
+ $stateUsersTable = TableRegistry::getTableLocator()->get('StateUsers');
+ $existingStateUsers = $stateUsersTable->find('all')->select(['id'])->where(['id IN' => $state_user_ids])->order(['id'])->all();
+ $existing_state_user_ids = [];
+ $finalEntities = [];
+ foreach($existingStateUsers as $stateUser) {
+ $existing_state_user_ids[] = $stateUser->id;
+ }
+ foreach($entities as $entity) {
+ if(in_array($entity->state_user_id, $existing_state_user_ids)) {
+ array_push($finalEntities, $entity);
+ }
+ }
+
+
+ $results = $this->Transactions->StateUserTransactions->saveMany($finalEntities);
+ foreach($entities as $i => $entity) {
+ $errors = $entity->getErrors();
+ /* if(count($errors)) {
+ echo "$i: ";
+ echo json_encode($errors);
+ echo "
";
+ echo "state_user_id: " . $entity->state_user_id;
+ echo "
";
+ }*/
+ }
+ $this->set('results', $results);
+ $this->set('entities', $entities);
+ }
+
+ $this->set('missing_transactions', $missing_transaction_ids);
+ $this->set('count1', $count1);
+ $this->set('count2', $count2);
+ $timeUsed = microtime(true) - $startTime;
+ $this->set('timeUsed', $timeUsed);
+ }
/**
* View method
diff --git a/community_server/src/Model/Messages/GPBMetadata/BasicTypes.php b/community_server/src/GPBMetadata/BasicTypes.php
similarity index 62%
rename from community_server/src/Model/Messages/GPBMetadata/BasicTypes.php
rename to community_server/src/GPBMetadata/BasicTypes.php
index 113202053..f74eb472b 100644
--- a/community_server/src/Model/Messages/GPBMetadata/BasicTypes.php
+++ b/community_server/src/GPBMetadata/BasicTypes.php
@@ -15,14 +15,15 @@ class BasicTypes
return;
}
$pool->internalAddGeneratedFile(hex2bin(
- "0ae0030a10426173696354797065732e70726f746f12076772616469646f" .
- "22380a034b657912110a076564323535313918022001280c480012170a0d" .
- "656432353531395f726566313018032001280c480042050a036b65792258" .
- "0a0d5369676e617475726550616972120e0a067075624b65791801200128" .
- "0c12110a076564323535313918022001280c480012170a0d656432353531" .
- "395f726566313018032001280c4800420b0a097369676e61747572652237" .
- "0a0c5369676e61747572654d617012270a07736967506169721801200328" .
- "0b32162e6772616469646f2e5369676e617475726550616972222b0a0954" .
+ "0afe030a10426173696354797065732e70726f746f12166d6f64656c2e6d" .
+ "657373616765732e6772616469646f22380a034b657912110a0765643235" .
+ "35313918022001280c480012170a0d656432353531395f72656631301803" .
+ "2001280c480042050a036b657922580a0d5369676e617475726550616972" .
+ "120e0a067075624b657918012001280c12110a0765643235353139180220" .
+ "01280c480012170a0d656432353531395f726566313018032001280c4800" .
+ "420b0a097369676e617475726522460a0c5369676e61747572654d617012" .
+ "360a077369675061697218012003280b32252e6d6f64656c2e6d65737361" .
+ "6765732e6772616469646f2e5369676e617475726550616972222b0a0954" .
"696d657374616d70120f0a077365636f6e6473180120012803120d0a056e" .
"616e6f7318022001280522230a1054696d657374616d705365636f6e6473" .
"120f0a077365636f6e647318012001280322590a0c53656e646572416d6f" .
diff --git a/community_server/src/GPBMetadata/StateCreateGroup.php b/community_server/src/GPBMetadata/StateCreateGroup.php
new file mode 100644
index 000000000..7098eb808
--- /dev/null
+++ b/community_server/src/GPBMetadata/StateCreateGroup.php
@@ -0,0 +1,32 @@
+internalAddGeneratedFile(hex2bin(
+ "0ae4010a16537461746543726561746547726f75702e70726f746f12166d" .
+ "6f64656c2e6d657373616765732e6772616469646f22a9010a1053746174" .
+ "6543726561746547726f7570120c0a046e616d6518012001280912330a0e" .
+ "67726f75705075626c69634b657918022001280b321b2e6d6f64656c2e6d" .
+ "657373616765732e6772616469646f2e4b657912390a14706172656e7447" .
+ "726f75705075626c69634b657918032001280b321b2e6d6f64656c2e6d65" .
+ "7373616765732e6772616469646f2e4b65794a0408041005521168656465" .
+ "7261436f6e73656e7375734964620670726f746f33"
+ ), true);
+
+ static::$is_initialized = true;
+ }
+}
+
diff --git a/community_server/src/GPBMetadata/StateGroupChangeParent.php b/community_server/src/GPBMetadata/StateGroupChangeParent.php
new file mode 100644
index 000000000..c87782041
--- /dev/null
+++ b/community_server/src/GPBMetadata/StateGroupChangeParent.php
@@ -0,0 +1,33 @@
+internalAddGeneratedFile(hex2bin(
+ "0a8a020a1c537461746547726f75704368616e6765506172656e742e7072" .
+ "6f746f12166d6f64656c2e6d657373616765732e6772616469646f22c901" .
+ "0a16537461746547726f75704368616e6765506172656e7412330a0e6772" .
+ "6f75705075626c69634b657918012001280b321b2e6d6f64656c2e6d6573" .
+ "73616765732e6772616469646f2e4b6579123c0a176e6577506172656e74" .
+ "47726f75705075626c69634b657918022001280b321b2e6d6f64656c2e6d" .
+ "657373616765732e6772616469646f2e4b6579123c0a176f6c6450617265" .
+ "6e7447726f75705075626c69634b657918032001280b321b2e6d6f64656c" .
+ "2e6d657373616765732e6772616469646f2e4b6579620670726f746f33"
+ ), true);
+
+ static::$is_initialized = true;
+ }
+}
+
diff --git a/community_server/src/GPBMetadata/Transaction.php b/community_server/src/GPBMetadata/Transaction.php
new file mode 100644
index 000000000..d86999450
--- /dev/null
+++ b/community_server/src/GPBMetadata/Transaction.php
@@ -0,0 +1,32 @@
+internalAddGeneratedFile(hex2bin(
+ "0ae4010a115472616e73616374696f6e2e70726f746f12166d6f64656c2e" .
+ "6d657373616765732e6772616469646f22ae010a0b5472616e7361637469" .
+ "6f6e120a0a026964180120012804123a0a08726563656976656418022001" .
+ "280b32282e6d6f64656c2e6d657373616765732e6772616469646f2e5469" .
+ "6d657374616d705365636f6e647312340a067369674d617018032001280b" .
+ "32242e6d6f64656c2e6d657373616765732e6772616469646f2e5369676e" .
+ "61747572654d6170120e0a0674784861736818042001280c12110a09626f" .
+ "6479427974657318052001280c620670726f746f33"
+ ), true);
+
+ static::$is_initialized = true;
+ }
+}
+
diff --git a/community_server/src/GPBMetadata/TransactionBody.php b/community_server/src/GPBMetadata/TransactionBody.php
new file mode 100644
index 000000000..61d99eb96
--- /dev/null
+++ b/community_server/src/GPBMetadata/TransactionBody.php
@@ -0,0 +1,46 @@
+internalAddGeneratedFile(hex2bin(
+ "0a84040a155472616e73616374696f6e426f64792e70726f746f12166d6f" .
+ "64656c2e6d657373616765732e6772616469646f1a165374617465437265" .
+ "61746547726f75702e70726f746f1a1c537461746547726f75704368616e" .
+ "6765506172656e742e70726f746f1a195472616e73616374696f6e437265" .
+ "6174696f6e2e70726f746f1a10426173696354797065732e70726f746f22" .
+ "e7020a0f5472616e73616374696f6e426f6479120c0a046d656d6f180120" .
+ "01280912390a076372656174656418022001280b32282e6d6f64656c2e6d" .
+ "657373616765732e6772616469646f2e54696d657374616d705365636f6e" .
+ "6473123f0a0b63726561746547726f757018062001280b32282e6d6f6465" .
+ "6c2e6d657373616765732e6772616469646f2e5374617465437265617465" .
+ "47726f75704800124b0a1167726f75704368616e6765506172656e741807" .
+ "2001280b322e2e6d6f64656c2e6d657373616765732e6772616469646f2e" .
+ "537461746547726f75704368616e6765506172656e74480012340a087472" .
+ "616e7366657218082001280b32202e6d6f64656c2e6d657373616765732e" .
+ "6772616469646f2e5472616e736665724800123f0a086372656174696f6e" .
+ "18092001280b322b2e6d6f64656c2e6d657373616765732e677261646964" .
+ "6f2e5472616e73616374696f6e4372656174696f6e480042060a04646174" .
+ "61620670726f746f33"
+ ), true);
+
+ static::$is_initialized = true;
+ }
+}
+
diff --git a/community_server/src/GPBMetadata/TransactionCreation.php b/community_server/src/GPBMetadata/TransactionCreation.php
new file mode 100644
index 000000000..f1ce2cf1a
--- /dev/null
+++ b/community_server/src/GPBMetadata/TransactionCreation.php
@@ -0,0 +1,32 @@
+internalAddGeneratedFile(hex2bin(
+ "0ae6010a195472616e73616374696f6e4372656174696f6e2e70726f746f" .
+ "12166d6f64656c2e6d657373616765732e6772616469646f22a8010a1354" .
+ "72616e73616374696f6e4372656174696f6e123e0a0e7265636569766572" .
+ "416d6f756e7418012001280b32262e6d6f64656c2e6d657373616765732e" .
+ "6772616469646f2e5265636569766572416d6f756e7412120a0a6964656e" .
+ "745f68617368180220012811123d0a0b7461726765745f64617465180320" .
+ "01280b32282e6d6f64656c2e6d657373616765732e6772616469646f2e54" .
+ "696d657374616d705365636f6e6473620670726f746f33"
+ ), true);
+
+ static::$is_initialized = true;
+ }
+}
+
diff --git a/community_server/src/Model/Messages/GPBMetadata/Transfer.php b/community_server/src/GPBMetadata/Transfer.php
similarity index 51%
rename from community_server/src/Model/Messages/GPBMetadata/Transfer.php
rename to community_server/src/GPBMetadata/Transfer.php
index f5236ee43..b0b2c3a2d 100644
--- a/community_server/src/Model/Messages/GPBMetadata/Transfer.php
+++ b/community_server/src/GPBMetadata/Transfer.php
@@ -16,11 +16,13 @@ class Transfer
}
\GPBMetadata\BasicTypes::initOnce();
$pool->internalAddGeneratedFile(hex2bin(
- "0a8d010a0e5472616e736665722e70726f746f12076772616469646f226a" .
- "0a085472616e73666572122c0a0d73656e646572416d6f756e7473180120" .
- "03280b32152e6772616469646f2e53656e646572416d6f756e7412300a0f" .
- "7265636569766572416d6f756e747318022003280b32172e677261646964" .
- "6f2e5265636569766572416d6f756e74620670726f746f33"
+ "0abb010a0e5472616e736665722e70726f746f12166d6f64656c2e6d6573" .
+ "73616765732e6772616469646f2288010a085472616e73666572123b0a0d" .
+ "73656e646572416d6f756e747318012003280b32242e6d6f64656c2e6d65" .
+ "7373616765732e6772616469646f2e53656e646572416d6f756e74123f0a" .
+ "0f7265636569766572416d6f756e747318022003280b32262e6d6f64656c" .
+ "2e6d657373616765732e6772616469646f2e5265636569766572416d6f75" .
+ "6e74620670726f746f33"
), true);
static::$is_initialized = true;
diff --git a/community_server/src/Model/Entity/StateBalance.php b/community_server/src/Model/Entity/StateBalance.php
index 30d0d091f..174e785b4 100644
--- a/community_server/src/Model/Entity/StateBalance.php
+++ b/community_server/src/Model/Entity/StateBalance.php
@@ -2,6 +2,7 @@
namespace App\Model\Entity;
use Cake\ORM\Entity;
+use Cake\I18n\Time;
/**
* StateBalance Entity
@@ -28,6 +29,39 @@ class StateBalance extends Entity
'state_user_id' => true,
'modified' => true,
'amount' => true,
+ 'record_date' => true,
'state_user' => true
];
+
+ protected $_virtual = ['decay'];
+
+ protected function _getDecay()
+ {
+ // decay factor in seconds per year
+ // q = e^((lg Kn - lg K0) / n)
+ // 0.999999978
+ //
+ // SELECT TIMESTAMPDIFF(SECOND, modified, CURDATE()) AS age_in_seconds from state_balances
+ // decay_for_duration = decay_factor^seconds
+ // decay = gradido_cent * decay_for_duration
+ $decay_duration = intval(Time::now()->getTimestamp() - $this->record_date->getTimestamp());
+ if($decay_duration === 0) {
+ return $this->amount;
+ }
+ return $this->amount * pow(0.99999997802044727, $decay_duration);
+
+ }
+ public function partDecay($target_date)
+ {
+ $decay_duration = intval($target_date->getTimestamp() - $this->record_date->getTimestamp());
+ if($decay_duration <= 0) {
+ return $this->amount;
+ }
+ return $this->amount * pow(0.99999997802044727, $decay_duration);
+ }
+
+ public function decayDuration($target_date)
+ {
+ return intval($target_date->getTimestamp() - $this->record_date->getTimestamp());
+ }
}
diff --git a/community_server/src/Model/Entity/StateUserTransaction.php b/community_server/src/Model/Entity/StateUserTransaction.php
new file mode 100644
index 000000000..9fed345a5
--- /dev/null
+++ b/community_server/src/Model/Entity/StateUserTransaction.php
@@ -0,0 +1,37 @@
+ true,
+ 'transaction_id' => true,
+ 'transaction_type_id' => true,
+ 'state_user' => true,
+ 'transaction' => true,
+ 'transaction_type' => true,
+ ];
+}
diff --git a/community_server/src/Model/Messages/GPBMetadata/StateCreateGroup.php b/community_server/src/Model/Messages/GPBMetadata/StateCreateGroup.php
deleted file mode 100644
index 24ef6b2a7..000000000
--- a/community_server/src/Model/Messages/GPBMetadata/StateCreateGroup.php
+++ /dev/null
@@ -1,31 +0,0 @@
-internalAddGeneratedFile(hex2bin(
- "0ab7010a16537461746543726561746547726f75702e70726f746f120767" .
- "72616469646f228b010a10537461746543726561746547726f7570120c0a" .
- "046e616d6518012001280912240a0e67726f75705075626c69634b657918" .
- "022001280b320c2e6772616469646f2e4b6579122a0a14706172656e7447" .
- "726f75705075626c69634b657918032001280b320c2e6772616469646f2e" .
- "4b65794a04080410055211686564657261436f6e73656e73757349646206" .
- "70726f746f33"
- ), true);
-
- static::$is_initialized = true;
- }
-}
-
diff --git a/community_server/src/Model/Messages/GPBMetadata/StateGroupChangeParent.php b/community_server/src/Model/Messages/GPBMetadata/StateGroupChangeParent.php
deleted file mode 100644
index 8f9111842..000000000
--- a/community_server/src/Model/Messages/GPBMetadata/StateGroupChangeParent.php
+++ /dev/null
@@ -1,31 +0,0 @@
-internalAddGeneratedFile(hex2bin(
- "0ace010a1c537461746547726f75704368616e6765506172656e742e7072" .
- "6f746f12076772616469646f229c010a16537461746547726f7570436861" .
- "6e6765506172656e7412240a0e67726f75705075626c69634b6579180120" .
- "01280b320c2e6772616469646f2e4b6579122d0a176e6577506172656e74" .
- "47726f75705075626c69634b657918022001280b320c2e6772616469646f" .
- "2e4b6579122d0a176f6c64506172656e7447726f75705075626c69634b65" .
- "7918032001280b320c2e6772616469646f2e4b6579620670726f746f33"
- ), true);
-
- static::$is_initialized = true;
- }
-}
-
diff --git a/community_server/src/Model/Messages/GPBMetadata/Transaction.php b/community_server/src/Model/Messages/GPBMetadata/Transaction.php
deleted file mode 100644
index 9f1d8ff23..000000000
--- a/community_server/src/Model/Messages/GPBMetadata/Transaction.php
+++ /dev/null
@@ -1,31 +0,0 @@
-internalAddGeneratedFile(hex2bin(
- "0ab7010a115472616e73616374696f6e2e70726f746f1207677261646964" .
- "6f2290010a0b5472616e73616374696f6e120a0a02696418012001280412" .
- "2b0a08726563656976656418022001280b32192e6772616469646f2e5469" .
- "6d657374616d705365636f6e647312250a067369674d617018032001280b" .
- "32152e6772616469646f2e5369676e61747572654d6170120e0a06747848" .
- "61736818042001280c12110a09626f6479427974657318052001280c6206" .
- "70726f746f33"
- ), true);
-
- static::$is_initialized = true;
- }
-}
-
diff --git a/community_server/src/Model/Messages/GPBMetadata/TransactionBody.php b/community_server/src/Model/Messages/GPBMetadata/TransactionBody.php
deleted file mode 100644
index d225389b6..000000000
--- a/community_server/src/Model/Messages/GPBMetadata/TransactionBody.php
+++ /dev/null
@@ -1,40 +0,0 @@
-internalAddGeneratedFile(hex2bin(
- "0aec020a155472616e73616374696f6e426f64792e70726f746f12076772" .
- "616469646f1a16537461746543726561746547726f75702e70726f746f1a" .
- "1c537461746547726f75704368616e6765506172656e742e70726f746f1a" .
- "195472616e73616374696f6e4372656174696f6e2e70726f746f22f0010a" .
- "0f5472616e73616374696f6e426f6479120c0a046d656d6f180120012809" .
- "12300a0b63726561746547726f757018022001280b32192e677261646964" .
- "6f2e537461746543726561746547726f75704800123c0a1167726f757043" .
- "68616e6765506172656e7418032001280b321f2e6772616469646f2e5374" .
- "61746547726f75704368616e6765506172656e74480012250a087472616e" .
- "7366657218042001280b32112e6772616469646f2e5472616e7366657248" .
- "0012300a086372656174696f6e18052001280b321c2e6772616469646f2e" .
- "5472616e73616374696f6e4372656174696f6e480042060a046461746162" .
- "0670726f746f33"
- ), true);
-
- static::$is_initialized = true;
- }
-}
-
diff --git a/community_server/src/Model/Messages/GPBMetadata/TransactionCreation.php b/community_server/src/Model/Messages/GPBMetadata/TransactionCreation.php
deleted file mode 100644
index c8984298c..000000000
--- a/community_server/src/Model/Messages/GPBMetadata/TransactionCreation.php
+++ /dev/null
@@ -1,29 +0,0 @@
-internalAddGeneratedFile(hex2bin(
- "0a88010a195472616e73616374696f6e4372656174696f6e2e70726f746f" .
- "12076772616469646f225a0a135472616e73616374696f6e437265617469" .
- "6f6e122f0a0e7265636569766572416d6f756e7418012001280b32172e67" .
- "72616469646f2e5265636569766572416d6f756e7412120a0a6964656e74" .
- "5f68617368180220012811620670726f746f33"
- ), true);
-
- static::$is_initialized = true;
- }
-}
-
diff --git a/community_server/src/Model/Table/StateUserTransactionsTable.php b/community_server/src/Model/Table/StateUserTransactionsTable.php
new file mode 100644
index 000000000..6cfe94a23
--- /dev/null
+++ b/community_server/src/Model/Table/StateUserTransactionsTable.php
@@ -0,0 +1,85 @@
+setTable('state_user_transactions');
+ $this->setDisplayField('id');
+ $this->setPrimaryKey('id');
+
+ $this->belongsTo('StateUsers', [
+ 'foreignKey' => 'state_user_id',
+ 'joinType' => 'INNER',
+ ]);
+ $this->belongsTo('Transactions', [
+ 'foreignKey' => 'transaction_id',
+ 'joinType' => 'INNER',
+ ]);
+ $this->belongsTo('TransactionTypes', [
+ 'foreignKey' => 'transaction_type_id',
+ 'joinType' => 'INNER',
+ ]);
+ }
+
+ /**
+ * Default validation rules.
+ *
+ * @param \Cake\Validation\Validator $validator Validator instance.
+ * @return \Cake\Validation\Validator
+ */
+ public function validationDefault(Validator $validator)
+ {
+ $validator
+ ->nonNegativeInteger('id')
+ ->allowEmptyString('id', null, 'create');
+
+ return $validator;
+ }
+
+ /**
+ * Returns a rules checker object that will be used for validating
+ * application integrity.
+ *
+ * @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
+ * @return \Cake\ORM\RulesChecker
+ */
+ public function buildRules(RulesChecker $rules)
+ {
+ $rules->add($rules->existsIn(['state_user_id'], 'StateUsers'));
+ $rules->add($rules->existsIn(['transaction_id'], 'Transactions'));
+ $rules->add($rules->existsIn(['transaction_type_id'], 'TransactionTypes'));
+
+ return $rules;
+ }
+}
diff --git a/community_server/src/Model/Table/TransactionsTable.php b/community_server/src/Model/Table/TransactionsTable.php
index 75659a5b2..6777070fe 100644
--- a/community_server/src/Model/Table/TransactionsTable.php
+++ b/community_server/src/Model/Table/TransactionsTable.php
@@ -73,6 +73,9 @@ class TransactionsTable extends Table
$this->hasMany('TransactionSignatures', [
'foreignKey' => 'transaction_id'
]);
+ $this->hasMany('StateUserTransactions', [
+ 'foreignKey' => 'transaction_id'
+ ]);
}
/**
diff --git a/community_server/src/Model/Transactions/TransactionBase.php b/community_server/src/Model/Transactions/TransactionBase.php
index 4198fbaaa..80ce8ef5d 100644
--- a/community_server/src/Model/Transactions/TransactionBase.php
+++ b/community_server/src/Model/Transactions/TransactionBase.php
@@ -6,7 +6,7 @@ use Cake\ORM\TableRegistry;
class TransactionBase {
private $errors = [];
- static $stateUsersTable = null;
+ static $tables = [];
public function getErrors() {
return $this->errors;
@@ -24,18 +24,17 @@ class TransactionBase {
return count($this->errors) > 0;
}
- public static function getStateUsersTable()
- {
- if(!self::$stateUsersTable) {
- self::$stateUsersTable = TableRegistry::getTableLocator()->get('state_users');
+ public static function getTable($tableName) {
+ if(!isset(self::$tables[$tableName])) {
+ self::$tables[$tableName] = TableRegistry::getTableLocator()->get($tableName);
}
- return self::$stateUsersTable;
+ return self::$tables[$tableName];
}
protected function getStateUserId($publicKey) {
- $stateUsersTable = self::getStateUsersTable();
+ $stateUsersTable = self::getTable('state_users');
$stateUser = $stateUsersTable->find('all')->select(['id'])->where(['public_key' => $publicKey])->first();
if($stateUser) {
return $stateUser->id;
@@ -53,7 +52,7 @@ class TransactionBase {
}
protected function getStateUser($id) {
- $stateUsersTable = self::getStateUsersTable();
+ $stateUsersTable = self::getTable('state_users');
$stateUser = $stateUsersTable->get($id);
if($stateUser) {
return $stateUser;
@@ -63,9 +62,9 @@ class TransactionBase {
}
- protected function updateStateBalance($stateUserId, $addAmountCent) {
+ protected function updateStateBalance($stateUserId, $addAmountCent, $recordDate) {
$finalBalance = 0;
- $stateBalancesTable = TableRegistry::getTableLocator()->get('stateBalances');
+ $stateBalancesTable = self::getTable('stateBalances');
$stateBalanceQuery = $stateBalancesTable
->find('all')
->select(['amount', 'id'])
@@ -75,12 +74,14 @@ class TransactionBase {
if($stateBalanceQuery->count() > 0) {
$stateBalanceEntry = $stateBalanceQuery->first();
+ $stateBalanceEntry->amount = $stateBalanceEntry->partDecay($recordDate) + $addAmountCent;
$stateBalanceEntry->amount += $addAmountCent;
} else {
$stateBalanceEntry = $stateBalancesTable->newEntity();
$stateBalanceEntry->state_user_id = $stateUserId;
$stateBalanceEntry->amount = $addAmountCent;
}
+ $stateBalanceEntry->record_date = $recordDate;
$finalBalance = $stateBalanceEntry->amount;
//echo "\ntry to save: "; var_dump($stateBalanceEntry); echo "\n";
if(!$stateBalancesTable->save($stateBalanceEntry)) {
@@ -90,4 +91,32 @@ class TransactionBase {
}
return $finalBalance;
}
+
+ protected function addStateUserTransaction($stateUserId, $transactionId, $transactionTypeId, $balance) {
+ $stateUserTransactionTable = self::getTable('state_user_transactions');
+ $stateUserTransactions = $stateUserTransactionTable
+ ->find('all')
+ ->where(['state_user_id' => $stateUserId])
+ ->order(['transaction_id DESC']);
+
+ if($stateUserTransactions->count() > 0) {
+ $stateBalanceTable = self::getTable('state_balances');
+ $balance_entity = $stateBalanceTable->newEntity();
+ $balance_entity->amount = $stateUserTransactions->first()->balance;
+ $balance_entity->record_date = $stateUserTransactions->first()->balance_date;
+ $balance = $balance_entity->decay + $balance;
+ }
+ $entity = $stateUserTransactionTable->newEntity();
+ $entity->state_user_id = $stateUserId;
+ $entity->transaction_id = $transactionId;
+ $entity->transaction_type_id = $transactionTypeId;
+ $entity->balance = $balance;
+
+ if(!$stateUserTransactionTable->save($entity)) {
+ $errors = $entity->getErrors();
+ $this->addError('TransactionBase::addStateUserTransaction', 'error saving state user balance with: ' . json_encode($errors));
+ return false;
+ }
+ return true;
+ }
}
\ No newline at end of file
diff --git a/community_server/src/Model/Transactions/TransactionBody.php b/community_server/src/Model/Transactions/TransactionBody.php
index 0dca15637..8164f33f7 100644
--- a/community_server/src/Model/Transactions/TransactionBody.php
+++ b/community_server/src/Model/Transactions/TransactionBody.php
@@ -79,7 +79,7 @@ class TransactionBody extends TransactionBase {
if ($transactionsTable->save($transactionEntity)) {
// success
$this->mTransactionID = $transactionEntity->id;
- if(!$this->mSpecificTransaction->save($transactionEntity->id, $firstPublic)) {
+ if(!$this->mSpecificTransaction->save($transactionEntity->id, $firstPublic, $transactionEntity->received)) {
$this->addErrors($this->mSpecificTransaction->getErrors());
return false;
}
diff --git a/community_server/src/Model/Transactions/TransactionCreation.php b/community_server/src/Model/Transactions/TransactionCreation.php
index d9caa9f7b..e8c6133cb 100644
--- a/community_server/src/Model/Transactions/TransactionCreation.php
+++ b/community_server/src/Model/Transactions/TransactionCreation.php
@@ -189,6 +189,12 @@ class TransactionCreation extends TransactionBase {
return false;
}
} else {*/
+ if($newSum2 <= 0) {
+ $this->addError(
+ 'TransactionCreation::validate',
+ 'Creation less than 0 GDD per Month for '. $receiverEmail .' in target_date not allowed'
+ );
+ }
if($newSum2 > 10000000) {
$this->addError(
'TransactionCreation::validate',
@@ -225,16 +231,27 @@ class TransactionCreation extends TransactionBase {
}
// update state balance
- if(false === $this->updateStateBalance($receiverUserId, $this->getAmount())) {
+ $final_balance = $this->updateStateBalance($receiverUserId, $this->getAmount(), $transactionCreationEntity->target_date);
+ if(false === $final_balance) {
return false;
}
-
+ $target_date = new FrozenDate($transactionCreationEntity->target_date);
+ $stateBalancesTable = self::getTable('stateBalances');
+ $state_balance = $stateBalancesTable->newEntity();
+ $state_balance->amount = $this->getAmount();
+ $state_balance->record_date = $target_date;
+ // decay is a virtual field which is calculated from amount and now() - record_date
+ if(!$this->addStateUserTransaction($receiverUserId, $transaction_id, 1, $state_balance->decay)) {
+ return false;
+ }
return true;
}
public function sendNotificationEmail($memo)
{
+ $disable_email = Configure::read('disableEmail', false);
+ if($disable_email) return true;
// send notification email
$receiverUserId = $this->getStateUserId($this->getReceiverPublic());
$receiverUser = $this->getStateUser($receiverUserId);
diff --git a/community_server/src/Model/Transactions/TransactionTransfer.php b/community_server/src/Model/Transactions/TransactionTransfer.php
index c89a0f175..a509cf0f3 100644
--- a/community_server/src/Model/Transactions/TransactionTransfer.php
+++ b/community_server/src/Model/Transactions/TransactionTransfer.php
@@ -154,11 +154,15 @@ class TransactionTransfer extends TransactionBase {
$this->addError($functionName, 'sender amount doesn\'t match receiver amount');
return false;
}
+ if($senderSum < 0) {
+ $this->addError($functionName, 'negative amount not supported');
+ return false;
+ }
//die("\n");
return true;
}
- public function save($transaction_id, $firstPublic) {
+ public function save($transaction_id, $firstPublic, $received) {
static $functionName = 'TransactionCreation::save';
@@ -182,11 +186,11 @@ class TransactionTransfer extends TransactionBase {
return false;
}
- $finalSenderBalance = $this->updateStateBalance($senderUserId, -$senderAmount->getAmount());
+ $finalSenderBalance = $this->updateStateBalance($senderUserId, -$senderAmount->getAmount(), $received);
if(false === $finalSenderBalance) {
return false;
}
- if(false === $this->updateStateBalance($receiverUserId, $receiverAmount->getAmount())) {
+ if(false === $this->updateStateBalance($receiverUserId, $receiverAmount->getAmount(), $received)) {
return false;
}
@@ -203,8 +207,12 @@ class TransactionTransfer extends TransactionBase {
return false;
}
-
-
+ if(!$this->addStateUserTransaction($senderUserId, $transaction_id, 2, $senderAmount->getAmount())) {
+ return false;
+ }
+ if(!$this->addStateUserTransaction($receiverUserId, $transaction_id, 2, -$senderAmount->getAmount())) {
+ return false;
+ }
//$this->addError('TransactionTransfer::save', 'not implemented yet');
//return false;
@@ -214,7 +222,9 @@ class TransactionTransfer extends TransactionBase {
public function sendNotificationEmail($memo)
{
// send notification email
-
+ $disable_email = Configure::read('disableEmail', false);
+ if($disable_email) return true;
+
$senderAmount = $this->protoTransactionTransfer->getSenderAmounts()[0];
$receiverAmount = $this->protoTransactionTransfer->getReceiverAmounts()[0];
$senderUserId = $this->getStateUserId($senderAmount->getEd25519SenderPubkey());
diff --git a/community_server/src/Template/StateUserTransactions/add.ctp b/community_server/src/Template/StateUserTransactions/add.ctp
new file mode 100644
index 000000000..b131a43e5
--- /dev/null
+++ b/community_server/src/Template/StateUserTransactions/add.ctp
@@ -0,0 +1,31 @@
+
+
+
| = $this->Paginator->sort('id') ?> | += $this->Paginator->sort('state_user_id') ?> | += $this->Paginator->sort('transaction_id') ?> | += $this->Paginator->sort('transaction_type_id') ?> | += __('Actions') ?> | +
|---|---|---|---|---|
| = $this->Number->format($stateUserTransaction->id) ?> | += $stateUserTransaction->has('state_user') ? $this->Html->link($stateUserTransaction->state_user->email, ['controller' => 'StateUsers', 'action' => 'view', $stateUserTransaction->state_user->id]) : '' ?> | += $stateUserTransaction->has('transaction') ? $this->Html->link($stateUserTransaction->transaction->id, ['controller' => 'Transactions', 'action' => 'view', $stateUserTransaction->transaction->id]) : '' ?> | += $stateUserTransaction->has('transaction_type') ? $this->Html->link($stateUserTransaction->transaction_type->name, ['controller' => 'TransactionTypes', 'action' => 'view', $stateUserTransaction->transaction_type->id]) : '' ?> | ++ = $this->Html->link(__('View'), ['action' => 'view', $stateUserTransaction->id]) ?> + = $this->Html->link(__('Edit'), ['action' => 'edit', $stateUserTransaction->id]) ?> + = $this->Form->postLink(__('Delete'), ['action' => 'delete', $stateUserTransaction->id], ['confirm' => __('Are you sure you want to delete # {0}?', $stateUserTransaction->id)]) ?> + | +
= $this->Paginator->counter(['format' => __('Page {{page}} of {{pages}}, showing {{current}} record(s) out of {{count}} total')]) ?>
+| = __('State User') ?> | += $stateUserTransaction->has('state_user') ? $this->Html->link($stateUserTransaction->state_user->email, ['controller' => 'StateUsers', 'action' => 'view', $stateUserTransaction->state_user->id]) : '' ?> | +
|---|---|
| = __('Transaction') ?> | += $stateUserTransaction->has('transaction') ? $this->Html->link($stateUserTransaction->transaction->id, ['controller' => 'Transactions', 'action' => 'view', $stateUserTransaction->transaction->id]) : '' ?> | +
| = __('Transaction Type') ?> | += $stateUserTransaction->has('transaction_type') ? $this->Html->link($stateUserTransaction->transaction_type->name, ['controller' => 'TransactionTypes', 'action' => 'view', $stateUserTransaction->transaction_type->id]) : '' ?> | +
| = __('Id') ?> | += $this->Number->format($stateUserTransaction->id) ?> | +
transactions count: = $count1 ?>
+state_user_transaction count: = $count2 ?>
+Missing count: = count($missing_transactions); ?>
+First 10 Missing ids:
+$id) { + if($i > 10) break; + if($i > 0) echo ', '; + echo $id['id']; +} ?>
+ + +