From 011b4a83eb1474b4eb619f94560f0b49fca07f2e Mon Sep 17 00:00:00 2001 From: cgfhtw Date: Mon, 11 Mar 2024 09:46:39 +0100 Subject: [PATCH] Filter => FhcApi --- .../controllers/api/frontend/v1/Filter.php | 231 ++++++++++++++++++ application/controllers/components/Filter.php | 1 + public/js/api/fhcapifactory.js | 4 +- public/js/api/filter.js | 89 +++++++ public/js/components/filter/API.js | 2 +- public/js/components/filter/Filter.js | 84 +++---- 6 files changed, 362 insertions(+), 49 deletions(-) create mode 100644 application/controllers/api/frontend/v1/Filter.php create mode 100644 public/js/api/filter.js diff --git a/application/controllers/api/frontend/v1/Filter.php b/application/controllers/api/frontend/v1/Filter.php new file mode 100644 index 000000000..45838fc5f --- /dev/null +++ b/application/controllers/api/frontend/v1/Filter.php @@ -0,0 +1,231 @@ +. + */ + +if (! defined('BASEPATH')) exit('No direct script access allowed'); + +/** + * This controller operates between (interface) the JS (GUI) and the FilterCmptLib (back-end) + * Provides data to the ajax get calls about the filter component + * Listens to ajax post calls to change the filter data + * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON + */ +class Filter extends FHCAPI_Controller +{ + const FILTER_UNIQUE_ID = 'filterUniqueId'; // Name of the filter cmpt unique id (mandatory) + const FILTER_TYPE = 'filterType'; // The filter type (PHP filter definition) used (mandatory) + const FILTER_ID = 'filterId'; // The id of the used filter (optional) + + /** + * Calls the parent's constructor and loads the FilterCmptLib + */ + public function __construct() + { + // NOTE: FilterCmpt has its own permissions checks + parent::__construct([ + 'getFilter' => self::PERM_LOGGED, + 'removeFilterField' => self::PERM_LOGGED, + 'addFilterField' => self::PERM_LOGGED, + 'applyFilterFields' => self::PERM_LOGGED, + 'removeCustomFilter' => self::PERM_LOGGED, + 'saveCustomFilter' => self::PERM_LOGGED, + 'reloadDataset' => self::PERM_LOGGED + ]); + + // Loads the FiltersModel + $this->load->model('system/Filters_model', 'FiltersModel'); + + // Loads the FilterCmptLib with HTTP GET/POST parameters + $this->_startFilterCmptLib(); + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + * Retrieves data about the current filter from the session and will be written on the output in JSON format + */ + public function getFilter() + { + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $session = $this->filtercmptlib->getSession(); + if (is_object($session)) { + // If stdClass it is an retval object + $session = $this->getDataOrTerminateWithError($session); + } + $this->terminateWithSuccess($session); + } + + /** + * Remove an applied filter (SQL where condition) from the current filter + */ + public function removeFilterField() + { + $this->form_validation->set_rules('filterField', 'filterField', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $result = $this->filtercmptlib->removeFilterField($this->input->post('filterField')); + + if (!$result) + $this->terminateWithError('Error occurred', self::ERROR_TYPE_GENERAL); + + $this->terminateWithSuccess('Field removed'); + } + + /** + * Add a filter (SQL where clause) to be applied to the current filter + */ + public function addFilterField() + { + $this->form_validation->set_rules('filterField', 'filterField', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $result = $this->filtercmptlib->addFilterField($this->input->post('filterField')); + + if (!$result) + $this->terminateWithError('Error occurred', self::ERROR_TYPE_GENERAL); + + $this->terminateWithSuccess('Field added'); + } + + /** + * Apply the filter changes + */ + public function applyFilterFields() + { + $this->form_validation->set_rules('filterFields', 'filterFields', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $result = $this->filtercmptlib->applyFilterFields($this->input->post('filterFields')); + + if (!$result) + $this->terminateWithError('Error occurred', self::ERROR_TYPE_GENERAL); + + $this->terminateWithSuccess('Applied'); + } + + /** + * Save the current filter as a custom filter for this user with the given description + */ + public function saveCustomFilter() + { + $this->form_validation->set_rules('customFilterName', 'customFilterName', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $result = $this->filtercmptlib->saveCustomFilter($this->input->post('customFilterName')); + + if (!$result) + $this->terminateWithError('Error occurred', self::ERROR_TYPE_GENERAL); + + $this->terminateWithSuccess('Saved'); + } + + /** + * Remove a custom filter by its filterId + */ + public function removeCustomFilter() + { + $this->form_validation->set_rules('filterId', 'filterId', 'required'); + + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $result = $this->filtercmptlib->removeCustomFilter($this->input->post('filterId')); + + if (!$result) + $this->terminateWithError('Error occurred', self::ERROR_TYPE_GENERAL); + + $this->terminateWithSuccess('Removed'); + } + + /** + * Reloads the dataset + */ + public function reloadDataset() + { + if (!$this->form_validation->run()) + $this->terminateWithValidationErrors($this->form_validation->error_array()); + + $this->filtercmptlib->reloadDataset(); + + $this->terminateWithSuccess('Success'); + } + + //------------------------------------------------------------------------------------------------------------------ + // Private methods + + /** + * Loads the FilterCmptLib with the FILTER_UNIQUE_ID parameter + * If the parameter FILTER_UNIQUE_ID is not given then the execution of the controller is terminated and + * an error message is printed + */ + private function _startFilterCmptLib() + { + $filterUniqueId = null; + $filterType = null; + $filterId = null; + + $validations = [ + [ + 'field' => self::FILTER_UNIQUE_ID, + 'label' => self::FILTER_UNIQUE_ID, + 'rules' => 'required' + ], + [ + 'field' => self::FILTER_TYPE, + 'label' => self::FILTER_TYPE, + 'rules' => 'required' + ], + ]; + + $this->load->library('form_validation'); + + if ($this->input->method() == 'get') + $this->form_validation->set_data($this->input->get()); + $this->form_validation->set_rules($validations); + + if ($this->form_validation->run()) { + $filterUniqueId = $this->input->post_get(self::FILTER_UNIQUE_ID); + $filterType = $this->input->post_get(self::FILTER_TYPE); + $filterId = $this->input->post_get(self::FILTER_ID); + + // Loads the FilterCmptLib that contains all the used logic + $this->load->library( + 'FilterCmptLib', + array( + 'filterUniqueId' => $filterUniqueId, + 'filterType' => $filterType, + 'filterId' => $filterId + ) + ); + + // Start the component + $this->filtercmptlib->start(); + } + } +} + diff --git a/application/controllers/components/Filter.php b/application/controllers/components/Filter.php index bde7d7ed7..617edd69f 100644 --- a/application/controllers/components/Filter.php +++ b/application/controllers/components/Filter.php @@ -9,6 +9,7 @@ if (! defined('BASEPATH')) exit('No direct script access allowed'); * This controller works with JSON calls on the HTTP GET or POST and the output is always JSON * NOTE: extends the FHC_Controller instead of the Auth_Controller because the FilterCmpt has its * own permissions check + * TODO(chris): deprecated */ class Filter extends FHC_Controller { diff --git a/public/js/api/fhcapifactory.js b/public/js/api/fhcapifactory.js index 2d3308e9f..08deb26b3 100644 --- a/public/js/api/fhcapifactory.js +++ b/public/js/api/fhcapifactory.js @@ -18,9 +18,11 @@ import search from "./search.js"; import phrasen from "./phrasen.js"; import navigation from "./navigation.js"; +import filter from "./filter.js"; export default { search, phrasen, - navigation + navigation, + filter }; diff --git a/public/js/api/filter.js b/public/js/api/filter.js new file mode 100644 index 000000000..a5920c758 --- /dev/null +++ b/public/js/api/filter.js @@ -0,0 +1,89 @@ +/** + * Copyright (C) 2024 fhcomplete.org + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +export default { + saveCustomFilter(wsParams) { + return this.$fhcApi.post( + '/api/frontend/v1/filter/saveCustomFilter', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + customFilterName: wsParams.customFilterName + } + ); + }, + removeCustomFilter(wsParams) { + return this.$fhcApi.post( + '/api/frontend/v1/filter/removeCustomFilter', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + filterId: wsParams.filterId + } + ); + }, + applyFilterFields(wsParams) { + return this.$fhcApi.post( + '/api/frontend/v1/filter/applyFilterFields', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + filterFields: wsParams.filterFields + } + ); + }, + addFilterField(wsParams) { + return this.$fhcApi.post( + '/api/frontend/v1/filter/addFilterField', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + filterField: wsParams.filterField + } + ); + }, + removeFilterField(wsParams) { + return this.$fhcApi.post( + '/api/frontend/v1/filter/removeFilterField', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + filterField: wsParams.filterField + } + ); + }, + getFilterById(wsParams) { + return this.$fhcApi.get( + '/api/frontend/v1/filter/getFilter', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType, + filterId: wsParams.filterId + } + ); + }, + getFilter(wsParams) { + return this.$fhcApi.get( + '/api/frontend/v1/filter/getFilter', + { + filterUniqueId: wsParams.filterUniqueId, + filterType: wsParams.filterType + } + ); + } +}; + diff --git a/public/js/components/filter/API.js b/public/js/components/filter/API.js index ff09c452d..58afc5bb3 100644 --- a/public/js/components/filter/API.js +++ b/public/js/components/filter/API.js @@ -21,7 +21,7 @@ import {CoreRESTClient} from '../../RESTClient.js'; const CORE_FILTER_CMPT_TIMEOUT = 7000; /** - * + * TODO(chris): deprecated */ export const CoreFilterAPIs = { /** diff --git a/public/js/components/filter/Filter.js b/public/js/components/filter/Filter.js index 64a75c910..6420c0dcf 100644 --- a/public/js/components/filter/Filter.js +++ b/public/js/components/filter/Filter.js @@ -15,8 +15,6 @@ * along with this program. If not, see . */ -import {CoreFilterAPIs} from './API.js'; -import {CoreRESTClient} from '../../RESTClient.js'; import {CoreFetchCmpt} from '../../components/Fetch.js'; import FilterConfig from './Filter/Config.js'; import FilterColumns from './Filter/Columns.js'; @@ -252,12 +250,12 @@ export const CoreFilterCmpt = { /** * */ - getFilter: function() { + getFilter() { if (this.selectedFilter === null) - this.startFetchCmpt(CoreFilterAPIs.getFilter, null, this.render); + this.startFetchCmpt(this.$fhcApi.factory.filter.getFilter, null, this.render); else this.startFetchCmpt( - CoreFilterAPIs.getFilterById, + this.$fhcApi.factory.filter.getFilterById, { filterId: this.selectedFilter }, @@ -267,55 +265,47 @@ export const CoreFilterCmpt = { /** * */ - render: function(response) { + render(response) { + let data = response; + this.filterName = data.filterName; + this.dataset = data.dataset; + this.datasetMetadata = data.datasetMetadata; - if (CoreRESTClient.hasData(response)) + this.fields = data.fields; + this.selectedFields = data.selectedFields; + this.notSelectedFields = this.fields.filter(x => this.selectedFields.indexOf(x) === -1); + this.filterFields = []; + + for (let i = 0; i < data.datasetMetadata.length; i++) { - let data = CoreRESTClient.getData(response); - this.filterName = data.filterName; - this.dataset = data.dataset; - this.datasetMetadata = data.datasetMetadata; - - this.fields = data.fields; - this.selectedFields = data.selectedFields; - this.notSelectedFields = this.fields.filter(x => this.selectedFields.indexOf(x) === -1); - this.filterFields = []; - - for (let i = 0; i < data.datasetMetadata.length; i++) + for (let j = 0; j < data.filters.length; j++) { - for (let j = 0; j < data.filters.length; j++) + if (data.datasetMetadata[i].name == data.filters[j].name) { - if (data.datasetMetadata[i].name == data.filters[j].name) - { - let filter = data.filters[j]; - filter.type = data.datasetMetadata[i].type; + let filter = data.filters[j]; + filter.type = data.datasetMetadata[i].type; - this.filterFields.push(filter); - //break; - } + this.filterFields.push(filter); + //break; } } + } - // If the side menu is active - if (this.sideMenu === true) - { - this.setSideMenu(data); - } - else // otherwise use the dropdown in the filter options - { - this.setDropDownMenu(data); - } - this.updateTabulator(); - } - else + // If the side menu is active + if (this.sideMenu === true) { - console.error(CoreRESTClient.getError(response)); + this.setSideMenu(data); } + else // otherwise use the dropdown in the filter options + { + this.setDropDownMenu(data); + } + this.updateTabulator(); }, /** * Set the menu */ - setSideMenu: function(data) { + setSideMenu(data) { let filters = data.sideMenu.filters; let personalFilters = data.sideMenu.personalFilters; let filtersArray = []; @@ -369,7 +359,7 @@ export const CoreFilterCmpt = { /** * Set the drop down menu */ - setDropDownMenu: function(data) { + setDropDownMenu(data) { let filters = data.sideMenu.filters; let personalFilters = data.sideMenu.personalFilters; let filtersArray = []; @@ -405,7 +395,7 @@ export const CoreFilterCmpt = { /** * Used to start/refresh the FetchCmpt */ - startFetchCmpt: function(apiFunction, apiFunctionParameters, dataFetchedCallback) { + startFetchCmpt(apiFunction, apiFunctionParameters, dataFetchedCallback) { // Assign the function api of the FetchCmpt binded property this.fetchCmptApiFunction = apiFunction; @@ -431,11 +421,11 @@ export const CoreFilterCmpt = { /** * */ - handlerSaveCustomFilter: function(customFilterName) { + handlerSaveCustomFilter(customFilterName) { this.selectedFilter = null; // this.startFetchCmpt( - CoreFilterAPIs.saveCustomFilter, + this.$fhcApi.factory.filter.saveCustomFilter, { customFilterName }, @@ -445,13 +435,13 @@ export const CoreFilterCmpt = { /** * */ - handlerRemoveCustomFilter: function(event) { + handlerRemoveCustomFilter(event) { let filterId = event.currentTarget.getAttribute("href").substring(1); if (filterId === this.selectedFilter) this.selectedFilter = null; // this.startFetchCmpt( - CoreFilterAPIs.removeCustomFilter, + this.$fhcApi.factory.filter.removeCustomFilter, { filterId: filterId }, @@ -488,7 +478,7 @@ export const CoreFilterCmpt = { applyFilterConfig(filterFields) { this.selectedFilter = null; this.startFetchCmpt( - CoreFilterAPIs.applyFilterFields, + this.$fhcApi.factory.filter.applyFilterFields, { filterFields },