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
},