chg: [metaTemplate] Update system and conflict resolution interfaces - WiP
parent
6865114118
commit
aa83b1aa37
|
@ -176,7 +176,6 @@ class CRUDComponent extends Component
|
||||||
->order(['is_default' => 'DESC'])
|
->order(['is_default' => 'DESC'])
|
||||||
->where([
|
->where([
|
||||||
'scope' => $metaFieldsBehavior->getScope(),
|
'scope' => $metaFieldsBehavior->getScope(),
|
||||||
'enabled' => 1
|
|
||||||
])
|
])
|
||||||
->contain('MetaTemplateFields')
|
->contain('MetaTemplateFields')
|
||||||
->formatResults(function (\Cake\Collection\CollectionInterface $metaTemplates) { // Set meta-template && meta-template-fields indexed by their ID
|
->formatResults(function (\Cake\Collection\CollectionInterface $metaTemplates) { // Set meta-template && meta-template-fields indexed by their ID
|
||||||
|
@ -307,7 +306,7 @@ class CRUDComponent extends Component
|
||||||
}
|
}
|
||||||
|
|
||||||
// prune empty values and marshall fields
|
// prune empty values and marshall fields
|
||||||
private function massageMetaFields($entity, $input, $allMetaTemplates=[])
|
public function massageMetaFields($entity, $input, $allMetaTemplates=[])
|
||||||
{
|
{
|
||||||
if (empty($input['MetaTemplates'] || !$this->metaFieldsSupported())) {
|
if (empty($input['MetaTemplates'] || !$this->metaFieldsSupported())) {
|
||||||
return ['entity' => $entity, 'metafields_to_delete' => []];
|
return ['entity' => $entity, 'metafields_to_delete' => []];
|
||||||
|
@ -548,11 +547,12 @@ class CRUDComponent extends Component
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function attachMetaTemplates($data, $metaTemplates)
|
public function attachMetaTemplates($data, $metaTemplates, $pruneEmptyDisabled=true)
|
||||||
{
|
{
|
||||||
|
$this->MetaTemplates = TableRegistry::getTableLocator()->get('MetaTemplates');
|
||||||
$metaFields = [];
|
$metaFields = [];
|
||||||
if (!empty($data->id)) {
|
if (!empty($data->id)) {
|
||||||
$metaFields = $this->getMetaFields($data->id, $data);
|
$metaFields = $this->getMetaFields($data->id);
|
||||||
}
|
}
|
||||||
foreach ($metaTemplates as $i => $metaTemplate) {
|
foreach ($metaTemplates as $i => $metaTemplate) {
|
||||||
if (isset($metaFields[$metaTemplate->id])) {
|
if (isset($metaFields[$metaTemplate->id])) {
|
||||||
|
@ -563,6 +563,14 @@ class CRUDComponent extends Component
|
||||||
$metaTemplates[$metaTemplate->id]->meta_template_fields[$j]['metaFields'] = [];
|
$metaTemplates[$metaTemplate->id]->meta_template_fields[$j]['metaFields'] = [];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (!empty($pruneEmptyDisabled) && !$metaTemplate->enabled) {
|
||||||
|
unset($metaTemplates[$i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$newestTemplate = $this->MetaTemplates->getNewestVersion($metaTemplate);
|
||||||
|
if (!empty($newestTemplate) && !empty($metaTemplates[$i])) {
|
||||||
|
$metaTemplates[$i]['hasNewerVersion'] = $newestTemplate;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$data['MetaTemplates'] = $metaTemplates;
|
$data['MetaTemplates'] = $metaTemplates;
|
||||||
|
@ -686,7 +694,6 @@ class CRUDComponent extends Component
|
||||||
}
|
}
|
||||||
$this->setResponseForController('delete', $bulkSuccesses, $message, $data, null, $additionalData);
|
$this->setResponseForController('delete', $bulkSuccesses, $message, $data, null, $additionalData);
|
||||||
}
|
}
|
||||||
$this->Controller->set('metaGroup', 'ContactDB');
|
|
||||||
$this->Controller->set('scope', 'users');
|
$this->Controller->set('scope', 'users');
|
||||||
$this->Controller->viewBuilder()->setLayout('ajax');
|
$this->Controller->viewBuilder()->setLayout('ajax');
|
||||||
$this->Controller->render('/genericTemplates/delete');
|
$this->Controller->render('/genericTemplates/delete');
|
||||||
|
|
|
@ -80,8 +80,8 @@ class MetaTemplatesNavigation extends BaseNavigation
|
||||||
if (empty($this->viewVars['updateableTemplate']['up-to-date'])) {
|
if (empty($this->viewVars['updateableTemplate']['up-to-date'])) {
|
||||||
$this->bcf->addAction('MetaTemplates', 'view', 'MetaTemplates', 'update', [
|
$this->bcf->addAction('MetaTemplates', 'view', 'MetaTemplates', 'update', [
|
||||||
'label' => __('Update template'),
|
'label' => __('Update template'),
|
||||||
'url' => '/metaTemplates/update/{{id}}',
|
'url' => '/metaTemplates/update/{{uuid}}',
|
||||||
'url_vars' => ['id' => 'id'],
|
'url_vars' => ['uuid' => 'uuid'],
|
||||||
'variant' => 'warning',
|
'variant' => 'warning',
|
||||||
'badge' => [
|
'badge' => [
|
||||||
'variant' => 'warning',
|
'variant' => 'warning',
|
||||||
|
|
|
@ -5,7 +5,11 @@ namespace App\Controller;
|
||||||
use App\Controller\AppController;
|
use App\Controller\AppController;
|
||||||
use Cake\Utility\Hash;
|
use Cake\Utility\Hash;
|
||||||
use Cake\Utility\Text;
|
use Cake\Utility\Text;
|
||||||
|
use Cake\Utility\Inflector;
|
||||||
|
use Cake\ORM\TableRegistry;
|
||||||
use \Cake\Database\Expression\QueryExpression;
|
use \Cake\Database\Expression\QueryExpression;
|
||||||
|
use Cake\Http\Exception\NotFoundException;
|
||||||
|
use Cake\Http\Exception\MethodNotAllowedException;
|
||||||
|
|
||||||
class MetaTemplatesController extends AppController
|
class MetaTemplatesController extends AppController
|
||||||
{
|
{
|
||||||
|
@ -13,22 +17,29 @@ class MetaTemplatesController extends AppController
|
||||||
public $filterFields = ['name', 'uuid', 'scope', 'namespace'];
|
public $filterFields = ['name', 'uuid', 'scope', 'namespace'];
|
||||||
public $containFields = ['MetaTemplateFields'];
|
public $containFields = ['MetaTemplateFields'];
|
||||||
|
|
||||||
public function update($template_id=false)
|
public function update($template_uuid=null)
|
||||||
{
|
{
|
||||||
if (!empty($template_id)) {
|
$metaTemplate = false;
|
||||||
$metaTemplate = $this->MetaTemplates->get($template_id);
|
if (!is_null($template_uuid)) {
|
||||||
|
$metaTemplate = $this->MetaTemplates->find()->where([
|
||||||
|
'uuid' => $template_uuid
|
||||||
|
])->first();
|
||||||
|
if (empty($metaTemplate)) {
|
||||||
|
throw new NotFoundException(__('Invalid {0}.', $this->MetaTemplates->getAlias()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if ($this->request->is('post')) {
|
if ($this->request->is('post')) {
|
||||||
$result = $this->MetaTemplates->update($template_id);
|
$updateStrategy = $this->request->getData('update_strategy', null);
|
||||||
|
$result = $this->MetaTemplates->update($template_uuid, $updateStrategy);
|
||||||
if ($this->ParamHandler->isRest()) {
|
if ($this->ParamHandler->isRest()) {
|
||||||
return $this->RestResponse->viewData($result, 'json');
|
return $this->RestResponse->viewData($result, 'json');
|
||||||
} else {
|
} else {
|
||||||
if ($result['success']) {
|
if ($result['success']) {
|
||||||
$message = __n('{0} templates updated.', 'The template has been updated.', empty($template_id), $result['updated']);
|
$message = __n('{0} templates updated.', 'The template has been updated.', empty($template_uuid), $result['files_processed']);
|
||||||
} else {
|
} else {
|
||||||
$message = __n('{0} templates could not be updated.', 'The template could not be updated.',empty($template_id), $result['updated']);
|
$message = __n('{0} templates could not be updated.', 'The template could not be updated.', empty($template_uuid), $result['files_processed']);
|
||||||
}
|
}
|
||||||
$this->CRUD->setResponseForController('update', $result['success'], $message, $metaTemplate, $metaTemplate->getErrors(), ['redirect' => $this->referer()]);
|
$this->CRUD->setResponseForController('update', $result['success'], $message, $result['files_processed'], $result['update_errors'], ['redirect' => $this->referer()]);
|
||||||
$responsePayload = $this->CRUD->getResponsePayload();
|
$responsePayload = $this->CRUD->getResponsePayload();
|
||||||
if (!empty($responsePayload)) {
|
if (!empty($responsePayload)) {
|
||||||
return $responsePayload;
|
return $responsePayload;
|
||||||
|
@ -36,7 +47,7 @@ class MetaTemplatesController extends AppController
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!$this->ParamHandler->isRest()) {
|
if (!$this->ParamHandler->isRest()) {
|
||||||
if (!empty($template_id)) {
|
if (!is_null($template_uuid)) {
|
||||||
$this->set('metaTemplate', $metaTemplate);
|
$this->set('metaTemplate', $metaTemplate);
|
||||||
$this->setUpdateStatus($metaTemplate->id);
|
$this->setUpdateStatus($metaTemplate->id);
|
||||||
} else {
|
} else {
|
||||||
|
@ -50,6 +61,105 @@ class MetaTemplatesController extends AppController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getMetaFieldsToUpdate($template_id)
|
||||||
|
{
|
||||||
|
$metaTemplate = $this->MetaTemplates->get($template_id);
|
||||||
|
$newestMetaTemplate = $this->MetaTemplates->getNewestVersion($metaTemplate);
|
||||||
|
$entities = $this->MetaTemplates->getEntitiesWithMetaFieldsToUpdate($template_id);
|
||||||
|
$this->set('metaTemplate', $metaTemplate);
|
||||||
|
$this->set('newestMetaTemplate', $newestMetaTemplate);
|
||||||
|
$this->set('entities', $entities);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function migrateOldMetaTemplateToNewestVersionForEntity($template_id, $entity_id)
|
||||||
|
{
|
||||||
|
$metaTemplate = $this->MetaTemplates->get($template_id, [
|
||||||
|
'contain' => ['MetaTemplateFields']
|
||||||
|
]);
|
||||||
|
$newestMetaTemplate = $this->MetaTemplates->getNewestVersion($metaTemplate, true);
|
||||||
|
$entity = $this->MetaTemplates->migrateMetaTemplateToNewVersion($metaTemplate, $newestMetaTemplate, $entity_id);
|
||||||
|
$conditions = [
|
||||||
|
'MetaFields.meta_template_id IN' => [$metaTemplate->id, $newestMetaTemplate->id]
|
||||||
|
];
|
||||||
|
$keyedMetaFields = $this->MetaTemplates->getKeyedMetaFields($metaTemplate->scope, $entity_id, $conditions);
|
||||||
|
if (empty($keyedMetaFields[$metaTemplate->id])) {
|
||||||
|
throw new NotFoundException(__('Invalid {0}. This entities does not have meta-fields to be moved to a newer template.', $this->MetaTemplates->getAlias()));
|
||||||
|
}
|
||||||
|
$mergedMetaFields = $this->MetaTemplates->mergeMetaFieldsInMetaTemplate($keyedMetaFields, [$metaTemplate, $newestMetaTemplate]);
|
||||||
|
$entity['MetaTemplates'] = $mergedMetaFields;
|
||||||
|
if ($this->request->is('post') || $this->request->is('put')) {
|
||||||
|
$className = Inflector::camelize(Inflector::pluralize($newestMetaTemplate->scope));
|
||||||
|
$entityTable = TableRegistry::getTableLocator()->get($className);
|
||||||
|
$inputData = $this->request->getData();
|
||||||
|
$massagedData = $this->MetaTemplates->massageMetaFieldsBeforeSave($entity, $inputData, $newestMetaTemplate);
|
||||||
|
unset($inputData['MetaTemplates']); // Avoid MetaTemplates to be overriden when patching entity
|
||||||
|
$data = $massagedData['entity'];
|
||||||
|
$metaFieldsToDelete = $massagedData['metafields_to_delete'];
|
||||||
|
foreach ($entity->meta_fields as $i => $metaField) {
|
||||||
|
if ($metaField->meta_template_id == $template_id) {
|
||||||
|
$metaFieldsToDelete[] = $entity->meta_fields[$i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$data = $entityTable->patchEntity($data, $inputData);
|
||||||
|
$savedData = $entityTable->save($data);
|
||||||
|
if ($savedData !== false) {
|
||||||
|
if (!empty($metaFieldsToDelete)) {
|
||||||
|
$entityTable->MetaFields->unlink($savedData, $metaFieldsToDelete);
|
||||||
|
}
|
||||||
|
$message = __('Data on old meta-template has been migrated to newest meta-template');
|
||||||
|
} else {
|
||||||
|
$message = __('Could not migrate data to newest meta-template');
|
||||||
|
}
|
||||||
|
$this->CRUD->setResponseForController(
|
||||||
|
'migrateOldMetaTemplateToNewestVersionForEntity',
|
||||||
|
$savedData !== false,
|
||||||
|
$message,
|
||||||
|
$savedData,
|
||||||
|
[],
|
||||||
|
['redirect' => [
|
||||||
|
'controller' => $className,
|
||||||
|
'action' => 'view', $entity_id]
|
||||||
|
]
|
||||||
|
);
|
||||||
|
$responsePayload = $this->CRUD->getResponsePayload();
|
||||||
|
if (!empty($responsePayload)) {
|
||||||
|
return $responsePayload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$conflicts = $this->MetaTemplates->checkForMetaTemplateConflicts($metaTemplate, $newestMetaTemplate);
|
||||||
|
foreach ($conflicts as $conflict) {
|
||||||
|
$existingMetaTemplateField = $conflict['existing_meta_template_field'];
|
||||||
|
foreach ($existingMetaTemplateField->metaFields as $metafield_id => $metaField) {
|
||||||
|
$metaField->setError('value', implode(', ', $existingMetaTemplateField->conflicts));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// automatically convert non-conflicting fields to new meta-template
|
||||||
|
$movedMetaTemplateFields = [];
|
||||||
|
foreach ($metaTemplate->meta_template_fields as $metaTemplateField) {
|
||||||
|
if (!empty($conflicts[$metaTemplateField->field]['conflicts'])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach ($newestMetaTemplate->meta_template_fields as $newMetaTemplateField) {
|
||||||
|
if ($metaTemplateField->field == $newMetaTemplateField->field && empty($newMetaTemplateField->metaFields)) {
|
||||||
|
$movedMetaTemplateFields[] = $metaTemplateField->id;
|
||||||
|
$copiedMetaFields = array_map(function ($e) use ($newMetaTemplateField) {
|
||||||
|
$e = $e->toArray();
|
||||||
|
$e['meta_template_id'] = $newMetaTemplateField->meta_template_id;
|
||||||
|
$e['meta_template_field_id'] = $newMetaTemplateField->id;
|
||||||
|
unset($e['id']);
|
||||||
|
return $e;
|
||||||
|
}, $metaTemplateField->metaFields);
|
||||||
|
$newMetaTemplateField->metaFields = $this->MetaTemplates->MetaTemplateFields->MetaFields->newEntities($copiedMetaFields);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$this->set('oldMetaTemplate', $metaTemplate);
|
||||||
|
$this->set('newMetaTemplate', $newestMetaTemplate);
|
||||||
|
$this->set('entity', $entity);
|
||||||
|
$this->set('conflicts', $conflicts);
|
||||||
|
$this->set('movedMetaTemplateFields', $movedMetaTemplateFields);
|
||||||
|
}
|
||||||
|
|
||||||
public function index()
|
public function index()
|
||||||
{
|
{
|
||||||
$updateableTemplate = $this->MetaTemplates->checkForUpdates();
|
$updateableTemplate = $this->MetaTemplates->checkForUpdates();
|
||||||
|
@ -69,7 +179,8 @@ class MetaTemplatesController extends AppController
|
||||||
'afterFind' => function($data) use ($updateableTemplate) {
|
'afterFind' => function($data) use ($updateableTemplate) {
|
||||||
foreach ($data as $i => $metaTemplate) {
|
foreach ($data as $i => $metaTemplate) {
|
||||||
if (!empty($updateableTemplate[$metaTemplate->uuid])) {
|
if (!empty($updateableTemplate[$metaTemplate->uuid])) {
|
||||||
$metaTemplate->set('status', $this->MetaTemplates->getTemplateStatus($updateableTemplate[$metaTemplate->uuid]));
|
$updateStatusForTemplate = $this->MetaTemplates->checkUpdateForMetaTemplate($updateableTemplate[$metaTemplate->uuid]['template'], $metaTemplate);
|
||||||
|
$metaTemplate->set('status', $this->MetaTemplates->getTemplateStatus($updateStatusForTemplate, $metaTemplate));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $data;
|
return $data;
|
||||||
|
@ -81,6 +192,7 @@ class MetaTemplatesController extends AppController
|
||||||
}
|
}
|
||||||
$updateableTemplate = [
|
$updateableTemplate = [
|
||||||
'not_up_to_date' => $this->MetaTemplates->getNotUpToDateTemplates(),
|
'not_up_to_date' => $this->MetaTemplates->getNotUpToDateTemplates(),
|
||||||
|
'can_be_removed' => $this->MetaTemplates->getCanBeRemovedTemplates(),
|
||||||
'new' => $this->MetaTemplates->getNewTemplates(),
|
'new' => $this->MetaTemplates->getNewTemplates(),
|
||||||
];
|
];
|
||||||
$this->set('defaultTemplatePerScope', $this->MetaTemplates->getDefaultTemplatePerScope());
|
$this->set('defaultTemplatePerScope', $this->MetaTemplates->getDefaultTemplatePerScope());
|
||||||
|
@ -100,6 +212,20 @@ class MetaTemplatesController extends AppController
|
||||||
$this->setUpdateStatus($id);
|
$this->setUpdateStatus($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function delete($id)
|
||||||
|
{
|
||||||
|
$updateableTemplate = $this->getUpdateStatus($id);
|
||||||
|
if (empty($updateableTemplate['can_be_removed'])) {
|
||||||
|
throw MethodNotAllowedException(__('This meta-template cannot be removed'));
|
||||||
|
}
|
||||||
|
$this->set('deletionText', __('The meta-template "{0}" has no meta-field and can be safely removed.', h($updateableTemplate['existing_template']->name)));
|
||||||
|
$this->CRUD->delete($id);
|
||||||
|
$responsePayload = $this->CRUD->getResponsePayload();
|
||||||
|
if (!empty($responsePayload)) {
|
||||||
|
return $responsePayload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function toggle($id, $fieldName = 'enabled')
|
public function toggle($id, $fieldName = 'enabled')
|
||||||
{
|
{
|
||||||
if ($this->request->is('POST') && $fieldName == 'is_default') {
|
if ($this->request->is('POST') && $fieldName == 'is_default') {
|
||||||
|
@ -115,11 +241,23 @@ class MetaTemplatesController extends AppController
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function getUpdateStatus($id): array
|
||||||
|
{
|
||||||
|
$metaTemplate = $this->MetaTemplates->get($id, [
|
||||||
|
'contain' => ['MetaTemplateFields']
|
||||||
|
]);
|
||||||
|
$templateOnDisk = $this->MetaTemplates->readTemplateFromDisk($metaTemplate->uuid);
|
||||||
|
$updateableTemplate = $this->MetaTemplates->checkUpdateForMetaTemplate($templateOnDisk, $metaTemplate);
|
||||||
|
return $updateableTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
public function setUpdateStatus($id)
|
public function setUpdateStatus($id)
|
||||||
{
|
{
|
||||||
$metaTemplate = $this->MetaTemplates->get($id);
|
$metaTemplate = $this->MetaTemplates->get($id, [
|
||||||
|
'contain' => ['MetaTemplateFields']
|
||||||
|
]);
|
||||||
$templateOnDisk = $this->MetaTemplates->readTemplateFromDisk($metaTemplate->uuid);
|
$templateOnDisk = $this->MetaTemplates->readTemplateFromDisk($metaTemplate->uuid);
|
||||||
$updateableTemplate = $this->MetaTemplates->checkUpdatesForTemplate($templateOnDisk);
|
$updateableTemplate = $this->MetaTemplates->checkUpdateForMetaTemplate($templateOnDisk, $metaTemplate);
|
||||||
$this->set('updateableTemplate', $updateableTemplate);
|
$this->set('updateableTemplate', $updateableTemplate);
|
||||||
$this->set('templateOnDisk', $templateOnDisk);
|
$this->set('templateOnDisk', $templateOnDisk);
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,6 +49,11 @@ class MetaFieldsTable extends AppTable
|
||||||
$metaFieldsTable = $context['providers']['table'];
|
$metaFieldsTable = $context['providers']['table'];
|
||||||
$entityData = $context['data'];
|
$entityData = $context['data'];
|
||||||
$metaTemplateField = $metaFieldsTable->MetaTemplateFields->get($entityData['meta_template_field_id']);
|
$metaTemplateField = $metaFieldsTable->MetaTemplateFields->get($entityData['meta_template_field_id']);
|
||||||
|
return $this->isValidMetaFieldForMetaTemplateField($value, $metaTemplateField);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isValidMetaFieldForMetaTemplateField($value, $metaTemplateField)
|
||||||
|
{
|
||||||
$typeValid = $this->isValidType($value, $metaTemplateField['type']);
|
$typeValid = $this->isValidType($value, $metaTemplateField['type']);
|
||||||
if ($typeValid !== true) {
|
if ($typeValid !== true) {
|
||||||
return $typeValid;
|
return $typeValid;
|
||||||
|
@ -72,7 +77,7 @@ class MetaFieldsTable extends AppTable
|
||||||
|
|
||||||
$re = $metaTemplateField['regex'];
|
$re = $metaTemplateField['regex'];
|
||||||
if (!preg_match("/^$re$/m", $value)) {
|
if (!preg_match("/^$re$/m", $value)) {
|
||||||
return __('Metafield value `{0}` for `{1}` doesn\'t pass regex validation', $value, $metaTemplateField->field);
|
return __('Metafield value `{0}` for `{1}` doesn\'t pass regex validation', $value, $metaTemplateField['field']);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,14 +20,21 @@ class MetaTemplateFieldsTable extends AppTable
|
||||||
$this->setDisplayField('field');
|
$this->setDisplayField('field');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function beforeSave($event, $entity, $options)
|
||||||
|
{
|
||||||
|
if (empty($entity->meta_template_id)) {
|
||||||
|
$event->stopPropagation();
|
||||||
|
$event->setResult(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function validationDefault(Validator $validator): Validator
|
public function validationDefault(Validator $validator): Validator
|
||||||
{
|
{
|
||||||
$validator
|
$validator
|
||||||
->notEmptyString('field')
|
->notEmptyString('field')
|
||||||
->notEmptyString('type')
|
->notEmptyString('type')
|
||||||
->numeric('meta_template_id')
|
->requirePresence(['field', 'type'], 'create');
|
||||||
->notBlank('meta_template_id')
|
|
||||||
->requirePresence(['meta_template_id', 'field', 'type'], 'create');
|
|
||||||
return $validator;
|
return $validator;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,8 +4,11 @@ namespace App\Model\Table;
|
||||||
|
|
||||||
use App\Model\Table\AppTable;
|
use App\Model\Table\AppTable;
|
||||||
use Cake\ORM\Table;
|
use Cake\ORM\Table;
|
||||||
|
use Cake\ORM\TableRegistry;
|
||||||
use Cake\Validation\Validator;
|
use Cake\Validation\Validator;
|
||||||
use Cake\Utility\Hash;
|
use Cake\Utility\Hash;
|
||||||
|
use Cake\Utility\Inflector;
|
||||||
|
use Cake\Utility\Text;
|
||||||
|
|
||||||
class MetaTemplatesTable extends AppTable
|
class MetaTemplatesTable extends AppTable
|
||||||
{
|
{
|
||||||
|
@ -21,7 +24,10 @@ class MetaTemplatesTable extends AppTable
|
||||||
$this->hasMany(
|
$this->hasMany(
|
||||||
'MetaTemplateFields',
|
'MetaTemplateFields',
|
||||||
[
|
[
|
||||||
'foreignKey' => 'meta_template_id'
|
'foreignKey' => 'meta_template_id',
|
||||||
|
'saveStrategy' => 'replace',
|
||||||
|
'dependent' => true,
|
||||||
|
'cascadeCallbacks' => true,
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
$this->setDisplayField('name');
|
$this->setDisplayField('name');
|
||||||
|
@ -40,7 +46,7 @@ class MetaTemplatesTable extends AppTable
|
||||||
return $validator;
|
return $validator;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function update(&$errors=[])
|
public function update($template_uuid=null, $strategy=null)
|
||||||
{
|
{
|
||||||
$files_processed = [];
|
$files_processed = [];
|
||||||
// foreach (self::TEMPLATE_PATH as $path) {
|
// foreach (self::TEMPLATE_PATH as $path) {
|
||||||
|
@ -60,22 +66,54 @@ class MetaTemplatesTable extends AppTable
|
||||||
$updatesErrors = [];
|
$updatesErrors = [];
|
||||||
$templates = $this->readTemplatesFromDisk($readErrors);
|
$templates = $this->readTemplatesFromDisk($readErrors);
|
||||||
foreach ($templates as $template) {
|
foreach ($templates as $template) {
|
||||||
$preUpdateChecks[$template['uuid']] = $this->checkForUpdates($template);
|
$updateStatus = $this->checkForUpdates($template['uuid']);
|
||||||
|
$preUpdateChecks[$template['uuid']] = $updateStatus;
|
||||||
|
if (is_null($template_uuid) || $template_uuid == $template['uuid']) {
|
||||||
|
$errors = [];
|
||||||
|
$success = false;
|
||||||
|
if ($updateStatus['up-to-date']) {
|
||||||
|
$errors['message'] = __('Meta-template already up-to-date');
|
||||||
|
$success = true;
|
||||||
|
} else if ($updateStatus['new']) {
|
||||||
|
$success = $this->saveNewMetaTemplate($template, $errors);
|
||||||
|
} else if ($updateStatus['updateable']) {
|
||||||
|
$success = $this->updateMetaTemplate($template, $errors);
|
||||||
|
} else if (!$updateStatus['up-to-date'] && is_null($strategy)) {
|
||||||
|
$errors['message'] = __('Cannot update meta-template, update strategy not provided');
|
||||||
|
} else if (!$updateStatus['up-to-date'] && !is_null($strategy)) {
|
||||||
|
$success = $this->updateMetaTemplateWithStrategy($template, $strategy, $errors);
|
||||||
|
} else {
|
||||||
|
$errors['message'] = __('Could not update. Something went wrong.');
|
||||||
|
}
|
||||||
|
if ($success) {
|
||||||
|
$files_processed[] = $template['uuid'];
|
||||||
|
}
|
||||||
|
if (!empty($errors)) {
|
||||||
|
$updatesErrors[] = $errors;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$errors = [
|
$results = [
|
||||||
'read_errors' => $readErrors,
|
'read_errors' => $readErrors,
|
||||||
'pre_update_errors' => $preUpdateChecks,
|
'pre_update_errors' => $preUpdateChecks,
|
||||||
'update_errors' => $updatesErrors,
|
'update_errors' => $updatesErrors,
|
||||||
|
'files_processed' => $files_processed,
|
||||||
|
'success' => !empty($files_processed),
|
||||||
];
|
];
|
||||||
return $files_processed;
|
return $results;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function checkForUpdates(): array
|
public function checkForUpdates($template_uuid=null): array
|
||||||
{
|
{
|
||||||
$templates = $this->readTemplatesFromDisk($readErrors);
|
$templates = $this->readTemplatesFromDisk($readErrors);
|
||||||
$result = [];
|
$result = [];
|
||||||
foreach ($templates as $template) {
|
foreach ($templates as $template) {
|
||||||
$result[$template['uuid']] = $this->checkUpdatesForTemplate($template);
|
if (is_null($template_uuid)) {
|
||||||
|
$result[$template['uuid']] = $this->checkUpdatesForTemplate($template);
|
||||||
|
} else if ($template['uuid'] == $template_uuid) {
|
||||||
|
$result = $this->checkUpdatesForTemplate($template);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
@ -105,13 +143,26 @@ class MetaTemplatesTable extends AppTable
|
||||||
return !$updateResult['updateable'] && !$updateResult['up-to-date'] && !$updateResult['new'];
|
return !$updateResult['updateable'] && !$updateResult['up-to-date'] && !$updateResult['new'];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTemplateStatus(array $updateResult): array
|
public function isUpdateableToExistingMetaTemplate($metaTemplate): bool
|
||||||
|
{
|
||||||
|
$newestTemplate = $this->getNewestVersion($metaTemplate);
|
||||||
|
return !empty($newestTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isRemovable(array $updateResult): bool
|
||||||
|
{
|
||||||
|
return !empty($updateResult['can_be_removed']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTemplateStatus(array $updateResult, $metaTemplate): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'up_to_date' => $this->isUpToDate($updateResult),
|
'up_to_date' => $this->isUpToDate($updateResult),
|
||||||
'updateable' => $this->isUpdateable($updateResult),
|
'updateable' => $this->isUpdateable($updateResult),
|
||||||
'is_new' => $this->isNew($updateResult),
|
'is_new' => $this->isNew($updateResult),
|
||||||
'has_conflict' => $this->hasConflict($updateResult),
|
'has_conflict' => $this->hasConflict($updateResult),
|
||||||
|
'to_existing' => $this->isUpdateableToExistingMetaTemplate($metaTemplate),
|
||||||
|
'can_be_removed' => $this->isRemovable($updateResult),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,6 +221,32 @@ class MetaTemplatesTable extends AppTable
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getNewestVersion($metaTemplate, $full=false)
|
||||||
|
{
|
||||||
|
$query = $this->find()->where([
|
||||||
|
'uuid' => $metaTemplate->uuid,
|
||||||
|
'id !=' => $metaTemplate->id,
|
||||||
|
'version >=' => $metaTemplate->version,
|
||||||
|
])
|
||||||
|
->order(['version' => 'DESC']);
|
||||||
|
if ($full) {
|
||||||
|
$query->contain(['MetaTemplateFields']);
|
||||||
|
}
|
||||||
|
$newestTemplate = $query->first();
|
||||||
|
return $newestTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCanBeRemovedTemplates($result=null): array
|
||||||
|
{
|
||||||
|
$result = is_null($result) ? $this->checkForUpdates() : $result;
|
||||||
|
foreach ($result as $i => $updateResult) {
|
||||||
|
if (!$this->isRemovable($updateResult)) {
|
||||||
|
unset($result[$i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
public function readTemplatesFromDisk(&$errors=[]): array
|
public function readTemplatesFromDisk(&$errors=[]): array
|
||||||
{
|
{
|
||||||
$templates = [];
|
$templates = [];
|
||||||
|
@ -232,6 +309,82 @@ class MetaTemplatesTable extends AppTable
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getEntitiesWithMetaFieldsToUpdate(int $template_id): array
|
||||||
|
{
|
||||||
|
$metaTemplate = $this->get($template_id);
|
||||||
|
$queryParentEntities = $this->MetaTemplateFields->MetaFields->find();
|
||||||
|
$queryParentEntities
|
||||||
|
->select(['parent_id'])
|
||||||
|
->where([
|
||||||
|
'meta_template_id' => $template_id
|
||||||
|
])
|
||||||
|
->group(['parent_id']);
|
||||||
|
|
||||||
|
$entitiesClassName = Inflector::camelize(Inflector::pluralize($metaTemplate->scope));
|
||||||
|
$entitiesTable = TableRegistry::getTableLocator()->get($entitiesClassName);
|
||||||
|
$entityQuery = $entitiesTable->find()
|
||||||
|
->where(['id IN' => $queryParentEntities])
|
||||||
|
->contain([
|
||||||
|
'MetaFields' => [
|
||||||
|
'conditions' => [
|
||||||
|
'meta_template_id' => $template_id
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
$entities = $entityQuery->all()->toList();
|
||||||
|
return $entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKeyedMetaFields(string $scope, int $entity_id, array $conditions=[])
|
||||||
|
{
|
||||||
|
$query = $this->MetaTemplateFields->MetaFields->find();
|
||||||
|
$query->where(array_merge(
|
||||||
|
$conditions,
|
||||||
|
[
|
||||||
|
'MetaFields.scope' => $scope,
|
||||||
|
'MetaFields.parent_id' => $entity_id
|
||||||
|
]
|
||||||
|
));
|
||||||
|
$metaFields = $query->all();
|
||||||
|
$data = [];
|
||||||
|
foreach ($metaFields as $metaField) {
|
||||||
|
if (empty($data[$metaField->meta_template_id][$metaField->meta_template_field_id])) {
|
||||||
|
$data[$metaField->meta_template_id][$metaField->meta_template_field_id] = [];
|
||||||
|
}
|
||||||
|
$data[$metaField->meta_template_id][$metaField->meta_template_field_id][$metaField->id] = $metaField;
|
||||||
|
}
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function mergeMetaFieldsInMetaTemplate(array $keyedMetaFields, array $metaTemplates)
|
||||||
|
{
|
||||||
|
$merged = [];
|
||||||
|
foreach ($metaTemplates as $metaTemplate) {
|
||||||
|
$metaTemplate['meta_template_fields'] = Hash::combine($metaTemplate['meta_template_fields'], '{n}.id', '{n}');
|
||||||
|
$merged[$metaTemplate->id] = $metaTemplate;
|
||||||
|
if (isset($keyedMetaFields[$metaTemplate->id])) {
|
||||||
|
foreach ($metaTemplate->meta_template_fields as $j => $meta_template_field) {
|
||||||
|
if (isset($keyedMetaFields[$metaTemplate->id][$meta_template_field->id])) {
|
||||||
|
$merged[$metaTemplate->id]->meta_template_fields[$j]['metaFields'] = $keyedMetaFields[$metaTemplate->id][$meta_template_field->id];
|
||||||
|
} else {
|
||||||
|
$merged[$metaTemplate->id]->meta_template_fields[$j]['metaFields'] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $merged;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function migrateMetaTemplateToNewVersion(\App\Model\Entity\MetaTemplate $oldMetaTemplate, \App\Model\Entity\MetaTemplate $newMetaTemplate, int $entityId)
|
||||||
|
{
|
||||||
|
$entitiesClassName = Inflector::camelize(Inflector::pluralize($oldMetaTemplate->scope));
|
||||||
|
$entitiesTable = TableRegistry::getTableLocator()->get($entitiesClassName);
|
||||||
|
$entity = $entitiesTable->get($entityId, [
|
||||||
|
'contain' => 'MetaFields'
|
||||||
|
]);
|
||||||
|
return $entity;
|
||||||
|
}
|
||||||
|
|
||||||
public function getTemplate($id)
|
public function getTemplate($id)
|
||||||
{
|
{
|
||||||
$query = $this->find();
|
$query = $this->find();
|
||||||
|
@ -265,88 +418,385 @@ class MetaTemplatesTable extends AppTable
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadAndSaveMetaFile(String $filePath)
|
// public function loadAndSaveMetaFile(String $filePath)
|
||||||
|
// {
|
||||||
|
// if (file_exists($filePath)) {
|
||||||
|
// $contents = file_get_contents($filePath);
|
||||||
|
// $metaTemplate = json_decode($contents, true);
|
||||||
|
// if (empty($metaTemplate)) {
|
||||||
|
// return __('Could not load template file. Error while decoding the template\'s JSON');
|
||||||
|
// }
|
||||||
|
// if (empty($metaTemplate['uuid']) || empty($metaTemplate['version'])) {
|
||||||
|
// return __('Could not load template file. Invalid template file. Missing template UUID or version');
|
||||||
|
// }
|
||||||
|
// return $this->saveMetaFile($metaTemplate);
|
||||||
|
// }
|
||||||
|
// return __('Could not load template file. File does not exists');
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public function saveMetaFile(array $newMetaTemplate)
|
||||||
|
// {
|
||||||
|
// $query = $this->find();
|
||||||
|
// $query->contain('MetaTemplateFields')->where(['uuid' => $newMetaTemplate['uuid']]);
|
||||||
|
// $metaTemplate = $query->first();
|
||||||
|
// if (empty($metaTemplate)) {
|
||||||
|
// $metaTemplate = $this->newEntity($newMetaTemplate);
|
||||||
|
// $result = $this->save($metaTemplate);
|
||||||
|
// if (!$result) {
|
||||||
|
// return __('Something went wrong, could not create the template.');
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// if ($metaTemplate->version >= $newMetaTemplate['version']) {
|
||||||
|
// return __('Could not update the template. Local version is newer.');
|
||||||
|
// }
|
||||||
|
// // Take care of meta template fields
|
||||||
|
// $metaTemplate = $this->patchEntity($metaTemplate, $newMetaTemplate);
|
||||||
|
// $metaTemplate = $this->save($metaTemplate);
|
||||||
|
// if (!$metaTemplate) {
|
||||||
|
// return __('Something went wrong, could not update the template.');
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// if ($result) {
|
||||||
|
// $this->MetaTemplateFields->deleteAll(['meta_template_id' => $template->id]);
|
||||||
|
// foreach ($newMetaTemplate['metaFields'] as $metaField) {
|
||||||
|
// $metaField['meta_template_id'] = $template->id;
|
||||||
|
// $metaField = $this->MetaTemplateFields->newEntity($metaField);
|
||||||
|
// $this->MetaTemplateFields->save($metaField);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
public function saveNewMetaTemplate(array $template, array &$errors=[], &$savedMetaTemplate=null): bool
|
||||||
{
|
{
|
||||||
if (file_exists($filePath)) {
|
$template['meta_template_fields'] = $template['metaFields'];
|
||||||
$contents = file_get_contents($filePath);
|
unset($template['metaFields']);
|
||||||
$metaTemplate = json_decode($contents, true);
|
$metaTemplate = $this->newEntity($template, [
|
||||||
if (empty($metaTemplate)) {
|
'associated' => ['MetaTemplateFields']
|
||||||
return __('Could not load template file. Error while decoding the template\'s JSON');
|
]);
|
||||||
}
|
$tmp = $this->save($metaTemplate, [
|
||||||
if (empty($metaTemplate['uuid']) || empty($metaTemplate['version'])) {
|
'associated' => ['MetaTemplateFields']
|
||||||
return __('Could not load template file. Invalid template file. Missing template UUID or version');
|
]);
|
||||||
}
|
$error = null;
|
||||||
return $this->saveMetaFile($metaTemplate);
|
if (empty($tmp)) {
|
||||||
|
$error = new UpdateError();
|
||||||
|
$error->success = false;
|
||||||
|
$error->message = __('Could not save the template.');
|
||||||
|
$error->errors = $metaTemplate->getErrors();
|
||||||
|
$errors[] = $error;
|
||||||
}
|
}
|
||||||
return __('Could not load template file. File does not exists');
|
$savedMetaTemplate = $tmp;
|
||||||
|
return !is_null($error);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function saveMetaFile(array $newMetaTemplate)
|
public function updateMetaTemplate(array $template, array &$errors=[]): bool
|
||||||
{
|
{
|
||||||
$query = $this->find();
|
$metaTemplate = $this->getMetaTemplateElligibleForUpdate($template);
|
||||||
$query->contain('MetaTemplateFields')->where(['uuid' => $newMetaTemplate['uuid']]);
|
if (is_string($metaTemplate)) {
|
||||||
|
$errors[] = new UpdateError(false, $metaTemplate);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$metaTemplate = $this->patchEntity($metaTemplate, $template, [
|
||||||
|
'associated' => ['MetaTemplateFields']
|
||||||
|
]);
|
||||||
|
$metaTemplate = $this->save($metaTemplate, [
|
||||||
|
'associated' => ['MetaTemplateFields']
|
||||||
|
]);
|
||||||
|
if (!empty($metaTemplate)) {
|
||||||
|
$errors[] = new UpdateError(false, __('Could not save the template.'), $metaTemplate->getErrors());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateMetaTemplateWithStrategy(array $template, string $strategy, array $errors=[]): bool
|
||||||
|
{
|
||||||
|
$metaTemplate = $this->getMetaTemplateElligibleForUpdate($template);
|
||||||
|
if (is_string($metaTemplate)) {
|
||||||
|
$errors[] = new UpdateError(false, $metaTemplate);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$success = $this->executeUpdateStrategy($strategy, $template, $metaTemplate);
|
||||||
|
if (is_string($success)) {
|
||||||
|
$errors[] = new UpdateError(false, $success);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMetaTemplateElligibleForUpdate($template)
|
||||||
|
{
|
||||||
|
$query = $this->find()
|
||||||
|
->contain('MetaTemplateFields')->where([
|
||||||
|
'uuid' => $template['uuid']
|
||||||
|
]);
|
||||||
$metaTemplate = $query->first();
|
$metaTemplate = $query->first();
|
||||||
if (empty($metaTemplate)) {
|
if (empty($metaTemplate)) {
|
||||||
$metaTemplate = $this->newEntity($newMetaTemplate);
|
return __('Meta-template not found.');
|
||||||
$result = $this->save($metaTemplate);
|
}
|
||||||
if (!$result) {
|
if ($metaTemplate->version >= $template['version']) {
|
||||||
return __('Something went wrong, could not create the template.');
|
return __('Could not update the template. Local version is newer.');
|
||||||
|
}
|
||||||
|
return $metaTemplate;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function executeUpdateStrategy(string $strategy, array $template, \App\Model\Entity\MetaTemplate $metaTemplate)
|
||||||
|
{
|
||||||
|
if ($strategy == 'keep_both') {
|
||||||
|
$result = $this->executeStrategyKeep($template, $metaTemplate);
|
||||||
|
} else if ($strategy == 'delete_all') {
|
||||||
|
$result = $this->executeStrategyDeleteAll($template, $metaTemplate);
|
||||||
|
} else {
|
||||||
|
return __('Invalid strategy {0}', $strategy);
|
||||||
|
}
|
||||||
|
if (is_string($result)) {
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Old template remains untouched
|
||||||
|
// Create new template
|
||||||
|
// Migrate all non-conflicting meta-fields for one entity to the new template
|
||||||
|
// Keep all the conflicting meta-fields for one entity on the old template
|
||||||
|
public function executeStrategyKeep(array $template, \App\Model\Entity\MetaTemplate $metaTemplate)
|
||||||
|
{
|
||||||
|
$savedMetaTemplate = null;
|
||||||
|
$conflicts = $this->checkForMetaTemplateConflicts($metaTemplate, $template);
|
||||||
|
$blockingConflict = Hash::extract($conflicts, '{s}.conflicts');
|
||||||
|
$errors = [];
|
||||||
|
if (empty($blockingConflict)) { // No conflict, everything can be updated without special care
|
||||||
|
$this->updateMetaTemplate($template, $errors);
|
||||||
|
return !empty($errors) ? $errors[0] : true;
|
||||||
|
}
|
||||||
|
$entities = $this->fetchEntitiesWithMetaFieldsForTemplate($metaTemplate);
|
||||||
|
|
||||||
|
$conflictingEntities = [];
|
||||||
|
foreach ($entities as $entity) {
|
||||||
|
$conflicts = $this->checkMetaFieldsValidityUnderTemplate($entity['meta_fields'], $template);
|
||||||
|
if (!empty($conflicts)) {
|
||||||
|
$conflictingEntities[$entity->id] = $entity->id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (empty($conflictingEntities)) {
|
||||||
|
$this->updateMetaTemplate($template, $errors);
|
||||||
|
return !empty($errors) ? $errors[0] : true;
|
||||||
|
}
|
||||||
|
$template['is_default'] = $metaTemplate['is_default'];
|
||||||
|
$template['enabled'] = $metaTemplate['enabled'];
|
||||||
|
if ($metaTemplate->is_default) {
|
||||||
|
$metaTemplate->set('is_default', false);
|
||||||
|
$this->save($metaTemplate);
|
||||||
|
}
|
||||||
|
$success = $this->saveNewMetaTemplate($template, $errors, $savedMetaTemplate);
|
||||||
|
if (!empty($savedMetaTemplate)) { // conflicting entities remain untouched
|
||||||
|
$savedMetaTemplateFieldByName = Hash::combine($savedMetaTemplate['meta_template_fields'], '{n}.field', '{n}');
|
||||||
|
foreach ($entities as $entity) {
|
||||||
|
if (empty($conflictingEntities[$entity->id])) {
|
||||||
|
foreach ($entity['meta_fields'] as $metaField) {
|
||||||
|
$savedMetaTemplateField = $savedMetaTemplateFieldByName[$metaField->field];
|
||||||
|
$success = $this->replaceMetaTemplate($metaField, $savedMetaTemplateField);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($metaTemplate->version >= $newMetaTemplate['version']) {
|
return $errors[0]->message;
|
||||||
return __('Could not update the template. Local version is newer.');
|
|
||||||
}
|
|
||||||
// Take care of meta template fields
|
|
||||||
$metaTemplate = $this->patchEntity($metaTemplate, $newMetaTemplate);
|
|
||||||
$metaTemplate = $this->save($metaTemplate);
|
|
||||||
if (!$metaTemplate) {
|
|
||||||
return __('Something went wrong, could not update the template.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($result) {
|
|
||||||
$this->MetaTemplateFields->deleteAll(['meta_template_id' => $template->id]);
|
|
||||||
foreach ($newMetaTemplate['metaFields'] as $metaField) {
|
|
||||||
$metaField['meta_template_id'] = $template->id;
|
|
||||||
$metaField = $this->MetaTemplateFields->newEntity($metaField);
|
|
||||||
$this->MetaTemplateFields->save($metaField);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleMetaTemplateFieldUpdateEdgeCase($metaTemplateField, $newMetaTemplateField)
|
// Delete conflicting meta-fields
|
||||||
|
// Update template to the new version
|
||||||
|
public function executeStrategyDeleteAll($template, $metaTemplate)
|
||||||
{
|
{
|
||||||
|
$errors = [];
|
||||||
|
$conflicts = $this->checkForMetaTemplateConflicts($metaTemplate, $template);
|
||||||
|
$blockingConflict = Hash::extract($conflicts, '{s}.conflicts');
|
||||||
|
if (empty($blockingConflict)) { // No conflict, everything can be updated without special care
|
||||||
|
$this->updateMetaTemplate($template, $errors);
|
||||||
|
return !empty($errors) ? $errors[0] : true;
|
||||||
|
}
|
||||||
|
$entities = $this->fetchEntitiesWithMetaFieldsForTemplate($metaTemplate);
|
||||||
|
|
||||||
|
foreach ($entities as $entity) {
|
||||||
|
$conflicts = $this->checkMetaFieldsValidityUnderTemplate($entity['meta_fields'], $template);
|
||||||
|
$result = $this->MetaTemplateFields->MetaFields->deleteAll([
|
||||||
|
'id IN' => $conflicts
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
$this->updateMetaTemplate($template, $errors);
|
||||||
|
return !empty($errors) ? $errors[0] : true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function checkForMetaFieldConflicts(\App\Model\Entity\MetaTemplateField $metaField, array $templateField): array
|
public function replaceMetaTemplate(\App\Model\Entity\MetaField $metaField, \App\Model\Entity\MetaTemplateField $savedMetaTemplateField)
|
||||||
|
{
|
||||||
|
$metaField->set('meta_template_id', $savedMetaTemplateField->meta_template_id);
|
||||||
|
$metaField->set('meta_template_field_id', $savedMetaTemplateField->id);
|
||||||
|
$metaField = $this->MetaTemplateFields->MetaFields->save($metaField);
|
||||||
|
return !empty($metaField);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkMetaFieldsValidityUnderTemplate(array $metaFields, array $template): array
|
||||||
|
{
|
||||||
|
$conflicting = [];
|
||||||
|
$metaTemplateFieldByName = [];
|
||||||
|
foreach ($template['metaFields'] as $metaField) {
|
||||||
|
$metaTemplateFieldByName[$metaField['field']] = $this->MetaTemplateFields->newEntity($metaField);
|
||||||
|
}
|
||||||
|
foreach ($metaFields as $metaField) {
|
||||||
|
$isValid = $this->MetaTemplateFields->MetaFields->isValidMetaFieldForMetaTemplateField(
|
||||||
|
$metaField->value,
|
||||||
|
$metaTemplateFieldByName[$metaField->field]
|
||||||
|
);
|
||||||
|
if ($isValid !== true) {
|
||||||
|
$conflicting[] = $metaField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $conflicting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkMetaFieldsValidityUnderExistingMetaTemplate(array $metaFields, \App\Model\Entity\MetaTemplate $metaTemplate): array
|
||||||
|
{
|
||||||
|
$conflicting = [];
|
||||||
|
$metaTemplateFieldByName = [];
|
||||||
|
foreach ($metaTemplate->meta_template_fields as $metaTemplateField) {
|
||||||
|
$metaTemplateFieldByName[$metaTemplateField->field] = $metaTemplateField;
|
||||||
|
}
|
||||||
|
foreach ($metaFields as $metaField) {
|
||||||
|
if ($metaField->meta_template_id != $metaTemplate->id) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$isValid = $this->MetaTemplateFields->MetaFields->isValidMetaFieldForMetaTemplateField(
|
||||||
|
$metaField->value,
|
||||||
|
$metaTemplateFieldByName[$metaField->field]
|
||||||
|
);
|
||||||
|
if ($isValid !== true) {
|
||||||
|
$conflicting[] = $metaField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $conflicting;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fetchEntitiesWithMetaFieldsForTemplate(\App\Model\Entity\MetaTemplate $metaTemplate): array
|
||||||
|
{
|
||||||
|
$entitiesIDWithMetaFields = $this->MetaTemplateFields->MetaFields->find()
|
||||||
|
->select(['parent_id', 'scope'])
|
||||||
|
->where(['MetaFields.meta_template_id' => $metaTemplate->id])
|
||||||
|
->group('parent_id')
|
||||||
|
->all()
|
||||||
|
->toList();
|
||||||
|
$className = Inflector::camelize(Inflector::pluralize($entitiesIDWithMetaFields[0]->scope));
|
||||||
|
|
||||||
|
$table = TableRegistry::getTableLocator()->get($className);
|
||||||
|
$entities = $table->find()
|
||||||
|
->where(['id IN' => Hash::extract($entitiesIDWithMetaFields, '{n}.parent_id')])
|
||||||
|
->contain([
|
||||||
|
'MetaFields' => [
|
||||||
|
'conditions' => [
|
||||||
|
'MetaFields.meta_template_id' => $metaTemplate->id
|
||||||
|
]
|
||||||
|
]
|
||||||
|
])
|
||||||
|
->all()->toList();
|
||||||
|
|
||||||
|
return $entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function checkForMetaFieldConflicts(\App\Model\Entity\MetaTemplateField $metaTemplateField, array $templateField): array
|
||||||
{
|
{
|
||||||
$result = [
|
$result = [
|
||||||
'updateable' => true,
|
'updateable' => true,
|
||||||
'conflicts' => [],
|
'conflicts' => [],
|
||||||
|
'conflictingEntities' => [],
|
||||||
];
|
];
|
||||||
if ($metaField->multiple && $templateField['multiple'] == false) { // Field is no longer multiple
|
if ($metaTemplateField->multiple && $templateField['multiple'] == false) { // Field is no longer multiple
|
||||||
$result['updateable'] = false;
|
$query = $this->MetaTemplateFields->MetaFields->find();
|
||||||
$result['conflicts'][] = __('This field is no longer multiple');
|
$query
|
||||||
|
->enableHydration(false)
|
||||||
|
->select([
|
||||||
|
'parent_id',
|
||||||
|
'meta_template_field_id',
|
||||||
|
'count' => $query->func()->count('meta_template_field_id'),
|
||||||
|
])
|
||||||
|
->where([
|
||||||
|
'meta_template_field_id' => $metaTemplateField->id,
|
||||||
|
])
|
||||||
|
->group(['parent_id'])
|
||||||
|
->having(['count >' => 1]);
|
||||||
|
$conflictingStatus = $query->all()->toList();
|
||||||
|
if (!empty($conflictingStatus)) {
|
||||||
|
$result['updateable'] = false;
|
||||||
|
$result['conflicts'][] = __('This field is no longer multiple');
|
||||||
|
$result['conflictingEntities'] = Hash::extract($conflictingStatus, '{n}.parent_id');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!empty($templateField['regex']) && $templateField['regex'] != $metaField->regex) {
|
if (!empty($templateField['regex']) && $templateField['regex'] != $metaTemplateField->regex) {
|
||||||
// FIXME: Check if all meta-fields pass the new validation
|
$query = $this->MetaTemplateFields->MetaFields->find();
|
||||||
$result['updateable'] = false;
|
$query
|
||||||
$result['conflicts'][] = __('This field is instantiated with values not passing the validation anymore');
|
->enableHydration(false)
|
||||||
|
->select([
|
||||||
|
'parent_id',
|
||||||
|
'scope',
|
||||||
|
'meta_template_field_id',
|
||||||
|
])
|
||||||
|
->where([
|
||||||
|
'meta_template_field_id' => $metaTemplateField->id,
|
||||||
|
]);
|
||||||
|
$entitiesWithMetaField = $query->all()->toList();
|
||||||
|
if (!empty($entitiesWithMetaField)) {
|
||||||
|
$className = Inflector::camelize(Inflector::pluralize($entitiesWithMetaField[0]['scope']));
|
||||||
|
$table = TableRegistry::getTableLocator()->get($className);
|
||||||
|
$entities = $table->find()
|
||||||
|
->where(['id IN' => Hash::extract($entitiesWithMetaField, '{n}.parent_id')])
|
||||||
|
->contain([
|
||||||
|
'MetaFields' => [
|
||||||
|
'conditions' => [
|
||||||
|
'MetaFields.meta_template_field_id' => $metaTemplateField->id
|
||||||
|
]
|
||||||
|
]
|
||||||
|
])
|
||||||
|
->all()->toList();
|
||||||
|
$conflictingEntities = [];
|
||||||
|
foreach ($entities as $entity) {
|
||||||
|
foreach ($entity['meta_fields'] as $metaField) {
|
||||||
|
$isValid = $this->MetaTemplateFields->MetaFields->isValidMetaFieldForMetaTemplateField(
|
||||||
|
$metaField->value,
|
||||||
|
$templateField
|
||||||
|
);
|
||||||
|
if ($isValid !== true) {
|
||||||
|
$conflictingEntities[] = $entity->id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!empty($conflictingEntities)) {
|
||||||
|
$result['updateable'] = $result['updateable'] && false;
|
||||||
|
$result['conflicts'][] = __('This field is instantiated with values not passing the validation anymore');
|
||||||
|
$result['conflictingEntities'] = $conflictingEntities;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function checkForMetaTemplateConflicts(\App\Model\Entity\MetaTemplate $metaTemplate, array $template): array
|
public function checkForMetaTemplateConflicts(\App\Model\Entity\MetaTemplate $metaTemplate, $template): array
|
||||||
{
|
{
|
||||||
|
$templateMetaFields = [];
|
||||||
|
if (!is_array($template) && get_class($template) == 'App\Model\Entity\MetaTemplate') {
|
||||||
|
$templateMetaFields = $template->meta_template_fields;
|
||||||
|
} else {
|
||||||
|
$templateMetaFields = $template['metaFields'];
|
||||||
|
}
|
||||||
$conflicts = [];
|
$conflicts = [];
|
||||||
$existingMetaTemplateFields = Hash::combine($metaTemplate->toArray(), 'meta_template_fields.{n}.field');
|
$existingMetaTemplateFields = Hash::combine($metaTemplate->toArray(), 'meta_template_fields.{n}.field');
|
||||||
foreach ($template['metaFields'] as $newMetaField) {
|
foreach ($templateMetaFields as $newMetaField) {
|
||||||
foreach ($metaTemplate->meta_template_fields as $metaField) {
|
foreach ($metaTemplate->meta_template_fields as $metaField) {
|
||||||
if ($newMetaField['field'] == $metaField->field) {
|
if ($newMetaField['field'] == $metaField->field) {
|
||||||
unset($existingMetaTemplateFields[$metaField->field]);
|
unset($existingMetaTemplateFields[$metaField->field]);
|
||||||
$templateConflicts = $this->checkForMetaFieldConflicts($metaField, $newMetaField);
|
$templateConflicts = $this->checkForMetaFieldConflicts($metaField, !is_array($newMetaField) && get_class($newMetaField) == 'App\Model\Entity\MetaTemplateField' ? $newMetaField->toArray() : $newMetaField);
|
||||||
if (!$templateConflicts['updateable']) {
|
$conflicts[$metaField->field] = $templateConflicts;
|
||||||
$conflicts[$metaField->field] = $templateConflicts;
|
$conflicts[$metaField->field]['existing_meta_template_field'] = $metaField;
|
||||||
}
|
$conflicts[$metaField->field]['existing_meta_template_field']['conflicts'] = $templateConflicts['conflicts'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -361,7 +811,7 @@ class MetaTemplatesTable extends AppTable
|
||||||
return $conflicts;
|
return $conflicts;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function checkUpdatesForTemplate($template): array
|
public function checkUpdatesForTemplate($template, $metaTemplate=null): array
|
||||||
{
|
{
|
||||||
$result = [
|
$result = [
|
||||||
'new' => true,
|
'new' => true,
|
||||||
|
@ -370,11 +820,15 @@ class MetaTemplatesTable extends AppTable
|
||||||
'conflicts' => [],
|
'conflicts' => [],
|
||||||
'template' => $template,
|
'template' => $template,
|
||||||
];
|
];
|
||||||
$query = $this->find()
|
if (is_null($metaTemplate)) {
|
||||||
->contain('MetaTemplateFields')->where([
|
$query = $this->find()
|
||||||
'uuid' => $template['uuid']
|
->contain('MetaTemplateFields')
|
||||||
]);
|
->where([
|
||||||
$metaTemplate = $query->first();
|
'uuid' => $template['uuid'],
|
||||||
|
])
|
||||||
|
->order(['version' => 'DESC']);
|
||||||
|
$metaTemplate = $query->first();
|
||||||
|
}
|
||||||
if (!empty($metaTemplate)) {
|
if (!empty($metaTemplate)) {
|
||||||
$result['existing_template'] = $metaTemplate;
|
$result['existing_template'] = $metaTemplate;
|
||||||
$result['current_version'] = $metaTemplate->version;
|
$result['current_version'] = $metaTemplate->version;
|
||||||
|
@ -386,6 +840,7 @@ class MetaTemplatesTable extends AppTable
|
||||||
$result['conflicts'][] = __('Could not update the template. Local version is equal or newer.');
|
$result['conflicts'][] = __('Could not update the template. Local version is equal or newer.');
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
$conflicts = $this->checkForMetaTemplateConflicts($metaTemplate, $template);
|
$conflicts = $this->checkForMetaTemplateConflicts($metaTemplate, $template);
|
||||||
if (!empty($conflicts)) {
|
if (!empty($conflicts)) {
|
||||||
$result['conflicts'] = $conflicts;
|
$result['conflicts'] = $conflicts;
|
||||||
|
@ -395,4 +850,105 @@ class MetaTemplatesTable extends AppTable
|
||||||
}
|
}
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function checkUpdateForMetaTemplate($template, $metaTemplate): array
|
||||||
|
{
|
||||||
|
$result = $this->checkUpdatesForTemplate($template, $metaTemplate);
|
||||||
|
$result['meta_field_amount'] = $this->MetaTemplateFields->MetaFields->find()->where(['meta_template_id' => $metaTemplate->id])->count();
|
||||||
|
$result['can_be_removed'] = empty($result['meta_field_amount']) && empty($result['to_existing']);
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function massageMetaFieldsBeforeSave($entity, $input, $metaTemplate)
|
||||||
|
{
|
||||||
|
$metaFieldsTable = $this->MetaTemplateFields->MetaFields;
|
||||||
|
$className = Inflector::camelize(Inflector::pluralize($metaTemplate->scope));
|
||||||
|
$entityTable = TableRegistry::getTableLocator()->get($className);
|
||||||
|
$metaFieldsIndex = [];
|
||||||
|
if (!empty($entity->meta_fields)) {
|
||||||
|
foreach ($entity->meta_fields as $i => $metaField) {
|
||||||
|
$metaFieldsIndex[$metaField->id] = $i;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$entity->meta_fields = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$metaFieldsToDelete = [];
|
||||||
|
foreach ($input['MetaTemplates'] as $template_id => $template) {
|
||||||
|
foreach ($template['meta_template_fields'] as $meta_template_field_id => $meta_template_field) {
|
||||||
|
$rawMetaTemplateField = $metaTemplate->meta_template_fields[$meta_template_field_id];
|
||||||
|
foreach ($meta_template_field['metaFields'] as $meta_field_id => $meta_field) {
|
||||||
|
if ($meta_field_id == 'new') { // create new meta_field
|
||||||
|
$new_meta_fields = $meta_field;
|
||||||
|
foreach ($new_meta_fields as $new_value) {
|
||||||
|
if (!empty($new_value)) {
|
||||||
|
$metaField = $metaFieldsTable->newEmptyEntity();
|
||||||
|
$metaFieldsTable->patchEntity($metaField, [
|
||||||
|
'value' => $new_value,
|
||||||
|
'scope' => $entityTable->getBehavior('MetaFields')->getScope(),
|
||||||
|
'field' => $rawMetaTemplateField->field,
|
||||||
|
'meta_template_id' => $rawMetaTemplateField->meta_template_id,
|
||||||
|
'meta_template_field_id' => $rawMetaTemplateField->id,
|
||||||
|
'parent_id' => $entity->id,
|
||||||
|
'uuid' => Text::uuid(),
|
||||||
|
]);
|
||||||
|
$entity->meta_fields[] = $metaField;
|
||||||
|
$entity->MetaTemplates[$template_id]->meta_template_fields[$meta_template_field_id]->metaFields[] = $metaField;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$new_value = $meta_field['value'];
|
||||||
|
if (!empty($new_value)) { // update meta_field and attach validation errors
|
||||||
|
if (!empty($metaFieldsIndex[$meta_field_id])) {
|
||||||
|
$index = $metaFieldsIndex[$meta_field_id];
|
||||||
|
$metaFieldsTable->patchEntity($entity->meta_fields[$index], [
|
||||||
|
'value' => $new_value, 'meta_template_field_id' => $rawMetaTemplateField->id
|
||||||
|
], ['value']);
|
||||||
|
$metaFieldsTable->patchEntity(
|
||||||
|
$entity->MetaTemplates[$template_id]->meta_template_fields[$meta_template_field_id]->metaFields[$meta_field_id],
|
||||||
|
['value' => $new_value, 'meta_template_field_id' => $rawMetaTemplateField->id],
|
||||||
|
['value']
|
||||||
|
);
|
||||||
|
} else { // metafield comes from a second post where the temporary entity has already been created
|
||||||
|
$metaField = $metaFieldsTable->newEmptyEntity();
|
||||||
|
$metaFieldsTable->patchEntity($metaField, [
|
||||||
|
'value' => $new_value,
|
||||||
|
'scope' => $entityTable->getBehavior('MetaFields')->getScope(), // get scope from behavior
|
||||||
|
'field' => $rawMetaTemplateField->field,
|
||||||
|
'meta_template_id' => $rawMetaTemplateField->meta_template_id,
|
||||||
|
'meta_template_field_id' => $rawMetaTemplateField->id,
|
||||||
|
'parent_id' => $entity->id,
|
||||||
|
'uuid' => Text::uuid(),
|
||||||
|
]);
|
||||||
|
$entity->meta_fields[] = $metaField;
|
||||||
|
$entity->MetaTemplates[$template_id]->meta_template_fields[$meta_template_field_id]->metaFields[] = $metaField;
|
||||||
|
}
|
||||||
|
} else { // Metafield value is empty, indicating the field should be removed
|
||||||
|
$index = $metaFieldsIndex[$meta_field_id];
|
||||||
|
$metaFieldsToDelete[] = $entity->meta_fields[$index];
|
||||||
|
unset($entity->meta_fields[$index]);
|
||||||
|
unset($entity->MetaTemplates[$template_id]->meta_template_fields[$meta_template_field_id]->metaFields[$meta_field_id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$entity->setDirty('meta_fields', true);
|
||||||
|
return ['entity' => $entity, 'metafields_to_delete' => $metaFieldsToDelete];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class UpdateError
|
||||||
|
{
|
||||||
|
public $success;
|
||||||
|
public $message = '';
|
||||||
|
public $errors = [];
|
||||||
|
|
||||||
|
public function __construct($success=false, $message='', $errors=[])
|
||||||
|
{
|
||||||
|
$this->success = $success;
|
||||||
|
$this->message = $message;
|
||||||
|
$this->errors = $errors;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
<?php
|
||||||
|
use Cake\Utility\Inflector;
|
||||||
|
use Cake\Routing\Router;
|
||||||
|
|
||||||
|
$urlNewestMetaTemplate = Router::url([
|
||||||
|
'controller' => 'metaTemplates',
|
||||||
|
'action' => 'view',
|
||||||
|
$newestMetaTemplate->id
|
||||||
|
]);
|
||||||
|
|
||||||
|
$bodyHtml = '';
|
||||||
|
$bodyHtml .= sprintf('<div><span>%s: </span><span class="fw-bold">%s</span></div>', __('Current version'), h($metaTemplate->version));
|
||||||
|
$bodyHtml .= sprintf('<div><span>%s: </span><a href="%s" target="_blank" class="fw-bold">%s</a></div>', __('Newest version'), $urlNewestMetaTemplate, h($newestMetaTemplate->version));
|
||||||
|
$bodyHtml .= sprintf('<h4 class="my-2">%s</h4>', __('Entities with meta-fields to be updated:'));
|
||||||
|
|
||||||
|
$bodyHtml .= '<ul>';
|
||||||
|
foreach ($entities as $entity) {
|
||||||
|
$url = Router::url([
|
||||||
|
'controller' => Inflector::pluralize($metaTemplate->scope),
|
||||||
|
'action' => 'view',
|
||||||
|
$entity->id
|
||||||
|
]);
|
||||||
|
$bodyHtml .= sprintf(
|
||||||
|
'<li><a href="%s" target="_blank">%s</a> <span class="fw-light">%s<span></li>',
|
||||||
|
$url,
|
||||||
|
__('{0}::{1}', h(Inflector::humanize($metaTemplate->scope)), $entity->id),
|
||||||
|
__('has {0} meta-fields to update', count($entity->meta_fields))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
$bodyHtml .= '</ul>';
|
||||||
|
|
||||||
|
echo $this->Bootstrap->modal([
|
||||||
|
'titleHtml' => __('{0} is an old meta-template and has meta-fields to be updated', sprintf('<i class="me-1">%s</i>', h($metaTemplate->name))),
|
||||||
|
'bodyHtml' => $bodyHtml,
|
||||||
|
'size' => 'lg',
|
||||||
|
'type' => 'ok-only',
|
||||||
|
]);
|
||||||
|
?>
|
|
@ -159,11 +159,6 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
||||||
'sort' => 'uuid',
|
'sort' => 'uuid',
|
||||||
'data_path' => 'uuid'
|
'data_path' => 'uuid'
|
||||||
],
|
],
|
||||||
[
|
|
||||||
'name' => __('Updateable'),
|
|
||||||
'data_path' => 'status',
|
|
||||||
'element' => 'update_status',
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
'title' => __('Meta Field Templates'),
|
'title' => __('Meta Field Templates'),
|
||||||
'description' => __('The various templates used to enrich certain objects by a set of standardised fields.'),
|
'description' => __('The various templates used to enrich certain objects by a set of standardised fields.'),
|
||||||
|
@ -176,15 +171,39 @@ echo $this->element('genericElements/IndexTable/index_table', [
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'open_modal' => '/metaTemplates/update/[onclick_params_data_path]',
|
'open_modal' => '/metaTemplates/update/[onclick_params_data_path]',
|
||||||
'modal_params_data_path' => 'id',
|
'modal_params_data_path' => 'uuid',
|
||||||
'title' => __('Update Meta-Template'),
|
'title' => __('Update Meta-Template'),
|
||||||
'icon' => 'download',
|
'icon' => 'download',
|
||||||
'complex_requirement' => [
|
'complex_requirement' => [
|
||||||
'function' => function ($row, $options) {
|
'function' => function ($row, $options) {
|
||||||
return empty($row['status']['up_to_date']);
|
return empty($row['status']['up_to_date']) && empty($row['status']['to_existing']);
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
]
|
],
|
||||||
|
[
|
||||||
|
'open_modal' => '/metaTemplates/getMetaFieldsToUpdate/[onclick_params_data_path]',
|
||||||
|
'modal_params_data_path' => 'id',
|
||||||
|
'title' => __('Get meta-fields that should be moved to the newest version of this meta-template'),
|
||||||
|
'icon' => 'exclamation-triangle',
|
||||||
|
'variant' => 'warning',
|
||||||
|
'complex_requirement' => [
|
||||||
|
'function' => function ($row, $options) {
|
||||||
|
return !empty($row['status']['to_existing']) && empty($row['status']['can_be_removed']);
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'open_modal' => '/metaTemplates/delete/[onclick_params_data_path]',
|
||||||
|
'modal_params_data_path' => 'id',
|
||||||
|
'title' => __('Get meta-fields that should be moved to the newest version of this meta-template'),
|
||||||
|
'icon' => 'trash',
|
||||||
|
'variant' => 'success',
|
||||||
|
'complex_requirement' => [
|
||||||
|
'function' => function ($row, $options) {
|
||||||
|
return !empty($row['status']['to_existing']) && !empty($row['status']['can_be_removed']);
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
]);
|
]);
|
||||||
|
|
|
@ -0,0 +1,92 @@
|
||||||
|
<?
|
||||||
|
|
||||||
|
?>
|
||||||
|
<h3><?= h($oldMetaTemplate->name) ?></h3>
|
||||||
|
<div class="container-fluid">
|
||||||
|
<div class="row gx-2">
|
||||||
|
<div class="col">
|
||||||
|
<div class="panel">
|
||||||
|
<h4 class="d-flex justify-content-between align-items-center">
|
||||||
|
<?= __('Version {0}', h($oldMetaTemplate->version)) ?>
|
||||||
|
<?=
|
||||||
|
$this->Bootstrap->badge([
|
||||||
|
'text' => __('Data to be migrated over'),
|
||||||
|
'variant' => 'danger',
|
||||||
|
'class' => 'fs-7'
|
||||||
|
])
|
||||||
|
?>
|
||||||
|
</h4>
|
||||||
|
<div>
|
||||||
|
<?=
|
||||||
|
$this->element('MetaTemplates/migrationToNewVersionForm', [
|
||||||
|
'metaTemplate' => $oldMetaTemplate,
|
||||||
|
'entity' => $entity,
|
||||||
|
])
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="col pt-4 d-flex justify-content-center" style="max-width: 32px;">
|
||||||
|
<?= $this->Bootstrap->icon('arrow-alt-circle-right') ?>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="panel">
|
||||||
|
<h4 class="d-flex justify-content-between align-items-center">
|
||||||
|
<?= __('Version {0}', h($newMetaTemplate->version)) ?>
|
||||||
|
<?=
|
||||||
|
$this->Bootstrap->badge([
|
||||||
|
'text' => __('Data to be saved'),
|
||||||
|
'variant' => 'success',
|
||||||
|
'class' => 'fs-7'
|
||||||
|
])
|
||||||
|
?>
|
||||||
|
</h4>
|
||||||
|
<div class="to-save-container">
|
||||||
|
<?=
|
||||||
|
$this->element('MetaTemplates/migrationToNewVersionForm', [
|
||||||
|
'metaTemplate' => $newMetaTemplate,
|
||||||
|
'entity' => $entity,
|
||||||
|
])
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex flex-row-reverse">
|
||||||
|
<?=
|
||||||
|
$this->Bootstrap->button([
|
||||||
|
'text' => __('Update to version {0}', h($newMetaTemplate->version)),
|
||||||
|
'variant' => 'success',
|
||||||
|
'params' => [
|
||||||
|
'onclick' => 'submitMigration()'
|
||||||
|
]
|
||||||
|
])
|
||||||
|
?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php
|
||||||
|
echo $this->Html->scriptBlock(sprintf(
|
||||||
|
'var csrfToken = %s;',
|
||||||
|
json_encode($this->request->getAttribute('csrfToken'))
|
||||||
|
));
|
||||||
|
?>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
const movedMetaTemplateFields = <?= json_encode($movedMetaTemplateFields) ?>;
|
||||||
|
const oldMetaTemplateID = <?= h($oldMetaTemplate->id) ?>;
|
||||||
|
movedMetaTemplateFields.forEach(metaTemplateId => {
|
||||||
|
let validInputPath = `MetaTemplates.${oldMetaTemplateID}.meta_template_fields.${movedMetaTemplateFields}`
|
||||||
|
const $inputs = $(`input[field^="${validInputPath}"]`)
|
||||||
|
$inputs.addClass('is-valid');
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
function submitMigration() {
|
||||||
|
const $form = $('.to-save-container form')
|
||||||
|
console.log($form.attr('action'));
|
||||||
|
AJAXApi.quickPostForm($form[0])
|
||||||
|
// $form.submit()
|
||||||
|
}
|
||||||
|
</script>
|
|
@ -17,6 +17,26 @@ if ($updateableTemplate['up-to-date']) {
|
||||||
'html' => __('This meta-template can be updated to version {0} (current: {1}).', sprintf('<strong>%s</strong>', h($templateOnDisk['version'])), h($metaTemplate->version)),
|
'html' => __('This meta-template can be updated to version {0} (current: {1}).', sprintf('<strong>%s</strong>', h($templateOnDisk['version'])), h($metaTemplate->version)),
|
||||||
'dismissible' => false,
|
'dismissible' => false,
|
||||||
]);
|
]);
|
||||||
|
$form = $this->element('genericElements/Form/genericForm', [
|
||||||
|
'entity' => null,
|
||||||
|
'ajax' => false,
|
||||||
|
'raw' => true,
|
||||||
|
'data' => [
|
||||||
|
'model' => 'MetaTemplate',
|
||||||
|
'fields' => [
|
||||||
|
[
|
||||||
|
'field' => 'update_strategy',
|
||||||
|
'type' => 'checkbox',
|
||||||
|
'value' => 'update',
|
||||||
|
'checked' => true,
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'submit' => [
|
||||||
|
'action' => $this->request->getParam('action')
|
||||||
|
],
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
$bodyHtml .= sprintf('<div class="d-none">%s</div>', $form);
|
||||||
} else {
|
} else {
|
||||||
$modalSize = 'xl';
|
$modalSize = 'xl';
|
||||||
$bodyHtml .= $this->Bootstrap->alert([
|
$bodyHtml .= $this->Bootstrap->alert([
|
||||||
|
@ -47,7 +67,7 @@ echo $this->Bootstrap->modal([
|
||||||
'size' => $modalSize,
|
'size' => $modalSize,
|
||||||
'type' => $modalType,
|
'type' => $modalType,
|
||||||
'confirmText' => __('Update meta-templates'),
|
'confirmText' => __('Update meta-templates'),
|
||||||
'confirmFunction' => 'updateMetaTemplate',
|
// 'confirmFunction' => 'updateMetaTemplate',
|
||||||
]);
|
]);
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,31 @@
|
||||||
<form>
|
<?php
|
||||||
|
$form = $this->element('genericElements/Form/genericForm', [
|
||||||
|
'entity' => null,
|
||||||
|
'ajax' => false,
|
||||||
|
'raw' => true,
|
||||||
|
'data' => [
|
||||||
|
'model' => 'MetaTemplate',
|
||||||
|
'fields' => [
|
||||||
|
[
|
||||||
|
'field' => 'update_strategy',
|
||||||
|
'type' => 'radio',
|
||||||
|
'options' => [
|
||||||
|
['value' => 'keep_both', 'text' => 'keep_both', 'id' => 'radio_keep_both'],
|
||||||
|
['value' => 'delete', 'text' => 'delete', 'id' => 'radio_delete'],
|
||||||
|
],
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'submit' => [
|
||||||
|
'action' => $this->request->getParam('action')
|
||||||
|
],
|
||||||
|
]
|
||||||
|
]);
|
||||||
|
?>
|
||||||
|
|
||||||
|
<div class="conflict-resolution-picker">
|
||||||
<div class="mt-3 d-flex justify-content-center">
|
<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">
|
<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>
|
<input type="radio" class="btn-check" name="btnradio" id="btnradio1" autocomplete="off" value="keep_both" checked>
|
||||||
<label class="btn btn-outline-primary mw-33" for="btnradio1">
|
<label class="btn btn-outline-primary mw-33" for="btnradio1">
|
||||||
<div>
|
<div>
|
||||||
<h5 class="mb-3"><?= __('Keep both template') ?></h5>
|
<h5 class="mb-3"><?= __('Keep both template') ?></h5>
|
||||||
|
@ -13,7 +37,7 @@
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<input type="radio" class="btn-check" name="btnradio" id="btnradio3" autocomplete="off">
|
<input type="radio" class="btn-check" name="btnradio" id="btnradio3" autocomplete="off" value="delete">
|
||||||
<label class="btn btn-outline-danger mw-33" for="btnradio3">
|
<label class="btn btn-outline-danger mw-33" for="btnradio3">
|
||||||
<div>
|
<div>
|
||||||
<h5 class="mb-3"><?= __('Delete conflicting fields') ?></h5>
|
<h5 class="mb-3"><?= __('Delete conflicting fields') ?></h5>
|
||||||
|
@ -25,4 +49,31 @@
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</div>
|
||||||
|
|
||||||
|
<div class="d-none conflict-resolution-form-container">
|
||||||
|
<?= $form ?>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
(function() {
|
||||||
|
const $form = $('.conflict-resolution-form-container form')
|
||||||
|
const $keep = $form.find('input#radio_keep_both')
|
||||||
|
const $delete = $form.find('input#radio_delete')
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('.conflict-resolution-picker').find('input[type="radio"]').change(function() {
|
||||||
|
updateSelected($(this).val())
|
||||||
|
})
|
||||||
|
updateSelected('keep_both')
|
||||||
|
})
|
||||||
|
|
||||||
|
function updateSelected(choice) {
|
||||||
|
if (choice == 'keep_both') {
|
||||||
|
$keep.prop('checked', true)
|
||||||
|
} else if (choice == 'delete') {
|
||||||
|
$delete.prop('checked', true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}())
|
||||||
|
</script>
|
|
@ -1,9 +1,14 @@
|
||||||
|
<?php
|
||||||
|
use Cake\Utility\Inflector;
|
||||||
|
use Cake\Routing\Router;
|
||||||
|
?>
|
||||||
|
|
||||||
<table class="table">
|
<table class="table">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th scope="col"><?= __('Field name') ?></th>
|
<th scope="col"><?= __('Field name') ?></th>
|
||||||
<th scope="col"><?= __('Conflict') ?></th>
|
<th scope="col"><?= __('Conflict') ?></th>
|
||||||
<th scope="col"><?= __('Automatic Resolution') ?></th>
|
<th scope="col"><?= __('Conflicting entities') ?></th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
@ -16,10 +21,21 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<?php
|
<?php
|
||||||
echo $this->Bootstrap->badge([
|
foreach ($fieldConflict['conflictingEntities'] as $i => $id) {
|
||||||
'text' => __('Affected meta-fields will be removed'),
|
if ($i > 0) {
|
||||||
'variant' => 'danger',
|
echo ', ';
|
||||||
])
|
}
|
||||||
|
if ($i > 10) {
|
||||||
|
echo sprintf('<span class="fw-light fs-7">%s</span>', __('{0} more', count($fieldConflict['conflictingEntities'])-$i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$url = Router::url([
|
||||||
|
'controller' => Inflector::pluralize($updateableTemplate['existing_template']->scope),
|
||||||
|
'action' => 'view',
|
||||||
|
$id
|
||||||
|
]);
|
||||||
|
echo sprintf('<a href="%s" target="_blank">%s</a>', $url, __('{0} #{1}', h(Inflector::humanize($updateableTemplate['existing_template']->scope)), h($id)));
|
||||||
|
}
|
||||||
?>
|
?>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
$formRandomValue = Cake\Utility\Security::randomString(8);
|
||||||
|
|
||||||
|
echo $this->Form->create($entity, ['id' => 'form-' . $formRandomValue]);
|
||||||
|
echo $this->element(
|
||||||
|
'genericElements/Form/metaTemplateForm',
|
||||||
|
[
|
||||||
|
'metaTemplate' => $metaTemplate,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
echo $this->Form->end();
|
||||||
|
?>
|
|
@ -0,0 +1,73 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Cake\Utility\Inflector;
|
||||||
|
|
||||||
|
$default_template = [
|
||||||
|
'inputContainer' => '<div class="row mb-3 metafield-container">{{content}}</div>',
|
||||||
|
'inputContainerError' => '<div class="row mb-3 metafield-container has-error">{{content}}</div>',
|
||||||
|
'formGroup' => '<label class="col-sm-2 col-form-label form-label" {{attrs}}>{{label}}</label><div class="col-sm-10">{{input}}{{error}}</div>',
|
||||||
|
'error' => '<div class="error-message invalid-feedback d-block">{{content}}</div>',
|
||||||
|
'errorList' => '<ul>{{content}}</ul>',
|
||||||
|
'errorItem' => '<li>{{text}}</li>',
|
||||||
|
];
|
||||||
|
$this->Form->setTemplates($default_template);
|
||||||
|
$backupTemplates = $this->Form->getTemplates();
|
||||||
|
|
||||||
|
$fieldsHtml = '';
|
||||||
|
foreach ($metaTemplate->meta_template_fields as $metaTemplateField) {
|
||||||
|
$metaTemplateField->label = Inflector::humanize($metaTemplateField->field);
|
||||||
|
if (!empty($metaTemplateField->metaFields)) {
|
||||||
|
if (!empty($metaTemplateField->multiple)) {
|
||||||
|
$fieldsHtml .= $this->element(
|
||||||
|
'genericElements/Form/multiFieldScaffold',
|
||||||
|
[
|
||||||
|
'metaFieldsEntities' => $metaTemplateField->metaFields,
|
||||||
|
'metaTemplateField' => $metaTemplateField,
|
||||||
|
'multiple' => !empty($metaTemplateField->multiple),
|
||||||
|
'form' => $this->Form,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$metaField = reset($metaTemplateField->metaFields);
|
||||||
|
$fieldData = [
|
||||||
|
'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.%s.value', $metaField->meta_template_id, $metaField->meta_template_field_id, $metaField->id),
|
||||||
|
'label' => $metaTemplateField->label,
|
||||||
|
];
|
||||||
|
$this->Form->setTemplates($backupTemplates);
|
||||||
|
$fieldsHtml .= $this->element(
|
||||||
|
'genericElements/Form/fieldScaffold',
|
||||||
|
[
|
||||||
|
'fieldData' => $fieldData,
|
||||||
|
'metaTemplateField' => $metaTemplateField,
|
||||||
|
'form' => $this->Form
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!empty($metaTemplateField->multiple)) {
|
||||||
|
$fieldsHtml .= $this->element(
|
||||||
|
'genericElements/Form/multiFieldScaffold',
|
||||||
|
[
|
||||||
|
'metaFieldsEntities' => [],
|
||||||
|
'metaTemplateField' => $metaTemplateField,
|
||||||
|
'multiple' => !empty($metaTemplateField->multiple),
|
||||||
|
'form' => $this->Form,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
$this->Form->setTemplates($backupTemplates);
|
||||||
|
$fieldData = [
|
||||||
|
'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new.0', $metaTemplateField->meta_template_id, $metaTemplateField->id),
|
||||||
|
'label' => $metaTemplateField->label,
|
||||||
|
];
|
||||||
|
$fieldsHtml .= $this->element(
|
||||||
|
'genericElements/Form/fieldScaffold',
|
||||||
|
[
|
||||||
|
'fieldData' => $fieldData,
|
||||||
|
'form' => $this->Form
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
echo $fieldsHtml;
|
|
@ -1,82 +1,18 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
use Cake\Utility\Inflector;
|
|
||||||
|
|
||||||
$default_template = [
|
|
||||||
'inputContainer' => '<div class="row mb-3 metafield-container">{{content}}</div>',
|
|
||||||
'inputContainerError' => '<div class="row mb-3 metafield-container has-error">{{content}}</div>',
|
|
||||||
'formGroup' => '<label class="col-sm-2 col-form-label form-label" {{attrs}}>{{label}}</label><div class="col-sm-10">{{input}}{{error}}</div>',
|
|
||||||
];
|
|
||||||
$this->Form->setTemplates($default_template);
|
|
||||||
$backupTemplates = $this->Form->getTemplates();
|
$backupTemplates = $this->Form->getTemplates();
|
||||||
$tabData = [];
|
$tabData = [];
|
||||||
foreach ($entity->MetaTemplates as $i => $metaTemplate) {
|
foreach ($entity->MetaTemplates as $i => $metaTemplate) {
|
||||||
if ($metaTemplate->is_default) {
|
$tabData['navs'][$i] = [
|
||||||
$tabData['navs'][$i] = [
|
'html' => $this->element('/genericElements/MetaTemplates/metaTemplateNav', ['metaTemplate' => $metaTemplate])
|
||||||
'html' => $this->element('/genericElements/MetaTemplates/metaTemplateNav', ['metaTemplate' => $metaTemplate])
|
];
|
||||||
];
|
|
||||||
} else {
|
|
||||||
$tabData['navs'][$i] = [
|
|
||||||
'text' => $metaTemplate->name
|
|
||||||
];
|
|
||||||
}
|
|
||||||
$fieldsHtml = '';
|
$fieldsHtml = '';
|
||||||
foreach ($metaTemplate->meta_template_fields as $metaTemplateField) {
|
$fieldsHtml .= $this->element(
|
||||||
$metaTemplateField->label = Inflector::humanize($metaTemplateField->field);
|
'genericElements/Form/metaTemplateForm',
|
||||||
if (!empty($metaTemplateField->metaFields)) {
|
[
|
||||||
if (!empty($metaTemplateField->multiple)) {
|
'metaTemplate' => $metaTemplate,
|
||||||
$fieldsHtml .= $this->element(
|
]
|
||||||
'genericElements/Form/multiFieldScaffold',
|
);
|
||||||
[
|
|
||||||
'metaFieldsEntities' => $metaTemplateField->metaFields,
|
|
||||||
'metaTemplateField' => $metaTemplateField,
|
|
||||||
'multiple' => !empty($metaTemplateField->multiple),
|
|
||||||
'form' => $this->Form,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$metaField = reset($metaTemplateField->metaFields);
|
|
||||||
$fieldData = [
|
|
||||||
'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.%s.value', $metaField->meta_template_id, $metaField->meta_template_field_id, $metaField->id),
|
|
||||||
'label' => $metaTemplateField->label,
|
|
||||||
];
|
|
||||||
$this->Form->setTemplates($backupTemplates);
|
|
||||||
$fieldsHtml .= $this->element(
|
|
||||||
'genericElements/Form/fieldScaffold',
|
|
||||||
[
|
|
||||||
'fieldData' => $fieldData,
|
|
||||||
'metaTemplateField' => $metaTemplateField,
|
|
||||||
'form' => $this->Form
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (!empty($metaTemplateField->multiple)) {
|
|
||||||
$fieldsHtml .= $this->element(
|
|
||||||
'genericElements/Form/multiFieldScaffold',
|
|
||||||
[
|
|
||||||
'metaFieldsEntities' => [],
|
|
||||||
'metaTemplateField' => $metaTemplateField,
|
|
||||||
'multiple' => !empty($metaTemplateField->multiple),
|
|
||||||
'form' => $this->Form,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
$this->Form->setTemplates($backupTemplates);
|
|
||||||
$fieldData = [
|
|
||||||
'field' => sprintf('MetaTemplates.%s.meta_template_fields.%s.metaFields.new.0', $metaTemplateField->meta_template_id, $metaTemplateField->id),
|
|
||||||
'label' => $metaTemplateField->label,
|
|
||||||
];
|
|
||||||
$fieldsHtml .= $this->element(
|
|
||||||
'genericElements/Form/fieldScaffold',
|
|
||||||
[
|
|
||||||
'fieldData' => $fieldData,
|
|
||||||
'form' => $this->Form
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$tabData['content'][$i] = $fieldsHtml;
|
$tabData['content'][$i] = $fieldsHtml;
|
||||||
}
|
}
|
||||||
$this->Form->setTemplates($backupTemplates);
|
$this->Form->setTemplates($backupTemplates);
|
||||||
|
|
|
@ -101,12 +101,13 @@
|
||||||
$action['onclick'] = sprintf('UI.submissionModalForIndex(\'%s\', \'%s\', \'%s\')', $modal_url, $reload_url, $tableRandomValue);
|
$action['onclick'] = sprintf('UI.submissionModalForIndex(\'%s\', \'%s\', \'%s\')', $modal_url, $reload_url, $tableRandomValue);
|
||||||
}
|
}
|
||||||
echo sprintf(
|
echo sprintf(
|
||||||
'<a href="%s" title="%s" aria-label="%s" %s %s class="btn btn-sm btn-outline-dark table-link-action"><i class="%s"></i></a> ',
|
'<a href="%s" title="%s" aria-label="%s" %s %s class="btn btn-sm btn-%s table-link-action"><i class="%s"></i></a> ',
|
||||||
$url,
|
$url,
|
||||||
empty($action['title']) ? '' : h($action['title']),
|
empty($action['title']) ? '' : h($action['title']),
|
||||||
empty($action['title']) ? '' : h($action['title']),
|
empty($action['title']) ? '' : h($action['title']),
|
||||||
empty($action['dbclickAction']) ? '' : 'class="dblclickActionElement"',
|
empty($action['dbclickAction']) ? '' : 'class="dblclickActionElement"',
|
||||||
empty($action['onclick']) ? '' : sprintf('onClick="%s"', $action['onclick']),
|
empty($action['onclick']) ? '' : sprintf('onClick="%s"', $action['onclick']),
|
||||||
|
empty($action['variant']) ? 'outline-dark' : h($action['variant']),
|
||||||
$this->FontAwesome->getClass($action['icon'])
|
$this->FontAwesome->getClass($action['icon'])
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
<?
|
|
||||||
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);
|
|
||||||
?>
|
|
|
@ -1,4 +1,12 @@
|
||||||
<span>
|
<span>
|
||||||
<?= h($metaTemplate->name) ?>
|
<?= h($metaTemplate->name) ?>
|
||||||
<i class="<?= $this->FontAwesome->getClass('star')?> small align-text-top" title="<?= __('Default Meta template') ?>"></i>
|
<?=
|
||||||
|
$this->Bootstrap->badge([
|
||||||
|
'variant' => !empty($metaTemplate['hasNewerVersion']) ? 'warning' : 'primary',
|
||||||
|
'text' => sprintf('v%s', h($metaTemplate->version))
|
||||||
|
])
|
||||||
|
?>
|
||||||
|
<?php if (!empty($metaTemplate->is_default)): ?>
|
||||||
|
<i class="<?= $this->FontAwesome->getClass('star')?> small align-text-top" title="<?= __('Default Meta template') ?>"></i>
|
||||||
|
<?php endif; ?>
|
||||||
</span>
|
</span>
|
|
@ -1,19 +1,15 @@
|
||||||
<?php
|
<?php
|
||||||
|
use \Cake\Routing\Router;
|
||||||
|
|
||||||
$tabData = [
|
$tabData = [
|
||||||
'navs' => [],
|
'navs' => [],
|
||||||
'content' => []
|
'content' => []
|
||||||
];
|
];
|
||||||
foreach($data['MetaTemplates'] as $metaTemplate) {
|
foreach($data['MetaTemplates'] as $metaTemplate) {
|
||||||
if (!empty($metaTemplate->meta_template_fields)) {
|
if (!empty($metaTemplate->meta_template_fields)) {
|
||||||
if ($metaTemplate->is_default) {
|
$tabData['navs'][] = [
|
||||||
$tabData['navs'][] = [
|
'html' => $this->element('/genericElements/MetaTemplates/metaTemplateNav', ['metaTemplate' => $metaTemplate])
|
||||||
'html' => $this->element('/genericElements/MetaTemplates/metaTemplateNav', ['metaTemplate' => $metaTemplate])
|
];
|
||||||
];
|
|
||||||
} else {
|
|
||||||
$tabData['navs'][] = [
|
|
||||||
'text' => $metaTemplate->name
|
|
||||||
];
|
|
||||||
}
|
|
||||||
$fields = [];
|
$fields = [];
|
||||||
foreach ($metaTemplate->meta_template_fields as $metaTemplateField) {
|
foreach ($metaTemplate->meta_template_fields as $metaTemplateField) {
|
||||||
$labelPrintedOnce = false;
|
$labelPrintedOnce = false;
|
||||||
|
@ -40,6 +36,28 @@ foreach($data['MetaTemplates'] as $metaTemplate) {
|
||||||
count($fields)
|
count($fields)
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
|
if (!empty($metaTemplate['hasNewerVersion'])) {
|
||||||
|
$listTable = $this->Bootstrap->alert([
|
||||||
|
'html' => sprintf(
|
||||||
|
'<div>%s</div><div>%s</div>',
|
||||||
|
__('These meta-fields are registered under an outdated template. Newest template is {0}, current is {1}.', $metaTemplate['hasNewerVersion']->version, $metaTemplate->version),
|
||||||
|
$this->Bootstrap->button([
|
||||||
|
'text' => __('Migrate to version {0}', $metaTemplate['hasNewerVersion']->version),
|
||||||
|
'variant' => 'success',
|
||||||
|
'nodeType' => 'a',
|
||||||
|
'params' => [
|
||||||
|
'href' => Router::url([
|
||||||
|
'controller' => 'metaTemplates',
|
||||||
|
'action' => 'migrateOldMetaTemplateToNewestVersionForEntity',
|
||||||
|
$metaTemplate->id,
|
||||||
|
$data->id,
|
||||||
|
])
|
||||||
|
]
|
||||||
|
])
|
||||||
|
),
|
||||||
|
'variant' => 'warning',
|
||||||
|
]) . $listTable;
|
||||||
|
}
|
||||||
$tabData['content'][] = $listTable;
|
$tabData['content'][] = $listTable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue