From 819d96e805121b1d1a23fb92f7a59b7a6536657e Mon Sep 17 00:00:00 2001 From: Sami Mokaddem Date: Wed, 1 Dec 2021 11:01:31 +0100 Subject: [PATCH] new: [metaTemplate] Interface and functions to update meta-templates - WiP Actual update not implemented yet. --- .../Component/Navigation/MetaTemplates.php | 56 +++- src/Controller/MetaTemplatesController.php | 38 ++- src/Model/Entity/MetaTemplateField.php | 11 + src/Model/Table/MetaTemplatesTable.php | 276 +++++++++++++++++- templates/MetaTemplates/index.php | 39 ++- templates/MetaTemplates/update.php | 110 +++++++ templates/MetaTemplates/update_all.php | 108 +++++++ .../MetaTemplates/conflictResolution.php | 28 ++ .../element/MetaTemplates/conflictTable.php | 29 ++ .../IndexTable/Fields/update_status.php | 9 + 10 files changed, 670 insertions(+), 34 deletions(-) create mode 100644 src/Model/Entity/MetaTemplateField.php create mode 100644 templates/MetaTemplates/update.php create mode 100644 templates/MetaTemplates/update_all.php create mode 100644 templates/element/MetaTemplates/conflictResolution.php create mode 100644 templates/element/MetaTemplates/conflictTable.php create mode 100644 templates/element/genericElements/IndexTable/Fields/update_status.php diff --git a/src/Controller/Component/Navigation/MetaTemplates.php b/src/Controller/Component/Navigation/MetaTemplates.php index 35ea221..e088cc2 100644 --- a/src/Controller/Component/Navigation/MetaTemplates.php +++ b/src/Controller/Component/Navigation/MetaTemplates.php @@ -12,26 +12,42 @@ class MetaTemplatesNavigation extends BaseNavigation $this->bcf->addRoute('MetaTemplates', 'view', $this->bcf->defaultCRUD('MetaTemplates', 'view')); $this->bcf->addRoute('MetaTemplates', 'enable', [ 'label' => __('Enable'), - 'icon' => 'check', + 'icon' => 'check-square', 'url' => '/metaTemplates/enable/{{id}}/enabled', 'url_vars' => ['id' => 'id'], ]); $this->bcf->addRoute('MetaTemplates', 'set_default', [ 'label' => __('Set as default'), - 'icon' => 'check', + 'icon' => 'check-square', 'url' => '/metaTemplates/toggle/{{id}}/default', 'url_vars' => ['id' => 'id'], ]); - $this->bcf->addRoute('MetaTemplates', 'update', [ + + $totalUpdateCount = 0; + if (!empty($this->viewVars['updateableTemplate']['updateable']) && !empty($this->viewVars['updateableTemplate']['new'])) { + $udpateCount = count($this->viewVars['updateableTemplate']['updateable']) ?? 0; + $newCount = count($this->viewVars['updateableTemplate']['new']) ?? 0; + $totalUpdateCount = $udpateCount + $newCount; + } + $updateRouteConfig = [ 'label' => __('Update all templates'), 'icon' => 'download', 'url' => '/metaTemplates/update', - ]); + ]; + if ($totalUpdateCount > 0) { + $updateRouteConfig['badge'] = [ + 'text' => h($totalUpdateCount), + 'variant' => 'warning', + 'title' => __('There are {0} new meta-template(s) and {1} update(s) available', h($newCount), h($udpateCount)), + ]; + } + $this->bcf->addRoute('MetaTemplates', 'update', $updateRouteConfig); } public function addParents() { $this->bcf->addParent('MetaTemplates', 'view', 'MetaTemplates', 'index'); + $this->bcf->addParent('MetaTemplates', 'update', 'MetaTemplates', 'index'); } public function addLinks() @@ -41,12 +57,38 @@ class MetaTemplatesNavigation extends BaseNavigation public function addActions() { - $this->bcf->addAction('MetaTemplates', 'index', 'MetaTemplates', 'update'); - $this->bcf->addAction('MetaTemplates', 'view', 'MetaTemplates', 'update', [ + $totalUpdateCount = 0; + if (!empty($this->viewVars['updateableTemplate']['not_up_to_date']) && !empty($this->viewVars['updateableTemplate']['new'])) { + $udpateCount = count($this->viewVars['updateableTemplate']['not_up_to_date']) ?? 0; + $newCount = count($this->viewVars['updateableTemplate']['new']) ?? 0; + $totalUpdateCount = $udpateCount + $newCount; + } + $updateActionConfig = [ 'label' => __('Update template'), 'url' => '/metaTemplates/update/{{id}}', 'url_vars' => ['id' => 'id'], - ]); + ]; + if ($totalUpdateCount > 0) { + $updateActionConfig['badge'] = [ + 'text' => h($totalUpdateCount), + 'variant' => 'warning', + 'title' => __('There are {0} new meta-template(s) and {1} update(s) available', h($newCount), h($udpateCount)), + ]; + } + $this->bcf->addAction('MetaTemplates', 'index', 'MetaTemplates', 'update', $updateActionConfig); + + if (empty($this->viewVars['updateableTemplate']['up-to-date'])) { + $this->bcf->addAction('MetaTemplates', 'view', 'MetaTemplates', 'update', [ + 'label' => __('Update template'), + 'url' => '/metaTemplates/update/{{id}}', + 'url_vars' => ['id' => 'id'], + 'variant' => 'warning', + 'badge' => [ + 'variant' => 'warning', + 'title' => __('Update available') + ] + ]); + } $this->bcf->addAction('MetaTemplates', 'view', 'MetaTemplates', 'enable'); $this->bcf->addAction('MetaTemplates', 'view', 'MetaTemplates', 'set_default'); } diff --git a/src/Controller/MetaTemplatesController.php b/src/Controller/MetaTemplatesController.php index 78bedaf..42348e0 100644 --- a/src/Controller/MetaTemplatesController.php +++ b/src/Controller/MetaTemplatesController.php @@ -37,21 +37,22 @@ class MetaTemplatesController extends AppController } else { if (!$this->ParamHandler->isRest()) { if (!empty($template_id)) { - $this->set('title', __('Update Meta Templates #{0}', h($template_id))); - $this->set('question', __('Are you sure you wish to update the Meta Template definitions of the template `{0}`?', h($metaTemplate->name))); + $this->set('metaTemplate', $metaTemplate); + $this->setUpdateStatus($metaTemplate->id); } else { $this->set('title', __('Update Meta Templates')); $this->set('question', __('Are you sure you wish to update the Meta Template definitions')); + $updateableTemplates = $this->MetaTemplates->checkForUpdates(); + $this->set('updateableTemplates', $updateableTemplates); + $this->render('updateAll'); } - $this->set('actionName', __('Update')); - $this->set('path', ['controller' => 'metaTemplates', 'action' => 'update']); - $this->render('/genericTemplates/confirm'); } } } public function index() { + $updateableTemplate = $this->MetaTemplates->checkForUpdates(); $this->CRUD->index([ 'filters' => $this->filterFields, 'quickFilters' => $this->quickFilterFields, @@ -64,15 +65,27 @@ class MetaTemplatesController extends AppController ], ] ], - 'contain' => $this->containFields + 'contain' => $this->containFields, + 'afterFind' => function($data) use ($updateableTemplate) { + foreach ($data as $i => $metaTemplate) { + if (!empty($updateableTemplate[$metaTemplate->uuid])) { + $metaTemplate->set('status', $this->MetaTemplates->getTemplateStatus($updateableTemplate[$metaTemplate->uuid])); + } + } + return $data; + } ]); $responsePayload = $this->CRUD->getResponsePayload(); if (!empty($responsePayload)) { return $responsePayload; } + $updateableTemplate = [ + 'not_up_to_date' => $this->MetaTemplates->getNotUpToDateTemplates(), + 'new' => $this->MetaTemplates->getNewTemplates(), + ]; $this->set('defaultTemplatePerScope', $this->MetaTemplates->getDefaultTemplatePerScope()); $this->set('alignmentScope', 'individuals'); - $this->set('metaGroup', 'Administration'); + $this->set('updateableTemplate', $updateableTemplate); } public function view($id) @@ -84,7 +97,7 @@ class MetaTemplatesController extends AppController if (!empty($responsePayload)) { return $responsePayload; } - $this->set('metaGroup', 'Administration'); + $this->setUpdateStatus($id); } public function toggle($id, $fieldName = 'enabled') @@ -101,4 +114,13 @@ class MetaTemplatesController extends AppController return $responsePayload; } } + + public function setUpdateStatus($id) + { + $metaTemplate = $this->MetaTemplates->get($id); + $templateOnDisk = $this->MetaTemplates->readTemplateFromDisk($metaTemplate->uuid); + $updateableTemplate = $this->MetaTemplates->checkUpdatesForTemplate($templateOnDisk); + $this->set('updateableTemplate', $updateableTemplate); + $this->set('templateOnDisk', $templateOnDisk); + } } diff --git a/src/Model/Entity/MetaTemplateField.php b/src/Model/Entity/MetaTemplateField.php new file mode 100644 index 0000000..7baf30a --- /dev/null +++ b/src/Model/Entity/MetaTemplateField.php @@ -0,0 +1,11 @@ + $file) { + // if (substr($file, -5) === '.json') { + // if ($this->loadAndSaveMetaFile($path . $file) === true) { + // $files_processed[] = $file; + // } + // } + // } + // } + // } + $readErrors = []; + $preUpdateChecks = []; + $updatesErrors = []; + $templates = $this->readTemplatesFromDisk($readErrors); + foreach ($templates as $template) { + $preUpdateChecks[$template['uuid']] = $this->checkForUpdates($template); + } + $errors = [ + 'read_errors' => $readErrors, + 'pre_update_errors' => $preUpdateChecks, + 'update_errors' => $updatesErrors, + ]; + return $files_processed; + } + + public function checkForUpdates(): array + { + $templates = $this->readTemplatesFromDisk($readErrors); + $result = []; + foreach ($templates as $template) { + $result[$template['uuid']] = $this->checkUpdatesForTemplate($template); + } + return $result; + } + + public function isUpToDate(array $updateResult): bool + { + return $updateResult['up-to-date'] || $updateResult['new']; + } + + public function isUpdateable(array $updateResult): bool + { + return $updateResult['updateable']; + } + + public function isNew(array $updateResult): bool + { + return $updateResult['new']; + } + + public function hasNoConflict(array $updateResult): bool + { + return $this->hasConflict($updateResult); + } + + public function hasConflict(array $updateResult): bool + { + return !$updateResult['updateable'] && !$updateResult['up-to-date'] && !$updateResult['new']; + } + + public function getTemplateStatus(array $updateResult): array + { + return [ + 'up_to_date' => $this->isUpToDate($updateResult), + 'updateable' => $this->isUpdateable($updateResult), + 'is_new' => $this->isNew($updateResult), + 'has_conflict' => $this->hasConflict($updateResult), + ]; + } + + public function getUpToDateTemplates($result=null): array + { + $result = is_null($result) ? $this->checkForUpdates() : $result; + foreach ($result as $i => $updateResult) { + if (!$this->isUpToDate($updateResult)) { + unset($result[$i]); + } + } + return $result; + } + + public function getNotUpToDateTemplates($result=null): array + { + $result = is_null($result) ? $this->checkForUpdates() : $result; + foreach ($result as $i => $updateResult) { + if ($this->isUpToDate($updateResult)) { + unset($result[$i]); + } + } + return $result; + } + + public function getUpdateableTemplates($result=null): array + { + $result = is_null($result) ? $this->checkForUpdates() : $result; + foreach ($result as $i => $updateResult) { + if (!$this->isUpdateable($updateResult)) { + unset($result[$i]); + } + } + return $result; + } + + public function getNewTemplates($result=null): array + { + $result = is_null($result) ? $this->checkForUpdates() : $result; + foreach ($result as $i => $updateResult) { + if (!$this->isNew($updateResult)) { + unset($result[$i]); + } + } + return $result; + } + + public function getConflictTemplates($result=null): array + { + $result = is_null($result) ? $this->checkForUpdates() : $result; + foreach ($result as $i => $updateResult) { + if (!$this->hasConflict($updateResult)) { + unset($result[$i]); + } + } + return $result; + } + + public function readTemplatesFromDisk(&$errors=[]): array + { + $templates = []; + $errors = []; + foreach (self::TEMPLATE_PATH as $path) { if (is_dir($path)) { $files = scandir($path); foreach ($files as $k => $file) { if (substr($file, -5) === '.json') { - if ($this->loadAndSaveMetaFile($path . $file) === true) { - $files_processed[] = $file; + $errorMessage = ''; + $metaTemplate = $this->decodeTemplateFromDisk($path . $file, $errorMessage); + if (!empty($metaTemplate)) { + $templates[] = $metaTemplate; + } else { + $errors[] = $errorMessage; } } } } } - return $files_processed; + return $templates; + } + + public function readTemplateFromDisk(string $uuid, &$error=''): ?array + { + foreach (self::TEMPLATE_PATH as $path) { + if (is_dir($path)) { + $files = scandir($path); + foreach ($files as $k => $file) { + if (substr($file, -5) === '.json') { + $errorMessage = ''; + $metaTemplate = $this->decodeTemplateFromDisk($path . $file, $errorMessage); + if (!empty($metaTemplate) && $metaTemplate['uuid'] == $uuid) { + return $metaTemplate; + } + } + } + } + } + $error = __('Could not find meta-template with UUID {0}', $uuid); + return null; + } + + public function decodeTemplateFromDisk(string $filePath, &$errorMessage=''): ?array + { + if (file_exists($filePath)) { + $explodedPath = explode('/', $filePath); + $filename = $explodedPath[count($explodedPath)-1]; + $contents = file_get_contents($filePath); + $metaTemplate = json_decode($contents, true); + if (empty($metaTemplate)) { + $errorMessage = __('Could not load template file `{0}`. Error while decoding the template\'s JSON', $filename); + return null; + } + if (empty($metaTemplate['uuid']) || empty($metaTemplate['version'])) { + $errorMessage = __('Could not load template file. Invalid template file. Missing template UUID or version'); + return null; + } + return $metaTemplate; + } } public function getTemplate($id) @@ -108,7 +284,7 @@ class MetaTemplatesTable extends AppTable public function saveMetaFile(array $newMetaTemplate) { $query = $this->find(); - $query->contain('MetaTemaplteFields')->where(['uuid' => $newMetaTemplate['uuid']]); + $query->contain('MetaTemplateFields')->where(['uuid' => $newMetaTemplate['uuid']]); $metaTemplate = $query->first(); if (empty($metaTemplate)) { $metaTemplate = $this->newEntity($newMetaTemplate); @@ -139,12 +315,84 @@ class MetaTemplatesTable extends AppTable public function handleMetaTemplateFieldUpdateEdgeCase($metaTemplateField, $newMetaTemplateField) { - if (false) { // Field has been removed + } + + public function checkForMetaFieldConflicts(\App\Model\Entity\MetaTemplateField $metaField, array $templateField): array + { + $result = [ + 'updateable' => true, + 'conflicts' => [], + ]; + if ($metaField->multiple && $templateField['multiple'] == false) { // Field is no longer multiple + $result['updateable'] = false; + $result['conflicts'][] = __('This field is no longer multiple'); } - if (false) { // Field no longer multiple + if (!empty($templateField['regex']) && $templateField['regex'] != $metaField->regex) { + // FIXME: Check if all meta-fields pass the new validation + $result['updateable'] = false; + $result['conflicts'][] = __('This field is instantiated with values not passing the validation anymore'); } - if (false) { // Field no longer pass validation + return $result; + } + + public function checkForMetaTemplateConflicts(\App\Model\Entity\MetaTemplate $metaTemplate, array $template): array + { + $conflicts = []; + $existingMetaTemplateFields = Hash::combine($metaTemplate->toArray(), 'meta_template_fields.{n}.field'); + foreach ($template['metaFields'] as $newMetaField) { + foreach ($metaTemplate->meta_template_fields as $metaField) { + if ($newMetaField['field'] == $metaField->field) { + unset($existingMetaTemplateFields[$metaField->field]); + $templateConflicts = $this->checkForMetaFieldConflicts($metaField, $newMetaField); + if (!$templateConflicts['updateable']) { + $conflicts[$metaField->field] = $templateConflicts; + } + } + } } - return true; + if (!empty($existingMetaTemplateFields)) { + foreach ($existingMetaTemplateFields as $field => $tmp) { + $conflicts[$field] = [ + 'updateable' => false, + 'conflicts' => [__('This field is intended to be removed')], + ]; + } + } + return $conflicts; + } + + public function checkUpdatesForTemplate($template): array + { + $result = [ + 'new' => true, + 'up-to-date' => false, + 'updateable' => false, + 'conflicts' => [], + 'template' => $template, + ]; + $query = $this->find() + ->contain('MetaTemplateFields')->where([ + 'uuid' => $template['uuid'] + ]); + $metaTemplate = $query->first(); + if (!empty($metaTemplate)) { + $result['existing_template'] = $metaTemplate; + $result['current_version'] = $metaTemplate->version; + $result['next_version'] = $template['version']; + $result['new'] = false; + if ($metaTemplate->version >= $template['version']) { + $result['up-to-date'] = true; + $result['updateable'] = false; + $result['conflicts'][] = __('Could not update the template. Local version is equal or newer.'); + return $result; + } + $conflicts = $this->checkForMetaTemplateConflicts($metaTemplate, $template); + if (!empty($conflicts)) { + $result['conflicts'] = $conflicts; + } else { + $result['updateable'] = true; + } + } + return $result; } } diff --git a/templates/MetaTemplates/index.php b/templates/MetaTemplates/index.php index 88c584a..8bfb4ca 100644 --- a/templates/MetaTemplates/index.php +++ b/templates/MetaTemplates/index.php @@ -1,4 +1,24 @@ %s %s', __('New meta-templates available!'), __n('There is one new template on disk that can be loaded in the database', 'There are {0} new templates on disk that can be loaded in the database:', count($updateableTemplate['new']))); + $alertList = Hash::combine( + $updateableTemplate, + null, + ['%s :: %s', 'new.{s}.template.namespace', 'new.{s}.template.name'], + 'new.{n}.template.namespace' + ); + $alertList = array_map(function($entry) { + return h($entry); + }, $alertList); + $alertHtml .= $this->Html->nestedList($alertList); + echo $this->Bootstrap->alert([ + 'html' => $alertHtml, + 'variant' => 'warning', + ]); +} + echo $this->element('genericElements/IndexTable/index_table', [ 'data' => [ 'data' => $data, @@ -138,7 +158,12 @@ echo $this->element('genericElements/IndexTable/index_table', [ 'name' => __('UUID'), 'sort' => 'uuid', 'data_path' => 'uuid' - ] + ], + [ + 'name' => __('Updateable'), + 'data_path' => 'status', + 'element' => 'update_status', + ], ], 'title' => __('Meta Field Templates'), 'description' => __('The various templates used to enrich certain objects by a set of standardised fields.'), @@ -150,11 +175,15 @@ echo $this->element('genericElements/IndexTable/index_table', [ 'icon' => 'eye' ], [ - 'url' => '/metaTemplates/update', - 'url_params_data_paths' => ['id'], - 'title' => __('Update'), + 'open_modal' => '/metaTemplates/update/[onclick_params_data_path]', + 'modal_params_data_path' => 'id', + 'title' => __('Update Meta-Template'), 'icon' => 'download', - 'requirement' => true // FIXME: Check if template can be updated + 'complex_requirement' => [ + 'function' => function ($row, $options) { + return empty($row['status']['up_to_date']); + } + ] ] ] ] diff --git a/templates/MetaTemplates/update.php b/templates/MetaTemplates/update.php new file mode 100644 index 0000000..de3f040 --- /dev/null +++ b/templates/MetaTemplates/update.php @@ -0,0 +1,110 @@ +Bootstrap->alert([ + 'variant' => 'success', + 'text' => __('This meta-template is already up-to-date!'), + 'dismissible' => false, + ]); + $modalType = 'ok-only'; +} else { + if ($updateableTemplate['updateable']) { + $bodyHtml .= $this->Bootstrap->alert([ + 'variant' => 'success', + 'html' => __('This meta-template can be updated to version {0} (current: {1}).', sprintf('%s', h($templateOnDisk['version'])), h($metaTemplate->version)), + 'dismissible' => false, + ]); + } else { + $modalSize = 'xl'; + $bodyHtml .= $this->Bootstrap->alert([ + 'variant' => 'warning', + 'text' => __('Updating to version {0} cannot be done automatically as it introduces some conflicts.', h($templateOnDisk['version'])), + 'dismissible' => false, + ]); + $conflictTable = $this->element('MetaTemplates/conflictTable', [ + 'updateableTemplate' => $updateableTemplate, + 'metaTemplate' => $metaTemplate, + 'templateOnDisk' => $templateOnDisk, + ]); + $bodyHtml .= $this->Bootstrap->collapse([ + 'title' => __('View conflicts'), + 'open' => false + ], $conflictTable); + $bodyHtml .= $this->element('MetaTemplates/conflictResolution', [ + 'updateableTemplate' => $updateableTemplate, + 'metaTemplate' => $metaTemplate, + 'templateOnDisk' => $templateOnDisk, + ]); + } +} + +echo $this->Bootstrap->modal([ + 'title' => __('Update Meta Templates #{0} ?', h($metaTemplate->id)), + 'bodyHtml' => $bodyHtml, + 'size' => $modalSize, + 'type' => $modalType, + 'confirmText' => __('Update meta-templates'), + 'confirmFunction' => 'updateMetaTemplate', +]); +?> + + \ No newline at end of file diff --git a/templates/MetaTemplates/update_all.php b/templates/MetaTemplates/update_all.php new file mode 100644 index 0000000..0edbd5e --- /dev/null +++ b/templates/MetaTemplates/update_all.php @@ -0,0 +1,108 @@ +'; +$tableHtml .= sprintf('%s', __('Template')); +$tableHtml .= sprintf('%s', __('Version')); +$tableHtml .= sprintf('%s', __('New Template')); +$tableHtml .= sprintf('%s', __('Update available')); +$tableHtml .= sprintf('%s', __('Has Conflicts')); +$tableHtml .= sprintf('%s', __('Will be updated')); +$tableHtml .= ''; +$numberOfUpdates = 0; +$numberOfSkippedUpdates = 0; +foreach ($updateableTemplates as $uuid => $status) { + $tableHtml .= ''; + if (!empty($status['new'])) { + $tableHtml .= sprintf('%s', h($uuid)); + } else { + $tableHtml .= sprintf('%s', + Router::url(['controller' => 'MetaTemplates', 'action' => 'view', 'plugin' => null, h($status['existing_template']->id)]), + h($status['existing_template']->name) + ); + } + if (!empty($status['new'])) { + $tableHtml .= sprintf('%s', __('N/A')); + } else { + $tableHtml .= sprintf('%s %s %s', + h($status['current_version']), + $this->Bootstrap->icon('arrow-right', ['class' => 'fs-8']), + h($status['next_version']) + ); + } + if (!empty($status['new'])) { + $numberOfUpdates += 1; + $tableHtml .= sprintf('%s', $this->Bootstrap->icon('check')); + } else { + $tableHtml .= sprintf('%s', $this->Bootstrap->icon('times')); + } + if (!empty($status['new'])) { + $tableHtml .= sprintf('%s', __('N/A')); + } else { + $tableHtml .= sprintf('%s', empty($status['up-to-date']) ? $this->Bootstrap->icon('check') : $this->Bootstrap->icon('times')); + } + if (!empty($status['new'])) { + $tableHtml .= sprintf('%s', __('N/A')); + } else { + $tableHtml .= sprintf('%s', !empty($status['conflicts']) ? $this->Bootstrap->icon('check') : $this->Bootstrap->icon('times')); + } + if (!empty($status['new'])) { + $tableHtml .= sprintf('%s', $this->Bootstrap->icon('check', ['class' => 'text-success'])); + } else { + if (!empty($status['new']) || !empty($status['updateable'])) { + $numberOfUpdates += 1; + $tableHtml .= sprintf('%s', $this->Bootstrap->icon('check', ['class' => 'text-success'])); + } else { + $numberOfSkippedUpdates += 1; + $tableHtml .= sprintf('%s', $this->Bootstrap->icon('times', ['class' => 'text-danger'])); + } + } + $tableHtml .= ''; +} +$tableHtml .= ''; + +if (empty($numberOfSkippedUpdates) && empty($numberOfUpdates)) { + $bodyHtml .= $this->Bootstrap->alert([ + 'variant' => 'success', + 'text' => __('All meta-templates are already up-to-date!'), + 'dismissible' => false, + ]); + $modalType = 'ok-only'; +} elseif ($numberOfSkippedUpdates == 0) { + $bodyHtml .= $this->Bootstrap->alert([ + 'variant' => 'success', + 'text' => __('All {0} meta-templates can be updated', $numberOfUpdates), + 'dismissible' => false, + ]); +} else { + $modalSize = 'xl'; + $alertHtml = ''; + if (!empty($numberOfUpdates)) { + $alertHtml .= sprintf('
%s
', __('{0} meta-templates can be updated.', sprintf('%s', $numberOfUpdates))); + } + if (!empty($numberOfSkippedUpdates)) { + $alertHtml .= sprintf('
%s
', __('{0} meta-templates will be skipped.', sprintf('%s', $numberOfSkippedUpdates))); + $alertHtml .= sprintf('
%s
', __('You can still choose the update strategy when updating each conflicting template manually.')); + } + $bodyHtml .= $this->Bootstrap->alert([ + 'variant' => 'warning', + 'html' => $alertHtml, + 'dismissible' => false, + ]); +} + +$bodyHtml .= $tableHtml; + +echo $this->Bootstrap->modal([ + 'title' => h($title), + 'bodyHtml' => $bodyHtml, + 'size' => $modalSize, + 'type' => $modalType, + 'confirmText' => __('Update meta-templates'), + 'confirmFunction' => 'updateMetaTemplate', +]); +?> diff --git a/templates/element/MetaTemplates/conflictResolution.php b/templates/element/MetaTemplates/conflictResolution.php new file mode 100644 index 0000000..ceb836a --- /dev/null +++ b/templates/element/MetaTemplates/conflictResolution.php @@ -0,0 +1,28 @@ +
+
+
+ + + + + +
+
+
\ No newline at end of file diff --git a/templates/element/MetaTemplates/conflictTable.php b/templates/element/MetaTemplates/conflictTable.php new file mode 100644 index 0000000..b47cf8f --- /dev/null +++ b/templates/element/MetaTemplates/conflictTable.php @@ -0,0 +1,29 @@ + + + + + + + + + + $fieldConflict) : ?> + + + + + + + + + +
+ + + Bootstrap->badge([ + 'text' => __('Affected meta-fields will be removed'), + 'variant' => 'danger', + ]) + ?> +
\ No newline at end of file diff --git a/templates/element/genericElements/IndexTable/Fields/update_status.php b/templates/element/genericElements/IndexTable/Fields/update_status.php new file mode 100644 index 0000000..8f266fc --- /dev/null +++ b/templates/element/genericElements/IndexTable/Fields/update_status.php @@ -0,0 +1,9 @@ +Bootstrap->icon($icon); +?> \ No newline at end of file