Merge branch 'develop' into main
commit
3b385ade74
|
@ -11,7 +11,7 @@ use Cake\Core\Configure;
|
||||||
|
|
||||||
class AuditLogsController extends AppController
|
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 $quickFilterFields = ['model', 'request_action', 'model_title'];
|
||||||
public $containFields = ['Users'];
|
public $containFields = ['Users'];
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ class AuditLogsController extends AppController
|
||||||
$this->CRUD->index([
|
$this->CRUD->index([
|
||||||
'contain' => $this->containFields,
|
'contain' => $this->containFields,
|
||||||
'order' => ['AuditLogs.id' => 'DESC'],
|
'order' => ['AuditLogs.id' => 'DESC'],
|
||||||
'filters' => $this->filterFields,
|
'filters' => $this->CRUD->getFilterFieldsName($this->filterFields),
|
||||||
'quickFilters' => $this->quickFilterFields,
|
'quickFilters' => $this->quickFilterFields,
|
||||||
'afterFind' => function($data) {
|
'afterFind' => function($data) {
|
||||||
$request_ip = is_resource($data['request_ip']) ? stream_get_contents($data['request_ip']) : $data['request_ip'];
|
$request_ip = is_resource($data['request_ip']) ? stream_get_contents($data['request_ip']) : $data['request_ip'];
|
||||||
|
|
|
@ -71,8 +71,12 @@ class AuthKeysController extends AppController
|
||||||
if (empty($currentUser['role']['perm_org_admin'])) {
|
if (empty($currentUser['role']['perm_org_admin'])) {
|
||||||
$userConditions['id'] = $currentUser['id'];
|
$userConditions['id'] = $currentUser['id'];
|
||||||
} else {
|
} else {
|
||||||
$role_ids = $this->Users->Roles->find()->where(['perm_admin' => 0])->all()->extract('id')->toList();
|
$role_ids = $this->Users->Roles->find()->where(['perm_admin' => 0, 'perm_org_admin' => 0])->all()->extract('id')->toList();
|
||||||
$userConditions['role_id IN'] = $role_ids;
|
$userConditions['organisation_id'] = $currentUser['organisation_id'];
|
||||||
|
$userConditions['OR'] = [
|
||||||
|
['role_id IN' => $role_ids],
|
||||||
|
['id' => $currentUser['id']],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$users = $this->Users->find('list');
|
$users = $this->Users->find('list');
|
||||||
|
@ -99,6 +103,7 @@ class AuthKeysController extends AppController
|
||||||
$dropdownData = [
|
$dropdownData = [
|
||||||
'user' => $users
|
'user' => $users
|
||||||
];
|
];
|
||||||
|
$this->entity->user_id = $currentUser['id'];
|
||||||
$this->set(compact('dropdownData'));
|
$this->set(compact('dropdownData'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -90,8 +90,8 @@ class ACLComponent extends Component
|
||||||
'edit' => ['perm_admin', 'perm_org_admin'],
|
'edit' => ['perm_admin', 'perm_org_admin'],
|
||||||
'filtering' => ['*'],
|
'filtering' => ['*'],
|
||||||
'index' => ['*'],
|
'index' => ['*'],
|
||||||
'tag' => ['perm_tagger'],
|
'tag' => ['*'],
|
||||||
'untag' => ['perm_tagger'],
|
'untag' => ['*'],
|
||||||
'view' => ['*'],
|
'view' => ['*'],
|
||||||
'viewTags' => ['*']
|
'viewTags' => ['*']
|
||||||
],
|
],
|
||||||
|
@ -140,20 +140,24 @@ class ACLComponent extends Component
|
||||||
'enable' => ['perm_admin'],
|
'enable' => ['perm_admin'],
|
||||||
'getMetaFieldsToUpdate' => ['perm_admin'],
|
'getMetaFieldsToUpdate' => ['perm_admin'],
|
||||||
'index' => ['perm_admin'],
|
'index' => ['perm_admin'],
|
||||||
|
'migrateMetafieldsToNewestTemplate' => ['perm_admin'],
|
||||||
'migrateOldMetaTemplateToNewestVersionForEntity' => ['perm_admin'],
|
'migrateOldMetaTemplateToNewestVersionForEntity' => ['perm_admin'],
|
||||||
'update' => ['perm_admin'],
|
'update' => ['perm_admin'],
|
||||||
'updateAllTemplates' => ['perm_admin'],
|
'updateAllTemplates' => ['perm_admin'],
|
||||||
'toggle' => ['perm_admin'],
|
'toggle' => ['perm_admin'],
|
||||||
'view' => ['perm_admin'],
|
'view' => ['perm_admin'],
|
||||||
],
|
],
|
||||||
|
'MetaTemplateNameDirectory' => [
|
||||||
|
'index' => ['perm_admin'],
|
||||||
|
],
|
||||||
'Organisations' => [
|
'Organisations' => [
|
||||||
'add' => ['perm_admin'],
|
'add' => ['perm_admin'],
|
||||||
'delete' => ['perm_admin'],
|
'delete' => ['perm_admin'],
|
||||||
'edit' => ['perm_admin'],
|
'edit' => ['perm_admin'],
|
||||||
'filtering' => ['*'],
|
'filtering' => ['*'],
|
||||||
'index' => ['*'],
|
'index' => ['*'],
|
||||||
'tag' => ['perm_tagger'],
|
'tag' => ['perm_org_admin'],
|
||||||
'untag' => ['perm_tagger'],
|
'untag' => ['perm_org_admin'],
|
||||||
'view' => ['*'],
|
'view' => ['*'],
|
||||||
'viewTags' => ['*']
|
'viewTags' => ['*']
|
||||||
],
|
],
|
||||||
|
|
|
@ -10,6 +10,7 @@ use Cake\Utility\Text;
|
||||||
use Cake\View\ViewBuilder;
|
use Cake\View\ViewBuilder;
|
||||||
use Cake\ORM\TableRegistry;
|
use Cake\ORM\TableRegistry;
|
||||||
use Cake\ORM\Query;
|
use Cake\ORM\Query;
|
||||||
|
use Cake\Database\Expression\QueryExpression;
|
||||||
use Cake\Routing\Router;
|
use Cake\Routing\Router;
|
||||||
use Cake\Http\Exception\MethodNotAllowedException;
|
use Cake\Http\Exception\MethodNotAllowedException;
|
||||||
use Cake\Http\Exception\NotFoundException;
|
use Cake\Http\Exception\NotFoundException;
|
||||||
|
@ -225,22 +226,41 @@ class CRUDComponent extends Component
|
||||||
} else {
|
} else {
|
||||||
$this->Controller->set('metaFieldsEnabled', false);
|
$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();
|
$typeMap = $this->Table->getSchema()->typeMap();
|
||||||
$associatedtypeMap = !empty($this->Controller->filterFields) ? $this->_getAssociatedTypeMap() : [];
|
$associatedtypeMap = !empty($filtersName) ? $this->_getAssociatedTypeMap() : [];
|
||||||
$typeMap = array_merge(
|
$typeMap = array_merge(
|
||||||
$this->Table->getSchema()->typeMap(),
|
$this->Table->getSchema()->typeMap(),
|
||||||
$associatedtypeMap
|
$associatedtypeMap
|
||||||
);
|
);
|
||||||
$typeMap = array_filter($typeMap, function ($field) use ($filters) {
|
$typeMap = array_filter($typeMap, function ($field) use ($filtersName) {
|
||||||
return in_array($field, $filters);
|
return in_array($field, $filtersName);
|
||||||
}, ARRAY_FILTER_USE_KEY);
|
}, ARRAY_FILTER_USE_KEY);
|
||||||
$this->Controller->set('typeMap', $typeMap);
|
$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->viewBuilder()->setLayout('ajax');
|
||||||
$this->Controller->render('/genericTemplates/filters');
|
$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
|
* getResponsePayload Returns the adaquate response payload based on the request context
|
||||||
*
|
*
|
||||||
|
@ -289,6 +309,9 @@ class CRUDComponent extends Component
|
||||||
if ($this->metaFieldsSupported()) {
|
if ($this->metaFieldsSupported()) {
|
||||||
$metaTemplates = $this->getMetaTemplates();
|
$metaTemplates = $this->getMetaTemplates();
|
||||||
$data = $this->attachMetaTemplatesIfNeeded($data, $metaTemplates->toArray());
|
$data = $this->attachMetaTemplatesIfNeeded($data, $metaTemplates->toArray());
|
||||||
|
if (isset($params['afterFind'])) {
|
||||||
|
$data = $params['afterFind']($data, $params);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($this->request->is('post')) {
|
if ($this->request->is('post')) {
|
||||||
$patchEntityParams = [
|
$patchEntityParams = [
|
||||||
|
@ -548,16 +571,16 @@ class CRUDComponent extends Component
|
||||||
$query->where($params['conditions']);
|
$query->where($params['conditions']);
|
||||||
}
|
}
|
||||||
$data = $query->first();
|
$data = $query->first();
|
||||||
|
if ($this->metaFieldsSupported()) {
|
||||||
|
$metaTemplates = $this->getMetaTemplates();
|
||||||
|
$data = $this->attachMetaTemplatesIfNeeded($data, $metaTemplates->toArray());
|
||||||
|
}
|
||||||
if (isset($params['afterFind'])) {
|
if (isset($params['afterFind'])) {
|
||||||
$data = $params['afterFind']($data, $params);
|
$data = $params['afterFind']($data, $params);
|
||||||
}
|
}
|
||||||
if (empty($data)) {
|
if (empty($data)) {
|
||||||
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
|
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'])) {
|
if ($this->request->is(['post', 'put'])) {
|
||||||
$patchEntityParams = [
|
$patchEntityParams = [
|
||||||
'associated' => []
|
'associated' => []
|
||||||
|
@ -828,6 +851,9 @@ class CRUDComponent extends Component
|
||||||
$query->contain($params['contain']);
|
$query->contain($params['contain']);
|
||||||
}
|
}
|
||||||
$data = $query->first();
|
$data = $query->first();
|
||||||
|
if (isset($params['afterFind'])) {
|
||||||
|
$data = $params['afterFind']($data, $params);
|
||||||
|
}
|
||||||
if (empty($data)) {
|
if (empty($data)) {
|
||||||
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
|
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
|
||||||
}
|
}
|
||||||
|
@ -850,6 +876,9 @@ class CRUDComponent extends Component
|
||||||
$query->contain($params['contain']);
|
$query->contain($params['contain']);
|
||||||
}
|
}
|
||||||
$data = $query->first();
|
$data = $query->first();
|
||||||
|
if (isset($params['afterFind'])) {
|
||||||
|
$data = $params['afterFind']($data, $params);
|
||||||
|
}
|
||||||
if (isset($params['beforeSave'])) {
|
if (isset($params['beforeSave'])) {
|
||||||
try {
|
try {
|
||||||
$data = $params['beforeSave']($data);
|
$data = $params['beforeSave']($data);
|
||||||
|
@ -1225,7 +1254,7 @@ class CRUDComponent extends Component
|
||||||
}
|
}
|
||||||
$activeFilters[$filter] = $filterValue;
|
$activeFilters[$filter] = $filterValue;
|
||||||
if (is_array($filterValue)) {
|
if (is_array($filterValue)) {
|
||||||
$query->where([($filter . ' IN') => $filterValue]);
|
$query = $this->setInCondition($query, $filter, $filterValue);
|
||||||
} else {
|
} else {
|
||||||
$query = $this->setValueCondition($query, $filter, $filterValue);
|
$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)
|
protected function setFilteringContext($contextFilters, $params)
|
||||||
{
|
{
|
||||||
$filteringContexts = [];
|
$filteringContexts = [];
|
||||||
|
@ -1591,7 +1641,7 @@ class CRUDComponent extends Component
|
||||||
protected function _getAssociatedTypeMap(): array
|
protected function _getAssociatedTypeMap(): array
|
||||||
{
|
{
|
||||||
$typeMap = [];
|
$typeMap = [];
|
||||||
foreach ($this->Controller->filterFields as $filter) {
|
foreach ($this->getFilterFieldsName() as $filter) {
|
||||||
$exploded = explode('.', $filter);
|
$exploded = explode('.', $filter);
|
||||||
if (count($exploded) > 1) {
|
if (count($exploded) > 1) {
|
||||||
$model = $exploded[0];
|
$model = $exploded[0];
|
||||||
|
|
|
@ -56,6 +56,12 @@ class MetaTemplatesNavigation extends BaseNavigation
|
||||||
'url' => '/metaTemplates/prune_outdated_template',
|
'url' => '/metaTemplates/prune_outdated_template',
|
||||||
'isPOST' => true,
|
'isPOST' => true,
|
||||||
]);
|
]);
|
||||||
|
$this->bcf->addRoute('MetaTemplates', 'view_template_directory', [
|
||||||
|
'label' => __('View all known templates'),
|
||||||
|
'icon' => 'list',
|
||||||
|
'url' => '/metaTemplateNameDirectory/index',
|
||||||
|
'isRedirect' => true,
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function addParents()
|
public function addParents()
|
||||||
|
@ -78,7 +84,7 @@ class MetaTemplatesNavigation extends BaseNavigation
|
||||||
$totalUpdateCount = $udpateCount + $newCount;
|
$totalUpdateCount = $udpateCount + $newCount;
|
||||||
}
|
}
|
||||||
$updateAllActionConfig = [
|
$updateAllActionConfig = [
|
||||||
'label' => __('Update all template'),
|
'label' => __('Update all templates'),
|
||||||
'url' => '/metaTemplates/updateAllTemplates',
|
'url' => '/metaTemplates/updateAllTemplates',
|
||||||
'url_vars' => ['id' => 'id'],
|
'url_vars' => ['id' => 'id'],
|
||||||
];
|
];
|
||||||
|
@ -94,6 +100,9 @@ class MetaTemplatesNavigation extends BaseNavigation
|
||||||
'label' => __('Prune outdated templates'),
|
'label' => __('Prune outdated templates'),
|
||||||
'url' => '/metaTemplates/prune_outdated_template',
|
'url' => '/metaTemplates/prune_outdated_template',
|
||||||
]);
|
]);
|
||||||
|
$this->bcf->addAction('MetaTemplates', 'index', 'MetaTemplates', 'view_template_directory', [
|
||||||
|
'isRedirect' => true,
|
||||||
|
]);
|
||||||
|
|
||||||
if (empty($this->viewVars['templateStatus']['up-to-date'])) {
|
if (empty($this->viewVars['templateStatus']['up-to-date'])) {
|
||||||
$this->bcf->addAction('MetaTemplates', 'view', 'MetaTemplates', 'update', [
|
$this->bcf->addAction('MetaTemplates', 'view', 'MetaTemplates', 'update', [
|
||||||
|
|
|
@ -15,7 +15,7 @@ use Cake\Error\Debugger;
|
||||||
class EncryptionKeysController extends AppController
|
class EncryptionKeysController extends AppController
|
||||||
{
|
{
|
||||||
public $filterFields = ['owner_model', 'owner_id', 'encryption_key'];
|
public $filterFields = ['owner_model', 'owner_id', 'encryption_key'];
|
||||||
public $quickFilterFields = ['encryption_key'];
|
public $quickFilterFields = [['encryption_key' => true]];
|
||||||
public $containFields = ['Individuals', 'Organisations'];
|
public $containFields = ['Individuals', 'Organisations'];
|
||||||
public $statisticsFields = ['type'];
|
public $statisticsFields = ['type'];
|
||||||
|
|
||||||
|
@ -83,6 +83,9 @@ class EncryptionKeysController extends AppController
|
||||||
$individualConditions = [
|
$individualConditions = [
|
||||||
'id' => $currentUser['individual_id']
|
'id' => $currentUser['individual_id']
|
||||||
];
|
];
|
||||||
|
$orgConditions = [
|
||||||
|
'id' => -1, // Only org_admins are allowed to manage their org's encryption keys
|
||||||
|
];
|
||||||
} else {
|
} else {
|
||||||
$this->loadModel('Alignments');
|
$this->loadModel('Alignments');
|
||||||
$individualConditions = ['id IN' => $this->Alignments->find('list', [
|
$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(),
|
'organisation' => $this->Organisations->find('list')->order(['name' => 'asc'])->where($orgConditions)->all()->toArray(),
|
||||||
'individual' => $this->Individuals->find('list')->order(['email' => 'asc'])->where($individualConditions)->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;
|
return $params;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -68,25 +68,15 @@ class IndividualsController extends AppController
|
||||||
if (!empty($responsePayload)) {
|
if (!empty($responsePayload)) {
|
||||||
return $responsePayload;
|
return $responsePayload;
|
||||||
}
|
}
|
||||||
|
$this->set('canEdit', $this->canEdit($id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit($id)
|
public function edit($id)
|
||||||
{
|
{
|
||||||
$currentUser = $this->ACL->getUser();
|
if (!$this->canEdit($id)) {
|
||||||
if (!$currentUser['role']['perm_admin']) {
|
throw new MethodNotAllowedException(__('You cannot modify that individual.'));
|
||||||
$validIndividuals = $this->Individuals->getValidIndividualsToEdit($currentUser);
|
|
||||||
if (!in_array($id, $validIndividuals)) {
|
|
||||||
throw new MethodNotAllowedException(__('You cannot modify that individual.'));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
$currentUser = $this->ACL->getUser();
|
$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, [
|
$this->CRUD->edit($id, [
|
||||||
'beforeSave' => function($data) use ($currentUser) {
|
'beforeSave' => function($data) use ($currentUser) {
|
||||||
if ($currentUser['role']['perm_admin'] && isset($data['uuid'])) {
|
if ($currentUser['role']['perm_admin'] && isset($data['uuid'])) {
|
||||||
|
@ -104,7 +94,16 @@ class IndividualsController extends AppController
|
||||||
|
|
||||||
public function delete($id)
|
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();
|
$responsePayload = $this->CRUD->getResponsePayload();
|
||||||
if (!empty($responsePayload)) {
|
if (!empty($responsePayload)) {
|
||||||
return $responsePayload;
|
return $responsePayload;
|
||||||
|
@ -113,6 +112,9 @@ class IndividualsController extends AppController
|
||||||
|
|
||||||
public function tag($id)
|
public function tag($id)
|
||||||
{
|
{
|
||||||
|
if (!$this->canEdit($id)) {
|
||||||
|
throw new MethodNotAllowedException(__('You cannot tag that individual.'));
|
||||||
|
}
|
||||||
$this->CRUD->tag($id);
|
$this->CRUD->tag($id);
|
||||||
$responsePayload = $this->CRUD->getResponsePayload();
|
$responsePayload = $this->CRUD->getResponsePayload();
|
||||||
if (!empty($responsePayload)) {
|
if (!empty($responsePayload)) {
|
||||||
|
@ -122,6 +124,9 @@ class IndividualsController extends AppController
|
||||||
|
|
||||||
public function untag($id)
|
public function untag($id)
|
||||||
{
|
{
|
||||||
|
if (!$this->canEdit($id)) {
|
||||||
|
throw new MethodNotAllowedException(__('You cannot untag that individual.'));
|
||||||
|
}
|
||||||
$this->CRUD->untag($id);
|
$this->CRUD->untag($id);
|
||||||
$responsePayload = $this->CRUD->getResponsePayload();
|
$responsePayload = $this->CRUD->getResponsePayload();
|
||||||
if (!empty($responsePayload)) {
|
if (!empty($responsePayload)) {
|
||||||
|
@ -137,4 +142,17 @@ class IndividualsController extends AppController
|
||||||
return $responsePayload;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Controller;
|
||||||
|
|
||||||
|
use App\Controller\AppController;
|
||||||
|
use Cake\Utility\Hash;
|
||||||
|
use Cake\Utility\Text;
|
||||||
|
use Cake\Utility\Inflector;
|
||||||
|
use Cake\ORM\TableRegistry;
|
||||||
|
use \Cake\Database\Expression\QueryExpression;
|
||||||
|
use Cake\Http\Exception\NotFoundException;
|
||||||
|
use Cake\Http\Exception\MethodNotAllowedException;
|
||||||
|
use Cake\Routing\Router;
|
||||||
|
|
||||||
|
|
||||||
|
class MetaTemplateNameDirectoryController extends AppController
|
||||||
|
{
|
||||||
|
public $quickFilterFields = [['name' => 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -404,8 +404,18 @@ class MetaTemplatesController extends AppController
|
||||||
$metaTemplate = $this->MetaTemplates->get($template_id, [
|
$metaTemplate = $this->MetaTemplates->get($template_id, [
|
||||||
'contain' => ['MetaTemplateFields']
|
'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);
|
$templateStatus = $this->MetaTemplates->getStatusForMetaTemplate($templateOnDisk, $metaTemplate);
|
||||||
|
if (!empty($errorMessage)) {
|
||||||
|
$templateStatus['error'] = $errorMessage;
|
||||||
|
}
|
||||||
$this->set('templateOnDisk', $templateOnDisk);
|
$this->set('templateOnDisk', $templateOnDisk);
|
||||||
$this->set('templateStatus', $templateStatus);
|
$this->set('templateStatus', $templateStatus);
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -101,7 +101,6 @@ class OrganisationsController extends AppController
|
||||||
if (!empty($responsePayload)) {
|
if (!empty($responsePayload)) {
|
||||||
return $responsePayload;
|
return $responsePayload;
|
||||||
}
|
}
|
||||||
$this->set('metaGroup', 'ContactDB');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function view($id)
|
public function view($id)
|
||||||
|
@ -111,16 +110,12 @@ class OrganisationsController extends AppController
|
||||||
if (!empty($responsePayload)) {
|
if (!empty($responsePayload)) {
|
||||||
return $responsePayload;
|
return $responsePayload;
|
||||||
}
|
}
|
||||||
$this->set('metaGroup', 'ContactDB');
|
$this->set('canEdit', $this->canEdit($id));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit($id)
|
public function edit($id)
|
||||||
{
|
{
|
||||||
$currentUser = $this->ACL->getUser();
|
if (!$this->canEdit($id)) {
|
||||||
if (
|
|
||||||
!($currentUser['organisation']['id'] == $id && $currentUser['role']['perm_org_admin']) &&
|
|
||||||
!$currentUser['role']['perm_admin']
|
|
||||||
) {
|
|
||||||
throw new MethodNotAllowedException(__('You cannot modify that organisation.'));
|
throw new MethodNotAllowedException(__('You cannot modify that organisation.'));
|
||||||
}
|
}
|
||||||
$this->CRUD->edit($id);
|
$this->CRUD->edit($id);
|
||||||
|
@ -144,6 +139,9 @@ class OrganisationsController extends AppController
|
||||||
|
|
||||||
public function tag($id)
|
public function tag($id)
|
||||||
{
|
{
|
||||||
|
if (!$this->canEdit($id)) {
|
||||||
|
throw new MethodNotAllowedException(__('You cannot tag that organisation.'));
|
||||||
|
}
|
||||||
$this->CRUD->tag($id);
|
$this->CRUD->tag($id);
|
||||||
$responsePayload = $this->CRUD->getResponsePayload();
|
$responsePayload = $this->CRUD->getResponsePayload();
|
||||||
if (!empty($responsePayload)) {
|
if (!empty($responsePayload)) {
|
||||||
|
@ -153,6 +151,9 @@ class OrganisationsController extends AppController
|
||||||
|
|
||||||
public function untag($id)
|
public function untag($id)
|
||||||
{
|
{
|
||||||
|
if (!$this->canEdit($id)) {
|
||||||
|
throw new MethodNotAllowedException(__('You cannot untag that organisation.'));
|
||||||
|
}
|
||||||
$this->CRUD->untag($id);
|
$this->CRUD->untag($id);
|
||||||
$responsePayload = $this->CRUD->getResponsePayload();
|
$responsePayload = $this->CRUD->getResponsePayload();
|
||||||
if (!empty($responsePayload)) {
|
if (!empty($responsePayload)) {
|
||||||
|
@ -168,4 +169,16 @@ class OrganisationsController extends AppController
|
||||||
return $responsePayload;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,6 +120,12 @@ class UsersController extends AppController
|
||||||
if (Configure::read('keycloak.enabled')) {
|
if (Configure::read('keycloak.enabled')) {
|
||||||
$this->Users->enrollUserRouter($data);
|
$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();
|
$responsePayload = $this->CRUD->getResponsePayload();
|
||||||
|
@ -227,6 +233,14 @@ class UsersController extends AppController
|
||||||
if (!$this->ACL->canEditUser($currentUser, $user)) {
|
if (!$this->ACL->canEditUser($currentUser, $user)) {
|
||||||
throw new MethodNotAllowedException(__('You cannot edit the given 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;
|
return $user;
|
||||||
};
|
};
|
||||||
|
|
|
@ -124,12 +124,15 @@ class IndividualsTable extends AppTable
|
||||||
|
|
||||||
public function getValidIndividualsToEdit(object $currentUser): array
|
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(
|
$validIndividualIds = $this->Users->find('list')->select(['individual_id'])->where(
|
||||||
[
|
[
|
||||||
'organisation_id' => $currentUser['organisation_id'],
|
'organisation_id' => $currentUser['organisation_id'],
|
||||||
'disabled' => 0,
|
'disabled' => 0,
|
||||||
'role_id NOT IN' => array_keys($adminRoles)
|
'OR' => [
|
||||||
|
['role_id IN' => array_keys($validRoles)],
|
||||||
|
['id' => $currentUser['id']],
|
||||||
|
]
|
||||||
]
|
]
|
||||||
)->all()->toArray();
|
)->all()->toArray();
|
||||||
return array_keys($validIndividualIds);
|
return array_keys($validIndividualIds);
|
||||||
|
|
|
@ -7,6 +7,7 @@ use App\Model\Entity\MetaTemplateNameDirectory;
|
||||||
use App\Model\Table\AppTable;
|
use App\Model\Table\AppTable;
|
||||||
use Cake\ORM\RulesChecker;
|
use Cake\ORM\RulesChecker;
|
||||||
use Cake\Validation\Validator;
|
use Cake\Validation\Validator;
|
||||||
|
use Cake\Log\Log;
|
||||||
|
|
||||||
class MetaTemplateNameDirectoryTable extends AppTable
|
class MetaTemplateNameDirectoryTable extends AppTable
|
||||||
{
|
{
|
||||||
|
@ -20,6 +21,9 @@ class MetaTemplateNameDirectoryTable extends AppTable
|
||||||
'foreignKey' => 'meta_template_directory_id',
|
'foreignKey' => 'meta_template_directory_id',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
$this->hasOne('MetaTemplates', [
|
||||||
|
'foreignKey' => 'meta_template_directory_id',
|
||||||
|
]);
|
||||||
$this->setDisplayField('name');
|
$this->setDisplayField('name');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,7 +63,11 @@ class MetaTemplateNameDirectoryTable extends AppTable
|
||||||
if (!empty($existingTemplate)) {
|
if (!empty($existingTemplate)) {
|
||||||
return $existingTemplate;
|
return $existingTemplate;
|
||||||
}
|
}
|
||||||
$this->save($metaTemplateDirectory);
|
$savedEntity = $this->save($metaTemplateDirectory);
|
||||||
return $metaTemplateDirectory;
|
if ($savedEntity) {
|
||||||
|
return $metaTemplateDirectory;
|
||||||
|
}
|
||||||
|
Log::error(__('Could not save meta_template_directory. Reasons: {0}', json_encode($metaTemplateDirectory->getErrors())));
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -555,6 +555,10 @@ class MetaTemplatesTable extends AppTable
|
||||||
if (substr($file, -5) === '.json') {
|
if (substr($file, -5) === '.json') {
|
||||||
$errorMessage = '';
|
$errorMessage = '';
|
||||||
$template = $this->decodeTemplateFromDisk($path . $file, $errorMessage);
|
$template = $this->decodeTemplateFromDisk($path . $file, $errorMessage);
|
||||||
|
if (!empty($errorMessage)) {
|
||||||
|
$error = $errorMessage;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
if (!empty($template) && $template['uuid'] == $uuid) {
|
if (!empty($template) && $template['uuid'] == $uuid) {
|
||||||
return $template;
|
return $template;
|
||||||
}
|
}
|
||||||
|
@ -1318,6 +1322,13 @@ class MetaTemplatesTable extends AppTable
|
||||||
$updateStatus['next_version'] = $template['version'];
|
$updateStatus['next_version'] = $template['version'];
|
||||||
$updateStatus['new'] = false;
|
$updateStatus['new'] = false;
|
||||||
$updateStatus['automatically-updateable'] = 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'])) {
|
if (intval($metaTemplate->version) >= intval($template['version'])) {
|
||||||
$updateStatus['up-to-date'] = true;
|
$updateStatus['up-to-date'] = true;
|
||||||
$updateStatus['conflicts'][] = __('Could not update the template. Local version is equal or newer.');
|
$updateStatus['conflicts'][] = __('Could not update the template. Local version is equal or newer.');
|
||||||
|
|
|
@ -30,13 +30,17 @@ class PermissionLimitationsTable extends AppTable
|
||||||
public function getListOfLimitations(\App\Model\Entity\User $data)
|
public function getListOfLimitations(\App\Model\Entity\User $data)
|
||||||
{
|
{
|
||||||
$Users = TableRegistry::getTableLocator()->get('Users');
|
$Users = TableRegistry::getTableLocator()->get('Users');
|
||||||
$ownOrgUserIds = $Users->find('list', [
|
$includeOrganisationPermissions = !empty($data['organisation_id']);
|
||||||
'keyField' => 'id',
|
$ownOrgUserIds = [];
|
||||||
'valueField' => 'id',
|
if ($includeOrganisationPermissions) {
|
||||||
'conditions' => [
|
$ownOrgUserIds = $Users->find('list', [
|
||||||
'organisation_id' => $data['organisation_id']
|
'keyField' => 'id',
|
||||||
]
|
'valueField' => 'id',
|
||||||
])->all()->toList();
|
'conditions' => [
|
||||||
|
'organisation_id' => $data['organisation_id']
|
||||||
|
]
|
||||||
|
])->all()->toList();
|
||||||
|
}
|
||||||
$MetaFields = TableRegistry::getTableLocator()->get('MetaFields');
|
$MetaFields = TableRegistry::getTableLocator()->get('MetaFields');
|
||||||
$raw = $this->find()->select(['scope', 'permission', 'max_occurrence'])->disableHydration()->toArray();
|
$raw = $this->find()->select(['scope', 'permission', 'max_occurrence'])->disableHydration()->toArray();
|
||||||
$limitations = [];
|
$limitations = [];
|
||||||
|
@ -70,9 +74,12 @@ class PermissionLimitationsTable extends AppTable
|
||||||
if (!empty($ownOrgUserIds)) {
|
if (!empty($ownOrgUserIds)) {
|
||||||
$conditions['parent_id IN'] = array_values($ownOrgUserIds);
|
$conditions['parent_id IN'] = array_values($ownOrgUserIds);
|
||||||
}
|
}
|
||||||
$limitations[$field]['organisation']['current'] = $MetaFields->find('all', [
|
$limitations[$field]['organisation']['current'] = '?';
|
||||||
'conditions' => $conditions,
|
if ($includeOrganisationPermissions) {
|
||||||
])->count();
|
$limitations[$field]['organisation']['current'] = $MetaFields->find('all', [
|
||||||
|
'conditions' => $conditions,
|
||||||
|
])->count();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $limitations;
|
return $limitations;
|
||||||
|
@ -89,34 +96,35 @@ class PermissionLimitationsTable extends AppTable
|
||||||
if (!empty($data['MetaTemplates'])) {
|
if (!empty($data['MetaTemplates'])) {
|
||||||
foreach ($data['MetaTemplates'] as &$metaTemplate) {
|
foreach ($data['MetaTemplates'] as &$metaTemplate) {
|
||||||
foreach ($metaTemplate['meta_template_fields'] as &$meta_template_field) {
|
foreach ($metaTemplate['meta_template_fields'] as &$meta_template_field) {
|
||||||
$boolean = $meta_template_field['type'] === 'boolean';
|
if (isset($permissionLimitations[$meta_template_field['field']])) {
|
||||||
foreach ($meta_template_field['metaFields'] as &$metaField) {
|
foreach ($permissionLimitations[$meta_template_field['field']] as $scope => $value) {
|
||||||
if (isset($permissionLimitations[$metaField['field']])) {
|
$messageType = 'warning';
|
||||||
foreach ($permissionLimitations[$metaField['field']] as $scope => $value) {
|
if ($value['current'] == '?') {
|
||||||
$messageType = 'warning';
|
$messageType = 'info';
|
||||||
|
} else {
|
||||||
if ($value['limit'] > $value['current']) {
|
if ($value['limit'] > $value['current']) {
|
||||||
$messageType = 'info';
|
$messageType = 'info';
|
||||||
}
|
}
|
||||||
if ($value['limit'] < $value['current']) {
|
if ($value['limit'] < $value['current']) {
|
||||||
$messageType = 'danger';
|
$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(
|
|
||||||
' <span title="%s"><span class="text-dark"><i class="fas fa-%s"></i>: </span>%s/%s</span>',
|
|
||||||
$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(
|
||||||
|
' <span title="%s"><span class="text-dark"><i class="fas fa-%s"></i>: </span>%s/%s</span>',
|
||||||
|
$altText,
|
||||||
|
$icons[$scope],
|
||||||
|
$value['current'],
|
||||||
|
$value['limit']
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -321,7 +321,7 @@ class CerebrateSettingsProvider extends BaseSettingsProvider
|
||||||
2 => __('Debug On + SQL Dump'),
|
2 => __('Debug On + SQL Dump'),
|
||||||
],
|
],
|
||||||
'test' => function ($value, $setting, $validator) {
|
'test' => function ($value, $setting, $validator) {
|
||||||
$validator->range('value', [0, 3]);
|
$validator->range('value', [0, 2]);
|
||||||
return testValidator($value, $validator);
|
return testValidator($value, $validator);
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -73,13 +73,6 @@ class UsersTable extends AppTable
|
||||||
if (!$entity->isNew()) {
|
if (!$entity->isNew()) {
|
||||||
$success = $this->handleUserUpdateRouter($entity);
|
$success = $this->handleUserUpdateRouter($entity);
|
||||||
}
|
}
|
||||||
$permissionRestrictionCheck = $this->checkPermissionRestrictions($entity);
|
|
||||||
if ($permissionRestrictionCheck !== true) {
|
|
||||||
$entity->setErrors($permissionRestrictionCheck);
|
|
||||||
$event->stopPropagation();
|
|
||||||
$event->setResult(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return $success;
|
return $success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,10 +180,24 @@ class UsersTable extends AppTable
|
||||||
public function buildRules(RulesChecker $rules): RulesChecker
|
public function buildRules(RulesChecker $rules): RulesChecker
|
||||||
{
|
{
|
||||||
$rules->add($rules->isUnique(['username']));
|
$rules->add($rules->isUnique(['username']));
|
||||||
$allowDuplicateIndividuals = false;
|
|
||||||
if (empty(Configure::read('user.multiple-users-per-individual')) || !empty(Configure::read('keycloak.enabled'))) {
|
if (empty(Configure::read('user.multiple-users-per-individual')) || !empty(Configure::read('keycloak.enabled'))) {
|
||||||
$rules->add($rules->isUnique(['individual_id']));
|
$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;
|
return $rules;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ class BootstrapIcon extends BootstrapGeneric
|
||||||
{
|
{
|
||||||
private $icon = '';
|
private $icon = '';
|
||||||
private $defaultOptions = [
|
private $defaultOptions = [
|
||||||
|
'id' => '',
|
||||||
'class' => [],
|
'class' => [],
|
||||||
'title' => '',
|
'title' => '',
|
||||||
'attrs' => [],
|
'attrs' => [],
|
||||||
|
@ -48,6 +49,7 @@ class BootstrapIcon extends BootstrapGeneric
|
||||||
{
|
{
|
||||||
$html = $this->node('span', array_merge(
|
$html = $this->node('span', array_merge(
|
||||||
[
|
[
|
||||||
|
'id' => $this->options['id'] ?? '',
|
||||||
'class' => array_merge(
|
'class' => array_merge(
|
||||||
$this->options['class'],
|
$this->options['class'],
|
||||||
["fa fa-{$this->icon}"]
|
["fa fa-{$this->icon}"]
|
||||||
|
|
|
@ -215,8 +215,6 @@ class BootstrapTabs extends BootstrapGeneric
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"bg-{$this->options['header-variant']}",
|
"bg-{$this->options['header-variant']}",
|
||||||
"text-{$this->options['header-text-variant']}",
|
|
||||||
"border-{$this->options['header-border-variant']}"
|
|
||||||
]
|
]
|
||||||
)], $this->genNav());
|
)], $this->genNav());
|
||||||
$content = $this->node('div', ['class' => array_merge(
|
$content = $this->node('div', ['class' => array_merge(
|
||||||
|
@ -226,7 +224,6 @@ class BootstrapTabs extends BootstrapGeneric
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"bg-{$this->options['body-variant']}",
|
"bg-{$this->options['body-variant']}",
|
||||||
"text-{$this->options['body-text-variant']}"
|
|
||||||
]
|
]
|
||||||
)], $this->genContent());
|
)], $this->genContent());
|
||||||
|
|
||||||
|
@ -238,7 +235,6 @@ class BootstrapTabs extends BootstrapGeneric
|
||||||
($this->options['vertical-size'] == 'auto' ? 'flex-nowrap' : '')
|
($this->options['vertical-size'] == 'auto' ? 'flex-nowrap' : '')
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
"border-{$this->options['header-border-variant']}"
|
|
||||||
]
|
]
|
||||||
)], $containerContent);
|
)], $containerContent);
|
||||||
return $container;
|
return $container;
|
||||||
|
|
|
@ -450,7 +450,7 @@ class BootstrapGeneric
|
||||||
return sprintf('%s="%s"', h($key), (!empty($escape) ? h($value) : $value));
|
return sprintf('%s="%s"', h($key), (!empty($escape) ? h($value) : $value));
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
} else if (empty($value)) {
|
} else if (!isset($value)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return sprintf('%s="%s"', h($key), (!empty($escape) ? h($value) : $value));
|
return sprintf('%s="%s"', h($key), (!empty($escape) ? h($value) : $value));
|
||||||
|
|
|
@ -8,6 +8,15 @@ class FormFieldMassageHelper extends Helper
|
||||||
{
|
{
|
||||||
public function prepareFormElement(\Cake\View\Helper\FormHelper $form, array $controlParams, array $fieldData): string
|
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'])) {
|
if (!empty($fieldData['stateDependence'])) {
|
||||||
$controlParams['data-dependence-source'] = h($fieldData['stateDependence']['source']);
|
$controlParams['data-dependence-source'] = h($fieldData['stateDependence']['source']);
|
||||||
$controlParams['data-dependence-option'] = h($fieldData['stateDependence']['option']);
|
$controlParams['data-dependence-option'] = h($fieldData['stateDependence']['option']);
|
||||||
|
|
|
@ -14,7 +14,8 @@ echo $this->element('genericElements/Form/genericForm', [
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'field' => 'expiration',
|
'field' => 'expiration',
|
||||||
'label' => 'Expiration'
|
'label' => __('Expiration'),
|
||||||
|
'type' => 'datetime',
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
'submit' => [
|
'submit' => [
|
||||||
|
|
|
@ -103,6 +103,9 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
||||||
'icon' => 'trash',
|
'icon' => 'trash',
|
||||||
'complex_requirement' => [
|
'complex_requirement' => [
|
||||||
'function' => function ($row, $options) use ($loggedUser) {
|
'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'];
|
return (bool)$loggedUser['role']['perm_admin'];
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
|
@ -31,6 +31,7 @@ echo $this->element(
|
||||||
[
|
[
|
||||||
'key' => __('Tags'),
|
'key' => __('Tags'),
|
||||||
'type' => 'tags',
|
'type' => 'tags',
|
||||||
|
'editable' => $canEdit,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'key' => __('Alignments'),
|
'key' => __('Alignments'),
|
||||||
|
|
|
@ -10,7 +10,6 @@ echo $this->element('genericElements/Form/genericForm', [
|
||||||
'multiple' => true,
|
'multiple' => true,
|
||||||
'select2' => true,
|
'select2' => true,
|
||||||
'label' => __('Members'),
|
'label' => __('Members'),
|
||||||
'class' => 'select2-input',
|
|
||||||
'options' => $dropdownData['individuals']
|
'options' => $dropdownData['individuals']
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|
|
@ -0,0 +1,60 @@
|
||||||
|
<?php
|
||||||
|
use Cake\Utility\Hash;
|
||||||
|
|
||||||
|
echo $this->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' => []
|
||||||
|
]
|
||||||
|
]);
|
|
@ -11,6 +11,14 @@ if ($updateStatus['up-to-date']) {
|
||||||
'dismissible' => false,
|
'dismissible' => false,
|
||||||
]);
|
]);
|
||||||
$modalType = 'ok-only';
|
$modalType = 'ok-only';
|
||||||
|
} else if (empty($templateOnDisk)) {
|
||||||
|
$diskTemplateError = $templateStatus['error'] ?? __('Unknown');
|
||||||
|
$bodyHtml .= $this->Bootstrap->alert([
|
||||||
|
'variant' => 'danger',
|
||||||
|
'html' => sprintf('<strong>%s</strong> %s<p>%s</p>', __('Could not get template on disk.'), __('Reason:'), h($diskTemplateError)),
|
||||||
|
'dismissible' => false,
|
||||||
|
]);
|
||||||
|
$modalType = 'ok-only';
|
||||||
} else {
|
} else {
|
||||||
if ($updateStatus['automatically-updateable']) {
|
if ($updateStatus['automatically-updateable']) {
|
||||||
$bodyHtml .= $this->Bootstrap->alert([
|
$bodyHtml .= $this->Bootstrap->alert([
|
||||||
|
|
|
@ -57,7 +57,7 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
||||||
'data_path' => 'url',
|
'data_path' => 'url',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => __('Nationality'),
|
'name' => __('Country'),
|
||||||
'data_path' => 'nationality',
|
'data_path' => 'nationality',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
|
|
@ -10,12 +10,14 @@
|
||||||
array(
|
array(
|
||||||
'field' => 'uuid',
|
'field' => 'uuid',
|
||||||
'label' => '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(
|
array(
|
||||||
'field' => 'url'
|
'field' => 'url'
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
|
'label' => __('Country'),
|
||||||
'field' => 'nationality'
|
'field' => 'nationality'
|
||||||
),
|
),
|
||||||
array(
|
array(
|
||||||
|
|
|
@ -66,7 +66,7 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
||||||
'data_path' => 'url',
|
'data_path' => 'url',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'name' => __('Nationality'),
|
'name' => __('Country'),
|
||||||
'data_path' => 'nationality',
|
'data_path' => 'nationality',
|
||||||
'sort' => 'nationality',
|
'sort' => 'nationality',
|
||||||
],
|
],
|
||||||
|
|
|
@ -22,7 +22,7 @@ echo $this->element(
|
||||||
'path' => 'url'
|
'path' => 'url'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'key' => __('Nationality'),
|
'key' => __('Country'),
|
||||||
'path' => 'nationality'
|
'path' => 'nationality'
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
@ -40,6 +40,7 @@ echo $this->element(
|
||||||
[
|
[
|
||||||
'key' => __('Tags'),
|
'key' => __('Tags'),
|
||||||
'type' => 'tags',
|
'type' => 'tags',
|
||||||
|
'editable' => $canEdit,
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'key' => __('Alignments'),
|
'key' => __('Alignments'),
|
||||||
|
|
|
@ -1,28 +1,31 @@
|
||||||
<?php
|
<?php
|
||||||
|
$topbarChildren = [];
|
||||||
|
if (!empty($loggedUser->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', [
|
echo $this->element('genericElements/IndexTable/index_table', [
|
||||||
'data' => [
|
'data' => [
|
||||||
'data' => $data,
|
'data' => $data,
|
||||||
'top_bar' => [
|
'top_bar' => [
|
||||||
'children' => [
|
'children' => $topbarChildren,
|
||||||
[
|
|
||||||
'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'
|
|
||||||
]
|
|
||||||
]
|
|
||||||
],
|
],
|
||||||
'fields' => [
|
'fields' => [
|
||||||
[
|
[
|
||||||
|
|
|
@ -1,99 +1,145 @@
|
||||||
<?php
|
<?php
|
||||||
use Cake\Core\Configure;
|
|
||||||
$passwordRequired = false;
|
use Cake\Core\Configure;
|
||||||
$showPasswordField = false;
|
|
||||||
if ($this->request->getParam('action') === 'add') {
|
$passwordRequired = false;
|
||||||
$dropdownData['individual'] = ['new' => __('New individual')] + $dropdownData['individual'];
|
$showPasswordField = false;
|
||||||
if (!Configure::check('password_auth.enabled') || Configure::read('password_auth.enabled')) {
|
if ($this->request->getParam('action') === 'add') {
|
||||||
$passwordRequired = 'required';
|
$dropdownData['individual'] = ['new' => __('New individual')] + $dropdownData['individual'];
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!Configure::check('password_auth.enabled') || Configure::read('password_auth.enabled')) {
|
if (!Configure::check('password_auth.enabled') || Configure::read('password_auth.enabled')) {
|
||||||
$showPasswordField = true;
|
$passwordRequired = 'required';
|
||||||
}
|
}
|
||||||
echo $this->element('genericElements/Form/genericForm', [
|
}
|
||||||
'data' => [
|
if (!Configure::check('password_auth.enabled') || Configure::read('password_auth.enabled')) {
|
||||||
'description' => __('Roles define global rules for a set of users, including first and foremost access controls to certain functionalities.'),
|
$showPasswordField = true;
|
||||||
'model' => 'Roles',
|
}
|
||||||
'fields' => [
|
echo $this->element('genericElements/Form/genericForm', [
|
||||||
[
|
'data' => [
|
||||||
'field' => 'individual_id',
|
'description' => __('Roles define global rules for a set of users, including first and foremost access controls to certain functionalities.'),
|
||||||
'type' => 'dropdown',
|
'model' => 'Roles',
|
||||||
'label' => __('Associated individual'),
|
'fields' => [
|
||||||
'options' => isset($dropdownData['individual']) ? $dropdownData['individual'] : [],
|
[
|
||||||
'conditions' => $this->request->getParam('action') === 'add'
|
'field' => 'individual_id',
|
||||||
],
|
'type' => 'dropdown',
|
||||||
[
|
'label' => __('Associated individual'),
|
||||||
'field' => 'individual.email',
|
'options' => isset($dropdownData['individual']) ? $dropdownData['individual'] : [],
|
||||||
'stateDependence' => [
|
'conditions' => $this->request->getParam('action') === 'add'
|
||||||
'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')
|
'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')
|
||||||
]
|
]
|
||||||
]);
|
]
|
||||||
|
]);
|
||||||
?>
|
?>
|
||||||
</div>
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
const entity = <?= json_encode($entity) ?>;
|
||||||
|
console.log(entity);
|
||||||
|
if (entity.MetaTemplates) {
|
||||||
|
for (const [metaTemplateId, metaTemplate] of Object.entries(entity.MetaTemplates)) {
|
||||||
|
for (const [metaTemplateFieldId, metaTemplateField] of Object.entries(metaTemplate.meta_template_fields)) {
|
||||||
|
let metaFieldId = false
|
||||||
|
if (metaTemplateField.metaFields !== undefined && Object.keys(metaTemplateField.metaFields).length > 0) {
|
||||||
|
metaFieldId = Object.keys(metaTemplateField.metaFields)[0]
|
||||||
|
}
|
||||||
|
let metafieldInput
|
||||||
|
const baseQueryPath = `MetaTemplates.${metaTemplateId}.meta_template_fields.${metaTemplateFieldId}.metaFields`
|
||||||
|
if (metaFieldId) {
|
||||||
|
metafieldInput = document.getElementById(`${baseQueryPath}.${metaFieldId}.value-field`)
|
||||||
|
} else {
|
||||||
|
metafieldInput = document.getElementById(`${baseQueryPath}.new.0-field`)
|
||||||
|
}
|
||||||
|
if (metafieldInput !== null) {
|
||||||
|
const permissionWarnings = buildPermissionElement(metaTemplateField)
|
||||||
|
$(metafieldInput.parentElement).append(permissionWarnings)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPermissionElement(metaTemplateField) {
|
||||||
|
const warningTypes = ['danger', 'warning', 'info', ]
|
||||||
|
const $span = $('<span>').addClass('ms-2')
|
||||||
|
warningTypes.forEach(warningType => {
|
||||||
|
if (metaTemplateField[warningType]) {
|
||||||
|
$theWarning = $('<span>')
|
||||||
|
.addClass([
|
||||||
|
`text-${warningType}`,
|
||||||
|
'ms-1',
|
||||||
|
])
|
||||||
|
.append($(metaTemplateField[warningType]))
|
||||||
|
$span.append($theWarning)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return $span
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
|
@ -2,7 +2,7 @@
|
||||||
$seed = 's-' . mt_rand();
|
$seed = 's-' . mt_rand();
|
||||||
$controlParams = [
|
$controlParams = [
|
||||||
'type' => 'select',
|
'type' => 'select',
|
||||||
'options' => $fieldData['options'],
|
'options' => $fieldData['options'] ?? [],
|
||||||
'empty' => $fieldData['empty'] ?? false,
|
'empty' => $fieldData['empty'] ?? false,
|
||||||
'value' => $fieldData['value'] ?? null,
|
'value' => $fieldData['value'] ?? null,
|
||||||
'multiple' => $fieldData['multiple'] ?? false,
|
'multiple' => $fieldData['multiple'] ?? false,
|
||||||
|
@ -19,9 +19,13 @@ if (!empty($fieldData['label'])) {
|
||||||
if ($controlParams['options'] instanceof \Cake\ORM\Query) {
|
if ($controlParams['options'] instanceof \Cake\ORM\Query) {
|
||||||
$controlParams['options'] = $controlParams['options']->all()->toList();
|
$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'] .= ' select2-input';
|
||||||
}
|
}
|
||||||
|
$controlParams['class'] .= ' dropdown-custom-value' . "-$seed";
|
||||||
if (in_array('_custom', array_keys($controlParams['options']))) {
|
if (in_array('_custom', array_keys($controlParams['options']))) {
|
||||||
$customInputValue = $this->Form->getSourceValue($fieldData['field']);
|
$customInputValue = $this->Form->getSourceValue($fieldData['field']);
|
||||||
if (!in_array($customInputValue, $controlParams['options'])) {
|
if (!in_array($customInputValue, $controlParams['options'])) {
|
||||||
|
@ -34,7 +38,6 @@ if (in_array('_custom', array_keys($controlParams['options']))) {
|
||||||
} else {
|
} else {
|
||||||
$customInputValue = '';
|
$customInputValue = '';
|
||||||
}
|
}
|
||||||
$controlParams['class'] .= ' dropdown-custom-value' . "-$seed";
|
|
||||||
$adaptedField = $fieldData['field'] . '_custom';
|
$adaptedField = $fieldData['field'] . '_custom';
|
||||||
$controlParams['templates']['formGroup'] = sprintf(
|
$controlParams['templates']['formGroup'] = sprintf(
|
||||||
'<label class="col-sm-2 col-form-label form-label" {{attrs}}>{{label}}</label><div class="col-sm-10 multi-metafield-input-container"><div class="d-flex form-dropdown-with-freetext input-group">{{input}}{{error}}%s</div></div>',
|
'<label class="col-sm-2 col-form-label form-label" {{attrs}}>{{label}}</label><div class="col-sm-10 multi-metafield-input-container"><div class="d-flex form-dropdown-with-freetext input-group">{{input}}{{error}}%s</div></div>',
|
||||||
|
@ -52,14 +55,18 @@ echo $this->FormFieldMassage->prepareFormElement($this->Form, $controlParams, $f
|
||||||
$select.attr('onclick', 'toggleFreetextSelectField(this)')
|
$select.attr('onclick', 'toggleFreetextSelectField(this)')
|
||||||
$select.parent().find('input.custom-value').attr('oninput', 'updateAssociatedSelect(this)')
|
$select.parent().find('input.custom-value').attr('oninput', 'updateAssociatedSelect(this)')
|
||||||
updateAssociatedSelect($select.parent().find('input.custom-value')[0])
|
updateAssociatedSelect($select.parent().find('input.custom-value')[0])
|
||||||
<?php if (!empty($fieldData['select2'])) : ?>
|
<?php if ($initSelect2) : ?>
|
||||||
let $container = $select.closest('.modal-dialog')
|
// let $container = $select.closest('.modal-dialog .modal-body')
|
||||||
|
let $container = []
|
||||||
if ($container.length == 0) {
|
if ($container.length == 0) {
|
||||||
$container = $(document.body)
|
$container = $(document.body)
|
||||||
}
|
}
|
||||||
$select.select2({
|
const defaultSelect2Options = {
|
||||||
dropdownParent: $container,
|
dropdownParent: $container,
|
||||||
})
|
}
|
||||||
|
const passedSelect2Options = <?= json_encode($fieldData['select2']) ?>;
|
||||||
|
const select2Options = Object.assign({}, passedSelect2Options, defaultSelect2Options)
|
||||||
|
$select.select2(select2Options)
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -1,43 +1,43 @@
|
||||||
<?php
|
<?php
|
||||||
$random = Cake\Utility\Security::randomString(8);
|
$random = Cake\Utility\Security::randomString(8);
|
||||||
$params['div'] = false;
|
$params['div'] = false;
|
||||||
$this->Form->setTemplates([
|
|
||||||
'inputContainer' => '{{content}}',
|
$genUUIDButton = $this->Bootstrap->button([
|
||||||
'inputContainerError' => '{{content}}',
|
'id' => "uuid-gen-{$random}",
|
||||||
'formGroup' => '{{input}}',
|
'variant' => 'secondary',
|
||||||
]);
|
'text' => __('Generate'),
|
||||||
$label = $fieldData['label'];
|
]);
|
||||||
$formElement = $this->FormFieldMassage->prepareFormElement($this->Form, $params, $fieldData);
|
|
||||||
$temp = sprintf(
|
$this->Form->setTemplates([
|
||||||
'<div class="row mb-3">
|
'input' => sprintf('<div class="input-group">%s{{genUUIDButton}}</div>', $this->Form->getTemplates('input')),
|
||||||
<div class="col-sm-2 form-label">%s</div>
|
]);
|
||||||
<div class="col-sm-10">
|
$params['templateVars'] = [
|
||||||
<div class="input-group">
|
'genUUIDButton' => $genUUIDButton,
|
||||||
%s<span>%s</span>
|
];
|
||||||
</div>
|
|
||||||
</div>
|
$formElement = $this->FormFieldMassage->prepareFormElement($this->Form, $params, $fieldData);
|
||||||
</div>',
|
echo $formElement;
|
||||||
h($label),
|
|
||||||
$formElement,
|
|
||||||
sprintf(
|
|
||||||
'<span id="uuid-gen-%s" class="btn btn-secondary">%s</span>',
|
|
||||||
$random,
|
|
||||||
__('Generate')
|
|
||||||
)
|
|
||||||
);
|
|
||||||
echo $temp;
|
|
||||||
?>
|
?>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
$('#uuid-gen-<?= h($random) ?>').on('click', function() {
|
const $node = $('#uuid-gen-<?= h($random) ?>')
|
||||||
$.ajax({
|
$node.click(fetchUUID)
|
||||||
success:function (data, textStatus) {
|
|
||||||
$('#uuid-field').val(data["uuid"]);
|
function fetchUUID() {
|
||||||
},
|
const urlGet = '/organisations/generateUUID'
|
||||||
type: "get",
|
const options = {
|
||||||
cache: false,
|
statusNode: $node,
|
||||||
url: "/organisations/generateUUID",
|
}
|
||||||
});
|
return AJAXApi.quickFetchJSON(urlGet, options)
|
||||||
});
|
.then(function(data) {
|
||||||
|
$('#uuid-field').val(data["uuid"])
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
UI.toast({
|
||||||
|
variant: 'danger',
|
||||||
|
text: '<?= __('Could not generate UUID') ?>'
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
|
@ -11,11 +11,12 @@
|
||||||
$fieldData['label'] = \Cake\Utility\Inflector::humanize($fieldData['field']);
|
$fieldData['label'] = \Cake\Utility\Inflector::humanize($fieldData['field']);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!empty($fieldDesc[$fieldData['field']])) {
|
$fieldDescription = $fieldData['tooltip'] ?? ($fieldDesc[$fieldData['field']] ?? false);
|
||||||
$fieldData['label'] .= $this->element(
|
if (!empty($fieldDescription)) {
|
||||||
|
$fieldData['tooltip'] = $this->element(
|
||||||
'genericElements/Form/formInfo', array(
|
'genericElements/Form/formInfo', array(
|
||||||
'field' => $fieldData,
|
'field' => $fieldData,
|
||||||
'fieldDesc' => $fieldDesc[$fieldData['field']],
|
'fieldDesc' => $fieldDescription,
|
||||||
'modelForForm' => $modelForForm
|
'modelForForm' => $modelForForm
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
$seed = mt_rand();
|
||||||
if (!is_array($fieldDesc)) {
|
if (!is_array($fieldDesc)) {
|
||||||
$fieldDesc = array('info' => $fieldDesc);
|
$fieldDesc = array('info' => $fieldDesc);
|
||||||
$default = 'info';
|
$default = 'info';
|
||||||
|
@ -16,32 +17,46 @@
|
||||||
$default = 'info';
|
$default = 'info';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
echo sprintf(
|
$popoverID = sprintf("%sInfoPopover%s", h($field['field']), $seed);
|
||||||
'<span id = "%sInfoPopover" class="icon-info-sign" data-bs-toggle="popover" data-bs-trigger="hover"></span>',
|
echo $this->Bootstrap->icon('info-circle', [
|
||||||
h($field['field'])
|
'id' => $popoverID,
|
||||||
);
|
'class' => ['ms-1'],
|
||||||
|
'attrs' => [
|
||||||
|
'data-bs-toggle' => 'popover',
|
||||||
|
'data-bs-trigger' => 'hover',
|
||||||
|
]
|
||||||
|
]);
|
||||||
?>
|
?>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function() {
|
$(document).ready(function() {
|
||||||
new bootstrap.Popover('#<?php echo h($field['field']); ?>InfoPopover', {
|
new bootstrap.Popover('#<?= $popoverID ?>', {
|
||||||
html: true,
|
html: true,
|
||||||
content: function() {
|
content: function() {
|
||||||
var tempSelector = '#<?php echo h($modelForForm . \Cake\Utility\Inflector::camelize($field['field'])); ?>';
|
return $('<div>')
|
||||||
if ($(tempSelector)[0].nodeName === "SELECT" && Object.keys(fieldDesc).length > 1) {
|
.append(
|
||||||
return $('<div>').append(
|
$('<span>')
|
||||||
$('<span>').attr('class', 'blue bold').text($(tempSelector +" option:selected").text())
|
.attr('class', 'text-primary fw-bold')
|
||||||
).append(
|
.text('<?php echo h(\Cake\Utility\Inflector::humanize($field['field'])); ?>')
|
||||||
$('<span>').text(': ' + fieldDesc[$(tempSelector).val()])
|
)
|
||||||
|
.append(
|
||||||
|
$('<span>').text(": <?= h($fieldDesc["info"]) ?>")
|
||||||
);
|
);
|
||||||
} else {
|
// var tempSelector = '#<?php echo h($modelForForm . \Cake\Utility\Inflector::camelize($field['field'])); ?>';
|
||||||
return $('<div>').append(
|
// if ($(tempSelector)[0].nodeName === "SELECT" && Object.keys(fieldDesc).length > 1) {
|
||||||
$('<span>').attr('class', 'blue bold').text('<?php echo h(\Cake\Utility\Inflector::humanize($field['field'])); ?>')
|
// return $('<div>').append(
|
||||||
).append(
|
// $('<span>').attr('class', 'blue bold').text($(tempSelector +" option:selected").text())
|
||||||
$('<span>').text(": " + fieldDesc["info"])
|
// ).append(
|
||||||
);
|
// $('<span>').text(': ' + fieldDesc[$(tempSelector).val()])
|
||||||
}
|
// );
|
||||||
|
// } else {
|
||||||
|
// return $('<div>').append(
|
||||||
|
// $('<span>').attr('class', 'blue bold').text('<?php echo h(\Cake\Utility\Inflector::humanize($field['field'])); ?>')
|
||||||
|
// ).append(
|
||||||
|
// $('<span>').text(": " + fieldDesc["info"])
|
||||||
|
// );
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
var fieldDesc = <?php echo json_encode($fieldDesc); ?>;
|
// var fieldDesc = <?php echo json_encode($fieldDesc); ?>;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
$entity = isset($entity) ? $entity : null;
|
$entity = isset($entity) ? $entity : null;
|
||||||
$fieldsString = '';
|
$fieldsString = '';
|
||||||
$simpleFieldWhitelist = [
|
$simpleFieldWhitelist = [
|
||||||
'default', 'type', 'placeholder', 'label', 'empty', 'rows', 'div', 'required', 'templates', 'options', 'value', 'checked'
|
'default', 'type', 'placeholder', 'label', 'empty', 'rows', 'div', 'required', 'templates', 'options', 'value', 'checked',
|
||||||
];
|
];
|
||||||
if (empty($data['url'])) {
|
if (empty($data['url'])) {
|
||||||
$data['url'] = ["controller" => $this->request->getParam('controller'), "action" => $this->request->getParam('url')];
|
$data['url'] = ["controller" => $this->request->getParam('controller'), "action" => $this->request->getParam('url')];
|
||||||
|
@ -155,14 +155,5 @@
|
||||||
$('.formDropdown').on('change', function() {
|
$('.formDropdown').on('change', function() {
|
||||||
executeStateDependencyChecks('#' + this.id);
|
executeStateDependencyChecks('#' + this.id);
|
||||||
})
|
})
|
||||||
<?php if (!empty($initSelect2)): ?>
|
|
||||||
<?php
|
|
||||||
$dropdownParent = !empty($seedModal) ? sprintf("$('.modal-dialog.%s .modal-body')", $seedModal) : "$(document.body)";
|
|
||||||
?>
|
|
||||||
$('select.select2-input').select2({
|
|
||||||
dropdownParent: <?= $dropdownParent ?>,
|
|
||||||
width: '100%',
|
|
||||||
})
|
|
||||||
<?php endif; ?>
|
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
|
@ -4,7 +4,7 @@ use Cake\Utility\Inflector;
|
||||||
|
|
||||||
$default_template = [
|
$default_template = [
|
||||||
'inputContainer' => '<div class="row mb-3 metafield-container">{{content}}</div>',
|
'inputContainer' => '<div class="row mb-3 metafield-container">{{content}}</div>',
|
||||||
'inputContainerError' => '<div class="row mb-3 metafield-container has-error">{{content}}</div>',
|
'inputContainerError' => '<div class="row mb-3 metafield-container has-error">{{content}}{{error}}</div>',
|
||||||
'formGroup' => '<label class="col-sm-2 col-form-label form-label" {{attrs}}>{{label}}</label><div class="col-sm-10">{{input}}{{error}}</div>',
|
'formGroup' => '<label class="col-sm-2 col-form-label form-label" {{attrs}}>{{label}}</label><div class="col-sm-10">{{input}}{{error}}</div>',
|
||||||
'error' => '<div class="error-message invalid-feedback d-block">{{content}}</div>',
|
'error' => '<div class="error-message invalid-feedback d-block">{{content}}</div>',
|
||||||
'errorList' => '<ul>{{content}}</ul>',
|
'errorList' => '<ul>{{content}}</ul>',
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
$tags = Cake\Utility\Hash::get($data, 'tags');
|
$tags = Cake\Utility\Hash::get($data, 'tags');
|
||||||
echo $this->Tag->tags($tags, [
|
echo $this->Tag->tags($tags, [
|
||||||
'allTags' => $allTags,
|
'allTags' => $allTags,
|
||||||
'picker' => true,
|
'picker' => !empty($field['editable']),
|
||||||
'editable' => true,
|
'editable' => !empty($field['editable']),
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -28,9 +28,9 @@ foreach($data['MetaTemplates'] as $metaTemplate) {
|
||||||
]
|
]
|
||||||
]),
|
]),
|
||||||
'rawNoEscaping' => true,
|
'rawNoEscaping' => true,
|
||||||
'warning' => $metaField->warning ?? null,
|
'notice_warning' => $metaTemplateField->warning ?? null,
|
||||||
'info' => $metaField->info ?? null,
|
'notice_info' => $metaTemplateField->info ?? null,
|
||||||
'danger' => $metaField->danger ?? null
|
'notice_danger' => $metaTemplateField->danger ?? null
|
||||||
];
|
];
|
||||||
$labelPrintedOnce = true;
|
$labelPrintedOnce = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,8 @@ if (!empty($breadcrumb)) {
|
||||||
}
|
}
|
||||||
if (!empty($actionEntry['isPOST'])) {
|
if (!empty($actionEntry['isPOST'])) {
|
||||||
$onclickFunction = sprintf('UI.overlayUntilResolve(this, UI.submissionModalAutoGuess(\'%s\'))', h(Router::url($actionEntry['url'])));
|
$onclickFunction = sprintf('UI.overlayUntilResolve(this, UI.submissionModalAutoGuess(\'%s\'))', h(Router::url($actionEntry['url'])));
|
||||||
|
} else if (!empty($actionEntry['isRedirect'])) {
|
||||||
|
$onclickFunction = sprintf('window.location.replace(\'%s\');', h(Router::url($actionEntry['url'])));
|
||||||
} else {
|
} else {
|
||||||
$onclickFunction = sprintf('UI.overlayUntilResolve(this, UI.modalFromUrl(\'%s\'))', h(Router::url($actionEntry['url'])));
|
$onclickFunction = sprintf('UI.overlayUntilResolve(this, UI.modalFromUrl(\'%s\'))', h(Router::url($actionEntry['url'])));
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,19 +47,28 @@ $filteringForm = $this->Bootstrap->table(
|
||||||
__('Value'),
|
__('Value'),
|
||||||
sprintf('<sup class="fa fa-info" title="%s"><sup>', __('Supports strict matches and LIKE matches with the `%` character. Example: `%.com`'))
|
sprintf('<sup class="fa fa-info" title="%s"><sup>', __('Supports strict matches and LIKE matches with the `%` character. Example: `%.com`'))
|
||||||
),
|
),
|
||||||
'formatter' => function ($field, $row) use ($typeMap, $formTypeMap) {
|
'formatter' => function ($field, $row) use ($typeMap, $formTypeMap, $filtersConfig) {
|
||||||
$fieldName = $row['fieldname'];
|
$fieldName = $row['fieldname'];
|
||||||
$formType = $formTypeMap[$typeMap[$fieldName]] ?? 'text';
|
$formType = $formTypeMap[$typeMap[$fieldName]] ?? 'text';
|
||||||
|
$fieldData = [
|
||||||
|
'field' => $fieldName,
|
||||||
|
'type' => $formType,
|
||||||
|
'label' => '',
|
||||||
|
'class' => 'fieldValue form-control-sm'
|
||||||
|
];
|
||||||
|
if (!empty($filtersConfig[$fieldName]['multiple'])) {
|
||||||
|
$fieldData['type'] = 'dropdown';
|
||||||
|
$fieldData['multiple'] = true;
|
||||||
|
$fieldData['select2'] = [
|
||||||
|
'tags' => true,
|
||||||
|
'tokenSeparators' => [',', ' '],
|
||||||
|
];
|
||||||
|
}
|
||||||
$this->Form->setTemplates([
|
$this->Form->setTemplates([
|
||||||
'formGroup' => '<div class="col-sm-10">{{input}}</div>',
|
'formGroup' => '<div class="col-sm-10">{{input}}</div>',
|
||||||
]);
|
]);
|
||||||
return $this->element('genericElements/Form/fieldScaffold', [
|
return $this->element('genericElements/Form/fieldScaffold', [
|
||||||
'fieldData' => [
|
'fieldData' => $fieldData,
|
||||||
'field' => $fieldName,
|
|
||||||
'type' => $formType,
|
|
||||||
'label' => '',
|
|
||||||
'class' => 'fieldValue form-control-sm'
|
|
||||||
],
|
|
||||||
'params' => []
|
'params' => []
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
@ -169,6 +178,36 @@ echo $this->Bootstrap->modal([
|
||||||
}
|
}
|
||||||
setFilteringValues($filteringTable, field, value, operator)
|
setFilteringValues($filteringTable, field, value, operator)
|
||||||
}
|
}
|
||||||
|
if (tags.length > 0) {
|
||||||
|
setFilteringTags($filteringTable, tags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFilteringValues($filteringTable, field, value, operator) {
|
||||||
|
$row = $filteringTable.find('td > span.fieldName').filter(function() {
|
||||||
|
return $(this).data('fieldname') == field
|
||||||
|
}).closest('tr')
|
||||||
|
$row.find('.fieldOperator').val(operator)
|
||||||
|
const $formElement = $row.find('.fieldValue');
|
||||||
|
if ($formElement.attr('type') === 'datetime-local') {
|
||||||
|
$formElement.val(moment(value).format('yyyy-MM-DDThh:mm:ss'))
|
||||||
|
} else if ($formElement.is('select') && Array.isArray(value)) {
|
||||||
|
let newOptions = [];
|
||||||
|
value.forEach(aValue => {
|
||||||
|
const existingOption = $formElement.find('option').filter(function() {
|
||||||
|
return $(this).val() === aValue
|
||||||
|
})
|
||||||
|
if (existingOption.length == 0) {
|
||||||
|
newOptions.push(new Option(aValue, aValue, true, true))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
$formElement.append(newOptions).trigger('change');
|
||||||
|
} else {
|
||||||
|
$formElement.val(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setFilteringTags($filteringTable, tags) {
|
||||||
$select = $filteringTable.closest('.modal-body').find('select.select2-input')
|
$select = $filteringTable.closest('.modal-body').find('select.select2-input')
|
||||||
let passedTags = []
|
let passedTags = []
|
||||||
tags.forEach(tagname => {
|
tags.forEach(tagname => {
|
||||||
|
@ -185,19 +224,6 @@ echo $this->Bootstrap->modal([
|
||||||
.trigger('change')
|
.trigger('change')
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFilteringValues($filteringTable, field, value, operator) {
|
|
||||||
$row = $filteringTable.find('td > span.fieldName').filter(function() {
|
|
||||||
return $(this).data('fieldname') == field
|
|
||||||
}).closest('tr')
|
|
||||||
$row.find('.fieldOperator').val(operator)
|
|
||||||
const $formElement = $row.find('.fieldValue');
|
|
||||||
if ($formElement.attr('type') === 'datetime-local') {
|
|
||||||
$formElement.val(moment(value).format('yyyy-MM-DDThh:mm:ss'))
|
|
||||||
} else {
|
|
||||||
$formElement.val(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getDataFromRow($row) {
|
function getDataFromRow($row) {
|
||||||
const rowData = {};
|
const rowData = {};
|
||||||
rowData['name'] = $row.find('td > span.fieldName').data('fieldname')
|
rowData['name'] = $row.find('td > span.fieldName').data('fieldname')
|
||||||
|
|
|
@ -58,7 +58,7 @@ $sidebarOpen = $loggedUser->user_settings_by_name_with_fallback['ui.sidebar.expa
|
||||||
<?= $this->Html->css('CodeMirror/addon/hint/show-hint') ?>
|
<?= $this->Html->css('CodeMirror/addon/hint/show-hint') ?>
|
||||||
<?= $this->Html->css('CodeMirror/addon/lint/lint') ?>
|
<?= $this->Html->css('CodeMirror/addon/lint/lint') ?>
|
||||||
<?= $this->Html->css('select2.min') ?>
|
<?= $this->Html->css('select2.min') ?>
|
||||||
<?= $this->Html->css('select2-bootstrap5') ?>
|
<?= $this->Html->css('select2-bootstrap5-vars') ?>
|
||||||
<?= $this->Html->script('apexcharts.min') ?>
|
<?= $this->Html->script('apexcharts.min') ?>
|
||||||
<?= $this->Html->script('moment-with-locales.min') ?>
|
<?= $this->Html->script('moment-with-locales.min') ?>
|
||||||
<?= $this->Html->css('apexcharts') ?>
|
<?= $this->Html->css('apexcharts') ?>
|
||||||
|
|
|
@ -94,7 +94,7 @@ input[type="checkbox"]:disabled.change-cursor {
|
||||||
}
|
}
|
||||||
|
|
||||||
.select2-container {
|
.select2-container {
|
||||||
z-index: 900;
|
z-index: 1060;
|
||||||
}
|
}
|
||||||
|
|
||||||
.select2-container--bootstrap-5 {
|
.select2-container--bootstrap-5 {
|
||||||
|
|
|
@ -0,0 +1,516 @@
|
||||||
|
/*!
|
||||||
|
* Select2 v4 Bootstrap 5 theme v1.1.1
|
||||||
|
*/
|
||||||
|
.select2-container--bootstrap-5 {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 *:focus {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-selection {
|
||||||
|
width: 100%;
|
||||||
|
min-height: calc(1.5em + (0.75rem + 2px));
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
background-color: var(--bs-body-bg);
|
||||||
|
border: 1px solid var(--bs-gray-400);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.select2-container--bootstrap-5 .select2-selection {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5.select2-container--focus .select2-selection, .select2-container--bootstrap-5.select2-container--open .select2-selection {
|
||||||
|
border-color: rgba(var(--bs-primary-rgb), 0.5);
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.25);
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5.select2-container--open.select2-container--below .select2-selection {
|
||||||
|
border-bottom: 1px solid transparent;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5.select2-container--open.select2-container--above .select2-selection {
|
||||||
|
border-top: 1px solid transparent;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-search {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--single .select2-selection__clear,
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__clear {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 2.25rem;
|
||||||
|
width: 0.75rem;
|
||||||
|
height: 0.75rem;
|
||||||
|
padding: 0.25em 0.25em;
|
||||||
|
overflow: hidden;
|
||||||
|
text-indent: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23676a6d'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/0.75rem auto no-repeat;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--single .select2-selection__clear:hover,
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__clear:hover {
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/0.75rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--single .select2-selection__clear > span,
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__clear > span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--bootstrap-5 .select2-dropdown {
|
||||||
|
border-color: var(--bs-gray-400);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-dropdown.select2-dropdown--below {
|
||||||
|
border-top: 0 solid transparent;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-dropdown.select2-dropdown--above {
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-dropdown .select2-search {
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-dropdown .select2-search .select2-search__field {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
background-color: var(--bs-body-bg);
|
||||||
|
background-clip: padding-box;
|
||||||
|
border: 1px solid var(--bs-gray-400);
|
||||||
|
-webkit-appearance: none;
|
||||||
|
-moz-appearance: none;
|
||||||
|
appearance: none;
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out;
|
||||||
|
}
|
||||||
|
@media (prefers-reduced-motion: reduce) {
|
||||||
|
.select2-container--bootstrap-5 .select2-dropdown .select2-search .select2-search__field {
|
||||||
|
transition: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-dropdown .select2-search .select2-search__field:focus {
|
||||||
|
border-color: rgba(var(--bs-primary-rgb), 0.5);
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba(var(--bs-primary-rgb), 0.25);
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-dropdown .select2-results__options:not(.select2-results__options--nested) {
|
||||||
|
max-height: 15rem;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option {
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
background-color: var(--bs-body-bg);
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option.select2-results__message {
|
||||||
|
color: var(--bs-secondary);
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option.select2-results__option--highlighted {
|
||||||
|
color: #000;
|
||||||
|
background-color: var(--bs-light);
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option.select2-results__option--selected, .select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option[aria-selected=true] {
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
background-color: var(--bs-primary);
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option.select2-results__option--disabled, .select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option[aria-disabled=true] {
|
||||||
|
color: var(--bs-gray);
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option[role=group] {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option[role=group] .select2-results__group {
|
||||||
|
padding: 0.375rem 0.375rem;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--bs-gray);
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option[role=group] .select2-results__options--nested .select2-results__option {
|
||||||
|
padding: 0.375rem 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--single {
|
||||||
|
padding: 0.375rem 2.25rem 0.375rem 0.75rem;
|
||||||
|
background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 0.75rem center;
|
||||||
|
background-size: 16px 12px;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--single .select2-selection__rendered {
|
||||||
|
padding: 0;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--single .select2-selection__rendered .select2-selection__placeholder {
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--single .select2-selection__rendered .select2-selection__arrow {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
padding-left: 0;
|
||||||
|
margin: 0;
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered .select2-selection__choice {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.35em 0.65em;
|
||||||
|
margin-right: 0.375rem;
|
||||||
|
margin-bottom: 0.375rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
cursor: auto;
|
||||||
|
border: 1px solid var(--bs-gray-400);
|
||||||
|
border-radius: 0.25rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered .select2-selection__choice .select2-selection__choice__remove {
|
||||||
|
width: 0.75rem;
|
||||||
|
height: 0.75rem;
|
||||||
|
padding: 0.25em 0.25em;
|
||||||
|
margin-right: 0.25rem;
|
||||||
|
overflow: hidden;
|
||||||
|
text-indent: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23676a6d'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/0.75rem auto no-repeat;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered .select2-selection__choice .select2-selection__choice__remove:hover {
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/0.75rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered .select2-selection__choice .select2-selection__choice__remove > span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--multiple .select2-search {
|
||||||
|
display: block;
|
||||||
|
width: 100%;
|
||||||
|
height: 1.5rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--multiple .select2-search .select2-search__field {
|
||||||
|
width: 100%;
|
||||||
|
height: 1.5rem;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-left: 0;
|
||||||
|
font-family: inherit;
|
||||||
|
line-height: 1.5;
|
||||||
|
background-color: transparent;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__clear {
|
||||||
|
right: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--bootstrap-5.select2-container--disabled .select2-selection, .select2-container--bootstrap-5.select2-container--disabled.select2-container--focus .select2-selection {
|
||||||
|
color: var(--bs-secondary);
|
||||||
|
cursor: not-allowed;
|
||||||
|
background-color: var(--bs-gray-200);
|
||||||
|
border-color: var(--bs-gray-400);
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5.select2-container--disabled .select2-selection--multiple .select2-selection__clear, .select2-container--bootstrap-5.select2-container--disabled.select2-container--focus .select2-selection--multiple .select2-selection__clear {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5.select2-container--disabled .select2-selection--multiple .select2-selection__choice, .select2-container--bootstrap-5.select2-container--disabled.select2-container--focus .select2-selection--multiple .select2-selection__choice {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5.select2-container--disabled .select2-selection--multiple .select2-selection__choice .select2-selection__choice__remove, .select2-container--bootstrap-5.select2-container--disabled.select2-container--focus .select2-selection--multiple .select2-selection__choice .select2-selection__choice__remove {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5.select2-container--disabled .select2-selection--multiple .select2-selection__rendered:not(:empty), .select2-container--bootstrap-5.select2-container--disabled.select2-container--focus .select2-selection--multiple .select2-selection__rendered:not(:empty) {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5.select2-container--disabled .select2-selection--multiple .select2-selection__rendered:not(:empty) + .select2-search, .select2-container--bootstrap-5.select2-container--disabled.select2-container--focus .select2-selection--multiple .select2-selection__rendered:not(:empty) + .select2-search {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group:not(.has-validation) > :not(:last-child):not(.dropdown-toggle):not(.dropdown-menu).select2-container--bootstrap-5 .select2-selection {
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
.input-group.has-validation > :nth-last-child(n+3):not(.dropdown-toggle):not(.dropdown-menu).select2-container--bootstrap-5 .select2-selection {
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
border-bottom-right-radius: 0;
|
||||||
|
}
|
||||||
|
.input-group > .input-group-text ~ .select2-container--bootstrap-5 .select2-selection,
|
||||||
|
.input-group > .btn ~ .select2-container--bootstrap-5 .select2-selection,
|
||||||
|
.input-group > .dropdown-menu ~ .select2-container--bootstrap-5 .select2-selection {
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-bottom-left-radius: 0;
|
||||||
|
}
|
||||||
|
.input-group .select2-container--bootstrap-5 {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
.input-group .select2-container--bootstrap-5 .select2-selection {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.is-valid + .select2-container--bootstrap-5 .select2-selection, form.was-validated select:valid + .select2-container--bootstrap-5 .select2-selection {
|
||||||
|
border-color: var(--bs-success);
|
||||||
|
}
|
||||||
|
.is-valid + .select2-container--bootstrap-5.select2-container--focus .select2-selection, .is-valid + .select2-container--bootstrap-5.select2-container--open .select2-selection, form.was-validated select:valid + .select2-container--bootstrap-5.select2-container--focus .select2-selection, form.was-validated select:valid + .select2-container--bootstrap-5.select2-container--open .select2-selection {
|
||||||
|
border-color: var(--bs-success);
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba( var(--bs-success-rgb), 0.25);
|
||||||
|
}
|
||||||
|
.is-valid + .select2-container--bootstrap-5.select2-container--open.select2-container--below .select2-selection, form.was-validated select:valid + .select2-container--bootstrap-5.select2-container--open.select2-container--below .select2-selection {
|
||||||
|
border-bottom: 1px solid transparent;
|
||||||
|
}
|
||||||
|
.is-valid + .select2-container--bootstrap-5.select2-container--open.select2-container--above .select2-selection, form.was-validated select:valid + .select2-container--bootstrap-5.select2-container--open.select2-container--above .select2-selection {
|
||||||
|
border-top: 1px solid transparent;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
.is-invalid + .select2-container--bootstrap-5 .select2-selection, form.was-validated select:invalid + .select2-container--bootstrap-5 .select2-selection {
|
||||||
|
border-color: var(--bs-danger);
|
||||||
|
}
|
||||||
|
.is-invalid + .select2-container--bootstrap-5.select2-container--focus .select2-selection, .is-invalid + .select2-container--bootstrap-5.select2-container--open .select2-selection, form.was-validated select:invalid + .select2-container--bootstrap-5.select2-container--focus .select2-selection, form.was-validated select:invalid + .select2-container--bootstrap-5.select2-container--open .select2-selection {
|
||||||
|
border-color: var(--bs-danger);
|
||||||
|
box-shadow: 0 0 0 0.25rem rgba( var(--bs-danger-rgb), 0.25);
|
||||||
|
}
|
||||||
|
.is-invalid + .select2-container--bootstrap-5.select2-container--open.select2-container--below .select2-selection, form.was-validated select:invalid + .select2-container--bootstrap-5.select2-container--open.select2-container--below .select2-selection {
|
||||||
|
border-bottom: 1px solid transparent;
|
||||||
|
}
|
||||||
|
.is-invalid + .select2-container--bootstrap-5.select2-container--open.select2-container--above .select2-selection, form.was-validated select:invalid + .select2-container--bootstrap-5.select2-container--open.select2-container--above .select2-selection {
|
||||||
|
border-top: 1px solid transparent;
|
||||||
|
border-top-left-radius: 0;
|
||||||
|
border-top-right-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.select2-container--bootstrap-5 .select2--small ~ .select2-selection {
|
||||||
|
min-height: calc(1.5em + (0.5rem + 2px));
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--small ~ .select2-selection--single .select2-selection__clear,
|
||||||
|
.select2-container--bootstrap-5 .select2--small ~ .select2-selection--multiple .select2-selection__clear {
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
padding: 0.125rem 0.125rem;
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23676a6d'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/0.5rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--small ~ .select2-selection--single .select2-selection__clear:hover,
|
||||||
|
.select2-container--bootstrap-5 .select2--small ~ .select2-selection--multiple .select2-selection__clear:hover {
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/0.5rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--small ~ .select2-dropdown .select2-search .select2-search__field {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--small ~ .select2-dropdown .select2-results__options .select2-results__option {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--small ~ .select2-dropdown .select2-results__options .select2-results__option[role=group] .select2-results__group {
|
||||||
|
padding: 0.25rem 0.25rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--small ~ .select2-dropdown .select2-results__options .select2-results__option[role=group] .select2-results__options--nested .select2-results__option {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--small ~ .select2-selection--single {
|
||||||
|
padding: 0.25rem 2.25rem 0.25rem 0.5rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--small ~ .select2-selection--multiple .select2-selection__rendered:not(:empty) {
|
||||||
|
padding-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--small ~ .select2-selection--multiple .select2-selection__rendered .select2-selection__choice {
|
||||||
|
padding: 0.35em 0.65em;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--small ~ .select2-selection--multiple .select2-selection__rendered .select2-selection__choice .select2-selection__choice__remove {
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
padding: 0.125rem 0.125rem;
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23676a6d'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/0.5rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--small ~ .select2-selection--multiple .select2-selection__rendered .select2-selection__choice .select2-selection__choice__remove:hover {
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/0.5rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--small ~ .select2-selection--multiple .select2-selection__clear {
|
||||||
|
right: 0.5rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--large ~ .select2-selection {
|
||||||
|
min-height: calc(1.5em + (1rem + 2px));
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--large ~ .select2-selection--single .select2-selection__clear,
|
||||||
|
.select2-container--bootstrap-5 .select2--large ~ .select2-selection--multiple .select2-selection__clear {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
padding: 0.5rem 0.5rem;
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23676a6d'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--large ~ .select2-selection--single .select2-selection__clear:hover,
|
||||||
|
.select2-container--bootstrap-5 .select2--large ~ .select2-selection--multiple .select2-selection__clear:hover {
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--large ~ .select2-dropdown .select2-search .select2-search__field {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--large ~ .select2-dropdown .select2-results__options .select2-results__option {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--large ~ .select2-dropdown .select2-results__options .select2-results__option[role=group] .select2-results__group {
|
||||||
|
padding: 0.5rem 0.5rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--large ~ .select2-dropdown .select2-results__options .select2-results__option[role=group] .select2-results__options--nested .select2-results__option {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--large ~ .select2-selection--single {
|
||||||
|
padding: 0.5rem 2.25rem 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--large ~ .select2-selection--multiple .select2-selection__rendered:not(:empty) {
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--large ~ .select2-selection--multiple .select2-selection__rendered .select2-selection__choice {
|
||||||
|
padding: 0.35em 0.65em;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--large ~ .select2-selection--multiple .select2-selection__rendered .select2-selection__choice .select2-selection__choice__remove {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
padding: 0.5rem 0.5rem;
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23676a6d'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--large ~ .select2-selection--multiple .select2-selection__rendered .select2-selection__choice .select2-selection__choice__remove:hover {
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.select2-container--bootstrap-5 .select2--large ~ .select2-selection--multiple .select2-selection__clear {
|
||||||
|
right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-select-sm ~ .select2-container--bootstrap-5 .select2-selection {
|
||||||
|
min-height: calc(1.5em + (0.5rem + 2px));
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.form-select-sm ~ .select2-container--bootstrap-5 .select2-selection--single .select2-selection__clear,
|
||||||
|
.form-select-sm ~ .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__clear {
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
padding: 0.125rem 0.125rem;
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23676a6d'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/0.5rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.form-select-sm ~ .select2-container--bootstrap-5 .select2-selection--single .select2-selection__clear:hover,
|
||||||
|
.form-select-sm ~ .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__clear:hover {
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/0.5rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.form-select-sm ~ .select2-container--bootstrap-5 .select2-dropdown .select2-search .select2-search__field {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.form-select-sm ~ .select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.form-select-sm ~ .select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option[role=group] .select2-results__group {
|
||||||
|
padding: 0.25rem 0.25rem;
|
||||||
|
}
|
||||||
|
.form-select-sm ~ .select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option[role=group] .select2-results__options--nested .select2-results__option {
|
||||||
|
padding: 0.25rem 0.5rem;
|
||||||
|
}
|
||||||
|
.form-select-sm ~ .select2-container--bootstrap-5 .select2-selection--single {
|
||||||
|
padding: 0.25rem 2.25rem 0.25rem 0.5rem;
|
||||||
|
}
|
||||||
|
.form-select-sm ~ .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered:not(:empty) {
|
||||||
|
padding-bottom: 0.25rem;
|
||||||
|
}
|
||||||
|
.form-select-sm ~ .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered .select2-selection__choice {
|
||||||
|
padding: 0.35em 0.65em;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
}
|
||||||
|
.form-select-sm ~ .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered .select2-selection__choice .select2-selection__choice__remove {
|
||||||
|
width: 0.5rem;
|
||||||
|
height: 0.5rem;
|
||||||
|
padding: 0.125rem 0.125rem;
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23676a6d'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/0.5rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.form-select-sm ~ .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered .select2-selection__choice .select2-selection__choice__remove:hover {
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/0.5rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.form-select-sm ~ .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__clear {
|
||||||
|
right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-select-lg ~ .select2-container--bootstrap-5 .select2-selection {
|
||||||
|
min-height: calc(1.5em + (1rem + 2px));
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
.form-select-lg ~ .select2-container--bootstrap-5 .select2-selection--single .select2-selection__clear,
|
||||||
|
.form-select-lg ~ .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__clear {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
padding: 0.5rem 0.5rem;
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23676a6d'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.form-select-lg ~ .select2-container--bootstrap-5 .select2-selection--single .select2-selection__clear:hover,
|
||||||
|
.form-select-lg ~ .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__clear:hover {
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.form-select-lg ~ .select2-container--bootstrap-5 .select2-dropdown .select2-search .select2-search__field {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
.form-select-lg ~ .select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
.form-select-lg ~ .select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option[role=group] .select2-results__group {
|
||||||
|
padding: 0.5rem 0.5rem;
|
||||||
|
}
|
||||||
|
.form-select-lg ~ .select2-container--bootstrap-5 .select2-dropdown .select2-results__options .select2-results__option[role=group] .select2-results__options--nested .select2-results__option {
|
||||||
|
padding: 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
.form-select-lg ~ .select2-container--bootstrap-5 .select2-selection--single {
|
||||||
|
padding: 0.5rem 2.25rem 0.5rem 1rem;
|
||||||
|
}
|
||||||
|
.form-select-lg ~ .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered:not(:empty) {
|
||||||
|
padding-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
.form-select-lg ~ .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered .select2-selection__choice {
|
||||||
|
padding: 0.35em 0.65em;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
}
|
||||||
|
.form-select-lg ~ .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered .select2-selection__choice .select2-selection__choice__remove {
|
||||||
|
width: 1rem;
|
||||||
|
height: 1rem;
|
||||||
|
padding: 0.5rem 0.5rem;
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23676a6d'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.form-select-lg ~ .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__rendered .select2-selection__choice .select2-selection__choice__remove:hover {
|
||||||
|
background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 011.414 0L8 6.586 14.293.293a1 1 0 111.414 1.414L9.414 8l6.293 6.293a1 1 0 01-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 01-1.414-1.414L6.586 8 .293 1.707a1 1 0 010-1.414z'/%3e%3c/svg%3e") center/1rem auto no-repeat;
|
||||||
|
}
|
||||||
|
.form-select-lg ~ .select2-container--bootstrap-5 .select2-selection--multiple .select2-selection__clear {
|
||||||
|
right: 1rem;
|
||||||
|
}
|
Loading…
Reference in New Issue