427 lines
20 KiB
PHP
427 lines
20 KiB
PHP
<?php
|
|
|
|
namespace App\Controller;
|
|
|
|
use App\Controller\AppController;
|
|
use Cake\Utility\Hash;
|
|
use Cake\Utility\Text;
|
|
use Cake\Utility\Inflector;
|
|
use Cake\ORM\TableRegistry;
|
|
use \Cake\Database\Expression\QueryExpression;
|
|
use Cake\Http\Exception\NotFoundException;
|
|
use Cake\Http\Exception\MethodNotAllowedException;
|
|
use Cake\Routing\Router;
|
|
|
|
|
|
class MetaTemplatesController extends AppController
|
|
{
|
|
public $quickFilterFields = [['name' => true], 'uuid', ['scope' => true]];
|
|
public $filterFields = ['name', 'uuid', 'scope', 'namespace'];
|
|
public $containFields = ['MetaTemplateFields'];
|
|
|
|
public function updateAllTemplates()
|
|
{
|
|
if ($this->request->is('post')) {
|
|
$result = $this->MetaTemplates->updateAllTemplates();
|
|
if ($this->ParamHandler->isRest()) {
|
|
return $this->RestResponse->viewData($result, 'json');
|
|
} else {
|
|
if ($result['success']) {
|
|
$message = __n('{0} templates updated.', 'The template has been updated.', empty($template_id), count($result['files_processed']));
|
|
} else {
|
|
$message = __n('{0} templates could not be updated.', 'The template could not be updated.', empty($template_id), count($result['files_processed']));
|
|
}
|
|
$this->CRUD->setResponseForController('updateAllTemplate', $result['success'], $message, $result['files_processed'], $result['update_errors'], ['redirect' => $this->referer()]);
|
|
$responsePayload = $this->CRUD->getResponsePayload();
|
|
if (!empty($responsePayload)) {
|
|
return $responsePayload;
|
|
}
|
|
}
|
|
} else {
|
|
if (!$this->ParamHandler->isRest()) {
|
|
$this->set('title', __('Update All Meta Templates'));
|
|
$this->set('question', __('Are you sure you wish to update all the Meta Template definitions'));
|
|
$templatesUpdateStatus = $this->MetaTemplates->getUpdateStatusForTemplates();
|
|
$this->set('templatesUpdateStatus', $templatesUpdateStatus);
|
|
$this->render('updateAll');
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update the provided template or all templates
|
|
*
|
|
* @param int|null $template_id
|
|
*/
|
|
public function update($template_id=null)
|
|
{
|
|
$metaTemplate = false;
|
|
if (!is_null($template_id)) {
|
|
if (!is_numeric($template_id)) {
|
|
throw new NotFoundException(__('Invalid {0} for provided ID.', $this->MetaTemplates->getAlias(), $template_id));
|
|
}
|
|
$metaTemplate = $this->MetaTemplates->find()->where([
|
|
'id' => $template_id
|
|
])->contain(['MetaTemplateFields'])->first();
|
|
|
|
if (empty($metaTemplate)) {
|
|
throw new NotFoundException(__('Invalid {0} {1}.', $this->MetaTemplates->getAlias(), $template_id));
|
|
}
|
|
}
|
|
if ($this->request->is('post')) {
|
|
$params = $this->ParamHandler->harvestParams(['update_strategy']);
|
|
$updateStrategy = $params['update_strategy'] ?? null;
|
|
$result = $this->MetaTemplates->update($metaTemplate, $updateStrategy);
|
|
if ($this->ParamHandler->isRest()) {
|
|
return $this->RestResponse->viewData($result, 'json');
|
|
} else {
|
|
if ($result['success']) {
|
|
$message = __n('{0} templates updated.', 'The template has been updated.', empty($template_id), $result['files_processed']);
|
|
} else {
|
|
$message = __n('{0} templates could not be updated.', 'The template could not be updated.', empty($template_id), $result['files_processed']);
|
|
}
|
|
$this->CRUD->setResponseForController('update', $result['success'], $message, $result['files_processed'], $result['update_errors'], ['redirect' => $this->referer()]);
|
|
$responsePayload = $this->CRUD->getResponsePayload();
|
|
if (!empty($responsePayload)) {
|
|
return $responsePayload;
|
|
}
|
|
}
|
|
} else {
|
|
if (!$this->ParamHandler->isRest()) {
|
|
if (!empty($metaTemplate)) {
|
|
$this->set('metaTemplate', $metaTemplate);
|
|
$statuses = $this->setUpdateStatus($metaTemplate->id);
|
|
$this->set('updateStatus', $this->MetaTemplates->computeFullUpdateStatusForMetaTemplate($statuses['templateStatus'], $metaTemplate));
|
|
} else {
|
|
$this->set('title', __('Update All Meta Templates'));
|
|
$this->set('question', __('Are you sure you wish to update all the Meta Template definitions'));
|
|
$templatesUpdateStatus = $this->MetaTemplates->getUpdateStatusForTemplates();
|
|
$this->set('templatesUpdateStatus', $templatesUpdateStatus);
|
|
$this->render('updateAll');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create a new template by loading the template on the disk having the provided UUID.
|
|
*
|
|
* @param string $uuid
|
|
*/
|
|
public function createNewTemplate(string $uuid)
|
|
{
|
|
if ($this->request->is('post')) {
|
|
$result = $this->MetaTemplates->createNewTemplate($uuid);
|
|
if ($this->ParamHandler->isRest()) {
|
|
return $this->RestResponse->viewData($result, 'json');
|
|
} else {
|
|
if ($result['success']) {
|
|
$message = __('The template {0} has been created.', $uuid);
|
|
} else {
|
|
$message = __('The template {0} could not be created.', $uuid);
|
|
}
|
|
$this->CRUD->setResponseForController('createNewTemplate', $result['success'], $message, $result['files_processed'], $result['update_errors']);
|
|
$responsePayload = $this->CRUD->getResponsePayload();
|
|
if (!empty($responsePayload)) {
|
|
return $responsePayload;
|
|
}
|
|
}
|
|
} else {
|
|
if (!$this->ParamHandler->isRest()) {
|
|
$this->set('title', __('Create Meta Template'));
|
|
$this->set('question', __('Are you sure you wish to load the meta template with UUID: {0} in the database', h($uuid)));
|
|
$this->set('actionName', __('Create template'));
|
|
$this->set('path', ['controller' => 'meta-templates', 'action' => 'create_new_template', $uuid]);
|
|
$this->render('/genericTemplates/confirm');
|
|
}
|
|
}
|
|
}
|
|
|
|
public function getMetaFieldsToUpdate($template_id)
|
|
{
|
|
$metaTemplate = $this->MetaTemplates->get($template_id);
|
|
$newestMetaTemplate = $this->MetaTemplates->getNewestVersion($metaTemplate);
|
|
$amountOfEntitiesToUpdate = 0;
|
|
$entities = $this->MetaTemplates->getEntitiesHavingMetaFieldsFromTemplate($template_id, 10, $amountOfEntitiesToUpdate);
|
|
$this->set('metaTemplate', $metaTemplate);
|
|
$this->set('newestMetaTemplate', $newestMetaTemplate);
|
|
$this->set('entities', $entities);
|
|
$this->set('amountOfEntitiesToUpdate', $amountOfEntitiesToUpdate);
|
|
}
|
|
|
|
public function migrateOldMetaTemplateToNewestVersionForEntity($template_id, $entity_id)
|
|
{
|
|
$metaTemplate = $this->MetaTemplates->get($template_id, [
|
|
'contain' => ['MetaTemplateFields']
|
|
]);
|
|
$newestMetaTemplate = $this->MetaTemplates->getNewestVersion($metaTemplate, true);
|
|
$entity = $this->MetaTemplates->getEntity($metaTemplate, $entity_id);
|
|
$conditions = [
|
|
'MetaFields.meta_template_id IN' => [$metaTemplate->id, $newestMetaTemplate->id],
|
|
'MetaFields.scope' => $metaTemplate->scope,
|
|
];
|
|
$keyedMetaFields = $this->MetaTemplates->getKeyedMetaFieldsForEntity($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->insertMetaFieldsInMetaTemplates($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,
|
|
'url' => Router::url(['controller' => $className, 'action' => 'view', $entity_id])
|
|
]
|
|
]
|
|
);
|
|
$responsePayload = $this->CRUD->getResponsePayload();
|
|
if (!empty($responsePayload)) {
|
|
return $responsePayload;
|
|
}
|
|
}
|
|
$conflicts = $this->MetaTemplates->getMetaFieldsConflictsUnderTemplate($entity->meta_fields, $newestMetaTemplate);
|
|
foreach ($conflicts as $conflict) {
|
|
if (!empty($conflict['existing_meta_template_field'])) {
|
|
$existingMetaTemplateField = $conflict['existing_meta_template_field'];
|
|
foreach ($existingMetaTemplateField->metaFields as $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 $i => $newMetaTemplateField) {
|
|
if ($metaTemplateField->field == $newMetaTemplateField->field && empty($newMetaTemplateField->metaFields)) {
|
|
$movedMetaTemplateFields[] = $metaTemplateField->id;
|
|
$nonEmptyMetaFields = array_filter($metaTemplateField->metaFields, function ($e) {
|
|
return $e->value !== '';
|
|
});
|
|
$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;
|
|
}, $nonEmptyMetaFields);
|
|
$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 migrateMetafieldsToNewestTemplate(int $template_id, $forceMigration=false)
|
|
{
|
|
$oldMetaTemplate = $this->MetaTemplates->find()->where([
|
|
'id' => $template_id
|
|
])->contain(['MetaTemplateFields'])->first();
|
|
|
|
if (empty($oldMetaTemplate)) {
|
|
throw new NotFoundException(__('Invalid {0} {1}.', $this->MetaTemplates->getAlias(), $template_id));
|
|
}
|
|
$newestMetaTemplate = $this->MetaTemplates->getNewestVersion($oldMetaTemplate, true);
|
|
if ($oldMetaTemplate->id == $newestMetaTemplate->id) {
|
|
throw new NotFoundException(__('Invalid {0} {1}. Template already the newest version', $this->MetaTemplates->getAlias(), $template_id));
|
|
}
|
|
|
|
if ($this->request->is('post')) {
|
|
$result = $this->MetaTemplates->migrateMetafieldsToNewestTemplate($oldMetaTemplate, $newestMetaTemplate, !empty($forceMigration));
|
|
if ($this->ParamHandler->isRest()) {
|
|
return $this->RestResponse->viewData($result, 'json');
|
|
} else {
|
|
if ($result['success']) {
|
|
$message = __('{0} entities updated. {1} entities could not be automatically migrated.', $result['migrated_count'], $result['conflicting_entities']);
|
|
} else {
|
|
$message = __('{0} entities updated. {1} entities could not be automatically migrated. {2} entities could not be updated due to errors', $result['migrated_count'], $result['conflicting_entities'], $result['migration_errors']);
|
|
}
|
|
$this->CRUD->setResponseForController('update', $result['success'], $message, $result, $result['migration_errors'], ['redirect' => $this->referer()]);
|
|
$responsePayload = $this->CRUD->getResponsePayload();
|
|
if (!empty($responsePayload)) {
|
|
return $responsePayload;
|
|
}
|
|
}
|
|
} else {
|
|
$entities = $this->MetaTemplates->getEntitiesHavingMetaFieldsFromTemplate($oldMetaTemplate->id, null);
|
|
$conflictingEntities = [];
|
|
foreach ($entities as $entity) {
|
|
$conflicts = $this->MetaTemplates->getMetaFieldsConflictsUnderTemplate($entity->meta_fields, $newestMetaTemplate);
|
|
if (!empty($conflicts)) {
|
|
$conflictingEntities[] = $entity;
|
|
}
|
|
}
|
|
|
|
if (!$this->ParamHandler->isRest()) {
|
|
$this->set('oldMetaTemplate', $oldMetaTemplate);
|
|
$this->set('newestMetaTemplate', $newestMetaTemplate);
|
|
$this->set('conflictingEntities', $conflictingEntities);
|
|
$this->set('entityCount', count($entities));
|
|
}
|
|
}
|
|
}
|
|
|
|
public function index()
|
|
{
|
|
$templatesUpdateStatus = $this->MetaTemplates->getUpdateStatusForTemplates();
|
|
$this->CRUD->index([
|
|
'filters' => $this->filterFields,
|
|
'quickFilters' => $this->quickFilterFields,
|
|
'contextFilters' => [
|
|
'custom' => [
|
|
[
|
|
'default' => true,
|
|
'label' => __('Latest Templates'),
|
|
'filterConditionFunction' => function ($query) {
|
|
return $query->where([
|
|
'id IN' => $this->MetaTemplates->genQueryForAllNewestVersionIDs()
|
|
]);
|
|
}
|
|
],
|
|
]
|
|
],
|
|
'contain' => $this->containFields,
|
|
'afterFind' => function($metaTemplate) use ($templatesUpdateStatus) {
|
|
if (!empty($templatesUpdateStatus[$metaTemplate->uuid])) {
|
|
$templateStatus = $this->MetaTemplates->getStatusForMetaTemplate($templatesUpdateStatus[$metaTemplate->uuid]['template'], $metaTemplate);
|
|
$metaTemplate->set('updateStatus', $this->MetaTemplates->computeFullUpdateStatusForMetaTemplate($templateStatus, $metaTemplate));
|
|
}
|
|
return $metaTemplate;
|
|
}
|
|
]);
|
|
$responsePayload = $this->CRUD->getResponsePayload();
|
|
if (!empty($responsePayload)) {
|
|
return $responsePayload;
|
|
}
|
|
$updateableTemplates = [
|
|
'not-up-to-date' => $this->MetaTemplates->getNotUpToDateTemplates(),
|
|
'can-be-removed' => $this->MetaTemplates->getCanBeRemovedTemplates(),
|
|
'new' => $this->MetaTemplates->getNewTemplates(),
|
|
];
|
|
$this->set('defaultTemplatePerScope', $this->MetaTemplates->getDefaultTemplatePerScope());
|
|
$this->set('alignmentScope', 'individuals');
|
|
$this->set('updateableTemplates', $updateableTemplates);
|
|
}
|
|
|
|
public function view($id)
|
|
{
|
|
$this->CRUD->view($id, [
|
|
'contain' => ['MetaTemplateFields']
|
|
]);
|
|
$responsePayload = $this->CRUD->getResponsePayload();
|
|
if (!empty($responsePayload)) {
|
|
return $responsePayload;
|
|
}
|
|
$this->setUpdateStatus($id);
|
|
}
|
|
|
|
public function delete($id)
|
|
{
|
|
$metaTemplate = $this->MetaTemplates->get($id, [
|
|
'contain' => ['MetaTemplateFields']
|
|
]);
|
|
$templateOnDisk = $this->MetaTemplates->readTemplateFromDisk($metaTemplate->uuid);
|
|
$templateStatus = $this->MetaTemplates->getStatusForMetaTemplate($templateOnDisk, $metaTemplate);
|
|
if (empty($templateStatus['can-be-removed'])) {
|
|
throw new MethodNotAllowedException(__('This meta-template cannot be removed'));
|
|
}
|
|
$this->set('deletionText', __('The meta-template "{0}" has no meta-field and can be safely removed.', h($templateStatus['existing_template']->name)));
|
|
$this->CRUD->delete($id);
|
|
$responsePayload = $this->CRUD->getResponsePayload();
|
|
if (!empty($responsePayload)) {
|
|
return $responsePayload;
|
|
}
|
|
}
|
|
|
|
public function toggle($id, $fieldName = 'enabled')
|
|
{
|
|
if ($this->request->is('POST') && $fieldName == 'is_default') {
|
|
$template = $this->MetaTemplates->get($id);
|
|
$this->MetaTemplates->removeDefaultFlag($template->scope);
|
|
$this->CRUD->toggle($id, $fieldName, ['force_state' => !$template->is_default]);
|
|
} else {
|
|
$this->CRUD->toggle($id, $fieldName);
|
|
}
|
|
$responsePayload = $this->CRUD->getResponsePayload();
|
|
if (!empty($responsePayload)) {
|
|
return $responsePayload;
|
|
}
|
|
}
|
|
|
|
private function getUpdateStatus($id): array
|
|
{
|
|
$metaTemplate = $this->MetaTemplates->get($id, [
|
|
'contain' => ['MetaTemplateFields']
|
|
]);
|
|
$templateOnDisk = $this->MetaTemplates->readTemplateFromDisk($metaTemplate->uuid);
|
|
$templateStatus = $this->MetaTemplates->getStatusForMetaTemplate($templateOnDisk, $metaTemplate);
|
|
return $templateStatus;
|
|
}
|
|
|
|
/**
|
|
* Retreive the template stored on disk and compute the status for the provided template id.
|
|
*
|
|
* @param [type] $id
|
|
* @return array
|
|
*/
|
|
private function setUpdateStatus($template_id): array
|
|
{
|
|
$metaTemplate = $this->MetaTemplates->get($template_id, [
|
|
'contain' => ['MetaTemplateFields']
|
|
]);
|
|
$error = '';
|
|
$errorMessage = '';
|
|
$templateOnDisk = $this->MetaTemplates->readTemplateFromDisk($metaTemplate->uuid, $error);
|
|
if (is_null($templateOnDisk)) {
|
|
$errorMessage = __('Could not retreive template\'s status. Reason: {0}', $error);
|
|
$this->Flash->error($errorMessage);
|
|
$templateOnDisk = [];
|
|
}
|
|
$templateStatus = $this->MetaTemplates->getStatusForMetaTemplate($templateOnDisk, $metaTemplate);
|
|
if (!empty($errorMessage)) {
|
|
$templateStatus['error'] = $errorMessage;
|
|
}
|
|
$this->set('templateOnDisk', $templateOnDisk);
|
|
$this->set('templateStatus', $templateStatus);
|
|
return [
|
|
'templateOnDisk' => $templateOnDisk,
|
|
'templateStatus' => $templateStatus,
|
|
];
|
|
}
|
|
}
|