diff --git a/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php b/libraries/default/OutboxProcessors/BroodsOutboxProcessor.php index 946e548..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); @@ -126,7 +134,7 @@ class ResendFailedMessageProcessor extends BroodsOutboxProcessor implements Gene [], $success, $success ? $messageSuccess : $messageFail, - [] + $jsonReply['errors'] ?? [] ); } diff --git a/src/Controller/AppController.php b/src/Controller/AppController.php index 0bb2373..006736c 100644 --- a/src/Controller/AppController.php +++ b/src/Controller/AppController.php @@ -110,6 +110,7 @@ class AppController extends Controller } unset($user['password']); $this->ACL->setUser($user); + $this->Navigation->genBreadcrumbs($user); $this->request->getSession()->write('authUser', $user); $this->isAdmin = $user['role']['perm_admin']; if (!$this->ParamHandler->isRest()) { diff --git a/src/Controller/Component/ACLComponent.php b/src/Controller/Component/ACLComponent.php index fb51f49..a1c4533 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' => ['*'] @@ -277,9 +277,29 @@ class ACLComponent extends Component $this->user = $user; } - public function getUser(): User + public function getUser(): ?User { - return $this->user; + if (!empty($this->user)) { + return $this->user; + } + return null; + } + + public function canEditUser(User $currentUser, User $user): bool + { + if (empty($user) || empty($currentUser)) { + return false; + } + if (!$currentUser['role']['perm_admin']) { + if (!$currentUser['role']['perm_org_admin']) { + return false; + } else { + if ($currentUser['organisation_id'] !== $user['organisation_id']) { + return false; + } + } + } + return true; } /* diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index d1c78fc..8ec8692 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'])) { @@ -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,13 +295,13 @@ 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' => [] @@ -352,6 +352,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 +472,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); diff --git a/src/Controller/Component/Navigation/Users.php b/src/Controller/Component/Navigation/Users.php index 7e228db..860545d 100644 --- a/src/Controller/Component/Navigation/Users.php +++ b/src/Controller/Component/Navigation/Users.php @@ -1,7 +1,7 @@ bcf; $request = $this->request; $passedData = $this->request->getParam('pass'); - $this->bcf->addLink('Users', 'view', 'UserSettings', 'index', function ($config) use ($bcf, $request, $passedData) { - if (!empty($passedData[0])) { - $user_id = $passedData[0]; - $linkData = [ - 'label' => __('Account settings', h($user_id)), - 'url' => sprintf('/users/settings/%s', h($user_id)) - ]; - return $linkData; - } - return []; - }); + $currentUser = $this->currentUser; + $ownUser = (!empty($passedData[0]) && $passedData[0] === $currentUser['id']); + if ($ownUser) { + $this->bcf->addLink('Users', 'view', 'UserSettings', 'index', function ($config) use ($bcf, $request, $passedData, $currentUser) { + if (!empty($passedData[0])) { + $user_id = $passedData[0]; + $linkData = [ + 'label' => __('Account settings', h($user_id)), + 'url' => sprintf('/users/settings/%s', h($user_id)) + ]; + return $linkData; + } + return []; + }); + } $this->bcf->addLink('Users', 'view', 'UserSettings', 'index', function ($config) use ($bcf, $request, $passedData) { if (!empty($passedData[0])) { $user_id = $passedData[0]; diff --git a/src/Controller/Component/Navigation/base.php b/src/Controller/Component/Navigation/base.php index 84dde96..b426ab5 100644 --- a/src/Controller/Component/Navigation/base.php +++ b/src/Controller/Component/Navigation/base.php @@ -5,6 +5,7 @@ class BaseNavigation { protected $bcf; protected $request; + public $currentUser; public function __construct($bcf, $request) { @@ -12,8 +13,13 @@ class BaseNavigation $this->request = $request; } + public function setCurrentUser($currentUser) + { + $this->currentUser = $currentUser; + } + public function addRoutes() {} public function addParents() {} public function addLinks() {} public function addActions() {} -} \ No newline at end of file +} diff --git a/src/Controller/Component/NavigationComponent.php b/src/Controller/Component/NavigationComponent.php index b8caee7..67fb39c 100644 --- a/src/Controller/Component/NavigationComponent.php +++ b/src/Controller/Component/NavigationComponent.php @@ -17,8 +17,9 @@ require_once(APP . 'Controller' . DS . 'Component' . DS . 'Navigation' . DS . 's class NavigationComponent extends Component { - private $user = null; + private $currentUser = null; public $breadcrumb = null; + public $fullBreadcrumb = null; public $iconToTableMapping = [ 'Individuals' => 'address-book', 'Organisations' => 'building', @@ -42,10 +43,10 @@ class NavigationComponent extends Component $this->request = $config['request']; } - public function beforeFilter($event) + public function genBreadcrumbs(\App\Model\Entity\User $user) { - $this->fullBreadcrumb = $this->genBreadcrumb(); - $this->breadcrumb = $this->getBreadcrumb(); + $this->currentUser = $user; + $this->breadcrumb = $this->fullBreadcrumb = $this->genBreadcrumb(); } public function getSideMenu(): array @@ -56,7 +57,7 @@ class NavigationComponent extends Component return $sidemenu; } - + public function addUserBookmarks($sidemenu): array { $bookmarks = $this->getUserBookmarks(); @@ -81,7 +82,7 @@ class NavigationComponent extends Component }, $bookmarks); return $links; } - + public function getBreadcrumb(): array { $controller = $this->request->getParam('controller'); @@ -141,6 +142,7 @@ class NavigationComponent extends Component require_once(APP . 'Controller' . DS . 'Component' . DS . 'Navigation' . DS . $navigationFile); $reflection = new \ReflectionClass("BreadcrumbNavigation\\{$navigationClassname}Navigation"); $navigationClasses[$navigationClassname] = $reflection->newInstance($bcf, $request); + $navigationClasses[$navigationClassname]->setCurrentUser($this->currentUser); } return $navigationClasses; } @@ -284,7 +286,7 @@ class BreadcrumbFactory $this->addLink($controller, 'view', $controller, 'edit'); $this->addLink($controller, 'edit', $controller, 'view'); $this->addSelfLink($controller, 'edit'); - + $this->addAction($controller, 'view', $controller, 'add'); $this->addAction($controller, 'view', $controller, 'delete'); $this->addAction($controller, 'edit', $controller, 'add'); 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); 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/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)) { 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 9ffb2fe..96c102f 100644 --- a/src/Controller/UsersController.php +++ b/src/Controller/UsersController.php @@ -33,16 +33,30 @@ 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')->select(['id', 'name'])->order(['name' => 'asc'])->where(['perm_admin' => 0])->all()->toArray(); + } else { + $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 +79,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'] ]), @@ -82,7 +94,8 @@ class UsersController extends AppController public function view($id = false) { - if (empty($id) || empty($this->ACL->getUser()['role']['perm_admin'])) { + $currentUser = $this->ACL->getUser(); + if (empty($id) || (empty($currentUser['role']['perm_org_admin']) && empty($currentUser['role']['perm_admin']))) { $id = $this->ACL->getUser()['id']; } $this->CRUD->view($id, [ @@ -98,6 +111,12 @@ 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(); + } else { + $validRoles = $this->Users->Roles->find('list')->order(['name' => 'asc'])->all()->toArray(); + } if (empty($id)) { $id = $currentUser['id']; } else { @@ -128,6 +147,21 @@ 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 (!in_array($data['role_id'], array_keys($validRoles))) { + throw new MethodNotAllowedException(__('You cannot edit the given privileged user.')); + } + if ($data['organisation_id'] !== $currentUser['organisation_id']) { + throw new MethodNotAllowedException(__('You cannot edit the given user.')); + } + return $data; + }; + } } $this->CRUD->edit($id, $params); $responsePayload = $this->CRUD->getResponsePayload(); @@ -135,9 +169,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 +193,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)) { @@ -218,10 +267,21 @@ 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); + $editingAnotherUser = false; + $currentUser = $this->ACL->getUser(); + if (empty($currentUser['role']['perm_admin']) || $user_id == $currentUser->id) { + $user = $currentUser; + } else { + $user = $this->Users->get($user_id, [ + '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']); $this->set('settings', $all['settings']); $this->set('settingsFlattened', $all['settingsFlattened']); diff --git a/src/Lib/default/local_tool_connectors/MispConnector.php b/src/Lib/default/local_tool_connectors/MispConnector.php index 4b6c653..c872675 100644 --- a/src/Lib/default/local_tool_connectors/MispConnector.php +++ b/src/Lib/default/local_tool_connectors/MispConnector.php @@ -526,6 +526,7 @@ class MispConnector extends CommonConnectorTools ] ], 'actions' => [ + /* [ 'open_modal' => '/localTools/action/' . h($params['connection']['id']) . '/editUser?id={{0}}', 'modal_params_data_path' => ['User.id'], @@ -538,6 +539,7 @@ class MispConnector extends CommonConnectorTools 'icon' => 'trash', 'reload_url' => '/localTools/action/' . h($params['connection']['id']) . '/serversAction' ] + */ ], 'title' => false, 'description' => false, diff --git a/src/Model/Entity/Organisation.php b/src/Model/Entity/Organisation.php index 6766963..a56d3c0 100644 --- a/src/Model/Entity/Organisation.php +++ b/src/Model/Entity/Organisation.php @@ -10,5 +10,10 @@ class Organisation extends AppModel protected $_accessible = [ '*' => true, 'id' => false, + 'created' => false + ]; + + protected $_accessibleOnNew = [ + 'created' => true ]; } diff --git a/src/Model/Table/AppTable.php b/src/Model/Table/AppTable.php index 4164456..8483770 100644 --- a/src/Model/Table/AppTable.php +++ b/src/Model/Table/AppTable.php @@ -46,4 +46,9 @@ class AppTable extends Table } } } + + public function isValidUrl($value, array $context): bool + { + return filter_var($value, FILTER_VALIDATE_URL); + } } diff --git a/src/Model/Table/BroodsTable.php b/src/Model/Table/BroodsTable.php index b0d3dca..0708c18 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; @@ -32,7 +33,11 @@ class BroodsTable extends AppTable ->requirePresence(['name', 'url', 'organisation_id'], 'create') ->notEmptyString('name') ->notEmptyString('url') - ->url('url', __('The provided value is not a valid URL')) + ->add('url', 'isValidUrl', [ + 'rule' => 'isValidUrl', + 'message' => __('The provided value is not a valid URL'), + 'provider' => 'table' + ]) ->naturalNumber('organisation_id', false); } @@ -69,7 +74,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/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']); 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; 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" } 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'), 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 ''; ?> 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 @@ ] ]); ?> - 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; + } + ] ], ] ] 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/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, ]); diff --git a/webroot/js/main.js b/webroot/js/main.js index c4383ff..420d1bf 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']) ) @@ -165,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) @@ -173,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, @@ -181,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', @@ -189,7 +191,7 @@ function deleteBookmark(bookmark, forSidebar=false) { bodyHtml: $('
').append( $('').text('Cancel deletion operation.'), $('