new: [enumerations] added enumerations system

- for string entry fields, simply add lists of values to convert the text entry for values
- helps with maintaining accurate lists
- currently the fields that are valid targets are organisations.nationality, organisations.sector, organisations.type
pull/116/merge
iglocska 2023-05-26 16:13:52 +02:00
parent 011f7f452c
commit 52e8a5c6a6
No known key found for this signature in database
GPG Key ID: BEA224F1FEF113AC
14 changed files with 564 additions and 0 deletions

View File

@ -75,6 +75,17 @@ class ACLComponent extends Component
'delete' => ['*'],
'index' => ['*']
],
'Enumerations' => [
'delete' => ['perm_admin'],
'index' => ['*']
],
'EnumerationCollections' => [
'view' => ['*'],
'add' => ['perm_admin'],
'edit' => ['perm_admin'],
'delete' => ['perm_admin'],
'index' => ['*']
],
'Inbox' => [
'createEntry' => ['OR' => ['perm_admin', 'perm_sync']],
'delete' => ['perm_admin'],

View File

@ -399,6 +399,14 @@ class CRUDComponent extends Component
if (!empty($params['fields'])) {
$this->Controller->set('fields', $params['fields']);
}
$EnumerationCollections = TableRegistry::getTableLocator()->get('EnumerationCollections');
$modelAlias = $this->Table->getAlias();
if (in_array($this->Table->getAlias(), $EnumerationCollections->getValidModelList())) {
$enumerations = $EnumerationCollections->getFieldValues($modelAlias);
if (!empty($enumerations)) {
$this->Controller->set('enumerations', $enumerations);
}
}
$this->Controller->entity = $data;
$this->Controller->set('entity', $data);
}

View File

@ -128,6 +128,11 @@ class Sidemenu {
'url' => '/permissionLimitations/index',
'icon' => 'jedi',
],
'Enumerations' => [
'label' => __('Collections'),
'url' => '/enumerationCollections/index',
'icon' => 'list',
],
]
],
'API' => [

View File

@ -0,0 +1,98 @@
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Utility\Hash;
use Cake\Utility\Text;
use \Cake\Database\Expression\QueryExpression;
use Cake\Http\Exception\NotFoundException;
use Cake\Http\Exception\MethodNotAllowedException;
use Cake\Http\Exception\ForbiddenException;
class EnumerationCollectionsController extends AppController
{
public $filterFields = ['name', 'target_model', 'target_field'];
public $quickFilterFields = ['name', 'target_model', 'target_field'];
public $containFields = [];
public function index()
{
$this->CRUD->index([
'filters' => $this->filterFields,
'quickFilters' => $this->quickFilterFields,
'contain' => ['Enumerations'],
'afterFind' => function($data) {
$data->value_count = isset($data->enumerations) ? count($data->enumerations) : 0;
$data->values = Hash::extract($data, 'enumerations.{n}.value');
unset($data->enumerations);
return $data;
}
]);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$this->set('metaGroup', 'Enumerations');
}
public function add()
{
$this->CRUD->add([
'afterSave' => function($data) {
$this->EnumerationCollections->captureValues($data);
}
]);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$this->set(compact('enumerations'));
$this->set('metaGroup', 'Enumerations');
}
public function view($id)
{
$this->CRUD->view($id);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$this->set('metaGroup', 'Enumerations');
}
public function edit($id)
{
$this->CRUD->edit($id, [
'afterSave' => function($data) {
$this->EnumerationCollections->purgeValues($data);
$this->EnumerationCollections->captureValues($data);
},
'contain' => ['Enumerations'],
'afterFind' => function($data) {
$values = [];
foreach ($data['enumerations'] as $enumeration) {
$values[] = $enumeration['value'];
}
$data->values = implode(PHP_EOL, $values);
return $data;
}
]);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$this->set('metaGroup', 'Enumerations');
$this->render('add');
}
public function delete($id)
{
$this->CRUD->delete($id);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$this->set('metaGroup', 'Enumerations');
}
}

View File

@ -0,0 +1,40 @@
<?php
namespace App\Controller;
use App\Controller\AppController;
use Cake\Utility\Hash;
use Cake\Utility\Text;
use \Cake\Database\Expression\QueryExpression;
use Cake\Http\Exception\NotFoundException;
use Cake\Http\Exception\MethodNotAllowedException;
use Cake\Http\Exception\ForbiddenException;
class EnumerationsController extends AppController
{
public $filterFields = ['value'];
public $quickFilterFields = ['value'];
public $containFields = [];
public function index()
{
$this->CRUD->index([
'filters' => $this->filterFields,
'quickFilters' => $this->quickFilterFields
]);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$this->set('metaGroup', 'Enumerations');
}
public function delete($id)
{
$this->CRUD->delete($id);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$this->set('metaGroup', 'Enumerations');
}
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
use Cake\ORM\Entity;
class Enumeration extends AppModel
{
}

View File

@ -0,0 +1,11 @@
<?php
namespace App\Model\Entity;
use App\Model\Entity\AppModel;
use Cake\ORM\Entity;
class EnumerationCollection extends AppModel
{
}

View File

@ -0,0 +1,118 @@
<?php
namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\ORM\Table;
use Cake\Validation\Validator;;
use Cake\Datasource\EntityInterface;
use Cake\Event\Event;
use Cake\Event\EventInterface;
use Cake\Utility\Text;
use ArrayObject;
class EnumerationCollectionsTable extends AppTable
{
private $fieldMapping = [
'Organisations' => [
'country',
'sector',
'type'
]
];
public function initialize(array $config): void
{
parent::initialize($config);
$this->addBehavior('UUID');
$this->addBehavior('AuditLog');
$this->addBehavior('Timestamp');
$this->hasMany(
'Enumerations',
[
'dependent' => true
]
);
$this->setDisplayField('name');
}
public function beforeMarshal(EventInterface $event, ArrayObject $data, ArrayObject $options)
{
if (empty($data['uuid'])) {
$data['uuid'] = Text::uuid();
}
return true;
}
public function validationDefault(Validator $validator): Validator
{
$validator
->notEmptyString('name')
->requirePresence(['name'], 'create')
->notEmptyString('uuid')
->requirePresence(['uuid'], 'create')
->notEmptyString('target_model')
->requirePresence(['target_model'], 'create')
->notEmptyString('target_field')
->requirePresence(['target_field'], 'create');
return $validator;
}
public function getValidFieldList(?string $model = null): array
{
if (!empty($model)) {
if (empty($this->fieldMapping[$model])) {
return [];
} else {
return $this->fieldMapping[$model];
}
} else {
return $this->fieldMapping;
}
}
public function getValidModelList(?string $model = null): array
{
return array_keys($this->fieldMapping);
}
public function getFieldValues($model): array
{
$collections = $this->find('all')->where(['target_model' => $model, 'enabled' => 1, 'deleted' => 0])->contain(['Enumerations'])->disableHydration()->all()->toArray();
$options = [];
foreach ($collections as $collection) {
if (empty($collection['target_field'])) {
$options[$collection['target_field']] = [];
}
foreach ($collection['enumerations'] as $enumeration) {
$options[$collection['target_field']][$enumeration['value']] = $enumeration['value'];
}
}
return $options;
}
public function purgeValues(\App\Model\Entity\EnumerationCollection $entity): void
{
$this->Enumerations->deleteAll([
'enumeration_collection_id' => $entity->id
]);
}
public function captureValues(\App\Model\Entity\EnumerationCollection $entity): void
{
if (!empty($entity->values)) {
$values = $entity->values;
$collection_id = $entity->id;
if (!is_array($values)) {
$values = explode("\n", $values);
}
foreach ($values as $value) {
$enumeration = $this->Enumerations->newEntity([
'value' => trim($value),
'enumeration_collection_id' => $entity->id
]);
$this->Enumerations->save($enumeration);
}
}
}
}

View File

@ -0,0 +1,29 @@
<?php
namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\ORM\Table;
use Cake\Validation\Validator;
class EnumerationsTable extends AppTable
{
public function initialize(array $config): void
{
parent::initialize($config);
$this->belongsTo(
'EnumerationCollection'
);
$this->setDisplayField('value');
}
public function validationDefault(Validator $validator): Validator
{
$validator
->notEmptyString('value')
->requirePresence(['value'], 'create')
->notEmptyString('enumeration_collection_id')
->requirePresence(['enumeration_collection_id'], 'create');
return $validator;
}
}

View File

@ -0,0 +1,40 @@
<?php
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' => 'EnumerationCollections',
'fields' => [
[
'field' => 'name',
'label' => __('Name')
],
[
'field' => 'enabled',
'label' => __('Enabled'),
'type' => 'checkbox',
],
[
'field' => 'target_model',
'label' => __('Model'),
],
[
'field' => 'target_field',
'label' => __('Field'),
],
[
'field' => 'description',
'label' => __('Description'),
],
[
'field' => 'values',
'label' => __('Values'),
'type' => 'textarea'
],
],
'submit' => [
'action' => $this->request->getParam('action')
]
]
]);
?>
</div>

View File

@ -0,0 +1,96 @@
<?php
$topbarChildren = [];
if (!empty($loggedUser->role->perm_admin)) {
$topbarChildren[] = [
'type' => 'simple',
'children' => [
'data' => [
'type' => 'simple',
'text' => __('Add Enumeration Collection'),
'class' => 'btn btn-primary',
'popover_url' => '/enumerationCollections/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' => $topbarChildren,
],
'fields' => [
[
'name' => '#',
'sort' => 'id',
'data_path' => 'id',
],
[
'name' => __('Name'),
'sort' => 'name',
'data_path' => 'name',
],
[
'name' => __('Enabled'),
'sort' => 'enabled',
'data_path' => 'enabled',
],
[
'name' => __('UUID'),
'sort' => 'uuid',
'data_path' => 'uuid',
],
[
'name' => __('Model'),
'sort' => 'target_model',
'data_path' => 'target_model',
],
[
'name' => __('Field'),
'sort' => 'target_field',
'data_path' => 'target_field',
],
[
'name' => __('Values'),
'sort' => 'value_count',
'data_path' => 'value_count',
],
[
'name' => __('Description'),
'data_path' => 'description',
],
],
'title' => __('Enumeration Collections Index'),
'description' => __('A list collections that can be used to convert string input fields into selections wherever it makes sense.'),
'pull' => 'right',
'actions' => [
[
'url' => '/enumerationCollections/view',
'url_params_data_paths' => ['id'],
'icon' => 'eye'
],
[
'open_modal' => '/enumerationCollections/edit/[onclick_params_data_path]',
'modal_params_data_path' => 'id',
'icon' => 'edit',
'requirement' => !empty($loggedUser['role']['perm_admin'])
],
[
'open_modal' => '/enumerationCollections/delete/[onclick_params_data_path]',
'modal_params_data_path' => 'id',
'icon' => 'trash',
'requirement' => !empty($loggedUser['role']['perm_admin'])
],
]
]
]);
echo '</div>';
?>

View File

@ -0,0 +1,46 @@
<?php
echo $this->element(
'/genericElements/SingleViews/single_view',
[
'data' => $entity,
'fields' => [
[
'key' => __('ID'),
'path' => 'id'
],
[
'key' => __('Name'),
'path' => 'name'
],
[
'key' => __('Enabled'),
'path' => 'enabled',
'type' => 'boolean'
],
[
'key' => __('UUID'),
'path' => 'uuid'
],
[
'key' => __('Model'),
'path' => 'target_model'
],
[
'key' => __('Field'),
'path' => 'target_field'
],
[
'key' => __('Description'),
'path' => 'description'
],
],
'children' => [
[
'url' => '/Enumerations/index?EnumerationCollection.id={{0}}',
'url_params' => ['id'],
'title' => __('Values')
]
]
]
);

View File

@ -0,0 +1,43 @@
<?php
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' => __('Value'),
'sort' => 'value',
'data_path' => 'value',
]
],
'title' => __('Enumerations Index'),
'description' => null,
'pull' => 'right',
'actions' => [
[
'open_modal' => '/enumerations/delete/[onclick_params_data_path]',
'modal_params_data_path' => 'id',
'icon' => 'trash',
'requirement' => !empty($loggedUser['role']['perm_admin'])
],
]
]
]);
echo '</div>';
?>

View File

@ -26,6 +26,14 @@
}
$formRandomValue = Cake\Utility\Security::randomString(8);
$initSelect2 = false;
if (!empty($enumerations)) {
foreach ($data['fields'] as $k => $field) {
if (isset($enumerations[$field['field']])) {
$data['fields'][$k]['options'] = $enumerations[$field['field']];
}
}
}
$formCreate = $this->Form->create($entity, ['id' => 'form-' . $formRandomValue]);
$default_template = [
'inputContainer' => '<div class="row mb-3">{{content}}</div>',