new: [metaTemplate] Interface and functions to update meta-templates - WiP

Actual update not implemented yet.
pull/93/head
Sami Mokaddem 2021-12-01 11:01:31 +01:00
parent a6ecab5b47
commit 819d96e805
No known key found for this signature in database
GPG Key ID: 164C473F627A06FA
10 changed files with 670 additions and 34 deletions

View File

@ -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');
}

View File

@ -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);
}
}

View File

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

View File

@ -5,9 +5,15 @@ namespace App\Model\Table;
use App\Model\Table\AppTable;
use Cake\ORM\Table;
use Cake\Validation\Validator;
use Cake\Utility\Hash;
class MetaTemplatesTable extends AppTable
{
public const TEMPLATE_PATH = [
ROOT . '/libraries/default/meta_fields/',
ROOT . '/libraries/custom/meta_fields/'
];
public function initialize(array $config): void
{
parent::initialize($config);
@ -34,26 +40,196 @@ class MetaTemplatesTable extends AppTable
return $validator;
}
public function update()
public function update(&$errors=[])
{
$paths = [
ROOT . '/libraries/default/meta_fields/',
ROOT . '/libraries/custom/meta_fields/'
];
$files_processed = [];
foreach ($paths as $path) {
// 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;
// }
// }
// }
// }
// }
$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;
}
}

View File

@ -1,4 +1,24 @@
<?php
use Cake\Utility\Hash;
if (!empty($updateableTemplate)) {
$alertHtml = sprintf('<strong>%s</strong> %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']);
}
]
]
]
]

View File

@ -0,0 +1,110 @@
<?php
$bodyHtml = '';
$modalType = 'confirm';
$modalSize = 'lg';
if ($updateableTemplate['up-to-date']) {
$bodyHtml .= $this->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('<strong>%s</strong>', 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',
]);
?>
<script>
// function updateMetaTemplate(idList, selectedData, $table) {
// const successCallback = function([data, modalObject]) {
// location.reload()
// }
// const failCallback = ([data, modalObject]) => {
// const tableData = selectedData.map(row => {
// entryInError = data.filter(error => error.data.id == row.id)[0]
// $faIcon = $('<i class="fa"></i>').addClass(entryInError.success ? 'fa-check text-success' : 'fa-times text-danger')
// return [row.id, row.first_name, row.last_name, row.email, entryInError.message, JSON.stringify(entryInError.errors), $faIcon]
// });
// handleMessageTable(
// modalObject.$modal,
// ['<?= __('ID') ?>', '<?= __('First name') ?>', '<?= __('Last name') ?>', '<?= __('email') ?>', '<?= __('Message') ?>', '<?= __('Error') ?>', '<?= __('State') ?>'],
// tableData
// )
// const $footer = $(modalObject.ajaxApi.statusNode).parent()
// modalObject.ajaxApi.statusNode.remove()
// const $cancelButton = $footer.find('button[data-bs-dismiss="modal"]')
// $cancelButton.text('<?= __('OK') ?>').removeClass('btn-secondary').addClass('btn-primary')
// }
// UI.submissionModal('[URL_HERE]', successCallback, failCallback).then(([modalObject, ajaxApi]) => {
// const $idsInput = modalObject.$modal.find('form').find('input#ids-field')
// $idsInput.val(JSON.stringify(idList))
// const tableData = selectedData.map(row => {
// return [row.id, row.first_name, row.last_name, row.email]
// });
// handleMessageTable(
// modalObject.$modal,
// ['<?= __('ID') ?>', '<?= __('First name') ?>', '<?= __('Last name') ?>', '<?= __('email') ?>'],
// tableData
// )
// })
// function constructMessageTable(header, data) {
// return HtmlHelper.table(
// header,
// data, {
// small: true,
// borderless: true,
// tableClass: ['message-table', 'mt-4 mb-0'],
// }
// )
// }
// function handleMessageTable($modal, header, data) {
// const $modalBody = $modal.find('.modal-body')
// const $messageTable = $modalBody.find('table.message-table')
// const messageTableHTML = constructMessageTable(header, data)[0].outerHTML
// if ($messageTable.length) {
// $messageTable.html(messageTableHTML)
// } else {
// $modalBody.append(messageTableHTML)
// }
// }
// }
</script>

View File

@ -0,0 +1,108 @@
<?php
use Cake\Routing\Router;
$bodyHtml = '';
$modalType = 'confirm';
$modalSize = 'lg';
$tableHtml = '<table class="table"><thead><tr>';
$tableHtml .= sprintf('<th class="text-nowrap">%s</th>', __('Template'));
$tableHtml .= sprintf('<th class="text-nowrap">%s</th>', __('Version'));
$tableHtml .= sprintf('<th class="text-nowrap">%s</th>', __('New Template'));
$tableHtml .= sprintf('<th class="text-nowrap">%s</th>', __('Update available'));
$tableHtml .= sprintf('<th class="text-nowrap">%s</th>', __('Has Conflicts'));
$tableHtml .= sprintf('<th class="text-nowrap">%s</th>', __('Will be updated'));
$tableHtml .= '</tr></thead><tbody>';
$numberOfUpdates = 0;
$numberOfSkippedUpdates = 0;
foreach ($updateableTemplates as $uuid => $status) {
$tableHtml .= '<tr>';
if (!empty($status['new'])) {
$tableHtml .= sprintf('<td>%s</td>', h($uuid));
} else {
$tableHtml .= sprintf('<td><a href="%s">%s</a></td>',
Router::url(['controller' => 'MetaTemplates', 'action' => 'view', 'plugin' => null, h($status['existing_template']->id)]),
h($status['existing_template']->name)
);
}
if (!empty($status['new'])) {
$tableHtml .= sprintf('<td>%s</td>', __('N/A'));
} else {
$tableHtml .= sprintf('<td>%s %s %s</td>',
h($status['current_version']),
$this->Bootstrap->icon('arrow-right', ['class' => 'fs-8']),
h($status['next_version'])
);
}
if (!empty($status['new'])) {
$numberOfUpdates += 1;
$tableHtml .= sprintf('<td>%s</td>', $this->Bootstrap->icon('check'));
} else {
$tableHtml .= sprintf('<td>%s</td>', $this->Bootstrap->icon('times'));
}
if (!empty($status['new'])) {
$tableHtml .= sprintf('<td>%s</td>', __('N/A'));
} else {
$tableHtml .= sprintf('<td>%s</td>', empty($status['up-to-date']) ? $this->Bootstrap->icon('check') : $this->Bootstrap->icon('times'));
}
if (!empty($status['new'])) {
$tableHtml .= sprintf('<td>%s</td>', __('N/A'));
} else {
$tableHtml .= sprintf('<td>%s</td>', !empty($status['conflicts']) ? $this->Bootstrap->icon('check') : $this->Bootstrap->icon('times'));
}
if (!empty($status['new'])) {
$tableHtml .= sprintf('<td>%s</td>', $this->Bootstrap->icon('check', ['class' => 'text-success']));
} else {
if (!empty($status['new']) || !empty($status['updateable'])) {
$numberOfUpdates += 1;
$tableHtml .= sprintf('<td>%s</td>', $this->Bootstrap->icon('check', ['class' => 'text-success']));
} else {
$numberOfSkippedUpdates += 1;
$tableHtml .= sprintf('<td>%s</td>', $this->Bootstrap->icon('times', ['class' => 'text-danger']));
}
}
$tableHtml .= '</tr>';
}
$tableHtml .= '</tbody></table>';
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('<div>%s</div>', __('{0} meta-templates can be updated.', sprintf('<strong>%s</strong>', $numberOfUpdates)));
}
if (!empty($numberOfSkippedUpdates)) {
$alertHtml .= sprintf('<div>%s</div>', __('{0} meta-templates will be skipped.', sprintf('<strong>%s</strong>', $numberOfSkippedUpdates)));
$alertHtml .= sprintf('<div>%s</div>', __('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',
]);
?>

View File

@ -0,0 +1,28 @@
<form>
<div class="mt-3 d-flex justify-content-center">
<div class="btn-group justify-content-center" role="group" aria-label="Basic radio toggle button group">
<input type="radio" class="btn-check" name="btnradio" id="btnradio1" autocomplete="off" checked>
<label class="btn btn-outline-primary mw-33" for="btnradio1">
<div>
<h5 class="mb-3"><?= __('Keep both template') ?></h5>
<ul class="text-start fs-7">
<li><?= __('Meta-fields not having conflicts will be migrated to the new meta-template.') ?></li>
<li><?= __('Meta-fields having a conflicts will stay on their current meta-template.') ?></li>
<li><?= __('Conflicts can be taken care of manually via the UI.') ?></li>
</ul>
</div>
</label>
<input type="radio" class="btn-check" name="btnradio" id="btnradio3" autocomplete="off">
<label class="btn btn-outline-danger mw-33" for="btnradio3">
<div>
<h5 class="mb-3"><?= __('Delete conflicting fields') ?></h5>
<ul class="text-start fs-7">
<li><?= __('Meta-fields not satisfying the new meta-template definition will be deleted.') ?></li>
<li><?= __('All other meta-fields will be upgraded to the new meta-template.') ?></li>
</ul>
</div>
</label>
</div>
</div>
</form>

View File

@ -0,0 +1,29 @@
<table class="table">
<thead>
<tr>
<th scope="col"><?= __('Field name') ?></th>
<th scope="col"><?= __('Conflict') ?></th>
<th scope="col"><?= __('Automatic Resolution') ?></th>
</tr>
</thead>
<tbody>
<?php foreach ($updateableTemplate['conflicts'] as $fieldName => $fieldConflict) : ?>
<?php foreach ($fieldConflict['conflicts'] as $conflict) : ?>
<tr>
<th scope="row"><?= h($fieldName) ?></th>
<td>
<?= h($conflict) ?>
</td>
<td>
<?php
echo $this->Bootstrap->badge([
'text' => __('Affected meta-fields will be removed'),
'variant' => 'danger',
])
?>
</td>
</tr>
<?php endforeach; ?>
<?php endforeach; ?>
</tbody>
</table>

View File

@ -0,0 +1,9 @@
<?
if (empty($field['data_path']) || empty($row[$field['data_path']])) {
return '';
}
$status = $row[$field['data_path']];
$icon = !empty($row['status']['updateable']) ? 'check' : 'times';
echo $this->Bootstrap->icon($icon);
?>