Merge branch 'develop' into main
commit
3b385ade74
|
@ -11,7 +11,7 @@ use Cake\Core\Configure;
|
|||
|
||||
class AuditLogsController extends AppController
|
||||
{
|
||||
public $filterFields = ['model_id', 'model', 'request_action', 'user_id', 'model_title', 'AuditLogs.created'];
|
||||
public $filterFields = ['model_id', 'model', ['name' => 'request_action', 'multiple' => true, ], 'user_id', 'model_title', 'AuditLogs.created'];
|
||||
public $quickFilterFields = ['model', 'request_action', 'model_title'];
|
||||
public $containFields = ['Users'];
|
||||
|
||||
|
@ -20,7 +20,7 @@ class AuditLogsController extends AppController
|
|||
$this->CRUD->index([
|
||||
'contain' => $this->containFields,
|
||||
'order' => ['AuditLogs.id' => 'DESC'],
|
||||
'filters' => $this->filterFields,
|
||||
'filters' => $this->CRUD->getFilterFieldsName($this->filterFields),
|
||||
'quickFilters' => $this->quickFilterFields,
|
||||
'afterFind' => function($data) {
|
||||
$request_ip = is_resource($data['request_ip']) ? stream_get_contents($data['request_ip']) : $data['request_ip'];
|
||||
|
|
|
@ -71,8 +71,12 @@ class AuthKeysController extends AppController
|
|||
if (empty($currentUser['role']['perm_org_admin'])) {
|
||||
$userConditions['id'] = $currentUser['id'];
|
||||
} else {
|
||||
$role_ids = $this->Users->Roles->find()->where(['perm_admin' => 0])->all()->extract('id')->toList();
|
||||
$userConditions['role_id IN'] = $role_ids;
|
||||
$role_ids = $this->Users->Roles->find()->where(['perm_admin' => 0, 'perm_org_admin' => 0])->all()->extract('id')->toList();
|
||||
$userConditions['organisation_id'] = $currentUser['organisation_id'];
|
||||
$userConditions['OR'] = [
|
||||
['role_id IN' => $role_ids],
|
||||
['id' => $currentUser['id']],
|
||||
];
|
||||
}
|
||||
}
|
||||
$users = $this->Users->find('list');
|
||||
|
@ -99,6 +103,7 @@ class AuthKeysController extends AppController
|
|||
$dropdownData = [
|
||||
'user' => $users
|
||||
];
|
||||
$this->entity->user_id = $currentUser['id'];
|
||||
$this->set(compact('dropdownData'));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,8 +90,8 @@ class ACLComponent extends Component
|
|||
'edit' => ['perm_admin', 'perm_org_admin'],
|
||||
'filtering' => ['*'],
|
||||
'index' => ['*'],
|
||||
'tag' => ['perm_tagger'],
|
||||
'untag' => ['perm_tagger'],
|
||||
'tag' => ['*'],
|
||||
'untag' => ['*'],
|
||||
'view' => ['*'],
|
||||
'viewTags' => ['*']
|
||||
],
|
||||
|
@ -140,20 +140,24 @@ class ACLComponent extends Component
|
|||
'enable' => ['perm_admin'],
|
||||
'getMetaFieldsToUpdate' => ['perm_admin'],
|
||||
'index' => ['perm_admin'],
|
||||
'migrateMetafieldsToNewestTemplate' => ['perm_admin'],
|
||||
'migrateOldMetaTemplateToNewestVersionForEntity' => ['perm_admin'],
|
||||
'update' => ['perm_admin'],
|
||||
'updateAllTemplates' => ['perm_admin'],
|
||||
'toggle' => ['perm_admin'],
|
||||
'view' => ['perm_admin'],
|
||||
],
|
||||
'MetaTemplateNameDirectory' => [
|
||||
'index' => ['perm_admin'],
|
||||
],
|
||||
'Organisations' => [
|
||||
'add' => ['perm_admin'],
|
||||
'delete' => ['perm_admin'],
|
||||
'edit' => ['perm_admin'],
|
||||
'filtering' => ['*'],
|
||||
'index' => ['*'],
|
||||
'tag' => ['perm_tagger'],
|
||||
'untag' => ['perm_tagger'],
|
||||
'tag' => ['perm_org_admin'],
|
||||
'untag' => ['perm_org_admin'],
|
||||
'view' => ['*'],
|
||||
'viewTags' => ['*']
|
||||
],
|
||||
|
|
|
@ -10,6 +10,7 @@ use Cake\Utility\Text;
|
|||
use Cake\View\ViewBuilder;
|
||||
use Cake\ORM\TableRegistry;
|
||||
use Cake\ORM\Query;
|
||||
use Cake\Database\Expression\QueryExpression;
|
||||
use Cake\Routing\Router;
|
||||
use Cake\Http\Exception\MethodNotAllowedException;
|
||||
use Cake\Http\Exception\NotFoundException;
|
||||
|
@ -225,22 +226,41 @@ class CRUDComponent extends Component
|
|||
} else {
|
||||
$this->Controller->set('metaFieldsEnabled', false);
|
||||
}
|
||||
$filters = !empty($this->Controller->filterFields) ? $this->Controller->filterFields : [];
|
||||
$filtersConfigRaw= !empty($this->Controller->filterFields) ? $this->Controller->filterFields : [];
|
||||
$filtersConfig = [];
|
||||
foreach ($filtersConfigRaw as $fieldConfig) {
|
||||
if (is_array($fieldConfig)) {
|
||||
$filtersConfig[$fieldConfig['name']] = $fieldConfig;
|
||||
} else {
|
||||
$filtersConfig[$fieldConfig] = ['name' => $fieldConfig];
|
||||
}
|
||||
}
|
||||
$filtersName = $this->getFilterFieldsName();
|
||||
$typeMap = $this->Table->getSchema()->typeMap();
|
||||
$associatedtypeMap = !empty($this->Controller->filterFields) ? $this->_getAssociatedTypeMap() : [];
|
||||
$associatedtypeMap = !empty($filtersName) ? $this->_getAssociatedTypeMap() : [];
|
||||
$typeMap = array_merge(
|
||||
$this->Table->getSchema()->typeMap(),
|
||||
$associatedtypeMap
|
||||
);
|
||||
$typeMap = array_filter($typeMap, function ($field) use ($filters) {
|
||||
return in_array($field, $filters);
|
||||
$typeMap = array_filter($typeMap, function ($field) use ($filtersName) {
|
||||
return in_array($field, $filtersName);
|
||||
}, ARRAY_FILTER_USE_KEY);
|
||||
$this->Controller->set('typeMap', $typeMap);
|
||||
$this->Controller->set('filters', $filters);
|
||||
$this->Controller->set('filters', $filtersName);
|
||||
$this->Controller->set('filtersConfig', $filtersConfig);
|
||||
$this->Controller->viewBuilder()->setLayout('ajax');
|
||||
$this->Controller->render('/genericTemplates/filters');
|
||||
}
|
||||
|
||||
public function getFilterFieldsName(): array
|
||||
{
|
||||
$filters = !empty($this->Controller->filterFields) ? $this->Controller->filterFields : [];
|
||||
$filters = array_map(function($item) {
|
||||
return is_array($item) ? $item['name'] : $item;
|
||||
}, $filters);
|
||||
return $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* getResponsePayload Returns the adaquate response payload based on the request context
|
||||
*
|
||||
|
@ -289,6 +309,9 @@ class CRUDComponent extends Component
|
|||
if ($this->metaFieldsSupported()) {
|
||||
$metaTemplates = $this->getMetaTemplates();
|
||||
$data = $this->attachMetaTemplatesIfNeeded($data, $metaTemplates->toArray());
|
||||
if (isset($params['afterFind'])) {
|
||||
$data = $params['afterFind']($data, $params);
|
||||
}
|
||||
}
|
||||
if ($this->request->is('post')) {
|
||||
$patchEntityParams = [
|
||||
|
@ -548,16 +571,16 @@ class CRUDComponent extends Component
|
|||
$query->where($params['conditions']);
|
||||
}
|
||||
$data = $query->first();
|
||||
if ($this->metaFieldsSupported()) {
|
||||
$metaTemplates = $this->getMetaTemplates();
|
||||
$data = $this->attachMetaTemplatesIfNeeded($data, $metaTemplates->toArray());
|
||||
}
|
||||
if (isset($params['afterFind'])) {
|
||||
$data = $params['afterFind']($data, $params);
|
||||
}
|
||||
if (empty($data)) {
|
||||
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
|
||||
}
|
||||
if ($this->metaFieldsSupported()) {
|
||||
$metaTemplates = $this->getMetaTemplates();
|
||||
$data = $this->attachMetaTemplatesIfNeeded($data, $metaTemplates->toArray());
|
||||
}
|
||||
if ($this->request->is(['post', 'put'])) {
|
||||
$patchEntityParams = [
|
||||
'associated' => []
|
||||
|
@ -828,6 +851,9 @@ class CRUDComponent extends Component
|
|||
$query->contain($params['contain']);
|
||||
}
|
||||
$data = $query->first();
|
||||
if (isset($params['afterFind'])) {
|
||||
$data = $params['afterFind']($data, $params);
|
||||
}
|
||||
if (empty($data)) {
|
||||
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
|
||||
}
|
||||
|
@ -850,6 +876,9 @@ class CRUDComponent extends Component
|
|||
$query->contain($params['contain']);
|
||||
}
|
||||
$data = $query->first();
|
||||
if (isset($params['afterFind'])) {
|
||||
$data = $params['afterFind']($data, $params);
|
||||
}
|
||||
if (isset($params['beforeSave'])) {
|
||||
try {
|
||||
$data = $params['beforeSave']($data);
|
||||
|
@ -1225,7 +1254,7 @@ class CRUDComponent extends Component
|
|||
}
|
||||
$activeFilters[$filter] = $filterValue;
|
||||
if (is_array($filterValue)) {
|
||||
$query->where([($filter . ' IN') => $filterValue]);
|
||||
$query = $this->setInCondition($query, $filter, $filterValue);
|
||||
} else {
|
||||
$query = $this->setValueCondition($query, $filter, $filterValue);
|
||||
}
|
||||
|
@ -1311,6 +1340,27 @@ class CRUDComponent extends Component
|
|||
}
|
||||
}
|
||||
|
||||
protected function setInCondition($query, $fieldName, $values)
|
||||
{
|
||||
$split = explode(' ', $fieldName);
|
||||
if (count($split) == 1) {
|
||||
$field = $fieldName;
|
||||
$operator = '=';
|
||||
} else {
|
||||
$field = $split[0];
|
||||
$operator = $split[1];
|
||||
}
|
||||
if ($operator == '=') {
|
||||
return $query->where(function (QueryExpression $exp, Query $q) use ($field, $values) {
|
||||
return $exp->in($field, $values);
|
||||
});
|
||||
} else if ($operator == '!=') {
|
||||
return $query->where(function (QueryExpression $exp, Query $q) use ($field, $values) {
|
||||
return $exp->notIn($field, $values);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected function setFilteringContext($contextFilters, $params)
|
||||
{
|
||||
$filteringContexts = [];
|
||||
|
@ -1591,7 +1641,7 @@ class CRUDComponent extends Component
|
|||
protected function _getAssociatedTypeMap(): array
|
||||
{
|
||||
$typeMap = [];
|
||||
foreach ($this->Controller->filterFields as $filter) {
|
||||
foreach ($this->getFilterFieldsName() as $filter) {
|
||||
$exploded = explode('.', $filter);
|
||||
if (count($exploded) > 1) {
|
||||
$model = $exploded[0];
|
||||
|
|
|
@ -56,6 +56,12 @@ class MetaTemplatesNavigation extends BaseNavigation
|
|||
'url' => '/metaTemplates/prune_outdated_template',
|
||||
'isPOST' => true,
|
||||
]);
|
||||
$this->bcf->addRoute('MetaTemplates', 'view_template_directory', [
|
||||
'label' => __('View all known templates'),
|
||||
'icon' => 'list',
|
||||
'url' => '/metaTemplateNameDirectory/index',
|
||||
'isRedirect' => true,
|
||||
]);
|
||||
}
|
||||
|
||||
public function addParents()
|
||||
|
@ -78,7 +84,7 @@ class MetaTemplatesNavigation extends BaseNavigation
|
|||
$totalUpdateCount = $udpateCount + $newCount;
|
||||
}
|
||||
$updateAllActionConfig = [
|
||||
'label' => __('Update all template'),
|
||||
'label' => __('Update all templates'),
|
||||
'url' => '/metaTemplates/updateAllTemplates',
|
||||
'url_vars' => ['id' => 'id'],
|
||||
];
|
||||
|
@ -94,6 +100,9 @@ class MetaTemplatesNavigation extends BaseNavigation
|
|||
'label' => __('Prune outdated templates'),
|
||||
'url' => '/metaTemplates/prune_outdated_template',
|
||||
]);
|
||||
$this->bcf->addAction('MetaTemplates', 'index', 'MetaTemplates', 'view_template_directory', [
|
||||
'isRedirect' => true,
|
||||
]);
|
||||
|
||||
if (empty($this->viewVars['templateStatus']['up-to-date'])) {
|
||||
$this->bcf->addAction('MetaTemplates', 'view', 'MetaTemplates', 'update', [
|
||||
|
|
|
@ -15,7 +15,7 @@ use Cake\Error\Debugger;
|
|||
class EncryptionKeysController extends AppController
|
||||
{
|
||||
public $filterFields = ['owner_model', 'owner_id', 'encryption_key'];
|
||||
public $quickFilterFields = ['encryption_key'];
|
||||
public $quickFilterFields = [['encryption_key' => true]];
|
||||
public $containFields = ['Individuals', 'Organisations'];
|
||||
public $statisticsFields = ['type'];
|
||||
|
||||
|
@ -83,6 +83,9 @@ class EncryptionKeysController extends AppController
|
|||
$individualConditions = [
|
||||
'id' => $currentUser['individual_id']
|
||||
];
|
||||
$orgConditions = [
|
||||
'id' => -1, // Only org_admins are allowed to manage their org's encryption keys
|
||||
];
|
||||
} else {
|
||||
$this->loadModel('Alignments');
|
||||
$individualConditions = ['id IN' => $this->Alignments->find('list', [
|
||||
|
@ -122,6 +125,11 @@ class EncryptionKeysController extends AppController
|
|||
'organisation' => $this->Organisations->find('list')->order(['name' => 'asc'])->where($orgConditions)->all()->toArray(),
|
||||
'individual' => $this->Individuals->find('list')->order(['email' => 'asc'])->where($individualConditions)->all()->toArray()
|
||||
];
|
||||
foreach ($dropdownData as $modelName => $list) {
|
||||
if (empty($list)) {
|
||||
unset($dropdownData[$modelName]);
|
||||
}
|
||||
}
|
||||
return $params;
|
||||
}
|
||||
|
||||
|
|
|
@ -68,25 +68,15 @@ class IndividualsController extends AppController
|
|||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
$this->set('canEdit', $this->canEdit($id));
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$currentUser = $this->ACL->getUser();
|
||||
if (!$currentUser['role']['perm_admin']) {
|
||||
$validIndividuals = $this->Individuals->getValidIndividualsToEdit($currentUser);
|
||||
if (!in_array($id, $validIndividuals)) {
|
||||
throw new MethodNotAllowedException(__('You cannot modify that individual.'));
|
||||
}
|
||||
if (!$this->canEdit($id)) {
|
||||
throw new MethodNotAllowedException(__('You cannot modify that individual.'));
|
||||
}
|
||||
$currentUser = $this->ACL->getUser();
|
||||
$validIndividualIds = [];
|
||||
if (!$currentUser['role']['perm_admin']) {
|
||||
$validIndividualIds = $this->Individuals->getValidIndividualsToEdit($currentUser);
|
||||
if (!in_array($id, $validIndividualIds)) {
|
||||
throw new NotFoundException(__('Invalid individual.'));
|
||||
}
|
||||
}
|
||||
$this->CRUD->edit($id, [
|
||||
'beforeSave' => function($data) use ($currentUser) {
|
||||
if ($currentUser['role']['perm_admin'] && isset($data['uuid'])) {
|
||||
|
@ -104,7 +94,16 @@ class IndividualsController extends AppController
|
|||
|
||||
public function delete($id)
|
||||
{
|
||||
$this->CRUD->delete($id);
|
||||
$params = [
|
||||
'contain' => ['Users'],
|
||||
'afterFind' => function($data, $params) {
|
||||
if (!empty($data['user'])) {
|
||||
throw new ForbiddenException(__('Individual associated to a user cannot be deleted.'));
|
||||
}
|
||||
return $data;
|
||||
}
|
||||
];
|
||||
$this->CRUD->delete($id, $params);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
|
@ -113,6 +112,9 @@ class IndividualsController extends AppController
|
|||
|
||||
public function tag($id)
|
||||
{
|
||||
if (!$this->canEdit($id)) {
|
||||
throw new MethodNotAllowedException(__('You cannot tag that individual.'));
|
||||
}
|
||||
$this->CRUD->tag($id);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
|
@ -122,6 +124,9 @@ class IndividualsController extends AppController
|
|||
|
||||
public function untag($id)
|
||||
{
|
||||
if (!$this->canEdit($id)) {
|
||||
throw new MethodNotAllowedException(__('You cannot untag that individual.'));
|
||||
}
|
||||
$this->CRUD->untag($id);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
|
@ -137,4 +142,17 @@ class IndividualsController extends AppController
|
|||
return $responsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
private function canEdit($indId): bool
|
||||
{
|
||||
$currentUser = $this->ACL->getUser();
|
||||
if ($currentUser['role']['perm_admin']) {
|
||||
return true;
|
||||
}
|
||||
$validIndividuals = $this->Individuals->getValidIndividualsToEdit($currentUser);
|
||||
if (in_array($indId, $validIndividuals)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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, [
|
||||
'contain' => ['MetaTemplateFields']
|
||||
]);
|
||||
$templateOnDisk = $this->MetaTemplates->readTemplateFromDisk($metaTemplate->uuid);
|
||||
$error = '';
|
||||
$errorMessage = '';
|
||||
$templateOnDisk = $this->MetaTemplates->readTemplateFromDisk($metaTemplate->uuid, $error);
|
||||
if (is_null($templateOnDisk)) {
|
||||
$errorMessage = __('Could not retreive template\'s status. Reason: {0}', $error);
|
||||
$this->Flash->error($errorMessage);
|
||||
$templateOnDisk = [];
|
||||
}
|
||||
$templateStatus = $this->MetaTemplates->getStatusForMetaTemplate($templateOnDisk, $metaTemplate);
|
||||
if (!empty($errorMessage)) {
|
||||
$templateStatus['error'] = $errorMessage;
|
||||
}
|
||||
$this->set('templateOnDisk', $templateOnDisk);
|
||||
$this->set('templateStatus', $templateStatus);
|
||||
return [
|
||||
|
|
|
@ -101,7 +101,6 @@ class OrganisationsController extends AppController
|
|||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
$this->set('metaGroup', 'ContactDB');
|
||||
}
|
||||
|
||||
public function view($id)
|
||||
|
@ -111,16 +110,12 @@ class OrganisationsController extends AppController
|
|||
if (!empty($responsePayload)) {
|
||||
return $responsePayload;
|
||||
}
|
||||
$this->set('metaGroup', 'ContactDB');
|
||||
$this->set('canEdit', $this->canEdit($id));
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$currentUser = $this->ACL->getUser();
|
||||
if (
|
||||
!($currentUser['organisation']['id'] == $id && $currentUser['role']['perm_org_admin']) &&
|
||||
!$currentUser['role']['perm_admin']
|
||||
) {
|
||||
if (!$this->canEdit($id)) {
|
||||
throw new MethodNotAllowedException(__('You cannot modify that organisation.'));
|
||||
}
|
||||
$this->CRUD->edit($id);
|
||||
|
@ -144,6 +139,9 @@ class OrganisationsController extends AppController
|
|||
|
||||
public function tag($id)
|
||||
{
|
||||
if (!$this->canEdit($id)) {
|
||||
throw new MethodNotAllowedException(__('You cannot tag that organisation.'));
|
||||
}
|
||||
$this->CRUD->tag($id);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
|
@ -153,6 +151,9 @@ class OrganisationsController extends AppController
|
|||
|
||||
public function untag($id)
|
||||
{
|
||||
if (!$this->canEdit($id)) {
|
||||
throw new MethodNotAllowedException(__('You cannot untag that organisation.'));
|
||||
}
|
||||
$this->CRUD->untag($id);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
if (!empty($responsePayload)) {
|
||||
|
@ -168,4 +169,16 @@ class OrganisationsController extends AppController
|
|||
return $responsePayload;
|
||||
}
|
||||
}
|
||||
|
||||
private function canEdit($orgId): bool
|
||||
{
|
||||
$currentUser = $this->ACL->getUser();
|
||||
if ($currentUser['role']['perm_admin']) {
|
||||
return true;
|
||||
}
|
||||
if ($currentUser['role']['perm_org_admin'] && $currentUser['organisation']['id'] == $orgId) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,6 +120,12 @@ class UsersController extends AppController
|
|||
if (Configure::read('keycloak.enabled')) {
|
||||
$this->Users->enrollUserRouter($data);
|
||||
}
|
||||
},
|
||||
'afterFind' => function ($user, &$params) use ($currentUser) {
|
||||
if (!empty($user)) { // We don't have a 404
|
||||
$user = $this->fetchTable('PermissionLimitations')->attachLimitations($user);
|
||||
}
|
||||
return $user;
|
||||
}
|
||||
]);
|
||||
$responsePayload = $this->CRUD->getResponsePayload();
|
||||
|
@ -227,6 +233,14 @@ class UsersController extends AppController
|
|||
if (!$this->ACL->canEditUser($currentUser, $user)) {
|
||||
throw new MethodNotAllowedException(__('You cannot edit the given user.'));
|
||||
}
|
||||
$user = $this->fetchTable('PermissionLimitations')->attachLimitations($user);
|
||||
}
|
||||
return $user;
|
||||
};
|
||||
} else {
|
||||
$params['afterFind'] = function ($user, &$params) use ($currentUser) {
|
||||
if (!empty($user)) { // We don't have a 404
|
||||
$user = $this->fetchTable('PermissionLimitations')->attachLimitations($user);
|
||||
}
|
||||
return $user;
|
||||
};
|
||||
|
|
|
@ -124,12 +124,15 @@ class IndividualsTable extends AppTable
|
|||
|
||||
public function getValidIndividualsToEdit(object $currentUser): array
|
||||
{
|
||||
$adminRoles = $this->Users->Roles->find('list')->select(['id'])->where(['perm_admin' => 1])->all()->toArray();
|
||||
$validRoles = $this->Users->Roles->find('list')->select(['id'])->where(['perm_admin' => 0, 'perm_org_admin' => 0])->all()->toArray();
|
||||
$validIndividualIds = $this->Users->find('list')->select(['individual_id'])->where(
|
||||
[
|
||||
'organisation_id' => $currentUser['organisation_id'],
|
||||
'disabled' => 0,
|
||||
'role_id NOT IN' => array_keys($adminRoles)
|
||||
'OR' => [
|
||||
['role_id IN' => array_keys($validRoles)],
|
||||
['id' => $currentUser['id']],
|
||||
]
|
||||
]
|
||||
)->all()->toArray();
|
||||
return array_keys($validIndividualIds);
|
||||
|
|
|
@ -7,6 +7,7 @@ use App\Model\Entity\MetaTemplateNameDirectory;
|
|||
use App\Model\Table\AppTable;
|
||||
use Cake\ORM\RulesChecker;
|
||||
use Cake\Validation\Validator;
|
||||
use Cake\Log\Log;
|
||||
|
||||
class MetaTemplateNameDirectoryTable extends AppTable
|
||||
{
|
||||
|
@ -20,6 +21,9 @@ class MetaTemplateNameDirectoryTable extends AppTable
|
|||
'foreignKey' => 'meta_template_directory_id',
|
||||
]
|
||||
);
|
||||
$this->hasOne('MetaTemplates', [
|
||||
'foreignKey' => 'meta_template_directory_id',
|
||||
]);
|
||||
$this->setDisplayField('name');
|
||||
}
|
||||
|
||||
|
@ -59,7 +63,11 @@ class MetaTemplateNameDirectoryTable extends AppTable
|
|||
if (!empty($existingTemplate)) {
|
||||
return $existingTemplate;
|
||||
}
|
||||
$this->save($metaTemplateDirectory);
|
||||
return $metaTemplateDirectory;
|
||||
$savedEntity = $this->save($metaTemplateDirectory);
|
||||
if ($savedEntity) {
|
||||
return $metaTemplateDirectory;
|
||||
}
|
||||
Log::error(__('Could not save meta_template_directory. Reasons: {0}', json_encode($metaTemplateDirectory->getErrors())));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -555,6 +555,10 @@ class MetaTemplatesTable extends AppTable
|
|||
if (substr($file, -5) === '.json') {
|
||||
$errorMessage = '';
|
||||
$template = $this->decodeTemplateFromDisk($path . $file, $errorMessage);
|
||||
if (!empty($errorMessage)) {
|
||||
$error = $errorMessage;
|
||||
return null;
|
||||
}
|
||||
if (!empty($template) && $template['uuid'] == $uuid) {
|
||||
return $template;
|
||||
}
|
||||
|
@ -1318,6 +1322,13 @@ class MetaTemplatesTable extends AppTable
|
|||
$updateStatus['next_version'] = $template['version'];
|
||||
$updateStatus['new'] = false;
|
||||
$updateStatus['automatically-updateable'] = false;
|
||||
$updateStatus['conflicts'] = [];
|
||||
if (empty($template)) {
|
||||
$updateStatus['up-to-date'] = false;
|
||||
$updateStatus['automatically-updateable'] = false;
|
||||
$updateStatus['can-be-removed'] = false;
|
||||
return $updateStatus;
|
||||
}
|
||||
if (intval($metaTemplate->version) >= intval($template['version'])) {
|
||||
$updateStatus['up-to-date'] = true;
|
||||
$updateStatus['conflicts'][] = __('Could not update the template. Local version is equal or newer.');
|
||||
|
|
|
@ -30,13 +30,17 @@ class PermissionLimitationsTable extends AppTable
|
|||
public function getListOfLimitations(\App\Model\Entity\User $data)
|
||||
{
|
||||
$Users = TableRegistry::getTableLocator()->get('Users');
|
||||
$ownOrgUserIds = $Users->find('list', [
|
||||
'keyField' => 'id',
|
||||
'valueField' => 'id',
|
||||
'conditions' => [
|
||||
'organisation_id' => $data['organisation_id']
|
||||
]
|
||||
])->all()->toList();
|
||||
$includeOrganisationPermissions = !empty($data['organisation_id']);
|
||||
$ownOrgUserIds = [];
|
||||
if ($includeOrganisationPermissions) {
|
||||
$ownOrgUserIds = $Users->find('list', [
|
||||
'keyField' => 'id',
|
||||
'valueField' => 'id',
|
||||
'conditions' => [
|
||||
'organisation_id' => $data['organisation_id']
|
||||
]
|
||||
])->all()->toList();
|
||||
}
|
||||
$MetaFields = TableRegistry::getTableLocator()->get('MetaFields');
|
||||
$raw = $this->find()->select(['scope', 'permission', 'max_occurrence'])->disableHydration()->toArray();
|
||||
$limitations = [];
|
||||
|
@ -70,9 +74,12 @@ class PermissionLimitationsTable extends AppTable
|
|||
if (!empty($ownOrgUserIds)) {
|
||||
$conditions['parent_id IN'] = array_values($ownOrgUserIds);
|
||||
}
|
||||
$limitations[$field]['organisation']['current'] = $MetaFields->find('all', [
|
||||
'conditions' => $conditions,
|
||||
])->count();
|
||||
$limitations[$field]['organisation']['current'] = '?';
|
||||
if ($includeOrganisationPermissions) {
|
||||
$limitations[$field]['organisation']['current'] = $MetaFields->find('all', [
|
||||
'conditions' => $conditions,
|
||||
])->count();
|
||||
}
|
||||
}
|
||||
}
|
||||
return $limitations;
|
||||
|
@ -89,34 +96,35 @@ class PermissionLimitationsTable extends AppTable
|
|||
if (!empty($data['MetaTemplates'])) {
|
||||
foreach ($data['MetaTemplates'] as &$metaTemplate) {
|
||||
foreach ($metaTemplate['meta_template_fields'] as &$meta_template_field) {
|
||||
$boolean = $meta_template_field['type'] === 'boolean';
|
||||
foreach ($meta_template_field['metaFields'] as &$metaField) {
|
||||
if (isset($permissionLimitations[$metaField['field']])) {
|
||||
foreach ($permissionLimitations[$metaField['field']] as $scope => $value) {
|
||||
$messageType = 'warning';
|
||||
if (isset($permissionLimitations[$meta_template_field['field']])) {
|
||||
foreach ($permissionLimitations[$meta_template_field['field']] as $scope => $value) {
|
||||
$messageType = 'warning';
|
||||
if ($value['current'] == '?') {
|
||||
$messageType = 'info';
|
||||
} else {
|
||||
if ($value['limit'] > $value['current']) {
|
||||
$messageType = 'info';
|
||||
}
|
||||
if ($value['limit'] < $value['current']) {
|
||||
$messageType = 'danger';
|
||||
}
|
||||
if (empty($metaField[$messageType])) {
|
||||
$metaField[$messageType] = '';
|
||||
}
|
||||
$altText = __(
|
||||
'There is a limitation enforced on the number of users with this permission {0}. Currently {1} slot(s) are used up of a maximum of {2} slot(s).',
|
||||
$scope === 'global' ? __('instance wide') : __('for your organisation'),
|
||||
$value['current'],
|
||||
$value['limit']
|
||||
);
|
||||
$metaField[$messageType] .= sprintf(
|
||||
' <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'),
|
||||
],
|
||||
'test' => function ($value, $setting, $validator) {
|
||||
$validator->range('value', [0, 3]);
|
||||
$validator->range('value', [0, 2]);
|
||||
return testValidator($value, $validator);
|
||||
},
|
||||
],
|
||||
|
|
|
@ -73,13 +73,6 @@ class UsersTable extends AppTable
|
|||
if (!$entity->isNew()) {
|
||||
$success = $this->handleUserUpdateRouter($entity);
|
||||
}
|
||||
$permissionRestrictionCheck = $this->checkPermissionRestrictions($entity);
|
||||
if ($permissionRestrictionCheck !== true) {
|
||||
$entity->setErrors($permissionRestrictionCheck);
|
||||
$event->stopPropagation();
|
||||
$event->setResult(false);
|
||||
return false;
|
||||
}
|
||||
return $success;
|
||||
}
|
||||
|
||||
|
@ -187,10 +180,24 @@ class UsersTable extends AppTable
|
|||
public function buildRules(RulesChecker $rules): RulesChecker
|
||||
{
|
||||
$rules->add($rules->isUnique(['username']));
|
||||
$allowDuplicateIndividuals = false;
|
||||
if (empty(Configure::read('user.multiple-users-per-individual')) || !empty(Configure::read('keycloak.enabled'))) {
|
||||
$rules->add($rules->isUnique(['individual_id']));
|
||||
}
|
||||
|
||||
$rules->add(function($entity, $options) {
|
||||
$permissionRestrictionCheck = $this->checkPermissionRestrictions($entity);
|
||||
if ($permissionRestrictionCheck !== true) {
|
||||
foreach ($permissionRestrictionCheck as $permission_name => $errors) {
|
||||
foreach ($entity->meta_fields as $i => $metaField) {
|
||||
if ($metaField['field'] === $permission_name) {
|
||||
$entity->meta_fields[$i]->setErrors(['value' => $errors]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}, 'permissionLimitations');
|
||||
return $rules;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ class BootstrapIcon extends BootstrapGeneric
|
|||
{
|
||||
private $icon = '';
|
||||
private $defaultOptions = [
|
||||
'id' => '',
|
||||
'class' => [],
|
||||
'title' => '',
|
||||
'attrs' => [],
|
||||
|
@ -48,6 +49,7 @@ class BootstrapIcon extends BootstrapGeneric
|
|||
{
|
||||
$html = $this->node('span', array_merge(
|
||||
[
|
||||
'id' => $this->options['id'] ?? '',
|
||||
'class' => array_merge(
|
||||
$this->options['class'],
|
||||
["fa fa-{$this->icon}"]
|
||||
|
|
|
@ -215,8 +215,6 @@ class BootstrapTabs extends BootstrapGeneric
|
|||
],
|
||||
[
|
||||
"bg-{$this->options['header-variant']}",
|
||||
"text-{$this->options['header-text-variant']}",
|
||||
"border-{$this->options['header-border-variant']}"
|
||||
]
|
||||
)], $this->genNav());
|
||||
$content = $this->node('div', ['class' => array_merge(
|
||||
|
@ -226,7 +224,6 @@ class BootstrapTabs extends BootstrapGeneric
|
|||
],
|
||||
[
|
||||
"bg-{$this->options['body-variant']}",
|
||||
"text-{$this->options['body-text-variant']}"
|
||||
]
|
||||
)], $this->genContent());
|
||||
|
||||
|
@ -238,7 +235,6 @@ class BootstrapTabs extends BootstrapGeneric
|
|||
($this->options['vertical-size'] == 'auto' ? 'flex-nowrap' : '')
|
||||
],
|
||||
[
|
||||
"border-{$this->options['header-border-variant']}"
|
||||
]
|
||||
)], $containerContent);
|
||||
return $container;
|
||||
|
|
|
@ -450,7 +450,7 @@ class BootstrapGeneric
|
|||
return sprintf('%s="%s"', h($key), (!empty($escape) ? h($value) : $value));
|
||||
}
|
||||
return '';
|
||||
} else if (empty($value)) {
|
||||
} else if (!isset($value)) {
|
||||
return '';
|
||||
}
|
||||
return sprintf('%s="%s"', h($key), (!empty($escape) ? h($value) : $value));
|
||||
|
|
|
@ -8,6 +8,15 @@ class FormFieldMassageHelper extends Helper
|
|||
{
|
||||
public function prepareFormElement(\Cake\View\Helper\FormHelper $form, array $controlParams, array $fieldData): string
|
||||
{
|
||||
if ($fieldData['tooltip']) {
|
||||
$form->setTemplates([
|
||||
'label' => '{{text}}{{tooltip}}',
|
||||
]);
|
||||
$controlParams['templateVars'] = array_merge(
|
||||
$controlParams['templateVars'] ?? [],
|
||||
['tooltip' => $fieldData['tooltip'], ]
|
||||
);
|
||||
}
|
||||
if (!empty($fieldData['stateDependence'])) {
|
||||
$controlParams['data-dependence-source'] = h($fieldData['stateDependence']['source']);
|
||||
$controlParams['data-dependence-option'] = h($fieldData['stateDependence']['option']);
|
||||
|
|
|
@ -14,7 +14,8 @@ echo $this->element('genericElements/Form/genericForm', [
|
|||
],
|
||||
[
|
||||
'field' => 'expiration',
|
||||
'label' => 'Expiration'
|
||||
'label' => __('Expiration'),
|
||||
'type' => 'datetime',
|
||||
]
|
||||
],
|
||||
'submit' => [
|
||||
|
|
|
@ -103,6 +103,9 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
|||
'icon' => 'trash',
|
||||
'complex_requirement' => [
|
||||
'function' => function ($row, $options) use ($loggedUser) {
|
||||
if (!empty($row['user'])) { // cannot delete individuals with associated user(s)
|
||||
return false;
|
||||
}
|
||||
return (bool)$loggedUser['role']['perm_admin'];
|
||||
}
|
||||
]
|
||||
|
|
|
@ -31,6 +31,7 @@ echo $this->element(
|
|||
[
|
||||
'key' => __('Tags'),
|
||||
'type' => 'tags',
|
||||
'editable' => $canEdit,
|
||||
],
|
||||
[
|
||||
'key' => __('Alignments'),
|
||||
|
|
|
@ -10,7 +10,6 @@ echo $this->element('genericElements/Form/genericForm', [
|
|||
'multiple' => true,
|
||||
'select2' => true,
|
||||
'label' => __('Members'),
|
||||
'class' => 'select2-input',
|
||||
'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,
|
||||
]);
|
||||
$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 {
|
||||
if ($updateStatus['automatically-updateable']) {
|
||||
$bodyHtml .= $this->Bootstrap->alert([
|
||||
|
|
|
@ -57,7 +57,7 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
|||
'data_path' => 'url',
|
||||
],
|
||||
[
|
||||
'name' => __('Nationality'),
|
||||
'name' => __('Country'),
|
||||
'data_path' => 'nationality',
|
||||
],
|
||||
[
|
||||
|
|
|
@ -10,12 +10,14 @@
|
|||
array(
|
||||
'field' => 'uuid',
|
||||
'label' => 'UUID',
|
||||
'type' => 'uuid'
|
||||
'type' => 'uuid',
|
||||
'tooltip' => __('If the Organisation already has a known UUID in another application such as MISP or another Cerebrate, please re-use this one.'),
|
||||
),
|
||||
array(
|
||||
'field' => 'url'
|
||||
),
|
||||
array(
|
||||
'label' => __('Country'),
|
||||
'field' => 'nationality'
|
||||
),
|
||||
array(
|
||||
|
|
|
@ -66,7 +66,7 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
|||
'data_path' => 'url',
|
||||
],
|
||||
[
|
||||
'name' => __('Nationality'),
|
||||
'name' => __('Country'),
|
||||
'data_path' => 'nationality',
|
||||
'sort' => 'nationality',
|
||||
],
|
||||
|
|
|
@ -22,7 +22,7 @@ echo $this->element(
|
|||
'path' => 'url'
|
||||
],
|
||||
[
|
||||
'key' => __('Nationality'),
|
||||
'key' => __('Country'),
|
||||
'path' => 'nationality'
|
||||
],
|
||||
[
|
||||
|
@ -40,6 +40,7 @@ echo $this->element(
|
|||
[
|
||||
'key' => __('Tags'),
|
||||
'type' => 'tags',
|
||||
'editable' => $canEdit,
|
||||
],
|
||||
[
|
||||
'key' => __('Alignments'),
|
||||
|
|
|
@ -1,28 +1,31 @@
|
|||
<?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', [
|
||||
'data' => [
|
||||
'data' => $data,
|
||||
'top_bar' => [
|
||||
'children' => [
|
||||
[
|
||||
'type' => 'simple',
|
||||
'children' => [
|
||||
'data' => [
|
||||
'type' => 'simple',
|
||||
'text' => __('Add role'),
|
||||
'class' => 'btn btn-primary',
|
||||
'popover_url' => '/roles/add'
|
||||
]
|
||||
]
|
||||
],
|
||||
[
|
||||
'type' => 'search',
|
||||
'button' => __('Search'),
|
||||
'placeholder' => __('Enter value to search'),
|
||||
'data' => '',
|
||||
'searchKey' => 'value'
|
||||
]
|
||||
]
|
||||
'children' => $topbarChildren,
|
||||
],
|
||||
'fields' => [
|
||||
[
|
||||
|
|
|
@ -1,99 +1,145 @@
|
|||
<?php
|
||||
use Cake\Core\Configure;
|
||||
$passwordRequired = false;
|
||||
$showPasswordField = false;
|
||||
if ($this->request->getParam('action') === 'add') {
|
||||
$dropdownData['individual'] = ['new' => __('New individual')] + $dropdownData['individual'];
|
||||
if (!Configure::check('password_auth.enabled') || Configure::read('password_auth.enabled')) {
|
||||
$passwordRequired = 'required';
|
||||
}
|
||||
}
|
||||
|
||||
use Cake\Core\Configure;
|
||||
|
||||
$passwordRequired = false;
|
||||
$showPasswordField = false;
|
||||
if ($this->request->getParam('action') === 'add') {
|
||||
$dropdownData['individual'] = ['new' => __('New individual')] + $dropdownData['individual'];
|
||||
if (!Configure::check('password_auth.enabled') || Configure::read('password_auth.enabled')) {
|
||||
$showPasswordField = true;
|
||||
$passwordRequired = 'required';
|
||||
}
|
||||
echo $this->element('genericElements/Form/genericForm', [
|
||||
'data' => [
|
||||
'description' => __('Roles define global rules for a set of users, including first and foremost access controls to certain functionalities.'),
|
||||
'model' => 'Roles',
|
||||
'fields' => [
|
||||
[
|
||||
'field' => 'individual_id',
|
||||
'type' => 'dropdown',
|
||||
'label' => __('Associated individual'),
|
||||
'options' => isset($dropdownData['individual']) ? $dropdownData['individual'] : [],
|
||||
'conditions' => $this->request->getParam('action') === 'add'
|
||||
],
|
||||
[
|
||||
'field' => 'individual.email',
|
||||
'stateDependence' => [
|
||||
'source' => '#individual_id-field',
|
||||
'option' => 'new'
|
||||
],
|
||||
'required' => false
|
||||
],
|
||||
[
|
||||
'field' => 'individual.first_name',
|
||||
'label' => 'First name',
|
||||
'stateDependence' => [
|
||||
'source' => '#individual_id-field',
|
||||
'option' => 'new'
|
||||
],
|
||||
'required' => false
|
||||
],
|
||||
[
|
||||
'field' => 'individual.last_name',
|
||||
'label' => 'Last name',
|
||||
'stateDependence' => [
|
||||
'source' => '#individual_id-field',
|
||||
'option' => 'new'
|
||||
],
|
||||
'required' => false
|
||||
],
|
||||
[
|
||||
'field' => 'username',
|
||||
'autocomplete' => 'off'
|
||||
],
|
||||
[
|
||||
'field' => 'organisation_id',
|
||||
'type' => 'dropdown',
|
||||
'label' => __('Associated organisation'),
|
||||
'options' => $dropdownData['organisation'],
|
||||
'default' => $loggedUser['organisation_id']
|
||||
],
|
||||
[
|
||||
'field' => 'password',
|
||||
'label' => __('Password'),
|
||||
'type' => 'password',
|
||||
'required' => $passwordRequired,
|
||||
'autocomplete' => 'new-password',
|
||||
'value' => '',
|
||||
'requirements' => $showPasswordField,
|
||||
],
|
||||
[
|
||||
'field' => 'confirm_password',
|
||||
'label' => __('Confirm Password'),
|
||||
'type' => 'password',
|
||||
'required' => $passwordRequired,
|
||||
'autocomplete' => 'off',
|
||||
'requirements' => $showPasswordField,
|
||||
],
|
||||
[
|
||||
'field' => 'role_id',
|
||||
'type' => 'dropdown',
|
||||
'label' => __('Role'),
|
||||
'options' => $dropdownData['role'],
|
||||
'default' => $defaultRole ?? null
|
||||
],
|
||||
[
|
||||
'field' => 'disabled',
|
||||
'type' => 'checkbox',
|
||||
'label' => 'Disable'
|
||||
],
|
||||
}
|
||||
if (!Configure::check('password_auth.enabled') || Configure::read('password_auth.enabled')) {
|
||||
$showPasswordField = true;
|
||||
}
|
||||
echo $this->element('genericElements/Form/genericForm', [
|
||||
'data' => [
|
||||
'description' => __('Roles define global rules for a set of users, including first and foremost access controls to certain functionalities.'),
|
||||
'model' => 'Roles',
|
||||
'fields' => [
|
||||
[
|
||||
'field' => 'individual_id',
|
||||
'type' => 'dropdown',
|
||||
'label' => __('Associated individual'),
|
||||
'options' => isset($dropdownData['individual']) ? $dropdownData['individual'] : [],
|
||||
'conditions' => $this->request->getParam('action') === 'add'
|
||||
],
|
||||
'submit' => [
|
||||
'action' => $this->request->getParam('action')
|
||||
]
|
||||
[
|
||||
'field' => 'individual.email',
|
||||
'stateDependence' => [
|
||||
'source' => '#individual_id-field',
|
||||
'option' => 'new'
|
||||
],
|
||||
'required' => false
|
||||
],
|
||||
[
|
||||
'field' => 'individual.first_name',
|
||||
'label' => 'First name',
|
||||
'stateDependence' => [
|
||||
'source' => '#individual_id-field',
|
||||
'option' => 'new'
|
||||
],
|
||||
'required' => false
|
||||
],
|
||||
[
|
||||
'field' => 'individual.last_name',
|
||||
'label' => 'Last name',
|
||||
'stateDependence' => [
|
||||
'source' => '#individual_id-field',
|
||||
'option' => 'new'
|
||||
],
|
||||
'required' => false
|
||||
],
|
||||
[
|
||||
'field' => 'username',
|
||||
'autocomplete' => 'off'
|
||||
],
|
||||
[
|
||||
'field' => 'organisation_id',
|
||||
'type' => 'dropdown',
|
||||
'label' => __('Associated organisation'),
|
||||
'options' => $dropdownData['organisation'],
|
||||
'default' => $loggedUser['organisation_id']
|
||||
],
|
||||
[
|
||||
'field' => 'password',
|
||||
'label' => __('Password'),
|
||||
'type' => 'password',
|
||||
'required' => $passwordRequired,
|
||||
'autocomplete' => 'new-password',
|
||||
'value' => '',
|
||||
'requirements' => $showPasswordField,
|
||||
],
|
||||
[
|
||||
'field' => 'confirm_password',
|
||||
'label' => __('Confirm Password'),
|
||||
'type' => 'password',
|
||||
'required' => $passwordRequired,
|
||||
'autocomplete' => 'off',
|
||||
'requirements' => $showPasswordField,
|
||||
],
|
||||
[
|
||||
'field' => 'role_id',
|
||||
'type' => 'dropdown',
|
||||
'label' => __('Role'),
|
||||
'options' => $dropdownData['role'],
|
||||
'default' => $defaultRole ?? null
|
||||
],
|
||||
[
|
||||
'field' => 'disabled',
|
||||
'type' => 'checkbox',
|
||||
'label' => 'Disable'
|
||||
],
|
||||
],
|
||||
'submit' => [
|
||||
'action' => $this->request->getParam('action')
|
||||
]
|
||||
]);
|
||||
]
|
||||
]);
|
||||
?>
|
||||
</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();
|
||||
$controlParams = [
|
||||
'type' => 'select',
|
||||
'options' => $fieldData['options'],
|
||||
'options' => $fieldData['options'] ?? [],
|
||||
'empty' => $fieldData['empty'] ?? false,
|
||||
'value' => $fieldData['value'] ?? null,
|
||||
'multiple' => $fieldData['multiple'] ?? false,
|
||||
|
@ -19,9 +19,13 @@ if (!empty($fieldData['label'])) {
|
|||
if ($controlParams['options'] instanceof \Cake\ORM\Query) {
|
||||
$controlParams['options'] = $controlParams['options']->all()->toList();
|
||||
}
|
||||
if (!empty($fieldData['select2'])) {
|
||||
$initSelect2 = false;
|
||||
if (isset($fieldData['select2']) && $fieldData['select2'] == true) {
|
||||
$initSelect2 = true;
|
||||
$fieldData['select2'] = $fieldData['select2'] === true ? [] : $fieldData['select2'];
|
||||
$controlParams['class'] .= ' select2-input';
|
||||
}
|
||||
$controlParams['class'] .= ' dropdown-custom-value' . "-$seed";
|
||||
if (in_array('_custom', array_keys($controlParams['options']))) {
|
||||
$customInputValue = $this->Form->getSourceValue($fieldData['field']);
|
||||
if (!in_array($customInputValue, $controlParams['options'])) {
|
||||
|
@ -34,7 +38,6 @@ if (in_array('_custom', array_keys($controlParams['options']))) {
|
|||
} else {
|
||||
$customInputValue = '';
|
||||
}
|
||||
$controlParams['class'] .= ' dropdown-custom-value' . "-$seed";
|
||||
$adaptedField = $fieldData['field'] . '_custom';
|
||||
$controlParams['templates']['formGroup'] = sprintf(
|
||||
'<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.parent().find('input.custom-value').attr('oninput', 'updateAssociatedSelect(this)')
|
||||
updateAssociatedSelect($select.parent().find('input.custom-value')[0])
|
||||
<?php if (!empty($fieldData['select2'])) : ?>
|
||||
let $container = $select.closest('.modal-dialog')
|
||||
<?php if ($initSelect2) : ?>
|
||||
// let $container = $select.closest('.modal-dialog .modal-body')
|
||||
let $container = []
|
||||
if ($container.length == 0) {
|
||||
$container = $(document.body)
|
||||
}
|
||||
$select.select2({
|
||||
const defaultSelect2Options = {
|
||||
dropdownParent: $container,
|
||||
})
|
||||
}
|
||||
const passedSelect2Options = <?= json_encode($fieldData['select2']) ?>;
|
||||
const select2Options = Object.assign({}, passedSelect2Options, defaultSelect2Options)
|
||||
$select.select2(select2Options)
|
||||
<?php endif; ?>
|
||||
})
|
||||
|
||||
|
|
|
@ -1,43 +1,43 @@
|
|||
<?php
|
||||
$random = Cake\Utility\Security::randomString(8);
|
||||
$params['div'] = false;
|
||||
$this->Form->setTemplates([
|
||||
'inputContainer' => '{{content}}',
|
||||
'inputContainerError' => '{{content}}',
|
||||
'formGroup' => '{{input}}',
|
||||
]);
|
||||
$label = $fieldData['label'];
|
||||
$formElement = $this->FormFieldMassage->prepareFormElement($this->Form, $params, $fieldData);
|
||||
$temp = sprintf(
|
||||
'<div class="row mb-3">
|
||||
<div class="col-sm-2 form-label">%s</div>
|
||||
<div class="col-sm-10">
|
||||
<div class="input-group">
|
||||
%s<span>%s</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>',
|
||||
h($label),
|
||||
$formElement,
|
||||
sprintf(
|
||||
'<span id="uuid-gen-%s" class="btn btn-secondary">%s</span>',
|
||||
$random,
|
||||
__('Generate')
|
||||
)
|
||||
);
|
||||
echo $temp;
|
||||
$random = Cake\Utility\Security::randomString(8);
|
||||
$params['div'] = false;
|
||||
|
||||
$genUUIDButton = $this->Bootstrap->button([
|
||||
'id' => "uuid-gen-{$random}",
|
||||
'variant' => 'secondary',
|
||||
'text' => __('Generate'),
|
||||
]);
|
||||
|
||||
$this->Form->setTemplates([
|
||||
'input' => sprintf('<div class="input-group">%s{{genUUIDButton}}</div>', $this->Form->getTemplates('input')),
|
||||
]);
|
||||
$params['templateVars'] = [
|
||||
'genUUIDButton' => $genUUIDButton,
|
||||
];
|
||||
|
||||
$formElement = $this->FormFieldMassage->prepareFormElement($this->Form, $params, $fieldData);
|
||||
echo $formElement;
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$('#uuid-gen-<?= h($random) ?>').on('click', function() {
|
||||
$.ajax({
|
||||
success:function (data, textStatus) {
|
||||
$('#uuid-field').val(data["uuid"]);
|
||||
},
|
||||
type: "get",
|
||||
cache: false,
|
||||
url: "/organisations/generateUUID",
|
||||
});
|
||||
});
|
||||
const $node = $('#uuid-gen-<?= h($random) ?>')
|
||||
$node.click(fetchUUID)
|
||||
|
||||
function fetchUUID() {
|
||||
const urlGet = '/organisations/generateUUID'
|
||||
const options = {
|
||||
statusNode: $node,
|
||||
}
|
||||
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']);
|
||||
}
|
||||
}
|
||||
if (!empty($fieldDesc[$fieldData['field']])) {
|
||||
$fieldData['label'] .= $this->element(
|
||||
$fieldDescription = $fieldData['tooltip'] ?? ($fieldDesc[$fieldData['field']] ?? false);
|
||||
if (!empty($fieldDescription)) {
|
||||
$fieldData['tooltip'] = $this->element(
|
||||
'genericElements/Form/formInfo', array(
|
||||
'field' => $fieldData,
|
||||
'fieldDesc' => $fieldDesc[$fieldData['field']],
|
||||
'fieldDesc' => $fieldDescription,
|
||||
'modelForForm' => $modelForForm
|
||||
)
|
||||
);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
$seed = mt_rand();
|
||||
if (!is_array($fieldDesc)) {
|
||||
$fieldDesc = array('info' => $fieldDesc);
|
||||
$default = 'info';
|
||||
|
@ -16,32 +17,46 @@
|
|||
$default = 'info';
|
||||
}
|
||||
}
|
||||
echo sprintf(
|
||||
'<span id = "%sInfoPopover" class="icon-info-sign" data-bs-toggle="popover" data-bs-trigger="hover"></span>',
|
||||
h($field['field'])
|
||||
);
|
||||
$popoverID = sprintf("%sInfoPopover%s", h($field['field']), $seed);
|
||||
echo $this->Bootstrap->icon('info-circle', [
|
||||
'id' => $popoverID,
|
||||
'class' => ['ms-1'],
|
||||
'attrs' => [
|
||||
'data-bs-toggle' => 'popover',
|
||||
'data-bs-trigger' => 'hover',
|
||||
]
|
||||
]);
|
||||
?>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
new bootstrap.Popover('#<?php echo h($field['field']); ?>InfoPopover', {
|
||||
new bootstrap.Popover('#<?= $popoverID ?>', {
|
||||
html: true,
|
||||
content: function() {
|
||||
var tempSelector = '#<?php echo h($modelForForm . \Cake\Utility\Inflector::camelize($field['field'])); ?>';
|
||||
if ($(tempSelector)[0].nodeName === "SELECT" && Object.keys(fieldDesc).length > 1) {
|
||||
return $('<div>').append(
|
||||
$('<span>').attr('class', 'blue bold').text($(tempSelector +" option:selected").text())
|
||||
).append(
|
||||
$('<span>').text(': ' + fieldDesc[$(tempSelector).val()])
|
||||
return $('<div>')
|
||||
.append(
|
||||
$('<span>')
|
||||
.attr('class', 'text-primary fw-bold')
|
||||
.text('<?php echo h(\Cake\Utility\Inflector::humanize($field['field'])); ?>')
|
||||
)
|
||||
.append(
|
||||
$('<span>').text(": <?= h($fieldDesc["info"]) ?>")
|
||||
);
|
||||
} 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 tempSelector = '#<?php echo h($modelForForm . \Cake\Utility\Inflector::camelize($field['field'])); ?>';
|
||||
// if ($(tempSelector)[0].nodeName === "SELECT" && Object.keys(fieldDesc).length > 1) {
|
||||
// return $('<div>').append(
|
||||
// $('<span>').attr('class', 'blue bold').text($(tempSelector +" option:selected").text())
|
||||
// ).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>
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
$entity = isset($entity) ? $entity : null;
|
||||
$fieldsString = '';
|
||||
$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'])) {
|
||||
$data['url'] = ["controller" => $this->request->getParam('controller'), "action" => $this->request->getParam('url')];
|
||||
|
@ -155,14 +155,5 @@
|
|||
$('.formDropdown').on('change', function() {
|
||||
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>
|
|
@ -4,7 +4,7 @@ use Cake\Utility\Inflector;
|
|||
|
||||
$default_template = [
|
||||
'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>',
|
||||
'error' => '<div class="error-message invalid-feedback d-block">{{content}}</div>',
|
||||
'errorList' => '<ul>{{content}}</ul>',
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
$tags = Cake\Utility\Hash::get($data, 'tags');
|
||||
echo $this->Tag->tags($tags, [
|
||||
'allTags' => $allTags,
|
||||
'picker' => true,
|
||||
'editable' => true,
|
||||
'picker' => !empty($field['editable']),
|
||||
'editable' => !empty($field['editable']),
|
||||
]);
|
||||
|
|
|
@ -28,9 +28,9 @@ foreach($data['MetaTemplates'] as $metaTemplate) {
|
|||
]
|
||||
]),
|
||||
'rawNoEscaping' => true,
|
||||
'warning' => $metaField->warning ?? null,
|
||||
'info' => $metaField->info ?? null,
|
||||
'danger' => $metaField->danger ?? null
|
||||
'notice_warning' => $metaTemplateField->warning ?? null,
|
||||
'notice_info' => $metaTemplateField->info ?? null,
|
||||
'notice_danger' => $metaTemplateField->danger ?? null
|
||||
];
|
||||
$labelPrintedOnce = true;
|
||||
}
|
||||
|
|
|
@ -81,6 +81,8 @@ if (!empty($breadcrumb)) {
|
|||
}
|
||||
if (!empty($actionEntry['isPOST'])) {
|
||||
$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 {
|
||||
$onclickFunction = sprintf('UI.overlayUntilResolve(this, UI.modalFromUrl(\'%s\'))', h(Router::url($actionEntry['url'])));
|
||||
}
|
||||
|
|
|
@ -47,19 +47,28 @@ $filteringForm = $this->Bootstrap->table(
|
|||
__('Value'),
|
||||
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'];
|
||||
$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([
|
||||
'formGroup' => '<div class="col-sm-10">{{input}}</div>',
|
||||
]);
|
||||
return $this->element('genericElements/Form/fieldScaffold', [
|
||||
'fieldData' => [
|
||||
'field' => $fieldName,
|
||||
'type' => $formType,
|
||||
'label' => '',
|
||||
'class' => 'fieldValue form-control-sm'
|
||||
],
|
||||
'fieldData' => $fieldData,
|
||||
'params' => []
|
||||
]);
|
||||
}
|
||||
|
@ -169,6 +178,36 @@ echo $this->Bootstrap->modal([
|
|||
}
|
||||
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')
|
||||
let passedTags = []
|
||||
tags.forEach(tagname => {
|
||||
|
@ -185,19 +224,6 @@ echo $this->Bootstrap->modal([
|
|||
.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) {
|
||||
const rowData = {};
|
||||
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/lint/lint') ?>
|
||||
<?= $this->Html->css('select2.min') ?>
|
||||
<?= $this->Html->css('select2-bootstrap5') ?>
|
||||
<?= $this->Html->css('select2-bootstrap5-vars') ?>
|
||||
<?= $this->Html->script('apexcharts.min') ?>
|
||||
<?= $this->Html->script('moment-with-locales.min') ?>
|
||||
<?= $this->Html->css('apexcharts') ?>
|
||||
|
|
|
@ -94,7 +94,7 @@ input[type="checkbox"]:disabled.change-cursor {
|
|||
}
|
||||
|
||||
.select2-container {
|
||||
z-index: 900;
|
||||
z-index: 1060;
|
||||
}
|
||||
|
||||
.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