new: [metaTemplate] Interface and functions to update meta-templates - WiP
Actual update not implemented yet.pull/93/head
parent
a6ecab5b47
commit
819d96e805
src
Controller
Component/Navigation
Model
Entity
templates
MetaTemplates
element
MetaTemplates
genericElements/IndexTable/Fields
|
@ -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');
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?php
|
||||
|
||||
namespace App\Model\Entity;
|
||||
|
||||
use App\Model\Entity\AppModel;
|
||||
use Cake\ORM\Entity;
|
||||
|
||||
class MetaTemplateField extends AppModel
|
||||
{
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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']);
|
||||
}
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
|
|
@ -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>
|
|
@ -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',
|
||||
]);
|
||||
?>
|
|
@ -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>
|
|
@ -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>
|
|
@ -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);
|
||||
?>
|
Loading…
Reference in New Issue