diff --git a/src/Controller/AuditLogsController.php b/src/Controller/AuditLogsController.php index e3e4e0c..8836d49 100644 --- a/src/Controller/AuditLogsController.php +++ b/src/Controller/AuditLogsController.php @@ -11,7 +11,7 @@ use Cake\Core\Configure; class AuditLogsController extends AppController { - public $filterFields = ['model_id', 'model', 'request_action', 'user_id', 'model_title', 'AuditLogs.created']; + public $filterFields = ['model_id', 'model', ['name' => 'request_action', 'multiple' => true, ], 'user_id', 'model_title', 'AuditLogs.created']; public $quickFilterFields = ['model', 'request_action', 'model_title']; public $containFields = ['Users']; @@ -20,7 +20,7 @@ class AuditLogsController extends AppController $this->CRUD->index([ 'contain' => $this->containFields, 'order' => ['AuditLogs.id' => 'DESC'], - 'filters' => $this->filterFields, + 'filters' => $this->CRUD->getFilterFieldsName($this->filterFields), 'quickFilters' => $this->quickFilterFields, 'afterFind' => function($data) { $request_ip = is_resource($data['request_ip']) ? stream_get_contents($data['request_ip']) : $data['request_ip']; diff --git a/src/Controller/AuthKeysController.php b/src/Controller/AuthKeysController.php index 978810c..37aa356 100644 --- a/src/Controller/AuthKeysController.php +++ b/src/Controller/AuthKeysController.php @@ -71,8 +71,12 @@ class AuthKeysController extends AppController if (empty($currentUser['role']['perm_org_admin'])) { $userConditions['id'] = $currentUser['id']; } else { - $role_ids = $this->Users->Roles->find()->where(['perm_admin' => 0])->all()->extract('id')->toList(); - $userConditions['role_id IN'] = $role_ids; + $role_ids = $this->Users->Roles->find()->where(['perm_admin' => 0, 'perm_org_admin' => 0])->all()->extract('id')->toList(); + $userConditions['organisation_id'] = $currentUser['organisation_id']; + $userConditions['OR'] = [ + ['role_id IN' => $role_ids], + ['id' => $currentUser['id']], + ]; } } $users = $this->Users->find('list'); @@ -99,6 +103,7 @@ class AuthKeysController extends AppController $dropdownData = [ 'user' => $users ]; + $this->entity->user_id = $currentUser['id']; $this->set(compact('dropdownData')); } } diff --git a/src/Controller/Component/ACLComponent.php b/src/Controller/Component/ACLComponent.php index 9c3dd4c..cbd1cb8 100644 --- a/src/Controller/Component/ACLComponent.php +++ b/src/Controller/Component/ACLComponent.php @@ -90,8 +90,8 @@ class ACLComponent extends Component 'edit' => ['perm_admin', 'perm_org_admin'], 'filtering' => ['*'], 'index' => ['*'], - 'tag' => ['perm_tagger'], - 'untag' => ['perm_tagger'], + 'tag' => ['*'], + 'untag' => ['*'], 'view' => ['*'], 'viewTags' => ['*'] ], @@ -140,20 +140,24 @@ class ACLComponent extends Component 'enable' => ['perm_admin'], 'getMetaFieldsToUpdate' => ['perm_admin'], 'index' => ['perm_admin'], + 'migrateMetafieldsToNewestTemplate' => ['perm_admin'], 'migrateOldMetaTemplateToNewestVersionForEntity' => ['perm_admin'], 'update' => ['perm_admin'], 'updateAllTemplates' => ['perm_admin'], 'toggle' => ['perm_admin'], 'view' => ['perm_admin'], ], + 'MetaTemplateNameDirectory' => [ + 'index' => ['perm_admin'], + ], 'Organisations' => [ 'add' => ['perm_admin'], 'delete' => ['perm_admin'], 'edit' => ['perm_admin'], 'filtering' => ['*'], 'index' => ['*'], - 'tag' => ['perm_tagger'], - 'untag' => ['perm_tagger'], + 'tag' => ['perm_org_admin'], + 'untag' => ['perm_org_admin'], 'view' => ['*'], 'viewTags' => ['*'] ], diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index 39554d4..d56609c 100644 --- a/src/Controller/Component/CRUDComponent.php +++ b/src/Controller/Component/CRUDComponent.php @@ -10,6 +10,7 @@ use Cake\Utility\Text; use Cake\View\ViewBuilder; use Cake\ORM\TableRegistry; use Cake\ORM\Query; +use Cake\Database\Expression\QueryExpression; use Cake\Routing\Router; use Cake\Http\Exception\MethodNotAllowedException; use Cake\Http\Exception\NotFoundException; @@ -225,22 +226,41 @@ class CRUDComponent extends Component } else { $this->Controller->set('metaFieldsEnabled', false); } - $filters = !empty($this->Controller->filterFields) ? $this->Controller->filterFields : []; + $filtersConfigRaw= !empty($this->Controller->filterFields) ? $this->Controller->filterFields : []; + $filtersConfig = []; + foreach ($filtersConfigRaw as $fieldConfig) { + if (is_array($fieldConfig)) { + $filtersConfig[$fieldConfig['name']] = $fieldConfig; + } else { + $filtersConfig[$fieldConfig] = ['name' => $fieldConfig]; + } + } + $filtersName = $this->getFilterFieldsName(); $typeMap = $this->Table->getSchema()->typeMap(); - $associatedtypeMap = !empty($this->Controller->filterFields) ? $this->_getAssociatedTypeMap() : []; + $associatedtypeMap = !empty($filtersName) ? $this->_getAssociatedTypeMap() : []; $typeMap = array_merge( $this->Table->getSchema()->typeMap(), $associatedtypeMap ); - $typeMap = array_filter($typeMap, function ($field) use ($filters) { - return in_array($field, $filters); + $typeMap = array_filter($typeMap, function ($field) use ($filtersName) { + return in_array($field, $filtersName); }, ARRAY_FILTER_USE_KEY); $this->Controller->set('typeMap', $typeMap); - $this->Controller->set('filters', $filters); + $this->Controller->set('filters', $filtersName); + $this->Controller->set('filtersConfig', $filtersConfig); $this->Controller->viewBuilder()->setLayout('ajax'); $this->Controller->render('/genericTemplates/filters'); } + public function getFilterFieldsName(): array + { + $filters = !empty($this->Controller->filterFields) ? $this->Controller->filterFields : []; + $filters = array_map(function($item) { + return is_array($item) ? $item['name'] : $item; + }, $filters); + return $filters; + } + /** * getResponsePayload Returns the adaquate response payload based on the request context * @@ -289,6 +309,9 @@ class CRUDComponent extends Component if ($this->metaFieldsSupported()) { $metaTemplates = $this->getMetaTemplates(); $data = $this->attachMetaTemplatesIfNeeded($data, $metaTemplates->toArray()); + if (isset($params['afterFind'])) { + $data = $params['afterFind']($data, $params); + } } if ($this->request->is('post')) { $patchEntityParams = [ @@ -548,16 +571,16 @@ class CRUDComponent extends Component $query->where($params['conditions']); } $data = $query->first(); + if ($this->metaFieldsSupported()) { + $metaTemplates = $this->getMetaTemplates(); + $data = $this->attachMetaTemplatesIfNeeded($data, $metaTemplates->toArray()); + } if (isset($params['afterFind'])) { $data = $params['afterFind']($data, $params); } if (empty($data)) { throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias)); } - if ($this->metaFieldsSupported()) { - $metaTemplates = $this->getMetaTemplates(); - $data = $this->attachMetaTemplatesIfNeeded($data, $metaTemplates->toArray()); - } if ($this->request->is(['post', 'put'])) { $patchEntityParams = [ 'associated' => [] @@ -828,6 +851,9 @@ class CRUDComponent extends Component $query->contain($params['contain']); } $data = $query->first(); + if (isset($params['afterFind'])) { + $data = $params['afterFind']($data, $params); + } if (empty($data)) { throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias)); } @@ -850,6 +876,9 @@ class CRUDComponent extends Component $query->contain($params['contain']); } $data = $query->first(); + if (isset($params['afterFind'])) { + $data = $params['afterFind']($data, $params); + } if (isset($params['beforeSave'])) { try { $data = $params['beforeSave']($data); @@ -1225,7 +1254,7 @@ class CRUDComponent extends Component } $activeFilters[$filter] = $filterValue; if (is_array($filterValue)) { - $query->where([($filter . ' IN') => $filterValue]); + $query = $this->setInCondition($query, $filter, $filterValue); } else { $query = $this->setValueCondition($query, $filter, $filterValue); } @@ -1311,6 +1340,27 @@ class CRUDComponent extends Component } } + protected function setInCondition($query, $fieldName, $values) + { + $split = explode(' ', $fieldName); + if (count($split) == 1) { + $field = $fieldName; + $operator = '='; + } else { + $field = $split[0]; + $operator = $split[1]; + } + if ($operator == '=') { + return $query->where(function (QueryExpression $exp, Query $q) use ($field, $values) { + return $exp->in($field, $values); + }); + } else if ($operator == '!=') { + return $query->where(function (QueryExpression $exp, Query $q) use ($field, $values) { + return $exp->notIn($field, $values); + }); + } + } + protected function setFilteringContext($contextFilters, $params) { $filteringContexts = []; @@ -1591,7 +1641,7 @@ class CRUDComponent extends Component protected function _getAssociatedTypeMap(): array { $typeMap = []; - foreach ($this->Controller->filterFields as $filter) { + foreach ($this->getFilterFieldsName() as $filter) { $exploded = explode('.', $filter); if (count($exploded) > 1) { $model = $exploded[0]; diff --git a/src/Controller/Component/Navigation/MetaTemplates.php b/src/Controller/Component/Navigation/MetaTemplates.php index c9c6707..e4cec76 100644 --- a/src/Controller/Component/Navigation/MetaTemplates.php +++ b/src/Controller/Component/Navigation/MetaTemplates.php @@ -56,6 +56,12 @@ class MetaTemplatesNavigation extends BaseNavigation 'url' => '/metaTemplates/prune_outdated_template', 'isPOST' => true, ]); + $this->bcf->addRoute('MetaTemplates', 'view_template_directory', [ + 'label' => __('View all known templates'), + 'icon' => 'list', + 'url' => '/metaTemplateNameDirectory/index', + 'isRedirect' => true, + ]); } public function addParents() @@ -78,7 +84,7 @@ class MetaTemplatesNavigation extends BaseNavigation $totalUpdateCount = $udpateCount + $newCount; } $updateAllActionConfig = [ - 'label' => __('Update all template'), + 'label' => __('Update all templates'), 'url' => '/metaTemplates/updateAllTemplates', 'url_vars' => ['id' => 'id'], ]; @@ -94,6 +100,9 @@ class MetaTemplatesNavigation extends BaseNavigation 'label' => __('Prune outdated templates'), 'url' => '/metaTemplates/prune_outdated_template', ]); + $this->bcf->addAction('MetaTemplates', 'index', 'MetaTemplates', 'view_template_directory', [ + 'isRedirect' => true, + ]); if (empty($this->viewVars['templateStatus']['up-to-date'])) { $this->bcf->addAction('MetaTemplates', 'view', 'MetaTemplates', 'update', [ diff --git a/src/Controller/EncryptionKeysController.php b/src/Controller/EncryptionKeysController.php index 39453b2..a01adf7 100644 --- a/src/Controller/EncryptionKeysController.php +++ b/src/Controller/EncryptionKeysController.php @@ -15,7 +15,7 @@ use Cake\Error\Debugger; class EncryptionKeysController extends AppController { public $filterFields = ['owner_model', 'owner_id', 'encryption_key']; - public $quickFilterFields = ['encryption_key']; + public $quickFilterFields = [['encryption_key' => true]]; public $containFields = ['Individuals', 'Organisations']; public $statisticsFields = ['type']; @@ -83,6 +83,9 @@ class EncryptionKeysController extends AppController $individualConditions = [ 'id' => $currentUser['individual_id'] ]; + $orgConditions = [ + 'id' => -1, // Only org_admins are allowed to manage their org's encryption keys + ]; } else { $this->loadModel('Alignments'); $individualConditions = ['id IN' => $this->Alignments->find('list', [ @@ -122,6 +125,11 @@ class EncryptionKeysController extends AppController 'organisation' => $this->Organisations->find('list')->order(['name' => 'asc'])->where($orgConditions)->all()->toArray(), 'individual' => $this->Individuals->find('list')->order(['email' => 'asc'])->where($individualConditions)->all()->toArray() ]; + foreach ($dropdownData as $modelName => $list) { + if (empty($list)) { + unset($dropdownData[$modelName]); + } + } return $params; } diff --git a/src/Controller/IndividualsController.php b/src/Controller/IndividualsController.php index bd3c852..46e52e8 100644 --- a/src/Controller/IndividualsController.php +++ b/src/Controller/IndividualsController.php @@ -68,25 +68,15 @@ class IndividualsController extends AppController if (!empty($responsePayload)) { return $responsePayload; } + $this->set('canEdit', $this->canEdit($id)); } public function edit($id) { - $currentUser = $this->ACL->getUser(); - if (!$currentUser['role']['perm_admin']) { - $validIndividuals = $this->Individuals->getValidIndividualsToEdit($currentUser); - if (!in_array($id, $validIndividuals)) { - throw new MethodNotAllowedException(__('You cannot modify that individual.')); - } + if (!$this->canEdit($id)) { + throw new MethodNotAllowedException(__('You cannot modify that individual.')); } $currentUser = $this->ACL->getUser(); - $validIndividualIds = []; - if (!$currentUser['role']['perm_admin']) { - $validIndividualIds = $this->Individuals->getValidIndividualsToEdit($currentUser); - if (!in_array($id, $validIndividualIds)) { - throw new NotFoundException(__('Invalid individual.')); - } - } $this->CRUD->edit($id, [ 'beforeSave' => function($data) use ($currentUser) { if ($currentUser['role']['perm_admin'] && isset($data['uuid'])) { @@ -104,7 +94,16 @@ class IndividualsController extends AppController public function delete($id) { - $this->CRUD->delete($id); + $params = [ + 'contain' => ['Users'], + 'afterFind' => function($data, $params) { + if (!empty($data['user'])) { + throw new ForbiddenException(__('Individual associated to a user cannot be deleted.')); + } + return $data; + } + ]; + $this->CRUD->delete($id, $params); $responsePayload = $this->CRUD->getResponsePayload(); if (!empty($responsePayload)) { return $responsePayload; @@ -113,6 +112,9 @@ class IndividualsController extends AppController public function tag($id) { + if (!$this->canEdit($id)) { + throw new MethodNotAllowedException(__('You cannot tag that individual.')); + } $this->CRUD->tag($id); $responsePayload = $this->CRUD->getResponsePayload(); if (!empty($responsePayload)) { @@ -122,6 +124,9 @@ class IndividualsController extends AppController public function untag($id) { + if (!$this->canEdit($id)) { + throw new MethodNotAllowedException(__('You cannot untag that individual.')); + } $this->CRUD->untag($id); $responsePayload = $this->CRUD->getResponsePayload(); if (!empty($responsePayload)) { @@ -137,4 +142,17 @@ class IndividualsController extends AppController return $responsePayload; } } + + private function canEdit($indId): bool + { + $currentUser = $this->ACL->getUser(); + if ($currentUser['role']['perm_admin']) { + return true; + } + $validIndividuals = $this->Individuals->getValidIndividualsToEdit($currentUser); + if (in_array($indId, $validIndividuals)) { + return true; + } + return false; + } } diff --git a/src/Controller/MetaTemplateNameDirectoryController.php b/src/Controller/MetaTemplateNameDirectoryController.php new file mode 100644 index 0000000..98c6884 --- /dev/null +++ b/src/Controller/MetaTemplateNameDirectoryController.php @@ -0,0 +1,35 @@ + true], 'uuid', 'version']; + public $filterFields = ['name', 'uuid', 'version']; + public $containFields = ['MetaTemplates']; + + + public function index() + { + $this->CRUD->index([ + 'filters' => $this->filterFields, + 'quickFilters' => $this->quickFilterFields, + 'contain' => $this->containFields, + ]); + $responsePayload = $this->CRUD->getResponsePayload(); + if (!empty($responsePayload)) { + return $responsePayload; + } + } +} diff --git a/src/Controller/MetaTemplatesController.php b/src/Controller/MetaTemplatesController.php index 0c59120..ac8cd2e 100644 --- a/src/Controller/MetaTemplatesController.php +++ b/src/Controller/MetaTemplatesController.php @@ -404,8 +404,18 @@ class MetaTemplatesController extends AppController $metaTemplate = $this->MetaTemplates->get($template_id, [ 'contain' => ['MetaTemplateFields'] ]); - $templateOnDisk = $this->MetaTemplates->readTemplateFromDisk($metaTemplate->uuid); + $error = ''; + $errorMessage = ''; + $templateOnDisk = $this->MetaTemplates->readTemplateFromDisk($metaTemplate->uuid, $error); + if (is_null($templateOnDisk)) { + $errorMessage = __('Could not retreive template\'s status. Reason: {0}', $error); + $this->Flash->error($errorMessage); + $templateOnDisk = []; + } $templateStatus = $this->MetaTemplates->getStatusForMetaTemplate($templateOnDisk, $metaTemplate); + if (!empty($errorMessage)) { + $templateStatus['error'] = $errorMessage; + } $this->set('templateOnDisk', $templateOnDisk); $this->set('templateStatus', $templateStatus); return [ diff --git a/src/Controller/OrganisationsController.php b/src/Controller/OrganisationsController.php index 367df4f..9739d04 100644 --- a/src/Controller/OrganisationsController.php +++ b/src/Controller/OrganisationsController.php @@ -101,7 +101,6 @@ class OrganisationsController extends AppController if (!empty($responsePayload)) { return $responsePayload; } - $this->set('metaGroup', 'ContactDB'); } public function view($id) @@ -111,16 +110,12 @@ class OrganisationsController extends AppController if (!empty($responsePayload)) { return $responsePayload; } - $this->set('metaGroup', 'ContactDB'); + $this->set('canEdit', $this->canEdit($id)); } public function edit($id) { - $currentUser = $this->ACL->getUser(); - if ( - !($currentUser['organisation']['id'] == $id && $currentUser['role']['perm_org_admin']) && - !$currentUser['role']['perm_admin'] - ) { + if (!$this->canEdit($id)) { throw new MethodNotAllowedException(__('You cannot modify that organisation.')); } $this->CRUD->edit($id); @@ -144,6 +139,9 @@ class OrganisationsController extends AppController public function tag($id) { + if (!$this->canEdit($id)) { + throw new MethodNotAllowedException(__('You cannot tag that organisation.')); + } $this->CRUD->tag($id); $responsePayload = $this->CRUD->getResponsePayload(); if (!empty($responsePayload)) { @@ -153,6 +151,9 @@ class OrganisationsController extends AppController public function untag($id) { + if (!$this->canEdit($id)) { + throw new MethodNotAllowedException(__('You cannot untag that organisation.')); + } $this->CRUD->untag($id); $responsePayload = $this->CRUD->getResponsePayload(); if (!empty($responsePayload)) { @@ -168,4 +169,16 @@ class OrganisationsController extends AppController return $responsePayload; } } + + private function canEdit($orgId): bool + { + $currentUser = $this->ACL->getUser(); + if ($currentUser['role']['perm_admin']) { + return true; + } + if ($currentUser['role']['perm_org_admin'] && $currentUser['organisation']['id'] == $orgId) { + return true; + } + return false; + } } diff --git a/src/Controller/UsersController.php b/src/Controller/UsersController.php index 3bd715d..dad2bc7 100644 --- a/src/Controller/UsersController.php +++ b/src/Controller/UsersController.php @@ -120,6 +120,12 @@ class UsersController extends AppController if (Configure::read('keycloak.enabled')) { $this->Users->enrollUserRouter($data); } + }, + 'afterFind' => function ($user, &$params) use ($currentUser) { + if (!empty($user)) { // We don't have a 404 + $user = $this->fetchTable('PermissionLimitations')->attachLimitations($user); + } + return $user; } ]); $responsePayload = $this->CRUD->getResponsePayload(); @@ -227,6 +233,14 @@ class UsersController extends AppController if (!$this->ACL->canEditUser($currentUser, $user)) { throw new MethodNotAllowedException(__('You cannot edit the given user.')); } + $user = $this->fetchTable('PermissionLimitations')->attachLimitations($user); + } + return $user; + }; + } else { + $params['afterFind'] = function ($user, &$params) use ($currentUser) { + if (!empty($user)) { // We don't have a 404 + $user = $this->fetchTable('PermissionLimitations')->attachLimitations($user); } return $user; }; diff --git a/src/Model/Table/IndividualsTable.php b/src/Model/Table/IndividualsTable.php index 95828ff..277b2f7 100644 --- a/src/Model/Table/IndividualsTable.php +++ b/src/Model/Table/IndividualsTable.php @@ -124,12 +124,15 @@ class IndividualsTable extends AppTable public function getValidIndividualsToEdit(object $currentUser): array { - $adminRoles = $this->Users->Roles->find('list')->select(['id'])->where(['perm_admin' => 1])->all()->toArray(); + $validRoles = $this->Users->Roles->find('list')->select(['id'])->where(['perm_admin' => 0, 'perm_org_admin' => 0])->all()->toArray(); $validIndividualIds = $this->Users->find('list')->select(['individual_id'])->where( [ 'organisation_id' => $currentUser['organisation_id'], 'disabled' => 0, - 'role_id NOT IN' => array_keys($adminRoles) + 'OR' => [ + ['role_id IN' => array_keys($validRoles)], + ['id' => $currentUser['id']], + ] ] )->all()->toArray(); return array_keys($validIndividualIds); diff --git a/src/Model/Table/MetaTemplateNameDirectoryTable.php b/src/Model/Table/MetaTemplateNameDirectoryTable.php index 32e0869..df39103 100644 --- a/src/Model/Table/MetaTemplateNameDirectoryTable.php +++ b/src/Model/Table/MetaTemplateNameDirectoryTable.php @@ -7,6 +7,7 @@ use App\Model\Entity\MetaTemplateNameDirectory; use App\Model\Table\AppTable; use Cake\ORM\RulesChecker; use Cake\Validation\Validator; +use Cake\Log\Log; class MetaTemplateNameDirectoryTable extends AppTable { @@ -20,6 +21,9 @@ class MetaTemplateNameDirectoryTable extends AppTable 'foreignKey' => 'meta_template_directory_id', ] ); + $this->hasOne('MetaTemplates', [ + 'foreignKey' => 'meta_template_directory_id', + ]); $this->setDisplayField('name'); } @@ -59,7 +63,11 @@ class MetaTemplateNameDirectoryTable extends AppTable if (!empty($existingTemplate)) { return $existingTemplate; } - $this->save($metaTemplateDirectory); - return $metaTemplateDirectory; + $savedEntity = $this->save($metaTemplateDirectory); + if ($savedEntity) { + return $metaTemplateDirectory; + } + Log::error(__('Could not save meta_template_directory. Reasons: {0}', json_encode($metaTemplateDirectory->getErrors()))); + return false; } } diff --git a/src/Model/Table/MetaTemplatesTable.php b/src/Model/Table/MetaTemplatesTable.php index 57925a6..d42c97d 100644 --- a/src/Model/Table/MetaTemplatesTable.php +++ b/src/Model/Table/MetaTemplatesTable.php @@ -555,6 +555,10 @@ class MetaTemplatesTable extends AppTable if (substr($file, -5) === '.json') { $errorMessage = ''; $template = $this->decodeTemplateFromDisk($path . $file, $errorMessage); + if (!empty($errorMessage)) { + $error = $errorMessage; + return null; + } if (!empty($template) && $template['uuid'] == $uuid) { return $template; } @@ -1318,6 +1322,13 @@ class MetaTemplatesTable extends AppTable $updateStatus['next_version'] = $template['version']; $updateStatus['new'] = false; $updateStatus['automatically-updateable'] = false; + $updateStatus['conflicts'] = []; + if (empty($template)) { + $updateStatus['up-to-date'] = false; + $updateStatus['automatically-updateable'] = false; + $updateStatus['can-be-removed'] = false; + return $updateStatus; + } if (intval($metaTemplate->version) >= intval($template['version'])) { $updateStatus['up-to-date'] = true; $updateStatus['conflicts'][] = __('Could not update the template. Local version is equal or newer.'); diff --git a/src/Model/Table/PermissionLimitationsTable.php b/src/Model/Table/PermissionLimitationsTable.php index e40cf96..206ea8e 100644 --- a/src/Model/Table/PermissionLimitationsTable.php +++ b/src/Model/Table/PermissionLimitationsTable.php @@ -30,13 +30,17 @@ class PermissionLimitationsTable extends AppTable public function getListOfLimitations(\App\Model\Entity\User $data) { $Users = TableRegistry::getTableLocator()->get('Users'); - $ownOrgUserIds = $Users->find('list', [ - 'keyField' => 'id', - 'valueField' => 'id', - 'conditions' => [ - 'organisation_id' => $data['organisation_id'] - ] - ])->all()->toList(); + $includeOrganisationPermissions = !empty($data['organisation_id']); + $ownOrgUserIds = []; + if ($includeOrganisationPermissions) { + $ownOrgUserIds = $Users->find('list', [ + 'keyField' => 'id', + 'valueField' => 'id', + 'conditions' => [ + 'organisation_id' => $data['organisation_id'] + ] + ])->all()->toList(); + } $MetaFields = TableRegistry::getTableLocator()->get('MetaFields'); $raw = $this->find()->select(['scope', 'permission', 'max_occurrence'])->disableHydration()->toArray(); $limitations = []; @@ -70,9 +74,12 @@ class PermissionLimitationsTable extends AppTable if (!empty($ownOrgUserIds)) { $conditions['parent_id IN'] = array_values($ownOrgUserIds); } - $limitations[$field]['organisation']['current'] = $MetaFields->find('all', [ - 'conditions' => $conditions, - ])->count(); + $limitations[$field]['organisation']['current'] = '?'; + if ($includeOrganisationPermissions) { + $limitations[$field]['organisation']['current'] = $MetaFields->find('all', [ + 'conditions' => $conditions, + ])->count(); + } } } return $limitations; @@ -89,34 +96,35 @@ class PermissionLimitationsTable extends AppTable if (!empty($data['MetaTemplates'])) { foreach ($data['MetaTemplates'] as &$metaTemplate) { foreach ($metaTemplate['meta_template_fields'] as &$meta_template_field) { - $boolean = $meta_template_field['type'] === 'boolean'; - foreach ($meta_template_field['metaFields'] as &$metaField) { - if (isset($permissionLimitations[$metaField['field']])) { - foreach ($permissionLimitations[$metaField['field']] as $scope => $value) { - $messageType = 'warning'; + if (isset($permissionLimitations[$meta_template_field['field']])) { + foreach ($permissionLimitations[$meta_template_field['field']] as $scope => $value) { + $messageType = 'warning'; + if ($value['current'] == '?') { + $messageType = 'info'; + } else { if ($value['limit'] > $value['current']) { $messageType = 'info'; } if ($value['limit'] < $value['current']) { $messageType = 'danger'; } - if (empty($metaField[$messageType])) { - $metaField[$messageType] = ''; - } - $altText = __( - 'There is a limitation enforced on the number of users with this permission {0}. Currently {1} slot(s) are used up of a maximum of {2} slot(s).', - $scope === 'global' ? __('instance wide') : __('for your organisation'), - $value['current'], - $value['limit'] - ); - $metaField[$messageType] .= sprintf( - ' : %s/%s', - $altText, - $icons[$scope], - $value['current'], - $value['limit'] - ); } + if (empty($metaField[$messageType])) { + $metaField[$messageType] = ''; + } + $altText = __( + 'There is a limitation enforced on the number of users with this permission {0}. Currently {1} slot(s) are used up of a maximum of {2} slot(s).', + $scope === 'global' ? __('instance wide') : __('for your organisation'), + $value['current'], + $value['limit'] + ); + $meta_template_field[$messageType] .= sprintf( + ' : %s/%s', + $altText, + $icons[$scope], + $value['current'], + $value['limit'] + ); } } } diff --git a/src/Model/Table/SettingProviders/CerebrateSettingsProvider.php b/src/Model/Table/SettingProviders/CerebrateSettingsProvider.php index 3f9bb65..1876c7b 100644 --- a/src/Model/Table/SettingProviders/CerebrateSettingsProvider.php +++ b/src/Model/Table/SettingProviders/CerebrateSettingsProvider.php @@ -321,7 +321,7 @@ class CerebrateSettingsProvider extends BaseSettingsProvider 2 => __('Debug On + SQL Dump'), ], 'test' => function ($value, $setting, $validator) { - $validator->range('value', [0, 3]); + $validator->range('value', [0, 2]); return testValidator($value, $validator); }, ], diff --git a/src/Model/Table/UsersTable.php b/src/Model/Table/UsersTable.php index aa52040..86160d5 100644 --- a/src/Model/Table/UsersTable.php +++ b/src/Model/Table/UsersTable.php @@ -73,13 +73,6 @@ class UsersTable extends AppTable if (!$entity->isNew()) { $success = $this->handleUserUpdateRouter($entity); } - $permissionRestrictionCheck = $this->checkPermissionRestrictions($entity); - if ($permissionRestrictionCheck !== true) { - $entity->setErrors($permissionRestrictionCheck); - $event->stopPropagation(); - $event->setResult(false); - return false; - } return $success; } @@ -187,10 +180,24 @@ class UsersTable extends AppTable public function buildRules(RulesChecker $rules): RulesChecker { $rules->add($rules->isUnique(['username'])); - $allowDuplicateIndividuals = false; if (empty(Configure::read('user.multiple-users-per-individual')) || !empty(Configure::read('keycloak.enabled'))) { $rules->add($rules->isUnique(['individual_id'])); } + + $rules->add(function($entity, $options) { + $permissionRestrictionCheck = $this->checkPermissionRestrictions($entity); + if ($permissionRestrictionCheck !== true) { + foreach ($permissionRestrictionCheck as $permission_name => $errors) { + foreach ($entity->meta_fields as $i => $metaField) { + if ($metaField['field'] === $permission_name) { + $entity->meta_fields[$i]->setErrors(['value' => $errors]); + } + } + } + return false; + } + return true; + }, 'permissionLimitations'); return $rules; } diff --git a/src/View/Helper/BootstrapElements/BootstrapIcon.php b/src/View/Helper/BootstrapElements/BootstrapIcon.php index 71c0c8a..837095d 100644 --- a/src/View/Helper/BootstrapElements/BootstrapIcon.php +++ b/src/View/Helper/BootstrapElements/BootstrapIcon.php @@ -21,6 +21,7 @@ class BootstrapIcon extends BootstrapGeneric { private $icon = ''; private $defaultOptions = [ + 'id' => '', 'class' => [], 'title' => '', 'attrs' => [], @@ -48,6 +49,7 @@ class BootstrapIcon extends BootstrapGeneric { $html = $this->node('span', array_merge( [ + 'id' => $this->options['id'] ?? '', 'class' => array_merge( $this->options['class'], ["fa fa-{$this->icon}"] diff --git a/src/View/Helper/BootstrapElements/BootstrapTabs.php b/src/View/Helper/BootstrapElements/BootstrapTabs.php index e0ac5af..60beae5 100644 --- a/src/View/Helper/BootstrapElements/BootstrapTabs.php +++ b/src/View/Helper/BootstrapElements/BootstrapTabs.php @@ -215,8 +215,6 @@ class BootstrapTabs extends BootstrapGeneric ], [ "bg-{$this->options['header-variant']}", - "text-{$this->options['header-text-variant']}", - "border-{$this->options['header-border-variant']}" ] )], $this->genNav()); $content = $this->node('div', ['class' => array_merge( @@ -226,7 +224,6 @@ class BootstrapTabs extends BootstrapGeneric ], [ "bg-{$this->options['body-variant']}", - "text-{$this->options['body-text-variant']}" ] )], $this->genContent()); @@ -238,7 +235,6 @@ class BootstrapTabs extends BootstrapGeneric ($this->options['vertical-size'] == 'auto' ? 'flex-nowrap' : '') ], [ - "border-{$this->options['header-border-variant']}" ] )], $containerContent); return $container; diff --git a/src/View/Helper/BootstrapHelper.php b/src/View/Helper/BootstrapHelper.php index f814116..122ca7c 100644 --- a/src/View/Helper/BootstrapHelper.php +++ b/src/View/Helper/BootstrapHelper.php @@ -450,7 +450,7 @@ class BootstrapGeneric return sprintf('%s="%s"', h($key), (!empty($escape) ? h($value) : $value)); } return ''; - } else if (empty($value)) { + } else if (!isset($value)) { return ''; } return sprintf('%s="%s"', h($key), (!empty($escape) ? h($value) : $value)); diff --git a/src/View/Helper/FormFieldMassageHelper.php b/src/View/Helper/FormFieldMassageHelper.php index 183a9ed..b0db4ab 100644 --- a/src/View/Helper/FormFieldMassageHelper.php +++ b/src/View/Helper/FormFieldMassageHelper.php @@ -8,6 +8,15 @@ class FormFieldMassageHelper extends Helper { public function prepareFormElement(\Cake\View\Helper\FormHelper $form, array $controlParams, array $fieldData): string { + if ($fieldData['tooltip']) { + $form->setTemplates([ + 'label' => '{{text}}{{tooltip}}', + ]); + $controlParams['templateVars'] = array_merge( + $controlParams['templateVars'] ?? [], + ['tooltip' => $fieldData['tooltip'], ] + ); + } if (!empty($fieldData['stateDependence'])) { $controlParams['data-dependence-source'] = h($fieldData['stateDependence']['source']); $controlParams['data-dependence-option'] = h($fieldData['stateDependence']['option']); diff --git a/templates/AuthKeys/add.php b/templates/AuthKeys/add.php index c9f0219..4c829ec 100644 --- a/templates/AuthKeys/add.php +++ b/templates/AuthKeys/add.php @@ -14,7 +14,8 @@ echo $this->element('genericElements/Form/genericForm', [ ], [ 'field' => 'expiration', - 'label' => 'Expiration' + 'label' => __('Expiration'), + 'type' => 'datetime', ] ], 'submit' => [ diff --git a/templates/Individuals/index.php b/templates/Individuals/index.php index 4a662aa..d7d228f 100644 --- a/templates/Individuals/index.php +++ b/templates/Individuals/index.php @@ -103,6 +103,9 @@ echo $this->element('genericElements/IndexTable/index_table', [ 'icon' => 'trash', 'complex_requirement' => [ 'function' => function ($row, $options) use ($loggedUser) { + if (!empty($row['user'])) { // cannot delete individuals with associated user(s) + return false; + } return (bool)$loggedUser['role']['perm_admin']; } ] diff --git a/templates/Individuals/view.php b/templates/Individuals/view.php index 3eeddbc..423c7e6 100644 --- a/templates/Individuals/view.php +++ b/templates/Individuals/view.php @@ -31,6 +31,7 @@ echo $this->element( [ 'key' => __('Tags'), 'type' => 'tags', + 'editable' => $canEdit, ], [ 'key' => __('Alignments'), diff --git a/templates/MailingLists/add_individual.php b/templates/MailingLists/add_individual.php index 0455d22..beb19da 100644 --- a/templates/MailingLists/add_individual.php +++ b/templates/MailingLists/add_individual.php @@ -10,7 +10,6 @@ echo $this->element('genericElements/Form/genericForm', [ 'multiple' => true, 'select2' => true, 'label' => __('Members'), - 'class' => 'select2-input', 'options' => $dropdownData['individuals'] ], [ diff --git a/templates/MetaTemplateNameDirectory/index.php b/templates/MetaTemplateNameDirectory/index.php new file mode 100644 index 0000000..0b57d8c --- /dev/null +++ b/templates/MetaTemplateNameDirectory/index.php @@ -0,0 +1,60 @@ +element('genericElements/IndexTable/index_table', [ + 'data' => [ + 'data' => $data, + 'top_bar' => [ + 'children' => [ + [ + 'type' => 'search', + 'button' => __('Search'), + 'placeholder' => __('Enter value to search'), + 'data' => '', + 'searchKey' => 'value' + ] + ] + ], + 'fields' => [ + [ + 'name' => '#', + 'sort' => 'id', + 'data_path' => 'id', + ], + [ + 'name' => __('Name'), + 'sort' => 'name', + 'data_path' => 'name', + ], + [ + 'name' => __('Namespace'), + 'sort' => 'namespace', + 'data_path' => 'namespace', + ], + [ + 'name' => __('UUID'), + 'sort' => 'uuid', + 'data_path' => 'uuid' + ], + [ + 'name' => __('Version'), + 'sort' => 'version', + 'data_path' => 'version', + ], + [ + 'name' => __('Associated Meta-Template'), + 'sort' => 'meta_template.id', + 'data_path' => 'meta_template.id', + 'element' => 'function', + 'function' => function($row, $viewContext) { + return $viewContext->Bootstrap::node('a', [ + 'href' => h($baseurl . '/metaTemplates/view/' . $row->meta_template->id ?? ''), + ], !empty($row->meta_template->name) ? (sprintf('%s (v%s)', h($row->meta_template->name), h($row->meta_template->version))) :''); + } + ], + ], + 'title' => __('Meta Template Name Directory'), + 'description' => __('The directory of all meta templates known by the system.'), + 'actions' => [] + ] +]); diff --git a/templates/MetaTemplates/update.php b/templates/MetaTemplates/update.php index a1c1821..f2178bd 100644 --- a/templates/MetaTemplates/update.php +++ b/templates/MetaTemplates/update.php @@ -11,6 +11,14 @@ if ($updateStatus['up-to-date']) { 'dismissible' => false, ]); $modalType = 'ok-only'; +} else if (empty($templateOnDisk)) { + $diskTemplateError = $templateStatus['error'] ?? __('Unknown'); + $bodyHtml .= $this->Bootstrap->alert([ + 'variant' => 'danger', + 'html' => sprintf('%s %s
%s
', __('Could not get template on disk.'), __('Reason:'), h($diskTemplateError)), + 'dismissible' => false, + ]); + $modalType = 'ok-only'; } else { if ($updateStatus['automatically-updateable']) { $bodyHtml .= $this->Bootstrap->alert([ diff --git a/templates/Open/Organisations/index.php b/templates/Open/Organisations/index.php index fa6fcab..18be544 100644 --- a/templates/Open/Organisations/index.php +++ b/templates/Open/Organisations/index.php @@ -57,7 +57,7 @@ echo $this->element('genericElements/IndexTable/index_table', [ 'data_path' => 'url', ], [ - 'name' => __('Nationality'), + 'name' => __('Country'), 'data_path' => 'nationality', ], [ diff --git a/templates/Organisations/add.php b/templates/Organisations/add.php index 72b9a9e..e430cc6 100644 --- a/templates/Organisations/add.php +++ b/templates/Organisations/add.php @@ -10,12 +10,14 @@ array( 'field' => 'uuid', 'label' => 'UUID', - 'type' => 'uuid' + 'type' => 'uuid', + 'tooltip' => __('If the Organisation already has a known UUID in another application such as MISP or another Cerebrate, please re-use this one.'), ), array( 'field' => 'url' ), array( + 'label' => __('Country'), 'field' => 'nationality' ), array( diff --git a/templates/Organisations/index.php b/templates/Organisations/index.php index 34e4a10..2347b04 100644 --- a/templates/Organisations/index.php +++ b/templates/Organisations/index.php @@ -66,7 +66,7 @@ echo $this->element('genericElements/IndexTable/index_table', [ 'data_path' => 'url', ], [ - 'name' => __('Nationality'), + 'name' => __('Country'), 'data_path' => 'nationality', 'sort' => 'nationality', ], diff --git a/templates/Organisations/view.php b/templates/Organisations/view.php index 8ffbef8..0d3d2cd 100644 --- a/templates/Organisations/view.php +++ b/templates/Organisations/view.php @@ -22,7 +22,7 @@ echo $this->element( 'path' => 'url' ], [ - 'key' => __('Nationality'), + 'key' => __('Country'), 'path' => 'nationality' ], [ @@ -40,6 +40,7 @@ echo $this->element( [ 'key' => __('Tags'), 'type' => 'tags', + 'editable' => $canEdit, ], [ 'key' => __('Alignments'), diff --git a/templates/Roles/index.php b/templates/Roles/index.php index d846934..02299e6 100644 --- a/templates/Roles/index.php +++ b/templates/Roles/index.php @@ -1,28 +1,31 @@ role->perm_admin)) { + $topbarChildren[] = [ + 'type' => 'simple', + 'children' => [ + 'data' => [ + 'type' => 'simple', + 'text' => __('Add role'), + 'class' => 'btn btn-primary', + 'popover_url' => '/roles/add' + ] + ] + ]; +} +$topbarChildren[] = [ + 'type' => 'search', + 'button' => __('Search'), + 'placeholder' => __('Enter value to search'), + 'data' => '', + 'searchKey' => 'value' +]; + echo $this->element('genericElements/IndexTable/index_table', [ 'data' => [ 'data' => $data, 'top_bar' => [ - 'children' => [ - [ - 'type' => 'simple', - 'children' => [ - 'data' => [ - 'type' => 'simple', - 'text' => __('Add role'), - 'class' => 'btn btn-primary', - 'popover_url' => '/roles/add' - ] - ] - ], - [ - 'type' => 'search', - 'button' => __('Search'), - 'placeholder' => __('Enter value to search'), - 'data' => '', - 'searchKey' => 'value' - ] - ] + 'children' => $topbarChildren, ], 'fields' => [ [ diff --git a/templates/Users/add.php b/templates/Users/add.php index 93a8d56..9aff9d3 100644 --- a/templates/Users/add.php +++ b/templates/Users/add.php @@ -1,99 +1,145 @@ request->getParam('action') === 'add') { - $dropdownData['individual'] = ['new' => __('New individual')] + $dropdownData['individual']; - if (!Configure::check('password_auth.enabled') || Configure::read('password_auth.enabled')) { - $passwordRequired = 'required'; - } - } + +use Cake\Core\Configure; + +$passwordRequired = false; +$showPasswordField = false; +if ($this->request->getParam('action') === 'add') { + $dropdownData['individual'] = ['new' => __('New individual')] + $dropdownData['individual']; if (!Configure::check('password_auth.enabled') || Configure::read('password_auth.enabled')) { - $showPasswordField = true; + $passwordRequired = 'required'; } - echo $this->element('genericElements/Form/genericForm', [ - 'data' => [ - 'description' => __('Roles define global rules for a set of users, including first and foremost access controls to certain functionalities.'), - 'model' => 'Roles', - 'fields' => [ - [ - 'field' => 'individual_id', - 'type' => 'dropdown', - 'label' => __('Associated individual'), - 'options' => isset($dropdownData['individual']) ? $dropdownData['individual'] : [], - 'conditions' => $this->request->getParam('action') === 'add' - ], - [ - 'field' => 'individual.email', - 'stateDependence' => [ - 'source' => '#individual_id-field', - 'option' => 'new' - ], - 'required' => false - ], - [ - 'field' => 'individual.first_name', - 'label' => 'First name', - 'stateDependence' => [ - 'source' => '#individual_id-field', - 'option' => 'new' - ], - 'required' => false - ], - [ - 'field' => 'individual.last_name', - 'label' => 'Last name', - 'stateDependence' => [ - 'source' => '#individual_id-field', - 'option' => 'new' - ], - 'required' => false - ], - [ - 'field' => 'username', - 'autocomplete' => 'off' - ], - [ - 'field' => 'organisation_id', - 'type' => 'dropdown', - 'label' => __('Associated organisation'), - 'options' => $dropdownData['organisation'], - 'default' => $loggedUser['organisation_id'] - ], - [ - 'field' => 'password', - 'label' => __('Password'), - 'type' => 'password', - 'required' => $passwordRequired, - 'autocomplete' => 'new-password', - 'value' => '', - 'requirements' => $showPasswordField, - ], - [ - 'field' => 'confirm_password', - 'label' => __('Confirm Password'), - 'type' => 'password', - 'required' => $passwordRequired, - 'autocomplete' => 'off', - 'requirements' => $showPasswordField, - ], - [ - 'field' => 'role_id', - 'type' => 'dropdown', - 'label' => __('Role'), - 'options' => $dropdownData['role'], - 'default' => $defaultRole ?? null - ], - [ - 'field' => 'disabled', - 'type' => 'checkbox', - 'label' => 'Disable' - ], +} +if (!Configure::check('password_auth.enabled') || Configure::read('password_auth.enabled')) { + $showPasswordField = true; +} +echo $this->element('genericElements/Form/genericForm', [ + 'data' => [ + 'description' => __('Roles define global rules for a set of users, including first and foremost access controls to certain functionalities.'), + 'model' => 'Roles', + 'fields' => [ + [ + 'field' => 'individual_id', + 'type' => 'dropdown', + 'label' => __('Associated individual'), + 'options' => isset($dropdownData['individual']) ? $dropdownData['individual'] : [], + 'conditions' => $this->request->getParam('action') === 'add' ], - 'submit' => [ - 'action' => $this->request->getParam('action') - ] + [ + 'field' => 'individual.email', + 'stateDependence' => [ + 'source' => '#individual_id-field', + 'option' => 'new' + ], + 'required' => false + ], + [ + 'field' => 'individual.first_name', + 'label' => 'First name', + 'stateDependence' => [ + 'source' => '#individual_id-field', + 'option' => 'new' + ], + 'required' => false + ], + [ + 'field' => 'individual.last_name', + 'label' => 'Last name', + 'stateDependence' => [ + 'source' => '#individual_id-field', + 'option' => 'new' + ], + 'required' => false + ], + [ + 'field' => 'username', + 'autocomplete' => 'off' + ], + [ + 'field' => 'organisation_id', + 'type' => 'dropdown', + 'label' => __('Associated organisation'), + 'options' => $dropdownData['organisation'], + 'default' => $loggedUser['organisation_id'] + ], + [ + 'field' => 'password', + 'label' => __('Password'), + 'type' => 'password', + 'required' => $passwordRequired, + 'autocomplete' => 'new-password', + 'value' => '', + 'requirements' => $showPasswordField, + ], + [ + 'field' => 'confirm_password', + 'label' => __('Confirm Password'), + 'type' => 'password', + 'required' => $passwordRequired, + 'autocomplete' => 'off', + 'requirements' => $showPasswordField, + ], + [ + 'field' => 'role_id', + 'type' => 'dropdown', + 'label' => __('Role'), + 'options' => $dropdownData['role'], + 'default' => $defaultRole ?? null + ], + [ + 'field' => 'disabled', + 'type' => 'checkbox', + 'label' => 'Disable' + ], + ], + 'submit' => [ + 'action' => $this->request->getParam('action') ] - ]); + ] +]); ?> - + + \ No newline at end of file diff --git a/templates/element/genericElements/Form/Fields/dropdownField.php b/templates/element/genericElements/Form/Fields/dropdownField.php index 4db9707..5f84e5c 100644 --- a/templates/element/genericElements/Form/Fields/dropdownField.php +++ b/templates/element/genericElements/Form/Fields/dropdownField.php @@ -2,7 +2,7 @@ $seed = 's-' . mt_rand(); $controlParams = [ 'type' => 'select', - 'options' => $fieldData['options'], + 'options' => $fieldData['options'] ?? [], 'empty' => $fieldData['empty'] ?? false, 'value' => $fieldData['value'] ?? null, 'multiple' => $fieldData['multiple'] ?? false, @@ -19,9 +19,13 @@ if (!empty($fieldData['label'])) { if ($controlParams['options'] instanceof \Cake\ORM\Query) { $controlParams['options'] = $controlParams['options']->all()->toList(); } -if (!empty($fieldData['select2'])) { +$initSelect2 = false; +if (isset($fieldData['select2']) && $fieldData['select2'] == true) { + $initSelect2 = true; + $fieldData['select2'] = $fieldData['select2'] === true ? [] : $fieldData['select2']; $controlParams['class'] .= ' select2-input'; } +$controlParams['class'] .= ' dropdown-custom-value' . "-$seed"; if (in_array('_custom', array_keys($controlParams['options']))) { $customInputValue = $this->Form->getSourceValue($fieldData['field']); if (!in_array($customInputValue, $controlParams['options'])) { @@ -34,7 +38,6 @@ if (in_array('_custom', array_keys($controlParams['options']))) { } else { $customInputValue = ''; } - $controlParams['class'] .= ' dropdown-custom-value' . "-$seed"; $adaptedField = $fieldData['field'] . '_custom'; $controlParams['templates']['formGroup'] = sprintf( ' ', @@ -52,14 +55,18 @@ echo $this->FormFieldMassage->prepareFormElement($this->Form, $controlParams, $f $select.attr('onclick', 'toggleFreetextSelectField(this)') $select.parent().find('input.custom-value').attr('oninput', 'updateAssociatedSelect(this)') updateAssociatedSelect($select.parent().find('input.custom-value')[0]) - - let $container = $select.closest('.modal-dialog') + + // let $container = $select.closest('.modal-dialog .modal-body') + let $container = [] if ($container.length == 0) { $container = $(document.body) } - $select.select2({ + const defaultSelect2Options = { dropdownParent: $container, - }) + } + const passedSelect2Options = = json_encode($fieldData['select2']) ?>; + const select2Options = Object.assign({}, passedSelect2Options, defaultSelect2Options) + $select.select2(select2Options) }) diff --git a/templates/element/genericElements/Form/Fields/uuidField.php b/templates/element/genericElements/Form/Fields/uuidField.php index b065e08..b63e551 100644 --- a/templates/element/genericElements/Form/Fields/uuidField.php +++ b/templates/element/genericElements/Form/Fields/uuidField.php @@ -1,43 +1,43 @@ Form->setTemplates([ - 'inputContainer' => '{{content}}', - 'inputContainerError' => '{{content}}', - 'formGroup' => '{{input}}', - ]); - $label = $fieldData['label']; - $formElement = $this->FormFieldMassage->prepareFormElement($this->Form, $params, $fieldData); - $temp = sprintf( - '