From 899fa27a4538c3e8d41fe5dc7b63d64fd8e7523a Mon Sep 17 00:00:00 2001 From: mokaddem Date: Mon, 11 Jan 2021 12:48:58 +0100 Subject: [PATCH] chg: [component:CRUD] Improved flexibility --- src/Controller/Component/CRUDComponent.php | 35 ++++++++++++++++--- src/Controller/IndividualsController.php | 30 ++++++++++------ src/Controller/UsersController.php | 28 +++++++++------ templates/Individuals/index.php | 8 +++-- templates/Users/index.php | 21 +++++++++-- .../IndexTable/Fields/toggle.php | 3 -- .../ListTopBar/group_search.php | 2 +- .../genericElements/ListTopBar/scaffold.php | 4 ++- .../genericElements/side_menu_scaffold.php | 2 +- templates/genericTemplates/delete.php | 2 +- webroot/css/main.css | 5 +++ webroot/js/api-helper.js | 8 ++--- webroot/js/bootstrap-helper.js | 5 ++- 13 files changed, 111 insertions(+), 42 deletions(-) diff --git a/src/Controller/Component/CRUDComponent.php b/src/Controller/Component/CRUDComponent.php index 0ed15e0..81fd6d8 100644 --- a/src/Controller/Component/CRUDComponent.php +++ b/src/Controller/Component/CRUDComponent.php @@ -48,6 +48,23 @@ class CRUDComponent extends Component $this->Controller->set('data', $data); } } + + /** + * 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() { @@ -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(); + } } } diff --git a/src/Controller/IndividualsController.php b/src/Controller/IndividualsController.php index 7e6fe17..e0c7737 100644 --- a/src/Controller/IndividualsController.php +++ b/src/Controller/IndividualsController.php @@ -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'); } diff --git a/src/Controller/UsersController.php b/src/Controller/UsersController.php index 7d39103..db11453 100644 --- a/src/Controller/UsersController.php +++ b/src/Controller/UsersController.php @@ -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'); } diff --git a/templates/Individuals/index.php b/templates/Individuals/index.php index d5b72d8..7ff2e4c 100644 --- a/templates/Individuals/index.php +++ b/templates/Individuals/index.php @@ -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' ] diff --git a/templates/Users/index.php b/templates/Users/index.php index e73e860..7d7ef0d 100644 --- a/templates/Users/index.php +++ b/templates/Users/index.php @@ -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' ] diff --git a/templates/element/genericElements/IndexTable/Fields/toggle.php b/templates/element/genericElements/IndexTable/Fields/toggle.php index 39ecb67..b0fd866 100644 --- a/templates/element/genericElements/IndexTable/Fields/toggle.php +++ b/templates/element/genericElements/IndexTable/Fields/toggle.php @@ -75,9 +75,6 @@ ...correctOptions, APIConfirm: (tmpApi) => { return submitForm(tmpApi, url) - .catch(e => { - // Provide feedback inside modal? - }) }, } UI.modal(modalOptions) diff --git a/templates/element/genericElements/ListTopBar/group_search.php b/templates/element/genericElements/ListTopBar/group_search.php index cf74859..6732656 100644 --- a/templates/element/genericElements/ListTopBar/group_search.php +++ b/templates/element/genericElements/ListTopBar/group_search.php @@ -32,7 +32,7 @@ empty($data['value']) ? '' : h($data['value']) ); echo sprintf( - '
%s%s
', + '
%s%s
', h($tableRandomValue), $input, $button diff --git a/templates/element/genericElements/ListTopBar/scaffold.php b/templates/element/genericElements/ListTopBar/scaffold.php index 81c0a25..675a660 100644 --- a/templates/element/genericElements/ListTopBar/scaffold.php +++ b/templates/element/genericElements/ListTopBar/scaffold.php @@ -1,10 +1,12 @@ 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']); diff --git a/templates/element/genericElements/side_menu_scaffold.php b/templates/element/genericElements/side_menu_scaffold.php index 7374954..2993a0b 100644 --- a/templates/element/genericElements/side_menu_scaffold.php +++ b/templates/element/genericElements/side_menu_scaffold.php @@ -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 = '%s'; + $link_template = '%s'; } else { $link_template = '%s'; } diff --git a/templates/genericTemplates/delete.php b/templates/genericTemplates/delete.php index 64e8807..7da1377 100644 --- a/templates/genericTemplates/delete.php +++ b/templates/genericTemplates/delete.php @@ -13,7 +13,7 @@ Form->postLink( 'Delete', ['action' => 'delete', $id], - ['class' => 'btn btn-primary button-execute'] + ['class' => 'btn btn-primary button-execute', 'id' => 'submitButton'] ) ?> diff --git a/webroot/css/main.css b/webroot/css/main.css index 8468fc0..4997470 100644 --- a/webroot/css/main.css +++ b/webroot/css/main.css @@ -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; +} \ No newline at end of file diff --git a/webroot/js/api-helper.js b/webroot/js/api-helper.js index 62728cc..bb8e128 100644 --- a/webroot/js/api-helper.js +++ b/webroot/js/api-helper.js @@ -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) { diff --git a/webroot/js/bootstrap-helper.js b/webroot/js/bootstrap-helper.js index 66c65b0..591f075 100644 --- a/webroot/js/bootstrap-helper.js +++ b/webroot/js/bootstrap-helper.js @@ -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})