chg: [component:CRUD] Mirrored changes done in MISP3's CRUD component
parent
76b682e399
commit
4390face10
|
@ -7,6 +7,7 @@ use Cake\Error\Debugger;
|
||||||
use Cake\Utility\Hash;
|
use Cake\Utility\Hash;
|
||||||
use Cake\Utility\Inflector;
|
use Cake\Utility\Inflector;
|
||||||
use Cake\Utility\Text;
|
use Cake\Utility\Text;
|
||||||
|
use Cake\Validation\Validation;
|
||||||
use Cake\View\ViewBuilder;
|
use Cake\View\ViewBuilder;
|
||||||
use Cake\ORM\TableRegistry;
|
use Cake\ORM\TableRegistry;
|
||||||
use Cake\ORM\Query;
|
use Cake\ORM\Query;
|
||||||
|
@ -37,6 +38,7 @@ class CRUDComponent extends Component
|
||||||
$embedInModal = !empty($this->request->getQuery('embedInModal', false));
|
$embedInModal = !empty($this->request->getQuery('embedInModal', false));
|
||||||
$excludeStats = !empty($this->request->getQuery('excludeStats', false));
|
$excludeStats = !empty($this->request->getQuery('excludeStats', false));
|
||||||
$skipTableToolbar = !empty($this->request->getQuery('skipTableToolbar', false));
|
$skipTableToolbar = !empty($this->request->getQuery('skipTableToolbar', false));
|
||||||
|
$wrapResponse = !empty($options['wrapResponse']);
|
||||||
|
|
||||||
if (!empty($options['quickFilters'])) {
|
if (!empty($options['quickFilters'])) {
|
||||||
if (empty($options['filters'])) {
|
if (empty($options['filters'])) {
|
||||||
|
@ -114,34 +116,46 @@ class CRUDComponent extends Component
|
||||||
$totalCount = $this->Controller->getRequest()->getAttribute('paging')[$this->TableAlias]['count'];
|
$totalCount = $this->Controller->getRequest()->getAttribute('paging')[$this->TableAlias]['count'];
|
||||||
if ($isRestOrCSV) {
|
if ($isRestOrCSV) {
|
||||||
if (isset($options['hidden'])) {
|
if (isset($options['hidden'])) {
|
||||||
$data->each(function($value, $key) use ($options) {
|
$data->each(
|
||||||
$hidden = is_array($options['hidden']) ? $options['hidden'] : [$options['hidden']];
|
function ($value, $key) use ($options) {
|
||||||
$value->setHidden($hidden);
|
$hidden = is_array($options['hidden']) ? $options['hidden'] : [$options['hidden']];
|
||||||
return $value;
|
$value->setHidden($hidden);
|
||||||
});
|
return $value;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (isset($options['afterFind'])) {
|
if (isset($options['afterFind'])) {
|
||||||
$function = $options['afterFind'];
|
$function = $options['afterFind'];
|
||||||
if (is_callable($function)) {
|
if (is_callable($function)) {
|
||||||
$data = $data->map(function($value, $key) use ($function) {
|
$data = $data->map(
|
||||||
return $function($value);
|
function ($value, $key) use ($function) {
|
||||||
})->filter(function ($value) {
|
return $function($value);
|
||||||
return $value !== false;
|
}
|
||||||
});
|
)->filter(
|
||||||
|
function ($value) {
|
||||||
|
return $value !== false;
|
||||||
|
}
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
$t = $this->Table;
|
$t = $this->Table;
|
||||||
$data = $data->map(function($value, $key) use ($t, $function) {
|
$data = $data->map(
|
||||||
return $t->$function($value);
|
function ($value, $key) use ($t, $function) {
|
||||||
})->filter(function ($value) {
|
return $t->$function($value);
|
||||||
return $value !== false;
|
}
|
||||||
});
|
)->filter(
|
||||||
|
function ($value) {
|
||||||
|
return $value !== false;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ($this->metaFieldsSupported()) {
|
if ($this->metaFieldsSupported()) {
|
||||||
$metaTemplates = $this->getMetaTemplates()->toArray();
|
$metaTemplates = $this->getMetaTemplates()->toArray();
|
||||||
$data = $data->map(function($value, $key) use ($metaTemplates) {
|
$data = $data->map(
|
||||||
return $this->attachMetaTemplatesIfNeeded($value, $metaTemplates);
|
function ($value, $key) use ($metaTemplates) {
|
||||||
});
|
return $this->attachMetaTemplatesIfNeeded($value, $metaTemplates);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if ($this->request->is('csv')) {
|
if ($this->request->is('csv')) {
|
||||||
require_once(ROOT . '/src/Lib/Tools/CsvConverter.php');
|
require_once(ROOT . '/src/Lib/Tools/CsvConverter.php');
|
||||||
|
@ -154,27 +168,43 @@ class CRUDComponent extends Component
|
||||||
'X-Total-Count' => $totalCount,
|
'X-Total-Count' => $totalCount,
|
||||||
]);
|
]);
|
||||||
} else {
|
} else {
|
||||||
$this->Controller->restResponsePayload = $this->RestResponse->viewData($data, 'json', false, false, false, [
|
$this->Controller->restResponsePayload = $this->RestResponse->viewData(
|
||||||
'X-Total-Count' => $totalCount,
|
$data,
|
||||||
]);
|
'json',
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
[
|
||||||
|
'X-Total-Count' => $totalCount,
|
||||||
|
],
|
||||||
|
$wrapResponse
|
||||||
|
);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$this->Controller->setResponse($this->Controller->getResponse()->withHeader('X-Total-Count', $totalCount));
|
$this->Controller->setResponse($this->Controller->getResponse()->withHeader('X-Total-Count', $totalCount));
|
||||||
if (isset($options['afterFind'])) {
|
if (isset($options['afterFind'])) {
|
||||||
$function = $options['afterFind'];
|
$function = $options['afterFind'];
|
||||||
if (is_callable($function)) {
|
if (is_callable($function)) {
|
||||||
$data = $data->map(function($value, $key) use ($function) {
|
$data = $data->map(
|
||||||
return $function($value);
|
function ($value, $key) use ($function) {
|
||||||
})->filter(function($value) {
|
return $function($value);
|
||||||
return $value !== false;
|
}
|
||||||
});
|
)->filter(
|
||||||
|
function ($value) {
|
||||||
|
return $value !== false;
|
||||||
|
}
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
$t = $this->Table;
|
$t = $this->Table;
|
||||||
$data = $data->map(function($value, $key) use ($t, $function) {
|
$data = $data->map(
|
||||||
return $t->$function($value);
|
function ($value, $key) use ($t, $function) {
|
||||||
})->filter(function ($value) {
|
return $t->$function($value);
|
||||||
return $value !== false;
|
}
|
||||||
});
|
)->filter(
|
||||||
|
function ($value) {
|
||||||
|
return $value !== false;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->setFilteringContext($options['contextFilters'] ?? [], $params);
|
$this->setFilteringContext($options['contextFilters'] ?? [], $params);
|
||||||
|
@ -185,9 +215,15 @@ class CRUDComponent extends Component
|
||||||
$data[$i] = $this->attachMetaTemplatesIfNeeded($row, $metaTemplates);
|
$data[$i] = $this->attachMetaTemplatesIfNeeded($row, $metaTemplates);
|
||||||
}
|
}
|
||||||
$this->Controller->set('meta_templates', $metaTemplates);
|
$this->Controller->set('meta_templates', $metaTemplates);
|
||||||
$this->Controller->set('meta_templates_enabled', array_filter($metaTemplates, function($template) {
|
$this->Controller->set(
|
||||||
return $template['enabled'];
|
'meta_templates_enabled',
|
||||||
}));
|
array_filter(
|
||||||
|
$metaTemplates,
|
||||||
|
function ($template) {
|
||||||
|
return $template['enabled'];
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (empty($excludeStats)) { // check if stats are requested
|
if (empty($excludeStats)) { // check if stats are requested
|
||||||
$modelStatistics = [];
|
$modelStatistics = [];
|
||||||
|
@ -235,7 +271,7 @@ class CRUDComponent extends Component
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function filtering(): void
|
public function filtering(array $options = []): void
|
||||||
{
|
{
|
||||||
if ($this->taggingSupported()) {
|
if ($this->taggingSupported()) {
|
||||||
$this->Controller->set('taggingEnabled', true);
|
$this->Controller->set('taggingEnabled', true);
|
||||||
|
@ -256,7 +292,7 @@ class CRUDComponent extends Component
|
||||||
} else {
|
} else {
|
||||||
$this->Controller->set('metaFieldsEnabled', false);
|
$this->Controller->set('metaFieldsEnabled', false);
|
||||||
}
|
}
|
||||||
$filtersConfigRaw= !empty($this->Controller->filterFields) ? $this->Controller->filterFields : [];
|
$filtersConfigRaw = !empty($this->Controller->filterFields) ? $this->Controller->filterFields : [];
|
||||||
$filtersConfig = [];
|
$filtersConfig = [];
|
||||||
foreach ($filtersConfigRaw as $fieldConfig) {
|
foreach ($filtersConfigRaw as $fieldConfig) {
|
||||||
if (is_array($fieldConfig)) {
|
if (is_array($fieldConfig)) {
|
||||||
|
@ -279,9 +315,18 @@ class CRUDComponent extends Component
|
||||||
$this->Table->getSchema()->typeMap(),
|
$this->Table->getSchema()->typeMap(),
|
||||||
$associatedtypeMap
|
$associatedtypeMap
|
||||||
);
|
);
|
||||||
$typeMap = array_filter($typeMap, function ($field) use ($filtersName) {
|
$typeMap = array_filter(
|
||||||
return in_array($field, $filtersName);
|
$typeMap,
|
||||||
}, ARRAY_FILTER_USE_KEY);
|
function ($field) use ($filtersName) {
|
||||||
|
return in_array($field, $filtersName);
|
||||||
|
},
|
||||||
|
ARRAY_FILTER_USE_KEY
|
||||||
|
);
|
||||||
|
if (!empty($options['afterFind'])) {
|
||||||
|
$overrides = $options['afterFind']($filtersConfig, $typeMap);
|
||||||
|
$filtersConfig = $overrides['filtersConfig'];
|
||||||
|
$typeMap = $overrides['typeMap'];
|
||||||
|
}
|
||||||
$this->Controller->set('typeMap', $typeMap);
|
$this->Controller->set('typeMap', $typeMap);
|
||||||
$this->Controller->set('filters', $filtersName);
|
$this->Controller->set('filters', $filtersName);
|
||||||
$this->Controller->set('filtersConfig', $filtersConfig);
|
$this->Controller->set('filtersConfig', $filtersConfig);
|
||||||
|
@ -292,12 +337,42 @@ class CRUDComponent extends Component
|
||||||
public function getFilterFieldsName(): array
|
public function getFilterFieldsName(): array
|
||||||
{
|
{
|
||||||
$filters = !empty($this->Controller->filterFields) ? $this->Controller->filterFields : [];
|
$filters = !empty($this->Controller->filterFields) ? $this->Controller->filterFields : [];
|
||||||
$filters = array_map(function($item) {
|
$filters = array_map(
|
||||||
return is_array($item) ? $item['name'] : $item;
|
function ($item) {
|
||||||
}, $filters);
|
return is_array($item) ? $item['name'] : $item;
|
||||||
|
},
|
||||||
|
$filters
|
||||||
|
);
|
||||||
return $filters;
|
return $filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* From the provided fields and type mapping, convert any boolean field from the type `checkhox` to the type `radio`.
|
||||||
|
*
|
||||||
|
* @param array $fieldsConfig The array of fields to transform
|
||||||
|
* @param array $typeMap The mapping from the DB to the field type
|
||||||
|
* @param boolean $includeAny Should an `Any` option be included
|
||||||
|
* @param boolean $includeBoth Shoud a `Both` option be included
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function transformBooleanFieldsIntoRadio(array $fieldsConfig, array $typeMap, bool $includeAny = true, bool $isInline = true): array
|
||||||
|
{
|
||||||
|
foreach ($typeMap as $fieldname => $type) {
|
||||||
|
if ($type == 'boolean') {
|
||||||
|
$fieldsConfig[$fieldname]['type'] = 'radio';
|
||||||
|
$fieldsConfig[$fieldname]['inline'] = $isInline;
|
||||||
|
$fieldsConfig[$fieldname]['default'] = 'any';
|
||||||
|
$fieldsConfig[$fieldname]['options'] = [];
|
||||||
|
if ($includeAny) {
|
||||||
|
$fieldsConfig[$fieldname]['options'][] = ['value' => 'any', 'text' => __('Any'),];
|
||||||
|
}
|
||||||
|
$fieldsConfig[$fieldname]['options'][] = ['value' => 1, 'text' => __('Enabled'),];
|
||||||
|
$fieldsConfig[$fieldname]['options'][] = ['value' => 0, 'text' => __('Disabled'),];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $fieldsConfig;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* getResponsePayload Returns the adaquate response payload based on the request context
|
* getResponsePayload Returns the adaquate response payload based on the request context
|
||||||
*
|
*
|
||||||
|
@ -313,7 +388,7 @@ class CRUDComponent extends Component
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getMetaTemplates(array $metaTemplateConditions=[])
|
private function getMetaTemplates(array $metaTemplateConditions = [])
|
||||||
{
|
{
|
||||||
$metaTemplates = [];
|
$metaTemplates = [];
|
||||||
if (!$this->metaFieldsSupported()) {
|
if (!$this->metaFieldsSupported()) {
|
||||||
|
@ -323,19 +398,25 @@ class CRUDComponent extends Component
|
||||||
$metaQuery = $this->MetaTemplates->find();
|
$metaQuery = $this->MetaTemplates->find();
|
||||||
$metaQuery
|
$metaQuery
|
||||||
->order(['is_default' => 'DESC'])
|
->order(['is_default' => 'DESC'])
|
||||||
->where(array_merge(
|
->where(
|
||||||
$metaTemplateConditions,
|
array_merge(
|
||||||
['scope' => $metaFieldsBehavior->getScope(), ]
|
$metaTemplateConditions,
|
||||||
))
|
['scope' => $metaFieldsBehavior->getScope(),]
|
||||||
|
)
|
||||||
|
)
|
||||||
->contain('MetaTemplateFields')
|
->contain('MetaTemplateFields')
|
||||||
->formatResults(function (\Cake\Collection\CollectionInterface $metaTemplates) { // Set meta-template && meta-template-fields indexed by their ID
|
->formatResults(
|
||||||
return $metaTemplates
|
function (\Cake\Collection\CollectionInterface $metaTemplates) {
|
||||||
->map(function ($metaTemplate) {
|
// Set meta-template && meta-template-fields indexed by their ID
|
||||||
$metaTemplate->meta_template_fields = Hash::combine($metaTemplate->meta_template_fields, '{n}.id', '{n}');
|
return $metaTemplates->map(
|
||||||
return $metaTemplate;
|
function ($metaTemplate) {
|
||||||
})
|
$metaTemplate->meta_template_fields = Hash::combine($metaTemplate->meta_template_fields, '{n}.id', '{n}');
|
||||||
->indexBy('id');
|
return $metaTemplate;
|
||||||
});
|
}
|
||||||
|
)
|
||||||
|
->indexBy('id');
|
||||||
|
}
|
||||||
|
);
|
||||||
$metaTemplates = $metaQuery->all();
|
$metaTemplates = $metaQuery->all();
|
||||||
return $metaTemplates;
|
return $metaTemplates;
|
||||||
}
|
}
|
||||||
|
@ -483,7 +564,7 @@ class CRUDComponent extends Component
|
||||||
}
|
}
|
||||||
|
|
||||||
// prune empty values and marshall fields
|
// prune empty values and marshall fields
|
||||||
public 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' => []];
|
||||||
|
@ -511,16 +592,19 @@ class CRUDComponent extends Component
|
||||||
foreach ($new_meta_fields as $new_value) {
|
foreach ($new_meta_fields as $new_value) {
|
||||||
if (!empty($new_value)) {
|
if (!empty($new_value)) {
|
||||||
$metaField = $metaFieldsTable->newEmptyEntity();
|
$metaField = $metaFieldsTable->newEmptyEntity();
|
||||||
$metaFieldsTable->patchEntity($metaField, [
|
$metaFieldsTable->patchEntity(
|
||||||
'value' => $new_value,
|
$metaField,
|
||||||
'scope' => $this->Table->getBehavior('MetaFields')->getScope(),
|
[
|
||||||
'field' => $rawMetaTemplateField->field,
|
'value' => $new_value,
|
||||||
'meta_template_id' => $rawMetaTemplateField->meta_template_id,
|
'scope' => $this->Table->getBehavior('MetaFields')->getScope(),
|
||||||
'meta_template_field_id' => $rawMetaTemplateField->id,
|
'field' => $rawMetaTemplateField->field,
|
||||||
'meta_template_directory_id' => $allMetaTemplates[$template_id]->meta_template_directory_id,
|
'meta_template_id' => $rawMetaTemplateField->meta_template_id,
|
||||||
'parent_id' => $entity->id,
|
'meta_template_field_id' => $rawMetaTemplateField->id,
|
||||||
'uuid' => Text::uuid(),
|
'meta_template_directory_id' => $allMetaTemplates[$template_id]->meta_template_directory_id,
|
||||||
]);
|
'parent_id' => $entity->id,
|
||||||
|
'uuid' => Text::uuid(),
|
||||||
|
]
|
||||||
|
);
|
||||||
$entity->meta_fields[] = $metaField;
|
$entity->meta_fields[] = $metaField;
|
||||||
$entity->MetaTemplates[$template_id]->meta_template_fields[$meta_template_field_id]->metaFields[] = $metaField;
|
$entity->MetaTemplates[$template_id]->meta_template_fields[$meta_template_field_id]->metaFields[] = $metaField;
|
||||||
}
|
}
|
||||||
|
@ -531,9 +615,13 @@ class CRUDComponent extends Component
|
||||||
if (isset($metaFieldsIndex[$meta_field_id])) {
|
if (isset($metaFieldsIndex[$meta_field_id])) {
|
||||||
$index = $metaFieldsIndex[$meta_field_id];
|
$index = $metaFieldsIndex[$meta_field_id];
|
||||||
if ($entity->meta_fields[$index]->value != $new_value) { // nothing to do, value hasn't changed
|
if ($entity->meta_fields[$index]->value != $new_value) { // nothing to do, value hasn't changed
|
||||||
$metaFieldsTable->patchEntity($entity->meta_fields[$index], [
|
$metaFieldsTable->patchEntity(
|
||||||
'value' => $new_value, 'meta_template_field_id' => $rawMetaTemplateField->id
|
$entity->meta_fields[$index],
|
||||||
], ['value']);
|
[
|
||||||
|
'value' => $new_value, 'meta_template_field_id' => $rawMetaTemplateField->id
|
||||||
|
],
|
||||||
|
['value']
|
||||||
|
);
|
||||||
$metaFieldsTable->patchEntity(
|
$metaFieldsTable->patchEntity(
|
||||||
$entity->MetaTemplates[$template_id]->meta_template_fields[$meta_template_field_id]->metaFields[$meta_field_id],
|
$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' => $new_value, 'meta_template_field_id' => $rawMetaTemplateField->id],
|
||||||
|
@ -542,16 +630,19 @@ class CRUDComponent extends Component
|
||||||
}
|
}
|
||||||
} else { // metafield comes from a second post where the temporary entity has already been created
|
} else { // metafield comes from a second post where the temporary entity has already been created
|
||||||
$metaField = $metaFieldsTable->newEmptyEntity();
|
$metaField = $metaFieldsTable->newEmptyEntity();
|
||||||
$metaFieldsTable->patchEntity($metaField, [
|
$metaFieldsTable->patchEntity(
|
||||||
'value' => $new_value,
|
$metaField,
|
||||||
'scope' => $this->Table->getBehavior('MetaFields')->getScope(), // get scope from behavior
|
[
|
||||||
'field' => $rawMetaTemplateField->field,
|
'value' => $new_value,
|
||||||
'meta_template_id' => $rawMetaTemplateField->meta_template_id,
|
'scope' => $this->Table->getBehavior('MetaFields')->getScope(), // get scope from behavior
|
||||||
'meta_template_field_id' => $rawMetaTemplateField->id,
|
'field' => $rawMetaTemplateField->field,
|
||||||
'meta_template_directory_id' => $template->meta_template_directory_id,
|
'meta_template_id' => $rawMetaTemplateField->meta_template_id,
|
||||||
'parent_id' => $entity->id,
|
'meta_template_field_id' => $rawMetaTemplateField->id,
|
||||||
'uuid' => Text::uuid(),
|
'meta_template_directory_id' => $template->meta_template_directory_id,
|
||||||
]);
|
'parent_id' => $entity->id,
|
||||||
|
'uuid' => Text::uuid(),
|
||||||
|
]
|
||||||
|
);
|
||||||
$entity->meta_fields[] = $metaField;
|
$entity->meta_fields[] = $metaField;
|
||||||
$entity->MetaTemplates[$template_id]->meta_template_fields[$meta_template_field_id]->metaFields[] = $metaField;
|
$entity->MetaTemplates[$template_id]->meta_template_fields[$meta_template_field_id]->metaFields[] = $metaField;
|
||||||
}
|
}
|
||||||
|
@ -608,12 +699,12 @@ class CRUDComponent extends Component
|
||||||
$params['contain'] = [$params['contain'], 'MetaFields'];
|
$params['contain'] = [$params['contain'], 'MetaFields'];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$query = $this->Table->find()->where(["{$this->TableAlias}.id" => $id]);
|
$query = $this->Table->find()->where(["{$this->Table->getAlias()}.id" => $id]);
|
||||||
if (!empty($params['contain'])) {
|
if (!empty($params['contain'])) {
|
||||||
$query->contain($params['contain']);
|
$query->contain($params['contain']);
|
||||||
}
|
}
|
||||||
if (!empty($params['conditions'])) {
|
if (!empty($params['conditions'])) {
|
||||||
$query->where($params['conditions']);
|
$query->where($params['conditions']);
|
||||||
}
|
}
|
||||||
$data = $query->first();
|
$data = $query->first();
|
||||||
if ($this->metaFieldsSupported()) {
|
if ($this->metaFieldsSupported()) {
|
||||||
|
@ -711,9 +802,14 @@ class CRUDComponent extends Component
|
||||||
}
|
}
|
||||||
$metaFieldScope = $this->Table->getBehavior('MetaFields')->getScope();
|
$metaFieldScope = $this->Table->getBehavior('MetaFields')->getScope();
|
||||||
$query = $this->MetaTemplates->find()->where(['MetaTemplates.scope' => $metaFieldScope]);
|
$query = $this->MetaTemplates->find()->where(['MetaTemplates.scope' => $metaFieldScope]);
|
||||||
$query->contain(['MetaTemplateFields.MetaFields' => function ($q) use ($id, $metaFieldScope) {
|
$query->contain(
|
||||||
return $q->where(['MetaFields.scope' => $metaFieldScope, 'MetaFields.parent_id' => $id]);
|
[
|
||||||
}]);
|
'MetaTemplateFields.MetaFields' => function ($q) use ($id, $metaFieldScope) {
|
||||||
|
return $q->where(['MetaFields.scope' => $metaFieldScope, 'MetaFields.parent_id' => $id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
]
|
||||||
|
);
|
||||||
$query
|
$query
|
||||||
->order(['MetaTemplates.is_default' => 'DESC'])
|
->order(['MetaTemplates.is_default' => 'DESC'])
|
||||||
->order(['MetaTemplates.name' => 'ASC']);
|
->order(['MetaTemplates.name' => 'ASC']);
|
||||||
|
@ -756,7 +852,7 @@ class CRUDComponent extends Component
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function attachMetaTemplates($data, $metaTemplates, $pruneEmptyDisabled=true)
|
public function attachMetaTemplates($data, $metaTemplates, $pruneEmptyDisabled = true)
|
||||||
{
|
{
|
||||||
$this->MetaTemplates = TableRegistry::getTableLocator()->get('MetaTemplates');
|
$this->MetaTemplates = TableRegistry::getTableLocator()->get('MetaTemplates');
|
||||||
$metaFields = [];
|
$metaFields = [];
|
||||||
|
@ -811,11 +907,13 @@ class CRUDComponent extends Component
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$this->Controller->set('requestedMetaFields', $requestedMetaFields);
|
$this->Controller->set('requestedMetaFields', $requestedMetaFields);
|
||||||
return $query->contain([
|
return $query->contain(
|
||||||
'MetaFields' => [
|
[
|
||||||
'conditions' => $containConditions
|
'MetaFields' => [
|
||||||
|
'conditions' => $containConditions
|
||||||
|
]
|
||||||
]
|
]
|
||||||
]);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setRequestedEntryAmount()
|
protected function setRequestedEntryAmount()
|
||||||
|
@ -824,7 +922,7 @@ class CRUDComponent extends Component
|
||||||
$tableSettings = IndexSetting::getTableSetting($user, $this->Table);
|
$tableSettings = IndexSetting::getTableSetting($user, $this->Table);
|
||||||
if (!empty($tableSettings['number_of_element'])) {
|
if (!empty($tableSettings['number_of_element'])) {
|
||||||
if ($tableSettings['number_of_element'] === 'all') {
|
if ($tableSettings['number_of_element'] === 'all') {
|
||||||
$tableSettings['number_of_element'] = 10000; // Even with all, sure not to return too much data
|
$tableSettings['number_of_element'] = 10000; // Even with all selecect, make sure not to return too much data for the browser
|
||||||
}
|
}
|
||||||
$this->Controller->paginate['limit'] = intval($tableSettings['number_of_element']);
|
$this->Controller->paginate['limit'] = intval($tableSettings['number_of_element']);
|
||||||
}
|
}
|
||||||
|
@ -848,15 +946,29 @@ class CRUDComponent extends Component
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$data = $this->Table->get($id, $params);
|
$data = false;
|
||||||
|
if (Validation::uuid($id) && $this->Table->getSchema()->hasColumn('uuid')) {
|
||||||
|
$data = $this->Table->find()->where(
|
||||||
|
[
|
||||||
|
"{$this->Table->getAlias()}.uuid" => $id,
|
||||||
|
]
|
||||||
|
)->first();
|
||||||
|
} else {
|
||||||
|
$data = $this->Table->get($id, $params);
|
||||||
|
}
|
||||||
|
|
||||||
if (empty($data)) {
|
if (empty($data)) {
|
||||||
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
|
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
|
||||||
}
|
}
|
||||||
if ($this->metaFieldsSupported() && !empty($data['meta_fields'])) {
|
if ($this->metaFieldsSupported() && !empty($data['meta_fields'])) {
|
||||||
$usedMetaTemplateIDs = array_values(array_unique(Hash::extract($data['meta_fields'], '{n}.meta_template_id')));
|
$usedMetaTemplateIDs = array_values(array_unique(Hash::extract($data['meta_fields'], '{n}.meta_template_id')));
|
||||||
$data = $this->attachMetaTemplatesIfNeeded($data, null, [
|
$data = $this->attachMetaTemplatesIfNeeded(
|
||||||
'id IN' => $usedMetaTemplateIDs
|
$data,
|
||||||
]);
|
null,
|
||||||
|
[
|
||||||
|
'id IN' => $usedMetaTemplateIDs
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (isset($params['afterFind'])) {
|
if (isset($params['afterFind'])) {
|
||||||
$data = $params['afterFind']($data);
|
$data = $params['afterFind']($data);
|
||||||
|
@ -870,7 +982,7 @@ class CRUDComponent extends Component
|
||||||
$this->Controller->set('entity', $data);
|
$this->Controller->set('entity', $data);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function attachMetaTemplatesIfNeeded($data, array $metaTemplates = null, array $metaTemplateConditions=[])
|
public function attachMetaTemplatesIfNeeded($data, array $metaTemplates = null, array $metaTemplateConditions = [])
|
||||||
{
|
{
|
||||||
if (!$this->metaFieldsSupported()) {
|
if (!$this->metaFieldsSupported()) {
|
||||||
return $data;
|
return $data;
|
||||||
|
@ -878,11 +990,14 @@ class CRUDComponent extends Component
|
||||||
if (!is_null($metaTemplates)) {
|
if (!is_null($metaTemplates)) {
|
||||||
// We might be in the case where $metaTemplates gets re-used in a while loop
|
// We might be in the case where $metaTemplates gets re-used in a while loop
|
||||||
// We deep copy the meta-template so that the data attached is not preserved for the next iteration
|
// We deep copy the meta-template so that the data attached is not preserved for the next iteration
|
||||||
$metaTemplates = array_map(function ($metaTemplate) {
|
$metaTemplates = array_map(
|
||||||
$tmpEntity = $this->MetaTemplates->newEntity($metaTemplate->toArray());
|
function ($metaTemplate) {
|
||||||
$tmpEntity['meta_template_fields'] = Hash::combine($tmpEntity['meta_template_fields'], '{n}.id', '{n}'); // newEntity resets array indexing, see https://github.com/cakephp/cakephp/blob/32e3c532fea8abe2db8b697f07dfddf4dfc134ca/src/ORM/Marshaller.php#L369
|
$tmpEntity = $this->MetaTemplates->newEntity($metaTemplate->toArray());
|
||||||
return $tmpEntity;
|
$tmpEntity['meta_template_fields'] = Hash::combine($tmpEntity['meta_template_fields'], '{n}.id', '{n}'); // newEntity resets array indexing, see https://github.com/cakephp/cakephp/blob/32e3c532fea8abe2db8b697f07dfddf4dfc134ca/src/ORM/Marshaller.php#L369
|
||||||
}, $metaTemplates);
|
return $tmpEntity;
|
||||||
|
},
|
||||||
|
$metaTemplates
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
$metaTemplates = $this->getMetaTemplates($metaTemplateConditions)->toArray();
|
$metaTemplates = $this->getMetaTemplates($metaTemplateConditions)->toArray();
|
||||||
}
|
}
|
||||||
|
@ -890,59 +1005,49 @@ class CRUDComponent extends Component
|
||||||
return $data;
|
return $data;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function delete($id=false, $params=[]): void
|
public function delete($id = false, $params = []): void
|
||||||
{
|
{
|
||||||
|
$ids = $this->getIds($id);
|
||||||
|
$query = $this->prepareQueryForIDOrUUID($id);
|
||||||
|
if (!empty($params['conditions'])) {
|
||||||
|
$query->where($params['conditions']);
|
||||||
|
}
|
||||||
|
if (!empty($params['contain'])) {
|
||||||
|
$query->contain($params['contain']);
|
||||||
|
}
|
||||||
|
$entities = $query->all()->toList();
|
||||||
|
if (isset($params['afterFind'])) {
|
||||||
|
foreach ($entities as $i => $entity) {
|
||||||
|
$entities[$i] = $params['afterFind']($entity, $params);
|
||||||
|
}
|
||||||
|
}
|
||||||
if ($this->request->is('get')) {
|
if ($this->request->is('get')) {
|
||||||
if(!empty($id)) {
|
$this->Controller->set('entities', $entities);
|
||||||
$query = $this->Table->find()->where([$this->Table->getAlias() . '.id' => $id]);
|
$this->Controller->set('tableFields', $params['tableFields'] ?? []);
|
||||||
if (!empty($params['conditions'])) {
|
$this->Controller->viewBuilder()->setLayout('ajax');
|
||||||
$query->where($params['conditions']);
|
if (count($entities) > 1) {
|
||||||
}
|
$this->Controller->set('ids', $ids);
|
||||||
if (!empty($params['contain'])) {
|
$this->Controller->render('/genericTemplates/massDelete');
|
||||||
$query->contain($params['contain']);
|
|
||||||
}
|
|
||||||
$data = $query->first();
|
|
||||||
if (isset($params['afterFind'])) {
|
|
||||||
$data = $params['afterFind']($data, $params);
|
|
||||||
}
|
|
||||||
if (empty($data)) {
|
|
||||||
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
|
|
||||||
}
|
|
||||||
$this->Controller->set('id', $data['id']);
|
|
||||||
$this->Controller->set('data', $data);
|
|
||||||
$this->Controller->set('bulkEnabled', false);
|
|
||||||
} else {
|
} else {
|
||||||
$this->Controller->set('bulkEnabled', true);
|
$this->Controller->set('id', $id);
|
||||||
|
$this->Controller->render('/genericTemplates/delete');
|
||||||
}
|
}
|
||||||
} else if ($this->request->is('post') || $this->request->is('delete')) {
|
} else if ($this->request->is('post') || $this->request->is('delete')) {
|
||||||
$ids = $this->getIdsOrFail($id);
|
|
||||||
$isBulk = count($ids) > 1;
|
$isBulk = count($ids) > 1;
|
||||||
$bulkSuccesses = 0;
|
$bulkSuccesses = 0;
|
||||||
foreach ($ids as $id) {
|
foreach ($entities as $i => $entity) {
|
||||||
$query = $this->Table->find()->where([$this->Table->getAlias() . '.id' => $id]);
|
|
||||||
if (!empty($params['conditions'])) {
|
|
||||||
$query->where($params['conditions']);
|
|
||||||
}
|
|
||||||
if (!empty($params['contain'])) {
|
|
||||||
$query->contain($params['contain']);
|
|
||||||
}
|
|
||||||
$data = $query->first();
|
|
||||||
if (isset($params['afterFind'])) {
|
|
||||||
$data = $params['afterFind']($data, $params);
|
|
||||||
}
|
|
||||||
if (isset($params['beforeSave'])) {
|
if (isset($params['beforeSave'])) {
|
||||||
try {
|
try {
|
||||||
$data = $params['beforeSave']($data);
|
$entity = $params['beforeSave']($entity);
|
||||||
if ($data === false) {
|
if ($entity === false) {
|
||||||
throw new NotFoundException(__('Could not save {0} due to the input failing to meet expectations. Your input is bad and you should feel bad.', $this->ObjectAlias));
|
throw new NotFoundException(__('Could not save {0} due to the input failing to meet expectations. Your input is bad and you should feel bad.', $this->ObjectAlias));
|
||||||
}
|
}
|
||||||
} catch (Exception $e) {
|
} catch (\Exception $e) {
|
||||||
$data = false;
|
$entity = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!empty($data)) {
|
if (!empty($entity)) {
|
||||||
$success = $this->Table->delete($data);
|
$success = $this->Table->delete($entity);
|
||||||
$success = true;
|
|
||||||
} else {
|
} else {
|
||||||
$success = false;
|
$success = false;
|
||||||
}
|
}
|
||||||
|
@ -954,7 +1059,7 @@ class CRUDComponent extends Component
|
||||||
$bulkSuccesses == count($ids),
|
$bulkSuccesses == count($ids),
|
||||||
$isBulk,
|
$isBulk,
|
||||||
__('{0} deleted.', $this->ObjectAlias),
|
__('{0} deleted.', $this->ObjectAlias),
|
||||||
__('All {0} have been deleted.', Inflector::pluralize($this->ObjectAlias)),
|
__('All selected {0} have been deleted.', Inflector::pluralize($this->ObjectAlias)),
|
||||||
__('Could not delete {0}.', $this->ObjectAlias),
|
__('Could not delete {0}.', $this->ObjectAlias),
|
||||||
__(
|
__(
|
||||||
'{0} / {1} {2} have been deleted.',
|
'{0} / {1} {2} have been deleted.',
|
||||||
|
@ -963,21 +1068,14 @@ class CRUDComponent extends Component
|
||||||
Inflector::pluralize($this->ObjectAlias)
|
Inflector::pluralize($this->ObjectAlias)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
$additionalData = [];
|
$this->setResponseForController('massToggle', $bulkSuccesses == count($ids), $message, [], [], []);
|
||||||
if ($bulkSuccesses > 0) {
|
|
||||||
$additionalData['redirect'] = Router::url(['controller' => $this->Controller->getName(), 'action' => 'index']);
|
|
||||||
}
|
|
||||||
$this->setResponseForController('delete', $bulkSuccesses, $message, $data, null, $additionalData);
|
|
||||||
}
|
}
|
||||||
$this->Controller->set('scope', 'users');
|
|
||||||
$this->Controller->viewBuilder()->setLayout('ajax');
|
|
||||||
$this->Controller->render('/genericTemplates/delete');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function tag($id = false): void
|
public function tag($id = false): void
|
||||||
{
|
{
|
||||||
if (!$this->taggingSupported()) {
|
if (!$this->taggingSupported()) {
|
||||||
throw new Exception("Table {$this->TableAlias} does not support tagging");
|
throw new \Exception("Table {$this->TableAlias} does not support tagging");
|
||||||
}
|
}
|
||||||
if ($this->request->is('get')) {
|
if ($this->request->is('get')) {
|
||||||
$this->setAllTags();
|
$this->setAllTags();
|
||||||
|
@ -1037,7 +1135,7 @@ class CRUDComponent extends Component
|
||||||
public function untag($id = false): void
|
public function untag($id = false): void
|
||||||
{
|
{
|
||||||
if (!$this->taggingSupported()) {
|
if (!$this->taggingSupported()) {
|
||||||
throw new Exception("Table {$this->TableAlias} does not support tagging");
|
throw new \Exception("Table {$this->TableAlias} does not support tagging");
|
||||||
}
|
}
|
||||||
if ($this->request->is('get')) {
|
if ($this->request->is('get')) {
|
||||||
$this->setAllTags();
|
$this->setAllTags();
|
||||||
|
@ -1064,9 +1162,12 @@ class CRUDComponent extends Component
|
||||||
$input = $this->request->getData();
|
$input = $this->request->getData();
|
||||||
$tagsToRemove = json_decode($input['tag_list']);
|
$tagsToRemove = json_decode($input['tag_list']);
|
||||||
// patching will mirror tag in the DB, however, we only want to remove tags
|
// patching will mirror tag in the DB, however, we only want to remove tags
|
||||||
$input['tags'] = array_filter($entity->tags, function ($existingTag) use ($tagsToRemove) {
|
$input['tags'] = array_filter(
|
||||||
return !in_array($existingTag->name, $tagsToRemove);
|
$entity->tags,
|
||||||
});
|
function ($existingTag) use ($tagsToRemove) {
|
||||||
|
return !in_array($existingTag->name, $tagsToRemove);
|
||||||
|
}
|
||||||
|
);
|
||||||
$patchEntityParams = [
|
$patchEntityParams = [
|
||||||
'fields' => ['tags'],
|
'fields' => ['tags'],
|
||||||
];
|
];
|
||||||
|
@ -1099,7 +1200,7 @@ class CRUDComponent extends Component
|
||||||
public function viewTags(int $id, array $params = []): void
|
public function viewTags(int $id, array $params = []): void
|
||||||
{
|
{
|
||||||
if (!$this->taggingSupported()) {
|
if (!$this->taggingSupported()) {
|
||||||
throw new Exception("Table {$this->TableAlias} does not support tagging");
|
throw new \Exception("Table {$this->TableAlias} does not support tagging");
|
||||||
}
|
}
|
||||||
if (empty($id)) {
|
if (empty($id)) {
|
||||||
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
|
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
|
||||||
|
@ -1135,6 +1236,7 @@ class CRUDComponent extends Component
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($this->Controller->ParamHandler->isRest()) {
|
if ($this->Controller->ParamHandler->isRest()) {
|
||||||
|
$data = $data ?? $message;
|
||||||
$this->Controller->restResponsePayload = $this->RestResponse->viewData($data, 'json');
|
$this->Controller->restResponsePayload = $this->RestResponse->viewData($data, 'json');
|
||||||
} elseif ($this->Controller->ParamHandler->isAjax()) {
|
} elseif ($this->Controller->ParamHandler->isAjax()) {
|
||||||
if (!empty($additionalData['redirect'])) { // If a redirection occurs, we need to make sure the flash message gets displayed
|
if (!empty($additionalData['redirect'])) { // If a redirection occurs, we need to make sure the flash message gets displayed
|
||||||
|
@ -1166,23 +1268,27 @@ class CRUDComponent extends Component
|
||||||
* @throws NotFoundException when no ID could be found
|
* @throws NotFoundException when no ID could be found
|
||||||
*/
|
*/
|
||||||
public function getIdsOrFail($id = false): array
|
public function getIdsOrFail($id = false): array
|
||||||
|
{
|
||||||
|
$ids = $this->getIds($id);
|
||||||
|
if (empty($ids)) {
|
||||||
|
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
|
||||||
|
}
|
||||||
|
return $ids;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIds($id): array
|
||||||
{
|
{
|
||||||
$params = $this->Controller->ParamHandler->harvestParams(['ids']);
|
$params = $this->Controller->ParamHandler->harvestParams(['ids']);
|
||||||
if (!empty($params['ids'])) {
|
if (!empty($params['ids'])) {
|
||||||
$params['ids'] = json_decode($params['ids']);
|
$params['ids'] = json_decode($params['ids']);
|
||||||
}
|
}
|
||||||
$ids = [];
|
$ids = [];
|
||||||
if (empty($id)) {
|
if (empty($id) && !empty($params['ids'])) {
|
||||||
if (empty($params['ids'])) {
|
|
||||||
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
|
|
||||||
}
|
|
||||||
$ids = $params['ids'];
|
$ids = $params['ids'];
|
||||||
} else {
|
} else {
|
||||||
$id = $this->getInteger($id);
|
$id = $this->getInteger($id);
|
||||||
if (!is_null($id)) {
|
if (!is_null($id)) {
|
||||||
$ids = [$id];
|
$ids = [$id];
|
||||||
} else {
|
|
||||||
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return $ids;
|
return $ids;
|
||||||
|
@ -1239,10 +1345,13 @@ class CRUDComponent extends Component
|
||||||
$quickFilterFields = $options['quickFilters'];
|
$quickFilterFields = $options['quickFilters'];
|
||||||
$this->Controller->set('quickFilter', empty($quickFilterFields) ? [] : $quickFilterFields);
|
$this->Controller->set('quickFilter', empty($quickFilterFields) ? [] : $quickFilterFields);
|
||||||
if ($this->metaFieldsSupported() && !empty($options['quickFilterForMetaField']['enabled'])) {
|
if ($this->metaFieldsSupported() && !empty($options['quickFilterForMetaField']['enabled'])) {
|
||||||
$this->Controller->set('quickFilterForMetaField', [
|
$this->Controller->set(
|
||||||
'enabled' => $options['quickFilterForMetaField']['enabled'] ?? false,
|
'quickFilterForMetaField',
|
||||||
'wildcard_search' => $options['quickFilterForMetaField']['enabled'] ?? false,
|
[
|
||||||
]);
|
'enabled' => $options['quickFilterForMetaField']['enabled'] ?? false,
|
||||||
|
'wildcard_search' => $options['quickFilterForMetaField']['enabled'] ?? false,
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (!empty($params['quickFilter']) && !empty($quickFilterFields)) {
|
if (!empty($params['quickFilter']) && !empty($quickFilterFields)) {
|
||||||
$this->Controller->set('quickFilterValue', $params['quickFilter']);
|
$this->Controller->set('quickFilterValue', $params['quickFilter']);
|
||||||
|
@ -1304,6 +1413,7 @@ class CRUDComponent extends Component
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$activeFilters[$filter] = $filterValue;
|
$activeFilters[$filter] = $filterValue;
|
||||||
|
$filterValue = $this->convertBooleanAnyAndBothIfNeeded($filter, $filterValue);
|
||||||
if (is_array($filterValue)) {
|
if (is_array($filterValue)) {
|
||||||
$query = $this->setInCondition($query, $filter, $filterValue);
|
$query = $this->setInCondition($query, $filter, $filterValue);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1349,10 +1459,13 @@ class CRUDComponent extends Component
|
||||||
protected function setTagFilters($query, $tags)
|
protected function setTagFilters($query, $tags)
|
||||||
{
|
{
|
||||||
$modelAlias = $this->Table->getAlias();
|
$modelAlias = $this->Table->getAlias();
|
||||||
$subQuery = $this->Table->find('tagged', [
|
$subQuery = $this->Table->find(
|
||||||
'name' => $tags,
|
'tagged',
|
||||||
'OperatorAND' => true
|
[
|
||||||
])->select($modelAlias . '.id');
|
'name' => $tags,
|
||||||
|
'OperatorAND' => true
|
||||||
|
]
|
||||||
|
)->select($modelAlias . '.id');
|
||||||
return $query->where([$modelAlias . '.id IN' => $subQuery]);
|
return $query->where([$modelAlias . '.id IN' => $subQuery]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1367,23 +1480,27 @@ class CRUDComponent extends Component
|
||||||
$query = $this->setRelatedCondition($query, $modelName, $fieldName, $filterValue);
|
$query = $this->setRelatedCondition($query, $modelName, $fieldName, $filterValue);
|
||||||
} else {
|
} else {
|
||||||
$filterParts = array_slice($filterParts, 1);
|
$filterParts = array_slice($filterParts, 1);
|
||||||
$query = $query->matching($modelName, function (\Cake\ORM\Query $q) use ($filterParts, $filterValue) {
|
$query = $query->matching(
|
||||||
return $this->setNestedRelatedCondition($q, $filterParts, $filterValue);
|
$modelName,
|
||||||
});
|
function (\Cake\ORM\Query $q) use ($filterParts, $filterValue) {
|
||||||
|
return $this->setNestedRelatedCondition($q, $filterParts, $filterValue);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setRelatedCondition($query, $modelName, $fieldName, $filterValue)
|
protected function setRelatedCondition($query, $modelName, $fieldName, $filterValue)
|
||||||
{
|
{
|
||||||
return $query->matching($modelName, function (\Cake\ORM\Query $q) use ($fieldName, $filterValue) {
|
return $query->matching(
|
||||||
if (is_array($filterValue)) {
|
$modelName,
|
||||||
$query = $this->setInCondition($q, $fieldName, $filterValue);
|
function (\Cake\ORM\Query $q) use ($fieldName, $filterValue) {
|
||||||
} else {
|
if (is_array($filterValue)) {
|
||||||
$query = $this->setValueCondition($q, $fieldName, $filterValue);
|
return $this->setInCondition($q, $fieldName, $filterValue);
|
||||||
|
}
|
||||||
|
return $this->setValueCondition($q, $fieldName, $filterValue);
|
||||||
}
|
}
|
||||||
return $query;
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function setValueCondition($query, $fieldName, $value)
|
protected function setValueCondition($query, $fieldName, $value)
|
||||||
|
@ -1391,9 +1508,11 @@ class CRUDComponent extends Component
|
||||||
if (strlen(trim($value, '%')) === strlen($value)) {
|
if (strlen(trim($value, '%')) === strlen($value)) {
|
||||||
return $query->where([$fieldName => $value]);
|
return $query->where([$fieldName => $value]);
|
||||||
} else {
|
} else {
|
||||||
return $query->where(function ($exp, \Cake\ORM\Query $q) use ($fieldName, $value) {
|
return $query->where(
|
||||||
return $exp->like($fieldName, $value);
|
function ($exp, \Cake\ORM\Query $q) use ($fieldName, $value) {
|
||||||
});
|
return $exp->like($fieldName, $value);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1408,13 +1527,17 @@ class CRUDComponent extends Component
|
||||||
$operator = $split[1];
|
$operator = $split[1];
|
||||||
}
|
}
|
||||||
if ($operator == '=') {
|
if ($operator == '=') {
|
||||||
return $query->where(function (QueryExpression $exp, Query $q) use ($field, $values) {
|
return $query->where(
|
||||||
return $exp->in($field, $values);
|
function (QueryExpression $exp, Query $q) use ($field, $values) {
|
||||||
});
|
return $exp->in($field, $values);
|
||||||
|
}
|
||||||
|
);
|
||||||
} else if ($operator == '!=') {
|
} else if ($operator == '!=') {
|
||||||
return $query->where(function (QueryExpression $exp, Query $q) use ($field, $values) {
|
return $query->where(
|
||||||
return $exp->notIn($field, $values);
|
function (QueryExpression $exp, Query $q) use ($field, $values) {
|
||||||
});
|
return $exp->notIn($field, $values);
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1478,10 +1601,13 @@ class CRUDComponent extends Component
|
||||||
|
|
||||||
public function setParentConditionsForMetaFields($query, array $metaConditions)
|
public function setParentConditionsForMetaFields($query, array $metaConditions)
|
||||||
{
|
{
|
||||||
$metaTemplates = $this->MetaFields->MetaTemplates->find('list', [
|
$metaTemplates = $this->MetaFields->MetaTemplates->find(
|
||||||
'keyField' => 'name',
|
'list',
|
||||||
'valueField' => 'id'
|
[
|
||||||
])->where(['name IN' => array_keys($metaConditions)])->all()->toArray();
|
'keyField' => 'name',
|
||||||
|
'valueField' => 'id'
|
||||||
|
]
|
||||||
|
)->where(['name IN' => array_keys($metaConditions)])->all()->toArray();
|
||||||
$fieldsConditions = [];
|
$fieldsConditions = [];
|
||||||
foreach ($metaConditions as $templateName => $templateConditions) {
|
foreach ($metaConditions as $templateName => $templateConditions) {
|
||||||
$metaTemplateID = isset($metaTemplates[$templateName]) ? $metaTemplates[$templateName] : -1;
|
$metaTemplateID = isset($metaTemplates[$templateName]) ? $metaTemplates[$templateName] : -1;
|
||||||
|
@ -1497,7 +1623,7 @@ class CRUDComponent extends Component
|
||||||
private function getParentIDQueryForMetaANDConditions(array $metaANDConditions)
|
private function getParentIDQueryForMetaANDConditions(array $metaANDConditions)
|
||||||
{
|
{
|
||||||
if (empty($metaANDConditions)) {
|
if (empty($metaANDConditions)) {
|
||||||
throw new Exception('Invalid passed conditions');
|
throw new \Exception('Invalid passed conditions');
|
||||||
}
|
}
|
||||||
foreach ($metaANDConditions as $i => $conditions) {
|
foreach ($metaANDConditions as $i => $conditions) {
|
||||||
$metaANDConditions[$i]['scope'] = $this->Table->getBehavior('MetaFields')->getScope();
|
$metaANDConditions[$i]['scope'] = $this->Table->getBehavior('MetaFields')->getScope();
|
||||||
|
@ -1527,6 +1653,17 @@ class CRUDComponent extends Component
|
||||||
return $prefixedConditions;
|
return $prefixedConditions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function convertBooleanAnyAndBothIfNeeded(string $filterName, $filterValue)
|
||||||
|
{
|
||||||
|
$typeMap = $this->Table->getSchema()->typeMap();
|
||||||
|
if (!empty($typeMap[$filterName]) && $typeMap[$filterName] === 'boolean') {
|
||||||
|
if ($filterValue === 'any') {
|
||||||
|
return [true, false];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $filterValue;
|
||||||
|
}
|
||||||
|
|
||||||
public function taggingSupported()
|
public function taggingSupported()
|
||||||
{
|
{
|
||||||
return $this->Table->behaviors()->has('Tag');
|
return $this->Table->behaviors()->has('Tag');
|
||||||
|
@ -1608,7 +1745,78 @@ class CRUDComponent extends Component
|
||||||
$this->Controller->render('/genericTemplates/toggle');
|
$this->Controller->render('/genericTemplates/toggle');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function toggleEnabled(int $id, array $path, string $fieldName = 'enabled'): bool
|
public function massToggle($fieldName, $params): void
|
||||||
|
{
|
||||||
|
$ids = $this->getIdsOrFail(false);
|
||||||
|
$query = $this->Table->find()->where(
|
||||||
|
function (QueryExpression $exp) use ($ids) {
|
||||||
|
return $exp->in($this->Table->getAlias() . '.id', $ids);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
if (!empty($params['conditions'])) {
|
||||||
|
$query->where($params['conditions']);
|
||||||
|
}
|
||||||
|
if (!empty($params['contain'])) {
|
||||||
|
$query->contain($params['contain']);
|
||||||
|
}
|
||||||
|
$entities = $query->all()->toList();
|
||||||
|
if ($this->request->is('get')) {
|
||||||
|
$this->Controller->set('ids', $ids);
|
||||||
|
$this->Controller->set('entities', $entities);
|
||||||
|
$this->Controller->set('fieldName', $fieldName);
|
||||||
|
$this->Controller->set('tableFields', $params['tableFields'] ?? []);
|
||||||
|
$this->Controller->viewBuilder()->setLayout('ajax');
|
||||||
|
$this->Controller->set('fieldName', $fieldName);
|
||||||
|
$this->Controller->render('/genericTemplates/massToggle');
|
||||||
|
} else if ($this->request->is('post') || $this->request->is('delete')) {
|
||||||
|
$bulkSuccesses = 0;
|
||||||
|
foreach ($entities as $entity) {
|
||||||
|
if (isset($params['afterFind'])) {
|
||||||
|
$entity = $params['afterFind']($entity, $params);
|
||||||
|
}
|
||||||
|
if (isset($params['beforeSave'])) {
|
||||||
|
try {
|
||||||
|
$entity = $params['beforeSave']($entity);
|
||||||
|
if ($entity === false) {
|
||||||
|
throw new NotFoundException(__('Could not save {0} due to the input failing to meet expectations. Your input is bad and you should feel bad.', $this->ObjectAlias));
|
||||||
|
}
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
$entity = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!empty($entity)) {
|
||||||
|
if (isset($params['force_state'])) {
|
||||||
|
$entity->{$fieldName} = $params['force_state'];
|
||||||
|
} else {
|
||||||
|
$entity->{$fieldName} = !$entity->{$fieldName};
|
||||||
|
}
|
||||||
|
$savedData = $this->Table->save($entity);
|
||||||
|
$success = $savedData !== false;
|
||||||
|
} else {
|
||||||
|
$success = false;
|
||||||
|
}
|
||||||
|
if ($success) {
|
||||||
|
$bulkSuccesses++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$message = $this->getMessageBasedOnResult(
|
||||||
|
$bulkSuccesses == count($ids),
|
||||||
|
true,
|
||||||
|
__('{0} updated.', $this->ObjectAlias),
|
||||||
|
__('All selected {0} have been updated.', Inflector::pluralize($this->ObjectAlias)),
|
||||||
|
__('Could not update {0}.', $this->ObjectAlias),
|
||||||
|
__(
|
||||||
|
'{0} / {1} {2} have been updated.',
|
||||||
|
$bulkSuccesses,
|
||||||
|
count($ids),
|
||||||
|
Inflector::pluralize($this->ObjectAlias)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
$this->setResponseForController('massToggle', $bulkSuccesses == count($ids), $message, [], [], []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function toggleEnabled(int $id, array $path, string $fieldName = 'enabled'): void
|
||||||
{
|
{
|
||||||
if (empty($id)) {
|
if (empty($id)) {
|
||||||
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
|
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
|
||||||
|
@ -1644,15 +1852,16 @@ class CRUDComponent extends Component
|
||||||
$query = $associatedTable->find()->rightJoin(
|
$query = $associatedTable->find()->rightJoin(
|
||||||
[$this->Table->getAlias() => $this->Table->getTable()],
|
[$this->Table->getAlias() => $this->Table->getTable()],
|
||||||
[sprintf('%s.id = %s.%s', $this->Table->getAlias(), $associatedTable->getAlias(), $association->getForeignKey())]
|
[sprintf('%s.id = %s.%s', $this->Table->getAlias(), $associatedTable->getAlias(), $association->getForeignKey())]
|
||||||
)
|
)->where(
|
||||||
->where([
|
[
|
||||||
["$field IS NOT" => NULL]
|
["$field IS NOT" => null]
|
||||||
]);
|
]
|
||||||
|
);
|
||||||
} else if ($associationType == 'manyToOne') {
|
} else if ($associationType == 'manyToOne') {
|
||||||
$fieldToExtract = sprintf('%s.%s', Inflector::singularize(strtolower($model)), $subField);
|
$fieldToExtract = sprintf('%s.%s', Inflector::singularize(strtolower($model)), $subField);
|
||||||
$query = $this->Table->find()->contain($model);
|
$query = $this->Table->find()->contain($model);
|
||||||
} else {
|
} else {
|
||||||
throw new Exception("Association $associationType not supported in CRUD Component");
|
throw new \Exception("Association $associationType not supported in CRUD Component");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$fieldToExtract = $field;
|
$fieldToExtract = $field;
|
||||||
|
@ -1684,6 +1893,31 @@ class CRUDComponent extends Component
|
||||||
return $filters;
|
return $filters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare a find query for either the provided id/uuid or a list of json-encoded ids
|
||||||
|
*
|
||||||
|
* @param mixed $id Can be either ID, UUID or unset
|
||||||
|
* @return Query
|
||||||
|
* @throws NotFoundException
|
||||||
|
*/
|
||||||
|
public function prepareQueryForIDOrUUID($id = false): Query
|
||||||
|
{
|
||||||
|
$query = $this->Table->find();
|
||||||
|
$ids = $this->getIds($id);
|
||||||
|
if (Validation::uuid($id) && $this->Table->getSchema()->hasColumn('uuid')) {
|
||||||
|
$query->where(["{$this->Table->getAlias()}.uuid" => $id,]);
|
||||||
|
} else if (count($ids) > 0) {
|
||||||
|
$query->where(
|
||||||
|
function (QueryExpression $exp) use ($ids) {
|
||||||
|
return $exp->in($this->Table->getAlias() . '.id', $ids);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new NotFoundException(__('Invalid {0}.', $this->ObjectAlias));
|
||||||
|
}
|
||||||
|
return $query;
|
||||||
|
}
|
||||||
|
|
||||||
private function renderViewInVariable($templateRelativeName, $data)
|
private function renderViewInVariable($templateRelativeName, $data)
|
||||||
{
|
{
|
||||||
$builder = new ViewBuilder();
|
$builder = new ViewBuilder();
|
||||||
|
|
Loading…
Reference in New Issue