chg: [component:CRUD] Improved flexibility

pull/37/head
mokaddem 2021-01-11 12:48:58 +01:00
parent 2d26bc597f
commit 899fa27a45
13 changed files with 111 additions and 42 deletions

View File

@ -49,6 +49,23 @@ class CRUDComponent extends Component
}
}
/**
* getResponsePayload Returns the adaquate response payload based on the request context
*
* @return false or Array
*/
public function getResponsePayload()
{
if ($this->Controller->ParamHandler->isRest()) {
return $this->Controller->restResponsePayload;
} else if ($this->Controller->ParamHandler->isAjax() && $this->request->is(['post', 'put'])) {
if (empty($this->Controller->isFailResponse) || empty($this->Controller->ajax_with_html_on_failure)) {
return $this->Controller->ajaxResponsePayload;
}
}
return false;
}
private function getMetaTemplates()
{
$metaTemplates = [];
@ -301,8 +318,9 @@ class CRUDComponent extends Component
if ($this->Table->delete($data)) {
$message = __('{0} deleted.', $this->ObjectAlias);
if ($this->Controller->ParamHandler->isRest()) {
$data = $this->Table->get($id);
$this->Controller->restResponsePayload = $this->RestResponse->saveSuccessResponse($this->TableAlias, 'delete', $id, 'json', $message);
$this->Controller->restResponsePayload = $this->RestResponse->viewData($data, 'json');
} else if ($this->Controller->ParamHandler->isAjax()) {
$this->Controller->ajaxResponsePayload = $this->Controller->RestResponse->ajaxSuccessResponse($this->ObjectAlias, 'delete', $data, $message);
} else {
$this->Controller->Flash->success($message);
$this->Controller->redirect($this->Controller->referer());
@ -493,8 +511,17 @@ class CRUDComponent extends Component
}
}
private function getFilteringContextFromField($context)
private function getFilteringContextFromField($field)
{
return $this->Table->find()->distinct([$context])->all()->extract($context)->toList();
$exploded = explode('.', $field);
if (count($exploded) > 1) {
$model = $exploded[0];
$subField = $exploded[1];
return $this->Table->{$model}->find()
->distinct([$subField])
->extract($subField)->toList();
} else {
return $this->Table->find()->distinct([$field])->all()->extract($field)->toList();
}
}
}

View File

@ -15,8 +15,14 @@ class IndividualsController extends AppController
public function index()
{
$this->CRUD->index([
'filters' => ['uuid', 'email', 'first_name', 'last_name', 'position', 'Organisations.id'],
'filters' => ['uuid', 'email', 'first_name', 'last_name', 'position', 'Organisations.id', 'Alignments.type'],
'quickFilters' => ['uuid', 'email', 'first_name', 'last_name', 'position'],
'contextFilters' => [
'allow_all' => true,
'fields' => [
'Alignments.type'
]
],
'contain' => ['Alignments' => 'Organisations']
]);
if ($this->ParamHandler->isRest()) {
@ -29,8 +35,9 @@ class IndividualsController extends AppController
public function add()
{
$this->CRUD->add();
if ($this->ParamHandler->isRest()) {
return $this->restResponsePayload;
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$this->set('metaGroup', 'ContactDB');
}
@ -38,8 +45,9 @@ class IndividualsController extends AppController
public function view($id)
{
$this->CRUD->view($id, ['contain' => ['Alignments' => 'Organisations']]);
if ($this->ParamHandler->isRest()) {
return $this->restResponsePayload;
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$this->set('metaGroup', 'ContactDB');
}
@ -47,10 +55,9 @@ class IndividualsController extends AppController
public function edit($id)
{
$this->CRUD->edit($id);
if ($this->ParamHandler->isRest()) {
return $this->restResponsePayload;
} else if($this->ParamHandler->isAjax() && $this->request->is(['post', 'put'])) {
return $this->ajaxResponsePayload;
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$this->set('metaGroup', 'ContactDB');
$this->render('add');
@ -59,8 +66,9 @@ class IndividualsController extends AppController
public function delete($id)
{
$this->CRUD->delete($id);
if ($this->ParamHandler->isRest()) {
return $this->restResponsePayload;
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$this->set('metaGroup', 'ContactDB');
}

View File

@ -23,12 +23,9 @@ class UsersController extends AppController
public function add()
{
$this->CRUD->add();
if ($this->ParamHandler->isRest()) {
return $this->restResponsePayload;
} else if ($this->ParamHandler->isAjax() && $this->request->is(['post', 'put'])) {
if (empty($this->isFailResponse) || empty($this->ajax_with_html_on_failure)) {
return $this->ajaxResponsePayload;
}
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$dropdownData = [
'role' => $this->Users->Roles->find('list', [
@ -78,8 +75,9 @@ class UsersController extends AppController
$params['fields'][] = 'role_id';
}
$this->CRUD->edit($id, $params);
if ($this->ParamHandler->isRest()) {
return $this->restResponsePayload;
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$dropdownData = [
'role' => $this->Users->Roles->find('list', [
@ -94,11 +92,21 @@ class UsersController extends AppController
$this->render('add');
}
public function toggle($id, $fieldName = 'disabled')
{
$this->CRUD->toggle($id, $fieldName);
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
}
public function delete($id)
{
$this->CRUD->delete($id);
if ($this->ParamHandler->isRest()) {
return $this->restResponsePayload;
$responsePayload = $this->CRUD->getResponsePayload();
if (!empty($responsePayload)) {
return $responsePayload;
}
$this->set('metaGroup', $this->isAdmin ? 'Administration' : 'Cerebrate');
}

View File

@ -15,6 +15,10 @@ echo $this->element('genericElements/IndexTable/index_table', [
]
]
],
[
'type' => 'context_filters',
'context_filters' => $filteringContexts
],
[
'type' => 'search',
'button' => __('Filter'),
@ -68,12 +72,12 @@ echo $this->element('genericElements/IndexTable/index_table', [
'icon' => 'eye'
],
[
'onclick' => 'populateAndLoadModal(\'/individuals/edit/[onclick_params_data_path]\');',
'onclick' => 'openModalFromURL(\'/individuals/edit/[onclick_params_data_path]\');',
'onclick_params_data_path' => 'id',
'icon' => 'edit'
],
[
'onclick' => 'populateAndLoadModal(\'/individuals/delete/[onclick_params_data_path]\');',
'onclick' => 'openModalFromURL(\'/individuals/delete/[onclick_params_data_path]\');',
'onclick_params_data_path' => 'id',
'icon' => 'trash'
]

View File

@ -3,7 +3,6 @@ echo $this->element('genericElements/IndexTable/index_table', [
'data' => [
'data' => $data,
'top_bar' => [
'pull' => 'right',
'children' => [
[
'type' => 'simple',
@ -31,6 +30,22 @@ echo $this->element('genericElements/IndexTable/index_table', [
'sort' => 'id',
'data_path' => 'id',
],
[
'name' => __('Disabled'),
'sort' => 'disabled',
'data_path' => 'disabled',
'element' => 'toggle',
'url' => '/users/toggle/{{0}}',
'url_params_vars' => ['id'],
'toggle_data' => [
'editRequirement' => [
'function' => function($row, $options) {
return true;
},
],
'skip_full_reload' => true
]
],
[
'name' => __('Username'),
'sort' => 'username',
@ -71,12 +86,12 @@ echo $this->element('genericElements/IndexTable/index_table', [
'icon' => 'eye'
],
[
'onclick' => 'populateAndLoadModal(\'/users/edit/[onclick_params_data_path]\');',
'onclick' => 'openModalFromURL(\'/users/edit/[onclick_params_data_path]\');',
'onclick_params_data_path' => 'id',
'icon' => 'edit'
],
[
'onclick' => 'populateAndLoadModal(\'/users/delete/[onclick_params_data_path]\');',
'onclick' => 'openModalFromURL(\'/users/delete/[onclick_params_data_path]\');',
'onclick_params_data_path' => 'id',
'icon' => 'trash'
]

View File

@ -75,9 +75,6 @@
...correctOptions,
APIConfirm: (tmpApi) => {
return submitForm(tmpApi, url)
.catch(e => {
// Provide feedback inside modal?
})
},
}
UI.modal(modalOptions)

View File

@ -32,7 +32,7 @@
empty($data['value']) ? '' : h($data['value'])
);
echo sprintf(
'<div class="input-group" data-table-random-value="%s">%s%s</div>',
'<div class="input-group" data-table-random-value="%s" style="margin-left: auto;">%s%s</div>',
h($tableRandomValue),
$input,
$button

View File

@ -1,10 +1,12 @@
<?php
$groups = '';
$hasGroupSearch = false;
foreach ($data['children'] as $group) {
$groups .= $this->element('/genericElements/ListTopBar/group_' . (empty($group['type']) ? 'simple' : h($group['type'])), array('data' => $group, 'tableRandomValue' => $tableRandomValue));
$hasGroupSearch = $hasGroupSearch || (!empty($group['type']) && $group['type'] == 'search');
}
$tempClass = "btn-toolbar";
if (count($data['children']) > 1) {
if (count($data['children']) > 1 && !$hasGroupSearch) {
$tempClass .= ' justify-content-between';
} else if (!empty($data['pull'])) {
$tempClass .= ' float-' . h($data['pull']);

View File

@ -35,7 +35,7 @@ if (isset($menu[$metaGroup])) {
}
$active = ($scope === $this->request->getParam('controller') && $action === $this->request->getParam('action'));
if (!empty($data['popup'])) {
$link_template = '<a href="#" onClick="populateAndLoadModal(\'%s\')" class="list-group-item list-group-item-action %s %s pl-3 border-0 %s">%s</a>';
$link_template = '<a href="#" onClick="openModalFromURL(\'%s\')" class="list-group-item list-group-item-action %s %s pl-3 border-0 %s">%s</a>';
} else {
$link_template = '<a href="%s" class="list-group-item list-group-item-action %s %s pl-3 border-0 %s">%s</a>';
}

View File

@ -13,7 +13,7 @@
<?= $this->Form->postLink(
'Delete',
['action' => 'delete', $id],
['class' => 'btn btn-primary button-execute']
['class' => 'btn btn-primary button-execute', 'id' => 'submitButton']
)
?>
<button type="button" class="btn btn-secondary cancel-button" data-dismiss="modal"><?= __('Cancel') ?></button>

View File

@ -114,3 +114,8 @@
color: inherit;
text-decoration: inherit;
}
.btn-group > .btn:last-of-type:not(.dropdown-toggle), .btn-group > .btn-group:not(:last-of-type) > .btn {
border-top-right-radius: 0.2rem;
border-bottom-right-radius: 0.2rem;
}

View File

@ -142,7 +142,7 @@ class AJAXApi {
try {
const response = await fetch(url, AJAXApi.genericRequestConfigGET);
if (!response.ok) {
throw new Error('Network response was not ok')
throw new Error(`Network response was not ok. \`${response.statusText}\``)
}
const dataHtml = await response.text();
this.provideFeedback({
@ -179,7 +179,7 @@ class AJAXApi {
try {
const response = await fetch(url, AJAXApi.genericRequestConfigGET);
if (!response.ok) {
throw new Error('Network response was not ok')
throw new Error(`Network response was not ok. \`${response.statusText}\``)
}
const formHtml = await response.text();
let tmpNode = document.createElement("div");
@ -231,7 +231,7 @@ class AJAXApi {
};
const response = await fetch(form.action, options);
if (!response.ok) {
throw new Error('Network response was not ok')
throw new Error(`Network response was not ok. \`${response.statusText}\``)
}
const clonedResponse = response.clone()
try {
@ -268,7 +268,7 @@ class AJAXApi {
}, true, feedbackShown);
toReturn = Promise.reject(error);
}
} catch (error) {
} catch (error) { // -> probably not useful
toReturn = Promise.reject(error);
} finally {
if (!skipRequestHooks) {

View File

@ -503,7 +503,7 @@ class ModalFactory {
}
/** Attach the submission click listener for modals that have been generated by raw HTML */
findSubmitButtonAndAddListener() {
findSubmitButtonAndAddListener(clearOnclick=true) {
const $submitButton = this.$modal.find('.modal-footer #submitButton')
const formID = $submitButton.data('form-id')
let $form
@ -512,6 +512,9 @@ class ModalFactory {
} else {
$form = this.$modal.find('form')
}
if (clearOnclick) {
$submitButton[0].removeAttribute('onclick')
}
this.options.APIConfirm = (tmpApi) => {
tmpApi.mergeOptions({forceHTMLOnValidationFailure: true})