From 9506142de57f30702e1a7065815f194c6769daf8 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Wed, 11 Jan 2023 09:56:39 +0100 Subject: [PATCH 01/14] fix: [broods:add] Correctly index organisations by their ID --- src/Controller/BroodsController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Controller/BroodsController.php b/src/Controller/BroodsController.php index a857d43..8a054a5 100644 --- a/src/Controller/BroodsController.php +++ b/src/Controller/BroodsController.php @@ -45,7 +45,7 @@ class BroodsController extends AppController $dropdownData = [ 'organisation' => $this->Organisations->find('list', [ 'sort' => ['name' => 'asc'] - ]) + ])->toArray() ]; $this->set(compact('dropdownData')); } @@ -72,7 +72,7 @@ class BroodsController extends AppController $dropdownData = [ 'organisation' => $this->Organisations->find('list', [ 'sort' => ['name' => 'asc'] - ]) + ])->toArray() ]; $this->set(compact('dropdownData')); $this->render('add'); From 715ba1d75b239e60a6bd37d39a079e90340799b2 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 17 Jan 2023 09:17:49 +0100 Subject: [PATCH 02/14] chg: [ui:generic_index] Only show enabled meta-templates in table options --- src/Controller/Component/CRUDComponent.php | 3 ++ src/View/Helper/BootstrapHelper.php | 6 +++ .../ListTopBar/group_table_action.php | 46 ++++++++++--------- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index 8d2f08f..fa98a42 100644 --- a/src/Controller/Component/CRUDComponent.php +++ b/src/Controller/Component/CRUDComponent.php @@ -142,6 +142,9 @@ class CRUDComponent extends Component $data[$i] = $this->attachMetaTemplatesIfNeeded($row, $metaTemplates); } $this->Controller->set('meta_templates', $metaTemplates); + $this->Controller->set('meta_templates_enabled', array_filter($metaTemplates, function($template) { + return $template['enabled']; + })); } if (true) { // check if stats are requested $modelStatistics = []; diff --git a/src/View/Helper/BootstrapHelper.php b/src/View/Helper/BootstrapHelper.php index 5b487c6..e452bb0 100644 --- a/src/View/Helper/BootstrapHelper.php +++ b/src/View/Helper/BootstrapHelper.php @@ -2008,6 +2008,12 @@ class BoostrapDropdownMenu extends BootstrapGeneric } $classes = ['dropdown-item']; + if (!empty($entry['class'])) { + if (!is_array($entry['class'])) { + $entry['class'] = [$entry['class']]; + } + $classes = array_merge($classes, $entry['class']); + } $params = ['href' => '#']; if (!empty($entry['menu'])) { diff --git a/templates/element/genericElements/ListTopBar/group_table_action.php b/templates/element/genericElements/ListTopBar/group_table_action.php index fe538b2..d239d82 100644 --- a/templates/element/genericElements/ListTopBar/group_table_action.php +++ b/templates/element/genericElements/ListTopBar/group_table_action.php @@ -19,27 +19,31 @@ $availableColumnsHtml = $this->element('/genericElements/ListTopBar/group_table_ $metaTemplateColumnMenu = []; if (!empty($meta_templates)) { $metaTemplateColumnMenu[] = ['header' => true, 'text' => __('Meta Templates'), 'icon' => 'object-group',]; - foreach ($meta_templates as $meta_template) { - $numberActiveMetaField = !empty($tableSettings['visible_meta_column'][$meta_template->id]) ? count($tableSettings['visible_meta_column'][$meta_template->id]) : 0; - $metaTemplateColumnMenu[] = [ - 'text' => $meta_template->name, - 'sup' => $meta_template->version, - 'badge' => [ - 'text' => $numberActiveMetaField, - 'variant' => 'secondary', - 'title' => __n('{0} meta-field active for this meta-template', '{0} meta-fields active for this meta-template', $numberActiveMetaField, $numberActiveMetaField), - ], - 'keepOpen' => true, - 'menu' => [ - [ - 'html' => $this->element('/genericElements/ListTopBar/group_table_action/hiddenMetaColumns', [ - 'tableSettings' => $tableSettings, - 'table_setting_id' => $data['table_setting_id'], - 'meta_template' => $meta_template, - ]) - ] - ], - ]; + if (empty($meta_templates_enabled)) { + $metaTemplateColumnMenu[] = ['header' => false, 'text' => __('- No enabled Meta Templates found -'), 'class' => ['disabled', 'muted']]; + } else { + foreach ($meta_templates_enabled as $meta_template) { + $numberActiveMetaField = !empty($tableSettings['visible_meta_column'][$meta_template->id]) ? count($tableSettings['visible_meta_column'][$meta_template->id]) : 0; + $metaTemplateColumnMenu[] = [ + 'text' => $meta_template->name, + 'sup' => $meta_template->version, + 'badge' => [ + 'text' => $numberActiveMetaField, + 'variant' => 'secondary', + 'title' => __n('{0} meta-field active for this meta-template', '{0} meta-fields active for this meta-template', $numberActiveMetaField, $numberActiveMetaField), + ], + 'keepOpen' => true, + 'menu' => [ + [ + 'html' => $this->element('/genericElements/ListTopBar/group_table_action/hiddenMetaColumns', [ + 'tableSettings' => $tableSettings, + 'table_setting_id' => $data['table_setting_id'], + 'meta_template' => $meta_template, + ]) + ] + ], + ]; + } } } $indexColumnMenu = array_merge( From 2a754160ef07bd82612b693b651fd11b95440d73 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 17 Jan 2023 09:19:40 +0100 Subject: [PATCH 03/14] chg: [ui:genericDropdownField] Allow using select2 on dropdown field --- .../genericElements/Form/Fields/dropdownField.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/templates/element/genericElements/Form/Fields/dropdownField.php b/templates/element/genericElements/Form/Fields/dropdownField.php index b71b15b..4db9707 100644 --- a/templates/element/genericElements/Form/Fields/dropdownField.php +++ b/templates/element/genericElements/Form/Fields/dropdownField.php @@ -19,6 +19,9 @@ if (!empty($fieldData['label'])) { if ($controlParams['options'] instanceof \Cake\ORM\Query) { $controlParams['options'] = $controlParams['options']->all()->toList(); } +if (!empty($fieldData['select2'])) { + $controlParams['class'] .= ' select2-input'; +} if (in_array('_custom', array_keys($controlParams['options']))) { $customInputValue = $this->Form->getSourceValue($fieldData['field']); if (!in_array($customInputValue, $controlParams['options'])) { @@ -49,6 +52,15 @@ echo $this->FormFieldMassage->prepareFormElement($this->Form, $controlParams, $f $select.attr('onclick', 'toggleFreetextSelectField(this)') $select.parent().find('input.custom-value').attr('oninput', 'updateAssociatedSelect(this)') updateAssociatedSelect($select.parent().find('input.custom-value')[0]) + + let $container = $select.closest('.modal-dialog') + if ($container.length == 0) { + $container = $(document.body) + } + $select.select2({ + dropdownParent: $container, + }) + }) })() From d9df6ed4c391ca509f5aecdbf962b338af00afdd Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 17 Jan 2023 09:23:24 +0100 Subject: [PATCH 04/14] fix: [permissionLimitations] Prevent error for org without users --- src/Model/Table/PermissionLimitationsTable.php | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Model/Table/PermissionLimitationsTable.php b/src/Model/Table/PermissionLimitationsTable.php index abb8543..6300823 100644 --- a/src/Model/Table/PermissionLimitationsTable.php +++ b/src/Model/Table/PermissionLimitationsTable.php @@ -63,12 +63,15 @@ class PermissionLimitationsTable extends AppTable ])->count(); } if (isset($data['global'])) { + $conditions = [ + 'scope' => 'user', + 'field' => $field, + ]; + if (!empty($ownOrgUserIds)) { + $conditions['parent_id IN'] = array_values($ownOrgUserIds); + } $limitations[$field]['organisation']['current'] = $MetaFields->find('all', [ - 'conditions' => [ - 'scope' => 'user', - 'field' => $field, - 'parent_id IN' => array_values($ownOrgUserIds) - ] + 'conditions' => $conditions, ])->count(); } } From 697c068ca683aa1aa1fa335bfeb122518eb30065 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 17 Jan 2023 09:24:00 +0100 Subject: [PATCH 05/14] chg: [alignments:add] Usage of select2 for org selector --- templates/Alignments/add.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/templates/Alignments/add.php b/templates/Alignments/add.php index 186e937..0663b6c 100644 --- a/templates/Alignments/add.php +++ b/templates/Alignments/add.php @@ -10,7 +10,8 @@ echo $this->element('genericElements/Form/genericForm', array( array( 'field' => ($scope === 'individuals' ? 'organisation_id' : 'individual_id'), 'options' => ($scope === 'individuals' ? $organisations : $individuals), - 'type' => 'select' + 'type' => 'dropdown', + 'select2' => true, ), array( 'field' => 'type' From 025dcd9054ddb5c1628fec470d5e80bd44025187 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 17 Jan 2023 09:24:36 +0100 Subject: [PATCH 06/14] chg: [alignment] Added validation rule to avoid duplicates --- src/Model/Table/AlignmentsTable.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/Model/Table/AlignmentsTable.php b/src/Model/Table/AlignmentsTable.php index 3cbf5c5..975663a 100644 --- a/src/Model/Table/AlignmentsTable.php +++ b/src/Model/Table/AlignmentsTable.php @@ -4,6 +4,7 @@ namespace App\Model\Table; use App\Model\Table\AppTable; use Cake\ORM\Table; +use Cake\ORM\RulesChecker; use Cake\Validation\Validator; class AlignmentsTable extends AppTable @@ -24,7 +25,16 @@ class AlignmentsTable extends AppTable ->notEmptyString('organisation_id') ->requirePresence(['individual_id', 'organisation_id'], 'create'); return $validator; - } + } + + public function buildRules(RulesChecker $rules): RulesChecker + { + $rules->add($rules->isUnique( + ['individual_id', 'organisation_id', 'type'], + __('This alignment already exists.') + )); + return $rules; + } public function setAlignment($organisation_id, $individual_id, $type): void { From fc31ce8b08a46c160de6d53b442e788760b59ba5 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 17 Jan 2023 09:25:11 +0100 Subject: [PATCH 07/14] fix: [alignments:add] Index individuals and orgs by ID instead of array index --- src/Controller/AlignmentsController.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Controller/AlignmentsController.php b/src/Controller/AlignmentsController.php index dc92f0c..6f48af5 100644 --- a/src/Controller/AlignmentsController.php +++ b/src/Controller/AlignmentsController.php @@ -105,7 +105,7 @@ class AlignmentsController extends AppController } } if ($scope === 'organisations') { - $individuals = $this->Individuals->find('list', ['valueField' => 'email']); + $individuals = $this->Individuals->find('list', ['valueField' => 'email'])->toArray(); $this->set('individuals', $individuals); $organisation = $this->Organisations->find()->where(['id' => $source_id])->first(); if (empty($organisation)) { @@ -113,7 +113,7 @@ class AlignmentsController extends AppController } $this->set(compact('organisation')); } else { - $organisations = $this->Organisations->find('list', ['valueField' => 'name']); + $organisations = $this->Organisations->find('list', ['valueField' => 'name'])->toArray(); $this->set('organisations', $organisations); $individual = $this->Individuals->find()->where(['id' => $source_id])->first(); if (empty($individual)) { From 9ae446bb5d5475e7976486f0d7eb13b91af1b3e8 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 17 Jan 2023 09:25:43 +0100 Subject: [PATCH 08/14] fix: [behavior:authKeycloak] Correctly URLEncode username --- src/Model/Behavior/AuthKeycloakBehavior.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Model/Behavior/AuthKeycloakBehavior.php b/src/Model/Behavior/AuthKeycloakBehavior.php index 04d52c2..86c38c7 100644 --- a/src/Model/Behavior/AuthKeycloakBehavior.php +++ b/src/Model/Behavior/AuthKeycloakBehavior.php @@ -98,7 +98,7 @@ class AuthKeycloakBehavior extends Behavior public function getUserIdByUsername(string $username) { $response = $this->restApiRequest( - '%s/admin/realms/%s/users/?username=' . urlencode($username), + '%s/admin/realms/%s/users/?username=' . $this->urlencodeEscapeForSprintf($username), [], 'GET' ); From cd9bb838f66fbc8e9540320f0943baed4ac9d0b0 Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 17 Jan 2023 09:26:47 +0100 Subject: [PATCH 09/14] fix: [command:fieldSquasher] Allow persistence of changes --- src/Command/FieldSquasherCommand.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/Command/FieldSquasherCommand.php b/src/Command/FieldSquasherCommand.php index 6a5af22..4cf72ac 100644 --- a/src/Command/FieldSquasherCommand.php +++ b/src/Command/FieldSquasherCommand.php @@ -87,11 +87,10 @@ class FieldSquasherCommand extends Command if ($selection == 'Y') { $this->saveDataOnDisk($filename, $candidateResult['candidates']); } - die(1); - + $entities = $candidateResult['candidates']; $selection = $io->askChoice('A sample of the data you about to be saved is provided above. Would you like to proceed?', ['Y', 'N'], 'N'); if ($selection == 'Y') { - // $this->saveData($this->{$table}, $entities); + $this->saveData($this->{$table}, $entities); } } From a211aea267b288310d839c812235a009cd33aabc Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 17 Jan 2023 09:27:31 +0100 Subject: [PATCH 10/14] new: [command:metaTemaplate] Added meta-template shell to create and enabled template by UUID --- src/Command/MetaTemplateCommand.php | 48 +++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 src/Command/MetaTemplateCommand.php diff --git a/src/Command/MetaTemplateCommand.php b/src/Command/MetaTemplateCommand.php new file mode 100644 index 0000000..e25538b --- /dev/null +++ b/src/Command/MetaTemplateCommand.php @@ -0,0 +1,48 @@ +setDescription('Load and enable the provided meta-template'); + $parser->addArgument('uuid', [ + 'help' => 'The UUID of the meta-template to load and enable', + 'required' => true + ]); + return $parser; + } + + public function execute(Arguments $args, ConsoleIo $io) + { + $this->io = $io; + $template_uuid = $args->getArgument('uuid'); + $metaTemplateTable = $this->modelClass; + $this->loadModel($metaTemplateTable); + $result = $this->MetaTemplates->createNewTemplate($template_uuid); + if (empty($result['success'])) { + $this->io->error(__('Could not create meta-template')); + $this->io->error(json_encode($result)); + die(1); + } + $template = $this->MetaTemplates->find()->where(['uuid' => $template_uuid])->first(); + if (!empty($template)) { + $template->enabled = true; + $success = $this->MetaTemplates->save($template); + if (!empty($success)) { + $this->io->success(__('Meta-template loaded and enabled')); + } + } + } +} From 433d18c176e63a3cbd36483699f1e1be0d0c31ea Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 17 Jan 2023 09:28:27 +0100 Subject: [PATCH 11/14] fix: [command:importer] FIxed several bug preventing the save to happen --- src/Command/ImporterCommand.php | 34 ++++++++++++++++++--------------- 1 file changed, 19 insertions(+), 15 deletions(-) diff --git a/src/Command/ImporterCommand.php b/src/Command/ImporterCommand.php index 2e0c3f8..3aba536 100644 --- a/src/Command/ImporterCommand.php +++ b/src/Command/ImporterCommand.php @@ -190,7 +190,7 @@ class ImporterCommand extends Command if (is_null($metaEntity)) { $metaEntity = $this->MetaFields->newEmptyEntity(); $metaEntity->field = $fieldName; - $metaEntity->scope = $table->metaFields; + $metaEntity->scope = $table->getBehavior('MetaFields')->getScope(); $metaEntity->meta_template_id = $metaTemplate->id; if (isset($metaTemplateFieldsMapping[$fieldName])) { // a meta field template must exists $metaEntity->meta_template_field_id = $metaTemplateFieldsMapping[$fieldName]; @@ -248,6 +248,7 @@ class ImporterCommand extends Command { foreach ($entity->metaFields as $i => $metaEntity) { $metaEntity->parent_id = $entity->id; + $metaEntity->setNew(true); if ($metaEntity->hasErrors() || is_null($metaEntity->value)) { $this->io->error(json_encode(['entity' => $metaEntity, 'errors' => $metaEntity->getErrors()], JSON_PRETTY_PRINT)); unset($entity->metaFields[$i]); @@ -289,35 +290,35 @@ class ImporterCommand extends Command $values = array_map("self::{$fieldConfig['massage']}", $values); } if (isset($defaultFields[$key])) { - $data[$key] = $values; + $data[$key] = array_map('trim', $values); } else { - $data['metaFields'][$key] = $values; + $data['metaFields'][$key] = array_map('trim', $values); } } return $this->invertArray($data); } private function extractDataFromCSV($defaultFields, $config, $source) + { + $csvData = $this->csvToAssociativeArray($source); + return $this->extractDataFromJSON($defaultFields, $config, $csvData); + } + + private function csvToAssociativeArray($source): array { $rows = array_map('str_getcsv', explode(PHP_EOL, $source)); if (count($rows[0]) != count($rows[1])) { $this->io->error('Error while parsing source data. CSV doesn\'t have the same number of columns'); die(1); } - $header = array_shift($rows); - $data = array(); - foreach($rows as $row) { - $dataRow = []; - foreach ($header as $i => $headerField) { - if (isset($defaultFields[$headerField])) { - $dataRow[$headerField] = $row[$i]; - } else { - $dataRow['metaFields'][$headerField] = $row[$i]; - } + $csvData = []; + $headers = array_shift($rows); + foreach ($rows as $row) { + if (count($headers) == count($row)) { + $csvData[] = array_combine($headers, $row); } - $data[] = $dataRow; } - return $data; + return $csvData; } private function lockAccess(&$entity) @@ -477,6 +478,9 @@ class ImporterCommand extends Command foreach ($entities as $entity) { $row = []; foreach ($tableHeader as $key) { + if (in_array($key, $entity->getVirtual())) { + continue; + } $subKeys = explode('.', $key); if (in_array('metaFields', $subKeys)) { $found = false; From f36f33e7cf91a4f3e2a348b053e6639f916a2e5c Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Tue, 17 Jan 2023 09:29:59 +0100 Subject: [PATCH 12/14] new: [command:fastUserEnrolment] Added script to create alignment and enroll users from a provided CSV - The individual and Organisation must already exist in Cerebrate. They can be created using the ImporterCommand script --- src/Command/FastUserEnrolmentCommand.php | 284 +++++++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 src/Command/FastUserEnrolmentCommand.php diff --git a/src/Command/FastUserEnrolmentCommand.php b/src/Command/FastUserEnrolmentCommand.php new file mode 100644 index 0000000..90df05b --- /dev/null +++ b/src/Command/FastUserEnrolmentCommand.php @@ -0,0 +1,284 @@ +setDescription('Create alignements (and optionally enroll users) based on the provided CSV file.'); + $parser->addArgument('path', [ + 'help' => 'A path to the source file that should be used to create the alignments.', + 'required' => true + ]); + $parser->addOption('alignment_type', [ + 'short' => 't', + 'help' => 'The alignment type to use', + 'default' => 'member', + ]); + $parser->addOption('individual_email_column', [ + 'short' => 'e', + 'help' => 'The name of the column to find the individual email address', + 'default' => 'Email', + ]); + $parser->addOption('organisation_name_column', [ + 'short' => 'o', + 'help' => 'The name of the column to find the organisation name', + 'default' => 'TeamName', + ]); + $parser->addOption('create_user', [ + 'short' => 'c', + 'help' => 'Should the user be created', + 'boolean' => true, + 'default' => false, + ]); + $parser->addOption('role_id', [ + 'help' => 'The role to assign to the user', + ]); + $parser->addOption('yes', [ + 'short' => 'y', + 'help' => 'Automatically assume yes to any prompts', + 'default' => false, + 'boolean' => true + ]); + return $parser; + } + + public function execute(Arguments $args, ConsoleIo $io) + { + $this->io = $io; + $path = $args->getArgument('path'); + $this->alignment_type = $args->getOption('alignment_type'); + $this->individual_email_column = $args->getOption('individual_email_column'); + $this->organisation_name_column = $args->getOption('organisation_name_column'); + $this->create_user = $args->getOption('create_user'); + $this->role_id = $args->getArgument('role_id'); + $this->autoYes = $args->getOption('yes'); + + $alignmentTable = $this->modelClass; + $this->loadModel($alignmentTable); + $data = $this->getDataFromFile($path); + $updatedData = $this->updateBeforeSave($data); + $alignmentEntities = $this->marshalData($this->{$alignmentTable}, $updatedData); + $alignmentEntitiesSample = array_slice($alignmentEntities, 0, min(5, count($alignmentEntities))); + $ioTable = $this->transformEntitiesIntoTable($alignmentEntitiesSample); + + if ($this->autoYes) { + $this->saveAligmentData($this->{$alignmentTable}, $alignmentEntities); + } else { + $io->helper('Table')->output($ioTable); + $selection = $io->askChoice('A sample of the data you about to be saved is provided above. Would you like to proceed?', ['Y', 'N'], 'N'); + if ($selection == 'Y') { + $this->saveAligmentData($this->{$alignmentTabble}, $alignmentEntities); + } + } + + if ($this->create_user) { + $this->loadModel('Users'); + if (is_null($this->role_id)) { + $defaultRole = $this->Users->Roles->find()->select(['id'])->where(['is_default' => true])->first()->toArray(); + if (empty($defaultRole)) { + $this->io->error(__('No default role available. Create a defaul role or provide the role ID to be assigned.')); + die(1); + } + if (!empty($defaultRole['perm_admin'])) { + $selection = $io->askChoice('The default role has the `admin` permission. Confirm giving the admin permission to users to be enrolled.', ['Y', 'N'], 'N'); + if ($selection != 'Y') { + die(1); + } + } + $this->role_id = $defaultRole['id']; + } else { + $role = $this->Users->Roles->find()->select(['id'])->where(['id' => $this->role_id])->first(); + if (empty($role)) { + $this->io->error(__('Provided role ID does not exist')); + die(1); + } + } + $userEntities = $this->createEntitiesForUsers($alignmentEntities); + if ($this->autoYes) { + $this->enrolUsers($userEntities); + } else { + $userEntitiesSample = array_slice($userEntities, 0, min(5, count($userEntities))); + $ioTable = $this->transformEntitiesIntoTable($userEntitiesSample); + $io->helper('Table')->output($ioTable); + $selection = $io->askChoice('A sample of the data you about to be saved is provided above. Would you like to proceed?', ['Y', 'N'], 'N'); + if ($selection == 'Y') { + $this->enrolUsers($userEntities); + } + } + } + } + + private function saveAligmentData($alignmentTable, $entities) + { + $this->io->verbose('Saving data'); + $saveResult = $alignmentTable->saveMany($entities); + if ($saveResult === false) { + $errors = []; + $errorCount = 0; + foreach ($entities as $entity) { + $errorCount += 1; + $errors[json_encode($entity->getErrors())] = true; + } + $this->io->error(__('{0} Errors while saving data', $errorCount)); + $this->io->error(json_encode(array_keys($errors))); + $this->io->success(__('Saved {0} aligments', count($entities) - $errorCount)); + } + } + + private function enrolUsers($entities) + { + $this->io->verbose('Saving data'); + $errors = []; + $errorCount = 0; + foreach ($entities as $entity) { + $succes = $this->Users->save($entity); + if (empty($succes)) { + $errorCount += 1; + $errors[json_encode($entity->getErrors())] = true; + } else { + if (Configure::read('keycloak.enabled')) { + $this->Users->enrollUserRouter($succes); + } + } + } + if (!empty($errors)) { + $this->io->error(__('{0} Errors while saving data', $errorCount)); + $this->io->error(json_encode(array_keys($errors))); + } + $this->io->success(__('Enrolled {0} users', count($entities) - $errorCount)); + } + + private function createEntitiesForUsers($alignmentEntities) + { + $entities = []; + foreach ($alignmentEntities as $alignmentEntity) { + if ($alignmentEntity->individual_email != 'Lionel.Ferette@cert.be') { + continue; + } + $entity = $this->Users->newEntity([ + 'individual_id' => $alignmentEntity->individual_id, + 'organisation_id' => $alignmentEntity->organisation_id, + 'username' => $alignmentEntity->individual_email, + 'password' => Security::randomString(20), + 'role_id' => $this->role_id, + ]); + $entities[] = $entity; + } + return $entities; + } + + private function marshalData($alignmentTable, $data) + { + $entities = $alignmentTable->newEntities($data, [ + 'accessibleFields' => ($alignmentTable->newEmptyEntity())->getAccessibleFieldForNew() + ]); + return $entities; + } + + private function updateBeforeSave($data) + { + $this->loadModel('Individuals'); + $this->loadModel('Organisations'); + $updatedData = []; + foreach ($data as $entry) { + $new = [ + 'individual_id' => $this->getIndividualByEmail($entry[$this->individual_email_column]), + 'organisation_id' => $this->getOrganisationsByName($entry[$this->organisation_name_column]), + 'type' => $this->alignment_type, + 'individual_email' => $entry[$this->individual_email_column], + ]; + if (empty($new['organisation_id'])) { + $this->io->error("Error while parsing source data. Could not find organisation with name: " . $entry[$this->organisation_name_column]); + die(1); + } + if (empty($new['individual_id'])) { + $this->io->error("Error while parsing source data. Could not find individuals with email: " . $entry[$this->individual_email_column]); + die(1); + } + $new['individual_id'] = $new['individual_id']['id']; + $new['organisation_id'] = $new['organisation_id']['id']; + $updatedData[] = $new; + } + return $updatedData; + } + + private function getIndividualByEmail($email) + { + return $this->Individuals->find()->where([ + 'email' => $email, + ])->first()->toArray(); + } + + private function getOrganisationsByName($name) + { + return $this->Organisations->find()->where([ + 'name' => $name, + ])->first()->toArray(); + } + + private function getDataFromFile($path) + { + $file = new File($path); + if ($file->exists()) { + $this->io->verbose('Reading file'); + $text = $file->read(); + $file->close(); + if (!empty($text)) { + $rows = array_map('str_getcsv', explode(PHP_EOL, $text)); + if (count($rows[0]) != count($rows[1])) { + $this->io->error('Error while parsing source data. CSV doesn\'t have the same number of columns'); + die(1); + } + $csvData = []; + $headers = array_shift($rows); + foreach ($rows as $row) { + if (count($headers) == count($row)) { + $csvData[] = array_combine($headers, $row); + } + } + return $csvData; + } + } + return false; + } + + private function transformEntitiesIntoTable($entities, $header = []) + { + $table = [[]]; + if (!empty($entities)) { + $tableHeader = empty($header) ? array_keys(Hash::flatten($entities[0]->toArray())) : $header; + $tableContent = []; + foreach ($entities as $entity) { + $row = []; + foreach ($tableHeader as $key) { + if (in_array($key, $entity->getVirtual())) { + continue; + } + $row[] = (string) $entity[$key]; + } + $tableContent[] = $row; + } + $table = array_merge([$tableHeader], $tableContent); + } + return $table; + } +} \ No newline at end of file From ee6913d7f0238f94819534eb21d0428ac124f38a Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Wed, 18 Jan 2023 10:00:55 +0100 Subject: [PATCH 13/14] fix: [command:fastUserEnrolment] Better parsing of options and removed test instructions --- src/Command/FastUserEnrolmentCommand.php | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/Command/FastUserEnrolmentCommand.php b/src/Command/FastUserEnrolmentCommand.php index 90df05b..5385c31 100644 --- a/src/Command/FastUserEnrolmentCommand.php +++ b/src/Command/FastUserEnrolmentCommand.php @@ -34,7 +34,7 @@ class FastUserEnrolmentCommand extends Command 'default' => 'member', ]); $parser->addOption('individual_email_column', [ - 'short' => 'e', + 'short' => 'i', 'help' => 'The name of the column to find the individual email address', 'default' => 'Email', ]); @@ -50,6 +50,7 @@ class FastUserEnrolmentCommand extends Command 'default' => false, ]); $parser->addOption('role_id', [ + 'short' => 'r', 'help' => 'The role to assign to the user', ]); $parser->addOption('yes', [ @@ -69,7 +70,7 @@ class FastUserEnrolmentCommand extends Command $this->individual_email_column = $args->getOption('individual_email_column'); $this->organisation_name_column = $args->getOption('organisation_name_column'); $this->create_user = $args->getOption('create_user'); - $this->role_id = $args->getArgument('role_id'); + $this->role_id = $args->getOption('role_id'); $this->autoYes = $args->getOption('yes'); $alignmentTable = $this->modelClass; @@ -93,11 +94,12 @@ class FastUserEnrolmentCommand extends Command if ($this->create_user) { $this->loadModel('Users'); if (is_null($this->role_id)) { - $defaultRole = $this->Users->Roles->find()->select(['id'])->where(['is_default' => true])->first()->toArray(); + $defaultRole = $this->Users->Roles->find()->select(['id'])->where(['is_default' => true])->first(); if (empty($defaultRole)) { - $this->io->error(__('No default role available. Create a defaul role or provide the role ID to be assigned.')); + $this->io->error(__('No default role available. Create a default role or provide the role ID to be assigned.')); die(1); } + $defaultRole = $defaultRole->toArray(); if (!empty($defaultRole['perm_admin'])) { $selection = $io->askChoice('The default role has the `admin` permission. Confirm giving the admin permission to users to be enrolled.', ['Y', 'N'], 'N'); if ($selection != 'Y') { @@ -171,9 +173,6 @@ class FastUserEnrolmentCommand extends Command { $entities = []; foreach ($alignmentEntities as $alignmentEntity) { - if ($alignmentEntity->individual_email != 'Lionel.Ferette@cert.be') { - continue; - } $entity = $this->Users->newEntity([ 'individual_id' => $alignmentEntity->individual_id, 'organisation_id' => $alignmentEntity->organisation_id, From 5092f3d4f3973de25e61b28ac51384f09c571493 Mon Sep 17 00:00:00 2001 From: iglocska Date: Wed, 18 Jan 2023 10:10:00 +0100 Subject: [PATCH 14/14] chg: [version] bump --- src/VERSION.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/VERSION.json b/src/VERSION.json index 0c47f31..572ef01 100644 --- a/src/VERSION.json +++ b/src/VERSION.json @@ -1,4 +1,4 @@ { - "version": "1.10", + "version": "1.11", "application": "Cerebrate" }