From 932a28288d8cf5e4baf6108adafd7093df569133 Mon Sep 17 00:00:00 2001 From: iglocska Date: Fri, 21 Jan 2022 13:41:29 +0100 Subject: [PATCH 01/27] new: [CRUD] added some new useful features - afterFind for the edit functions to make last minute decisions on the modification after already having loaded the data to be modified - moved the field restrictions to be able to pass it to the view - try/catch for bulk deletions. A single failure in the beforeSave call will no longer block the entire saving process --- src/Controller/Component/CRUDComponent.php | 24 +++++++++++++--------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index d1c78fc..47dc5bf 100644 --- a/src/Controller/Component/CRUDComponent.php +++ b/src/Controller/Component/CRUDComponent.php @@ -157,9 +157,6 @@ class CRUDComponent extends Component { $this->getMetaTemplates(); $data = $this->Table->newEmptyEntity(); - if (!empty($params['fields'])) { - $this->Controller->set('fields', $params['fields']); - } if ($this->request->is('post')) { $patchEntityParams = [ 'associated' => [], @@ -223,6 +220,9 @@ class CRUDComponent extends Component } } } + if (!empty($params['fields'])) { + $this->Controller->set('fields', $params['fields']); + } $this->Controller->entity = $data; $this->Controller->set('entity', $data); } @@ -295,21 +295,18 @@ class CRUDComponent extends Component $data->where($params['conditions']); } $data = $data->first(); + if (isset($params['afterFind'])) { + $data = $params['afterFind']($data, $params); + } if (empty($data)) { throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias)); } $data = $this->getMetaFields($id, $data); - if (!empty($params['fields'])) { - $this->Controller->set('fields', $params['fields']); - } if ($this->request->is(['post', 'put'])) { $patchEntityParams = [ 'associated' => [] ]; $input = $this->__massageInput($params); - if (!empty($params['fields'])) { - $patchEntityParams['fields'] = $params['fields']; - } $data = $this->Table->patchEntity($data, $input, $patchEntityParams); if (isset($params['beforeSave'])) { $data = $params['beforeSave']($data); @@ -352,6 +349,9 @@ class CRUDComponent extends Component } } } + if (!empty($params['fields'])) { + $this->Controller->set('fields', $params['fields']); + } $this->Controller->entity = $data; $this->Controller->set('entity', $data); } @@ -469,7 +469,11 @@ class CRUDComponent extends Component } $data = $data->first(); if (isset($params['beforeSave'])) { - $data = $params['beforeSave']($data); + try { + $data = $params['beforeSave']($data); + } catch (Exception $e) { + $data = false; + } } if (!empty($data)) { $success = $this->Table->delete($data); From b556f7f22a860d5050481417634c6867668d51f9 Mon Sep 17 00:00:00 2001 From: Andras Iklody Date: Fri, 21 Jan 2022 14:39:43 +0100 Subject: [PATCH 02/27] Update VERSION.json --- src/VERSION.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VERSION.json b/src/VERSION.json index 5ddd625..f790db8 100644 --- a/src/VERSION.json +++ b/src/VERSION.json @@ -1,4 +1,4 @@ { - "version": "0.1", + "version": "1.3", "application": "Cerebrate" } From 57e2c753523ec792bbdd8974c58a5d3bf0969bf8 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 25 Jan 2022 11:34:22 +0100 Subject: [PATCH 03/27] fix: [users] role based action filtering added - to avoid annoying clickable, but blocked actions for og admins --- src/Controller/UsersController.php | 57 ++++++++++++++++++++++++++---- templates/Users/index.php | 40 +++++++++++++++++++-- 2 files changed, 88 insertions(+), 9 deletions(-) diff --git a/src/Controller/UsersController.php b/src/Controller/UsersController.php index 9ffb2fe..6fa1067 100644 --- a/src/Controller/UsersController.php +++ b/src/Controller/UsersController.php @@ -33,16 +33,28 @@ class UsersController extends AppController if (!empty($responsePayload)) { return $responsePayload; } + $this->set( + 'validRoles', + $this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_admin' => 0])->all()->toArray() + ); $this->set('metaGroup', $this->isAdmin ? 'Administration' : 'Cerebrate'); } public function add() { $currentUser = $this->ACL->getUser(); + $validRoles = []; + if (!$currentUser['role']['perm_admin']) { + $validRoles = $this->Users->Roles->find('list')->order(['name' => 'asc'])->all()->toArray(); + } + $this->CRUD->add([ - 'beforeSave' => function($data) use ($currentUser) { + 'beforeSave' => function($data) use ($currentUser, $validRoles) { if (!$currentUser['role']['perm_admin']) { $data['organisation_id'] = $currentUser['organisation_id']; + if (!in_array($data['role_id'], array_keys($validRoles))) { + throw new MethodNotAllowedException(__('You do not have permission to assign that role.')); + } } $this->Users->enrollUserRouter($data); return $data; @@ -65,9 +77,7 @@ class UsersController extends AppController $org_conditions = ['id' => $currentUser['organisation_id']]; } $dropdownData = [ - 'role' => $this->Users->Roles->find('list', [ - 'sort' => ['name' => 'asc'] - ]), + 'role' => $validRoles, 'individual' => $this->Users->Individuals->find('list', [ 'sort' => ['email' => 'asc'] ]), @@ -98,6 +108,10 @@ class UsersController extends AppController public function edit($id = false) { $currentUser = $this->ACL->getUser(); + $validRoles = []; + if (!$currentUser['role']['perm_admin']) { + $validRoles = $this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_admin' => 0])->all()->toArray(); + } if (empty($id)) { $id = $currentUser['id']; } else { @@ -128,6 +142,20 @@ class UsersController extends AppController $params['fields'][] = 'role_id'; $params['fields'][] = 'organisation_id'; $params['fields'][] = 'disabled'; + } else if (!empty($this->ACL->getUser()['role']['perm_org_admin'])) { + $params['fields'][] = 'username'; + $params['fields'][] = 'role_id'; + $params['fields'][] = 'disabled'; + if (!$currentUser['role']['perm_admin']) { + $params['afterFind'] = function ($data, &$params) use ($currentUser, $validRoles) { + if (!$currentUser['role']['perm_admin'] && $currentUser['role']['perm_org_admin']) { + if (!in_array($data['role_id'], array_keys($validRoles))) { + throw new MethodNotAllowedException(__('You cannot edit the given privileged user.')); + } + } + return $data; + }; + } } $this->CRUD->edit($id, $params); $responsePayload = $this->CRUD->getResponsePayload(); @@ -135,9 +163,7 @@ class UsersController extends AppController return $responsePayload; } $dropdownData = [ - 'role' => $this->Users->Roles->find('list', [ - 'sort' => ['name' => 'asc'] - ]), + 'role' => $validRoles, 'individual' => $this->Users->Individuals->find('list', [ 'sort' => ['email' => 'asc'] ]), @@ -161,6 +187,23 @@ class UsersController extends AppController public function delete($id) { + $validRoles = []; + if (!$currentUser['role']['perm_admin']) { + $validRoles = $this->Users->Roles->find('list')->order(['name' => 'asc'])->all()->toArray(); + } + $params = [ + 'beforeSave' => function($data) use ($currentUser, $validRoles) { + if (!$currentUser['role']['perm_admin']) { + if ($data['organisation_id'] !== $currentUser['organisation_id']) { + throw new MethodNotAllowedException(__('You do not have permission to remove the given user.')); + } + if (!in_array($data['role_id'], array_keys($validRoles))) { + throw new MethodNotAllowedException(__('You do not have permission to remove the given user.')); + } + } + return $data; + } + ]; $this->CRUD->delete($id); $responsePayload = $this->CRUD->getResponsePayload(); if (!empty($responsePayload)) { diff --git a/templates/Users/index.php b/templates/Users/index.php index 1ff36bf..21afdc5 100644 --- a/templates/Users/index.php +++ b/templates/Users/index.php @@ -102,12 +102,48 @@ echo $this->element('genericElements/IndexTable/index_table', [ [ 'open_modal' => '/users/edit/[onclick_params_data_path]', 'modal_params_data_path' => 'id', - 'icon' => 'edit' + 'icon' => 'edit', + 'complex_requirement' => [ + 'options' => [ + 'datapath' => [ + 'role_id' => 'role_id' + ] + ], + 'function' => function ($row, $options) use ($loggedUser, $validRoles) { + if (empty($loggedUser['role']['perm_admin'])) { + if (empty($loggedUser['role']['perm_org_admin'])) { + return false; + } + if (!isset($validRoles[$options['datapath']['role_id']])) { + return false; + } + } + return true; + } + ] ], [ 'open_modal' => '/users/delete/[onclick_params_data_path]', 'modal_params_data_path' => 'id', - 'icon' => 'trash' + 'icon' => 'trash', + 'complex_requirement' => [ + 'options' => [ + 'datapath' => [ + 'role_id' => 'role_id' + ] + ], + 'function' => function ($row, $options) use ($loggedUser, $validRoles) { + if (empty($loggedUser['role']['perm_admin'])) { + if (empty($loggedUser['role']['perm_org_admin'])) { + return false; + } + if (!isset($validRoles[$options['datapath']['role_id']])) { + return false; + } + } + return true; + } + ] ], ] ] From a7e2fb2ea7454eb72299662b267374c9f09c9bf8 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 20 Jan 2022 14:24:03 +0100 Subject: [PATCH 04/27] chg: [auditlog:index] Break text in changed column --- templates/AuditLogs/index.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/AuditLogs/index.php b/templates/AuditLogs/index.php index c4657cb..8b7b70e 100644 --- a/templates/AuditLogs/index.php +++ b/templates/AuditLogs/index.php @@ -52,7 +52,8 @@ echo $this->element('genericElements/IndexTable/index_table', [ 'name' => __('Changed'), 'sort' => 'changed', 'data_path' => 'changed', - 'element' => 'json' + 'element' => 'json', + 'class' => 'text-break' ], ], 'title' => __('Logs'), From 6005552e76037fbf14a45f948367d901d6c10360 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Thu, 20 Jan 2022 14:37:19 +0100 Subject: [PATCH 05/27] fix: [genericElements:tags] List tags when editing an entity --- templates/element/genericElements/Form/Fields/tagsField.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/templates/element/genericElements/Form/Fields/tagsField.php b/templates/element/genericElements/Form/Fields/tagsField.php index 1dcbc39..da3114a 100644 --- a/templates/element/genericElements/Form/Fields/tagsField.php +++ b/templates/element/genericElements/Form/Fields/tagsField.php @@ -1,6 +1,6 @@ Tag->tags($entity['tags'], [ - 'allTags' => [], + 'allTags' => $allTags ?? [], 'picker' => true, 'editable' => true, ]); From dc2bfcb6b2a0cc7d964f951b8e8310407d715f3e Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Fri, 21 Jan 2022 09:35:55 +0100 Subject: [PATCH 06/27] fix: [components:CRUD] Support of controller's paginate public variable --- src/Controller/Component/CRUDComponent.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index d1c78fc..316168e 100644 --- a/src/Controller/Component/CRUDComponent.php +++ b/src/Controller/Component/CRUDComponent.php @@ -88,7 +88,7 @@ class CRUDComponent extends Component $this->Controller->restResponsePayload = $this->RestResponse->viewData($data, 'json'); } else { $this->Controller->loadComponent('Paginator'); - $data = $this->Controller->Paginator->paginate($query); + $data = $this->Controller->Paginator->paginate($query, $this->Controller->paginate ?? []); if (isset($options['afterFind'])) { $function = $options['afterFind']; if (is_callable($options['afterFind'])) { From 7d227a43875ac13c799023d3939614ec48527d4d Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Fri, 21 Jan 2022 09:48:53 +0100 Subject: [PATCH 07/27] chg: [inbox:index] Sort messages by created datetime --- src/Controller/InboxController.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Controller/InboxController.php b/src/Controller/InboxController.php index 6532e01..0d07fb9 100644 --- a/src/Controller/InboxController.php +++ b/src/Controller/InboxController.php @@ -20,6 +20,12 @@ class InboxController extends AppController public $quickFilterFields = ['scope', 'action', ['title' => true], ['comment' => true]]; public $containFields = ['Users']; + public $paginate = [ + 'order' => [ + 'Inbox.created' => 'desc' + ] + ]; + public function beforeFilter(EventInterface $event) { parent::beforeFilter($event); From 4f8b663b87d6f89a35ec580a9d68df25c7219d71 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Mon, 24 Jan 2022 16:12:46 +0100 Subject: [PATCH 08/27] chg: [localtTools:connectionRequest] Provide more info on exception --- src/Controller/LocalToolsController.php | 12 +++++++++++- templates/LocalTools/connection_request.php | 1 - 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Controller/LocalToolsController.php b/src/Controller/LocalToolsController.php index 12d9d62..baf8995 100644 --- a/src/Controller/LocalToolsController.php +++ b/src/Controller/LocalToolsController.php @@ -304,7 +304,17 @@ class LocalToolsController extends AppController throw new MethodNotAllowedException(__('No local tool ID supplied.')); } $params['local_tool_id'] = $postParams['local_tool_id']; - $encodingResult = $this->LocalTools->encodeConnection($params); + try { + $encodingResult = $this->LocalTools->encodeConnection($params); + } catch (\Exception $e) { + $encodingResult = [ + 'inboxResult' => [ + 'success' => false, + 'message' => __('Error while trying to encode connection'), + 'errors' => [$e->getMessage()], + ], + ]; + } $inboxResult = $encodingResult['inboxResult']; if ($inboxResult['success']) { if ($this->ParamHandler->isRest()) { diff --git a/templates/LocalTools/connection_request.php b/templates/LocalTools/connection_request.php index 0b8a52e..035af55 100644 --- a/templates/LocalTools/connection_request.php +++ b/templates/LocalTools/connection_request.php @@ -29,4 +29,3 @@ ] ]); ?> - From eef09f44c43505935dabbbf9942b457d96e81e10 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Mon, 24 Jan 2022 16:35:42 +0100 Subject: [PATCH 09/27] chg: [brood:connectionTest] Correctly handles network exceptions --- src/Model/Table/BroodsTable.php | 10 +++++++++- webroot/js/main.js | 4 +++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/Model/Table/BroodsTable.php b/src/Model/Table/BroodsTable.php index b0d3dca..9798260 100644 --- a/src/Model/Table/BroodsTable.php +++ b/src/Model/Table/BroodsTable.php @@ -9,6 +9,7 @@ use Cake\Core\Configure; use Cake\Http\Client; use Cake\Http\Client\Response; use Cake\Http\Exception\NotFoundException; +use Cake\Http\Client\Exception\NetworkException; use Cake\ORM\TableRegistry; use Cake\Error\Debugger; @@ -69,7 +70,14 @@ class BroodsTable extends AppTable { $brood = $this->find()->where(['id' => $id])->first(); $start = microtime(true); - $response = $this->HTTPClientGET('/instance/status.json', $brood); + try { + $response = $this->HTTPClientGET('/instance/status.json', $brood); + } catch (NetworkException $e) { + return [ + 'error' => __('Could not query status'), + 'reason' => $e->getMessage(), + ]; + } $ping = ((int)(100 * (microtime(true) - $start))); $errors = [ 403 => [ diff --git a/webroot/js/main.js b/webroot/js/main.js index c4383ff..fc2a4dc 100644 --- a/webroot/js/main.js +++ b/webroot/js/main.js @@ -55,8 +55,10 @@ function attachTestConnectionResultHtml(result, $container) { $testResultDiv.append(getKVHtml('Internal error', result, ['text-danger fw-bold'])) } else { if (result['error']) { + if (result['ping']) { + $testResultDiv.append('Status', 'OK', ['text-danger'], `${result['ping']} ms`); + } $testResultDiv.append( - getKVHtml('Status', 'OK', ['text-danger'], `${result['ping']} ms`), getKVHtml('Status', `Error: ${result['error']}`, ['text-danger']), getKVHtml('Reason', result['reason'], ['text-danger']) ) From 7faca9452028d97d0443b818e173737c7a4b07dd Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Mon, 24 Jan 2022 16:48:58 +0100 Subject: [PATCH 10/27] chg: [outboxProcessors:broods] Provide errors while trying to re-send a message --- libraries/default/OutboxProcessors/BroodsOutboxProcessor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php b/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php index 946e548..9019857 100644 --- a/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php +++ b/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php @@ -126,7 +126,7 @@ class ResendFailedMessageProcessor extends BroodsOutboxProcessor implements Gene [], $success, $success ? $messageSuccess : $messageFail, - [] + $jsonReply['errors'] ?? [] ); } From 88313679a6fb494f06eb1adafe5d251521d7a0a6 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Mon, 24 Jan 2022 17:36:12 +0100 Subject: [PATCH 11/27] chg: [outboxProcessors:brood] Gracefully catch server errors on remote broods --- .../default/OutboxProcessors/BroodsOutboxProcessor.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php b/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php index 9019857..9bf79ae 100644 --- a/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php +++ b/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php @@ -110,6 +110,14 @@ class ResendFailedMessageProcessor extends BroodsOutboxProcessor implements Gene $dataSent = $outboxRequest->data['sent']; $response = $this->Broods->sendRequest($brood, $url, true, $dataSent); $jsonReply = $response->getJson(); + if (is_null($jsonReply)) { + $jsonReply = [ + 'success' => false, + 'errors' => [ + __('Brood returned an invalid JSON.') + ] + ]; + } $success = !empty($jsonReply['success']); $messageSuccess = __('Message successfully sent to `{0}`', $brood->name); $messageFail = __('Could not send message to `{0}`.', $brood->name); From e05bf612515f05e49c72eb1349ef59090864127c Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Mon, 24 Jan 2022 17:37:32 +0100 Subject: [PATCH 12/27] chg: [inbox:createEntry] Checks for remote back connection is more flexible Handle the case of trailing slash --- src/Model/Table/InboxTable.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Model/Table/InboxTable.php b/src/Model/Table/InboxTable.php index 18faf47..a2c0e2c 100644 --- a/src/Model/Table/InboxTable.php +++ b/src/Model/Table/InboxTable.php @@ -62,8 +62,11 @@ class InboxTable extends AppTable $this->Broods = \Cake\ORM\TableRegistry::getTableLocator()->get('Broods'); $this->Individuals = \Cake\ORM\TableRegistry::getTableLocator()->get('Individuals'); $errors = []; + $originUrl = trim($entryData['origin'], '/'); $brood = $this->Broods->find() - ->where(['url' => $entryData['origin']]) + ->where([ + 'url IN' => [$originUrl, "{$originUrl}/"] + ]) ->first(); if (empty($brood)) { $errors[] = __('Unkown brood `{0}`', $entryData['data']['cerebrateURL']); From 578eacfd891b8d7a221f28b65dfebc423aae267e Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 25 Jan 2022 14:03:48 +0100 Subject: [PATCH 13/27] fix: [templates:common] Removed extra closing tag --- templates/Common/index.php | 1 - 1 file changed, 1 deletion(-) diff --git a/templates/Common/index.php b/templates/Common/index.php index 1f0a5f6..20e795e 100644 --- a/templates/Common/index.php +++ b/templates/Common/index.php @@ -1,4 +1,3 @@ element('genericElements/IndexTable/index_table', $data); - echo ''; ?> From 44913c5ed77a9fe7d2921808e70c6c3a3053d29e Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 25 Jan 2022 15:27:34 +0100 Subject: [PATCH 14/27] fix: [users:settings] Allow admin to see account settings of other users --- src/Controller/UsersController.php | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Controller/UsersController.php b/src/Controller/UsersController.php index 9ffb2fe..74aec0d 100644 --- a/src/Controller/UsersController.php +++ b/src/Controller/UsersController.php @@ -218,10 +218,18 @@ class UsersController extends AppController } } - public function settings() + public function settings($user_id=false) { - $this->set('user', $this->ACL->getUser()); - $all = $this->Users->UserSettings->getSettingsFromProviderForUser($this->ACL->getUser()['id'], true); + $currentUser = $this->ACL->getUser(); + if (empty($currentUser['role']['perm_admin'])) { + $user = $currentUser; + } else { + $user = $this->Users->get($user_id, [ + 'contain' => ['Roles', 'Individuals' => 'Organisations'] + ]); + } + $this->set('user', $user); + $all = $this->Users->UserSettings->getSettingsFromProviderForUser($user->id, true); $this->set('settingsProvider', $all['settingsProvider']); $this->set('settings', $all['settings']); $this->set('settingsFlattened', $all['settingsFlattened']); From 55782af52b8536878384ed6f59edaac7e53699b3 Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 25 Jan 2022 15:58:31 +0100 Subject: [PATCH 15/27] fix: [users] add - fixed role selection --- src/Controller/UsersController.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Controller/UsersController.php b/src/Controller/UsersController.php index 6fa1067..f03f329 100644 --- a/src/Controller/UsersController.php +++ b/src/Controller/UsersController.php @@ -45,6 +45,8 @@ class UsersController extends AppController $currentUser = $this->ACL->getUser(); $validRoles = []; if (!$currentUser['role']['perm_admin']) { + $validRoles = $this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_admin' => 0])->all()->toArray(); + } else { $validRoles = $this->Users->Roles->find('list')->order(['name' => 'asc'])->all()->toArray(); } @@ -111,6 +113,8 @@ class UsersController extends AppController $validRoles = []; if (!$currentUser['role']['perm_admin']) { $validRoles = $this->Users->Roles->find('list')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_admin' => 0])->all()->toArray(); + } else { + $validRoles = $this->Users->Roles->find('list')->order(['name' => 'asc'])->all()->toArray(); } if (empty($id)) { $id = $currentUser['id']; From 1086e41086f7184097e2cb70421e63710249118b Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 25 Jan 2022 17:01:27 +0100 Subject: [PATCH 16/27] fix: [modified] saving fixed for sync captures - set the field as not dirty to force an update - stops the exceptions thrown on pulling these objects in --- src/Model/Table/IndividualsTable.php | 1 + src/Model/Table/OrganisationsTable.php | 1 + src/Model/Table/SharingGroupsTable.php | 1 + 3 files changed, 3 insertions(+) diff --git a/src/Model/Table/IndividualsTable.php b/src/Model/Table/IndividualsTable.php index 70f63fd..16a3da2 100644 --- a/src/Model/Table/IndividualsTable.php +++ b/src/Model/Table/IndividualsTable.php @@ -66,6 +66,7 @@ class IndividualsTable extends AppTable $this->patchEntity($existingIndividual, $individual); $entityToSave = $existingIndividual; } + $entityToSave->setDirty('modified', false); $savedEntity = $this->save($entityToSave, ['associated' => false]); if (!$savedEntity) { return null; diff --git a/src/Model/Table/OrganisationsTable.php b/src/Model/Table/OrganisationsTable.php index c56720e..8cd7dec 100644 --- a/src/Model/Table/OrganisationsTable.php +++ b/src/Model/Table/OrganisationsTable.php @@ -71,6 +71,7 @@ class OrganisationsTable extends AppTable $this->patchEntity($existingOrg, $org); $entityToSave = $existingOrg; } + $entityToSave->setDirty('modified', false); $savedEntity = $this->save($entityToSave, ['associated' => false]); if (!$savedEntity) { return null; diff --git a/src/Model/Table/SharingGroupsTable.php b/src/Model/Table/SharingGroupsTable.php index ff39220..e976e8c 100644 --- a/src/Model/Table/SharingGroupsTable.php +++ b/src/Model/Table/SharingGroupsTable.php @@ -66,6 +66,7 @@ class SharingGroupsTable extends AppTable $this->patchEntity($existingSG, $input); $entityToSave = $existingSG; } + $entityToSave->setDirty('modified', false); $savedEntity = $this->save($entityToSave, ['associated' => false]); if (!$savedEntity) { return null; From 19c81b7c114d875453fe202b7d2c7e882be21aec Mon Sep 17 00:00:00 2001 From: iglocska Date: Tue, 25 Jan 2022 17:09:29 +0100 Subject: [PATCH 17/27] fix: [Sharing groups] UUID and owner org shouldn't be editable --- src/Controller/SharingGroupsController.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Controller/SharingGroupsController.php b/src/Controller/SharingGroupsController.php index 4e98df8..764f0e6 100644 --- a/src/Controller/SharingGroupsController.php +++ b/src/Controller/SharingGroupsController.php @@ -71,6 +71,7 @@ class SharingGroupsController extends AppController if (empty($currentUser['role']['perm_admin'])) { $params['conditions'] = ['organisation_id' => $currentUser['organisation_id']]; } + $params['fields'] = ['name', 'releasability', 'description', 'active']; $this->CRUD->edit($id, $params); $responsePayload = $this->CRUD->getResponsePayload(); if (!empty($responsePayload)) { From f53b458103827c231b976ccb796ed669242caf68 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Wed, 26 Jan 2022 12:11:44 +0100 Subject: [PATCH 18/27] fix: [userSettings] Allow admin to edit other user's settings --- src/Controller/Component/ACLComponent.php | 10 ++--- src/Controller/UserSettingsController.php | 49 ++++++++++++++++++----- src/Controller/UsersController.php | 7 +++- templates/Users/settings.php | 28 ++++++++++--- templates/element/Settings/category.php | 2 +- webroot/js/main.js | 12 +++--- webroot/js/table-settings.js | 4 +- 7 files changed, 80 insertions(+), 32 deletions(-) diff --git a/src/Controller/Component/ACLComponent.php b/src/Controller/Component/ACLComponent.php index fb51f49..8e50a67 100644 --- a/src/Controller/Component/ACLComponent.php +++ b/src/Controller/Component/ACLComponent.php @@ -188,12 +188,12 @@ class ACLComponent extends Component 'add' => ['*'], 'edit' => ['*'], 'delete' => ['*'], - 'getSettingByName' => ['*'], - 'setSetting' => ['*'], + 'getMySettingByName' => ['*'], + 'setMySetting' => ['*'], 'saveSetting' => ['*'], - 'getBookmarks' => ['*'], - 'saveBookmark' => ['*'], - 'deleteBookmark' => ['*'] + 'getMyBookmarks' => ['*'], + 'saveMyBookmark' => ['*'], + 'deleteMyBookmark' => ['*'] ], 'Api' => [ 'index' => ['*'] diff --git a/src/Controller/UserSettingsController.php b/src/Controller/UserSettingsController.php index d28f6ca..bced32b 100644 --- a/src/Controller/UserSettingsController.php +++ b/src/Controller/UserSettingsController.php @@ -124,7 +124,13 @@ class UserSettingsController extends AppController } } - public function getSettingByName($settingsName) + /** + * Get a setting by name for the currently logged-in user + * + * @param [type] $settingsName + * @return void + */ + public function getMySettingByName($settingsName) { $setting = $this->UserSettings->getSettingByName($this->ACL->getUser(), $settingsName); if (is_null($setting)) { @@ -140,7 +146,7 @@ class UserSettingsController extends AppController $this->render('view'); } - public function setSetting($settingsName = false) + public function setMySetting($settingsName = false) { if (!$this->request->is('get')) { $setting = $this->UserSettings->getSettingByName($this->ACL->getUser(), $settingsName); @@ -160,22 +166,23 @@ class UserSettingsController extends AppController $this->set('settingName', $settingsName); } - public function saveSetting() + public function saveSetting($user_id = false) { + $user = $this->getRequestedUserIfAllowed($user_id); if ($this->request->is('post')) { $data = $this->ParamHandler->harvestParams([ 'name', 'value' ]); - $setting = $this->UserSettings->getSettingByName($this->ACL->getUser(), $data['name']); + $setting = $this->UserSettings->getSettingByName($user, $data['name']); if (is_null($setting)) { // setting not found, create it - $result = $this->UserSettings->createSetting($this->ACL->getUser(), $data['name'], $data['value']); + $result = $this->UserSettings->createSetting($user, $data['name'], $data['value']); } else { - $result = $this->UserSettings->editSetting($this->ACL->getUser(), $data['name'], $data['value']); + $result = $this->UserSettings->editSetting($user, $data['name'], $data['value']); } $success = !empty($result); $message = $success ? __('Setting saved') : __('Could not save setting'); - $this->CRUD->setResponseForController('setSetting', $success, $message, $result); + $this->CRUD->setResponseForController('saveSetting', $success, $message, $result); $responsePayload = $this->CRUD->getResponsePayload(); if (!empty($responsePayload)) { return $responsePayload; @@ -183,7 +190,7 @@ class UserSettingsController extends AppController } } - public function getBookmarks($forSidebar = false) + public function getMyBookmarks($forSidebar = false) { $bookmarks = $this->UserSettings->getSettingByName($this->ACL->getUser(), $this->UserSettings->BOOKMARK_SETTING_NAME); $bookmarks = json_decode($bookmarks['value'], true); @@ -193,7 +200,7 @@ class UserSettingsController extends AppController $this->render('/element/UserSettings/saved-bookmarks'); } - public function saveBookmark() + public function saveMyBookmark() { if (!$this->request->is('get')) { $result = $this->UserSettings->saveBookmark($this->ACL->getUser(), $this->request->getData()); @@ -208,7 +215,7 @@ class UserSettingsController extends AppController $this->set('user_id', $this->ACL->getUser()->id); } - public function deleteBookmark() + public function deleteMyBookmark() { if (!$this->request->is('get')) { $result = $this->UserSettings->deleteBookmark($this->ACL->getUser(), $this->request->getData()); @@ -248,4 +255,26 @@ class UserSettingsController extends AppController } return $isAllowed; } + + /** + * Return the requested user if user permissions allow it. Otherwise, return the user currently logged-in + * + * @param bool|int $user_id + * @return void + */ + private function getRequestedUserIfAllowed($user_id = false) + { + $currentUser = $this->ACL->getUser(); + if (is_bool($user_id)) { + return $currentUser; + } + if (!empty($currentUser['role']['perm_admin'])) { + $user = $this->Users->get($user_id, [ + 'contain' => ['Roles', 'Individuals' => 'Organisations'] + ]); + } else { + $user = $currentUser; + } + return $user; + } } diff --git a/src/Controller/UsersController.php b/src/Controller/UsersController.php index 74aec0d..d484b5c 100644 --- a/src/Controller/UsersController.php +++ b/src/Controller/UsersController.php @@ -220,14 +220,17 @@ class UsersController extends AppController public function settings($user_id=false) { + $editingAnotherUser = false; $currentUser = $this->ACL->getUser(); - if (empty($currentUser['role']['perm_admin'])) { + if (empty($currentUser['role']['perm_admin']) || $user_id == $currentUser->id) { $user = $currentUser; } else { $user = $this->Users->get($user_id, [ - 'contain' => ['Roles', 'Individuals' => 'Organisations'] + 'contain' => ['Roles', 'Individuals' => 'Organisations', 'Organisations', 'UserSettings'] ]); + $editingAnotherUser = true; } + $this->set('editingAnotherUser', $editingAnotherUser); $this->set('user', $user); $all = $this->Users->UserSettings->getSettingsFromProviderForUser($user->id, true); $this->set('settingsProvider', $all['settingsProvider']); diff --git a/templates/Users/settings.php b/templates/Users/settings.php index da014ca..4681def 100644 --- a/templates/Users/settings.php +++ b/templates/Users/settings.php @@ -10,10 +10,12 @@ foreach ($settingsProvider as $settingTitle => $settingContent) { ]); } -$navLinks[] = __('Bookmarks'); -$tabContents[] = $this->element('UserSettings/saved-bookmarks', [ - 'bookmarks' => !empty($user->user_settings_by_name['ui.bookmarks']['value']) ? json_decode($user->user_settings_by_name['ui.bookmarks']['value'], true) : [] -]); +if (empty($editingAnotherUser)) { + $navLinks[] = __('Bookmarks'); + $tabContents[] = $this->element('UserSettings/saved-bookmarks', [ + 'bookmarks' => !empty($user->user_settings_by_name['ui.bookmarks']['value']) ? json_decode($user->user_settings_by_name['ui.bookmarks']['value'], true) : [] + ]); +} $tabsOptions = [ 'vertical' => true, @@ -29,11 +31,15 @@ $tabsOptions = [ ]; $tabs = $this->Bootstrap->tabs($tabsOptions); echo $this->Html->script('settings'); +$saveUrl = '/userSettings/saveSetting'; +if(!empty($editingAnotherUser)) { + $saveUrl .= '/' . h($user->id); +} ?>

@@ -43,7 +49,17 @@ echo $this->Html->script('settings'); username) ?> individual->full_name) ?> -
+ + Bootstrap->alert([ + 'text' => __('Currently editing the account setting of another user.'), + 'variant' => 'warning', + 'dismissible' => false + ]) + ?> + +
+
diff --git a/templates/element/Settings/category.php b/templates/element/Settings/category.php index 03e17e4..317dac0 100644 --- a/templates/element/Settings/category.php +++ b/templates/element/Settings/category.php @@ -57,6 +57,6 @@ $mainPanelHeight = 'calc(100vh - 42px - 1rem - 56px - 38px - 1rem)';
- + %s

', __('No settings available for this category')) ?>
\ No newline at end of file diff --git a/webroot/js/main.js b/webroot/js/main.js index fc2a4dc..420d1bf 100644 --- a/webroot/js/main.js +++ b/webroot/js/main.js @@ -167,7 +167,7 @@ function saveSetting(statusNode, settingName, settingValue) { } function openSaveBookmarkModal(bookmark_url = '') { - const url = '/user-settings/saveBookmark'; + const url = '/user-settings/saveMyBookmark'; UI.submissionModal(url).then(([modalFactory, ajaxApi]) => { const $input = modalFactory.$modal.find('input[name="bookmark_url"]') $input.val(bookmark_url) @@ -175,7 +175,7 @@ function openSaveBookmarkModal(bookmark_url = '') { } function deleteBookmark(bookmark, forSidebar=false) { - const url = '/user-settings/deleteBookmark' + const url = '/user-settings/deleteMyBookmark' AJAXApi.quickFetchAndPostForm(url, { bookmark_name: bookmark.name, bookmark_url: bookmark.url, @@ -183,7 +183,7 @@ function deleteBookmark(bookmark, forSidebar=false) { provideFeedback: true, statusNode: $('.bookmark-table-container'), }).then((apiResult) => { - const url = `/userSettings/getBookmarks/${forSidebar ? '1' : '0'}` + const url = `/userSettings/getMyBookmarks/${forSidebar ? '1' : '0'}` UI.reload(url, $('.bookmark-table-container').parent()) const theToast = UI.toast({ variant: 'success', @@ -191,7 +191,7 @@ function deleteBookmark(bookmark, forSidebar=false) { bodyHtml: $('
').append( $('').text('Cancel deletion operation.'), $('