From f8076559ab19d6cac8993a28e828a956ecadfc4c Mon Sep 17 00:00:00 2001 From: Jaap Jansma <jaap.jansma@civicoop.org> Date: Tue, 9 Jul 2019 23:15:50 +0200 Subject: [PATCH] refactor of filters --- CRM/Dataprocessor/BAO/DataProcessor.php | 2 +- CRM/Dataprocessor/DAO/DataProcessorFilter.php | 34 ++- CRM/Dataprocessor/Form/Filter.php | 16 +- CRM/Dataprocessor/Form/FilterValue.php | 110 ++++++++ .../Form/Output/AbstractUIOutputForm.php | 15 +- .../AbstractFieldFilterHandler.php | 98 +++++++ .../FilterHandler/AbstractFilterHandler.php | 244 +++++++++++++++--- .../FilterHandler/CaseRoleFilter.php | 80 ++---- .../FilterHandler/ContactFilter.php | 98 ++----- .../FilterHandler/ContactInGroupFilter.php | 84 ++---- .../FilterHandler/InApiFilter.php | 105 ++------ .../FilterHandler/SimpleSqlFilter.php | 72 +----- Civi/DataProcessor/Output/Api.php | 5 +- api/v3/DataProcessorFilter.php | 5 + info.xml | 2 +- sql/auto_install.sql | 4 +- .../Form/DataProcessorBlocks/Filters.tpl | 17 +- templates/CRM/Dataprocessor/Form/Filter.tpl | 9 +- .../CRM/Dataprocessor/Form/FilterValue.tpl | 17 ++ xml/Menu/dataprocessor.xml | 7 + .../CRM/Dataprocessor/DataProcessorFilter.xml | 15 ++ 21 files changed, 655 insertions(+), 384 deletions(-) create mode 100644 CRM/Dataprocessor/Form/FilterValue.php create mode 100644 Civi/DataProcessor/FilterHandler/AbstractFieldFilterHandler.php create mode 100644 templates/CRM/Dataprocessor/Form/FilterValue.tpl diff --git a/CRM/Dataprocessor/BAO/DataProcessor.php b/CRM/Dataprocessor/BAO/DataProcessor.php index 1e0b55a0..8a526e5f 100644 --- a/CRM/Dataprocessor/BAO/DataProcessor.php +++ b/CRM/Dataprocessor/BAO/DataProcessor.php @@ -84,7 +84,7 @@ class CRM_Dataprocessor_BAO_DataProcessor extends CRM_Dataprocessor_DAO_DataProc if ($filterHandler) { $filterHandler->setDataProcessor($dataProcessorClass); try { - $filterHandler->initialize($filter['name'], $filter['title'], $filter['is_required'], $filter['configuration']); + $filterHandler->initialize($filter); $dataProcessorClass->addFilterHandler($filterHandler); } catch (\Exception $e) { CRM_Core_Session::setStatus($e->getMessage(), E::ts("Invalid filter"), 'error'); diff --git a/CRM/Dataprocessor/DAO/DataProcessorFilter.php b/CRM/Dataprocessor/DAO/DataProcessorFilter.php index 3a34a33c..0ea5708b 100644 --- a/CRM/Dataprocessor/DAO/DataProcessorFilter.php +++ b/CRM/Dataprocessor/DAO/DataProcessorFilter.php @@ -6,7 +6,7 @@ * * Generated from /buildkit/build/search/sites/default/files/civicrm/ext/dataprocessor/xml/schema/CRM/Dataprocessor/DataProcessorFilter.xml * DO NOT EDIT. Generated by CRM_Core_CodeGen - * (GenCodeChecksum:a0b00b49eb3a0197adf06272fc6e31f7) + * (GenCodeChecksum:a5f8d79f22a2e1d8467977bdfec1209e) */ /** @@ -67,11 +67,21 @@ class CRM_Dataprocessor_DAO_DataProcessorFilter extends CRM_Core_DAO { */ public $is_required; + /** + * @var boolean + */ + public $is_exposed; + /** * @var text */ public $configuration; + /** + * @var text + */ + public $filter_value; + /** * Class constructor. */ @@ -180,6 +190,17 @@ class CRM_Dataprocessor_DAO_DataProcessorFilter extends CRM_Core_DAO { 'bao' => 'CRM_Dataprocessor_DAO_DataProcessorFilter', 'localizable' => 0, ], + 'is_exposed' => [ + 'name' => 'is_exposed', + 'type' => CRM_Utils_Type::T_BOOLEAN, + 'title' => CRM_Dataprocessor_ExtensionUtil::ts('Is exposed'), + 'required' => TRUE, + 'default' => '1', + 'table_name' => 'civicrm_data_processor_filter', + 'entity' => 'DataProcessorFilter', + 'bao' => 'CRM_Dataprocessor_DAO_DataProcessorFilter', + 'localizable' => 0, + ], 'configuration' => [ 'name' => 'configuration', 'type' => CRM_Utils_Type::T_TEXT, @@ -191,6 +212,17 @@ class CRM_Dataprocessor_DAO_DataProcessorFilter extends CRM_Core_DAO { 'localizable' => 0, 'serialize' => self::SERIALIZE_JSON, ], + 'filter_value' => [ + 'name' => 'filter_value', + 'type' => CRM_Utils_Type::T_TEXT, + 'title' => CRM_Dataprocessor_ExtensionUtil::ts('Default Filter Value'), + 'required' => FALSE, + 'table_name' => 'civicrm_data_processor_filter', + 'entity' => 'DataProcessorFilter', + 'bao' => 'CRM_Dataprocessor_DAO_DataProcessorFilter', + 'localizable' => 0, + 'serialize' => self::SERIALIZE_JSON, + ], ]; CRM_Core_DAO_AllCoreTables::invoke(__CLASS__, 'fields_callback', Civi::$statics[__CLASS__]['fields']); } diff --git a/CRM/Dataprocessor/Form/Filter.php b/CRM/Dataprocessor/Form/Filter.php index 132a72e8..c9256f50 100644 --- a/CRM/Dataprocessor/Form/Filter.php +++ b/CRM/Dataprocessor/Form/Filter.php @@ -45,11 +45,16 @@ class CRM_Dataprocessor_Form_Filter extends CRM_Core_Form { if ($this->id) { $this->filter = civicrm_api3('DataProcessorFilter', 'getsingle', array('id' => $this->id)); $this->assign('filter', $this->filter); - $this->filterTypeClass = $factory->getFilterByName($this->filter['type']); - $this->assign('has_configuration', $this->filterTypeClass->hasConfiguration()); + } + if (!$this->filter) { + $this->filter['data_processor_id'] = $this->dataProcessorId; + $this->filter['type'] = 'simple_sql_filter'; } $type = CRM_Utils_Request::retrieve('type', 'String'); + if (!$type && $this->filter && isset($this->filter['type'])) { + $type = $this->filter['type']; + } if ($type) { $this->filterTypeClass = $factory->getFilterByName($type); $this->assign('has_configuration', $this->filterTypeClass->hasConfiguration()); @@ -73,6 +78,7 @@ class CRM_Dataprocessor_Form_Filter extends CRM_Core_Form { } else { $this->add('text', 'name', E::ts('Name'), array('size' => CRM_Utils_Type::HUGE), FALSE); $this->add('text', 'title', E::ts('Title'), array('size' => CRM_Utils_Type::HUGE), TRUE); + $this->add('checkbox', 'is_exposed', E::ts('Filter is exposed to the user')); $factory = dataprocessor_get_factory(); $this->add('select', 'type', E::ts('Select Filter'), $factory->getFilters(), true, array('style' => 'min-width:250px', @@ -113,6 +119,11 @@ class CRM_Dataprocessor_Form_Filter extends CRM_Core_Form { if (isset($this->filter['name'])) { $defaults['name'] = $this->filter['name']; } + if ($this->_action == CRM_Core_Action::ADD) { + $defaults['is_exposed'] = 1; + } elseif (isset($this->filter['is_exposed'])) { + $defaults['is_exposed'] = $this->filter['is_exposed']; + } return $defaults; } @@ -142,6 +153,7 @@ class CRM_Dataprocessor_Form_Filter extends CRM_Core_Form { $params['title'] = $values['title']; $params['type'] = $values['type']; $params['is_required'] = isset($values['is_required']) && $values['is_required'] ? 1 : 0; + $params['is_exposed'] = isset($values['is_exposed']) && $values['is_exposed'] ? 1 : 0; if ($this->dataProcessorId) { $params['data_processor_id'] = $this->dataProcessorId; } diff --git a/CRM/Dataprocessor/Form/FilterValue.php b/CRM/Dataprocessor/Form/FilterValue.php new file mode 100644 index 00000000..6dbdb2e4 --- /dev/null +++ b/CRM/Dataprocessor/Form/FilterValue.php @@ -0,0 +1,110 @@ +<?php + +use CRM_Dataprocessor_ExtensionUtil as E; + +/** + * Form controller class + * + * @see https://wiki.civicrm.org/confluence/display/CRMDOC/QuickForm+Reference + */ +class CRM_Dataprocessor_Form_FilterValue extends CRM_Core_Form { + + private $dataProcessorId; + + /** + * @var \Civi\DataProcessor\ProcessorType\AbstractProcessorType + */ + private $dataProcessorClass; + + /** + * @var array + */ + private $dataProcessor; + + private $id; + + private $filter; + + /** + * @var Civi\DataProcessor\FilterHandler\AbstractFilterHandler + */ + private $filterTypeClass; + + private $snippet; + + /** + * Function to perform processing before displaying form (overrides parent function) + * + * @access public + */ + function preProcess() { + $this->snippet = CRM_Utils_Request::retrieve('snippet', 'String'); + if ($this->snippet) { + $this->assign('suppressForm', TRUE); + $this->controller->_generateQFKey = FALSE; + } + + $factory = dataprocessor_get_factory(); + $this->dataProcessorId = CRM_Utils_Request::retrieve('data_processor_id', 'Integer'); + $this->dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $this->dataProcessorId)); + $this->dataProcessorClass = CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($this->dataProcessor); + $this->assign('data_processor_id', $this->dataProcessorId); + + $this->id = CRM_Utils_Request::retrieve('id', 'Integer'); + $this->assign('id', $this->id); + + + $this->filter = civicrm_api3('DataProcessorFilter', 'getsingle', array('id' => $this->id)); + $this->assign('filter', $this->filter); + $this->filterTypeClass = $factory->getFilterByName($this->filter['type']); + $this->filterTypeClass->setDataProcessor($this->dataProcessorClass); + $this->filterTypeClass->initialize($this->filter); + + $title = E::ts('Data Processor Default Filter Value'); + CRM_Utils_System::setTitle($title); + } + + public function buildQuickForm() { + $this->add('hidden', 'data_processor_id'); + $this->add('hidden', 'id'); + + $this->filterTypeClass->addToFilterForm($this, $this->filter['filter_value']); + $this->assign('filter_template', $this->filterTypeClass->getTemplateFileName()); + + $this->addButtons(array( + array('type' => 'next', 'name' => E::ts('Save'), 'isDefault' => TRUE,), + array('type' => 'cancel', 'name' => E::ts('Cancel')))); + parent::buildQuickForm(); + } + + function setDefaultValues() { + $defaults = array(); + $defaults['data_processor_id'] = $this->dataProcessorId; + $defaults['id'] = $this->id; + return $defaults; + } + + /** + * Function that can be defined in Form to override or. + * perform specific action on cancel action + */ + public function cancelAction() { + $this->dataProcessorId = CRM_Utils_Request::retrieve('data_processor_id', 'Integer'); + $redirectUrl = CRM_Utils_System::url('civicrm/dataprocessor/form/edit', array('reset' => 1, 'action' => 'update', 'id' => $this->dataProcessorId)); + CRM_Utils_System::redirect($redirectUrl); + } + + public function postProcess() { + $session = CRM_Core_Session::singleton(); + $redirectUrl = CRM_Utils_System::url('civicrm/dataprocessor/form/edit', array('reset' => 1, 'action' => 'update', 'id' => $this->dataProcessorId)); + $values = $this->exportValues(); + $default_filter_value = $this->filterTypeClass->processSubmittedValues($values); + $this->filter['filter_value'] = $default_filter_value; + + civicrm_api3('DataProcessorFilter', 'create', $this->filter); + + CRM_Utils_System::redirect($redirectUrl); + parent::postProcess(); + } + +} \ No newline at end of file diff --git a/CRM/Dataprocessor/Form/Output/AbstractUIOutputForm.php b/CRM/Dataprocessor/Form/Output/AbstractUIOutputForm.php index 9f1b8cd5..eae35dd6 100644 --- a/CRM/Dataprocessor/Form/Output/AbstractUIOutputForm.php +++ b/CRM/Dataprocessor/Form/Output/AbstractUIOutputForm.php @@ -96,7 +96,7 @@ abstract class CRM_Dataprocessor_Form_Output_AbstractUIOutputForm extends CRM_Co protected function hasRequiredFilters() { if ($this->dataProcessorClass->getFilterHandlers()) { foreach ($this->dataProcessorClass->getFilterHandlers() as $filter) { - if ($filter->isRequired()) { + if ($filter->isRequired() && $filter->isExposed()) { return true; } } @@ -113,7 +113,9 @@ abstract class CRM_Dataprocessor_Form_Output_AbstractUIOutputForm extends CRM_Co $errors = array(); if ($this->dataProcessorClass->getFilterHandlers()) { foreach ($this->dataProcessorClass->getFilterHandlers() as $filter) { - $errors = array_merge($errors, $filter->validateSubmittedFilterParams($this->_formValues)); + if ($filter->isExposed()) { + $errors = array_merge($errors, $filter->validateSubmittedFilterParams($this->_formValues)); + } } } return $errors; @@ -127,7 +129,10 @@ abstract class CRM_Dataprocessor_Form_Output_AbstractUIOutputForm extends CRM_Co public static function applyFilters(\Civi\DataProcessor\ProcessorType\AbstractProcessorType $dataProcessor, $submittedValues) { if ($dataProcessor->getFilterHandlers()) { foreach ($dataProcessor->getFilterHandlers() as $filter) { - $filter->applyFilterFromSubmittedFilterParams($submittedValues); + if ($filter->isExposed()) { + $filterValues = $filter->processSubmittedValues($submittedValues); + $filter->applyFilterFromSubmittedFilterParams($filterValues); + } } } } @@ -140,10 +145,10 @@ abstract class CRM_Dataprocessor_Form_Output_AbstractUIOutputForm extends CRM_Co if ($this->dataProcessorClass->getFilterHandlers()) { foreach ($this->dataProcessorClass->getFilterHandlers() as $filterHandler) { $fieldSpec = $filterHandler->getFieldSpecification(); - if (!$fieldSpec) { + if (!$fieldSpec || !$filterHandler->isExposed()) { continue; } - $filterElements[$fieldSpec->alias]['filter'] = $filterHandler->addToFilterForm($this); + $filterElements[$fieldSpec->alias]['filter'] = $filterHandler->addToFilterForm($this, $filterHandler->getDefaultFilterValues()); $filterElements[$fieldSpec->alias]['template'] = $filterHandler->getTemplateFileName(); } $this->assign('filters', $filterElements); diff --git a/Civi/DataProcessor/FilterHandler/AbstractFieldFilterHandler.php b/Civi/DataProcessor/FilterHandler/AbstractFieldFilterHandler.php new file mode 100644 index 00000000..b3057025 --- /dev/null +++ b/Civi/DataProcessor/FilterHandler/AbstractFieldFilterHandler.php @@ -0,0 +1,98 @@ +<?php +/** + * @author Jaap Jansma <jaap.jansma@civicoop.org> + * @license AGPL-3.0 + */ + +namespace Civi\DataProcessor\FilterHandler; + +use Civi\DataProcessor\DataFlow\SqlDataFlow; +use Civi\DataProcessor\Exception\DataSourceNotFoundException; +use Civi\DataProcessor\Exception\FieldNotFoundException; + +abstract class AbstractFieldFilterHandler extends AbstractFilterHandler { + + /** + * @var \Civi\DataProcessor\DataSpecification\FieldSpecification + */ + protected $fieldSpecification; + + /** + * @var \Civi\DataProcessor\Source\SourceInterface + */ + protected $dataSource; + + /** + * @var \Civi\DataProcessor\DataFlow\SqlDataFlow\WhereClauseInterface + */ + protected $whereClause; + + /** + * @param $datasource_name + * @param $field_name + * + * @throws \Civi\DataProcessor\Exception\DataSourceNotFoundException + * @throws \Civi\DataProcessor\Exception\FieldNotFoundException + */ + protected function initializeField($datasource_name, $field_name) { + $this->dataSource = $this->data_processor->getDataSourceByName($datasource_name); + if (!$this->dataSource) { + throw new DataSourceNotFoundException(E::ts("Filter %1 requires data source '%2' which could not be found. Did you rename or deleted the data source?", array(1=>$this->title, 2=>$datasource_name))); + } + $this->fieldSpecification = clone $this->dataSource->getAvailableFilterFields()->getFieldSpecificationByName($field_name); + if (!$this->fieldSpecification) { + throw new FieldNotFoundException(E::ts("Filter %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", array( + 1 => $this->title, + 2 => $field_name, + 3 => $datasource_name + ))); + } + $this->fieldSpecification->alias = $this->alias; + $this->fieldSpecification->title = $this->title; + } + + + /** + * @return \Civi\DataProcessor\DataSpecification\FieldSpecification + */ + public function getFieldSpecification() { + return $this->fieldSpecification; + } + + /** + * Resets the filter + * + * @return void + * @throws \Exception + */ + public function resetFilter() { + if (!$this->isInitialized()) { + return; + } + $dataFlow = $this->dataSource->ensureField($this->fieldSpecification->name); + if ($dataFlow && $dataFlow instanceof SqlDataFlow && $this->whereClause) { + $dataFlow->removeWhereClause($this->whereClause); + unset($this->whereClause); + } + } + + /** + * @param array $filter + * The filter settings + * @return mixed + * @throws \Exception + */ + public function setFilter($filter) { + $this->resetFilter(); + $dataFlow = $this->dataSource->ensureField($this->fieldSpecification->name); + if ($dataFlow && $dataFlow instanceof SqlDataFlow) { + $value = $filter['value']; + if (!is_array($value)) { + $value = explode(",", $value); + } + $this->whereClause = new SqlDataFlow\SimpleWhereClause($dataFlow->getName(), $this->fieldSpecification->name, $filter['op'], $value, $this->fieldSpecification->type); + $dataFlow->addWhereClause($this->whereClause); + } + } + +} \ No newline at end of file diff --git a/Civi/DataProcessor/FilterHandler/AbstractFilterHandler.php b/Civi/DataProcessor/FilterHandler/AbstractFilterHandler.php index ba961ba9..0c5c4feb 100644 --- a/Civi/DataProcessor/FilterHandler/AbstractFilterHandler.php +++ b/Civi/DataProcessor/FilterHandler/AbstractFilterHandler.php @@ -23,20 +23,62 @@ abstract class AbstractFilterHandler { */ protected $is_required; + /** + * @var bool + */ + protected $is_exposed; + + /** + * @var array + */ + protected $defaultFilterValues; + + /** + * @var array + */ + protected $filter; + + /** + * @var String + */ + protected $alias; + + /** + * @var String + */ + protected $title; + + /** + * @var array + */ + protected $configuration; + + /** + * @var bool + */ + protected $is_initialized = false; + /** * @return \Civi\DataProcessor\DataSpecification\FieldSpecification */ abstract public function getFieldSpecification(); /** - * Initialize the processor + * Initialize the filter * - * @param String $alias - * @param String $title - * @param bool $is_required - * @param array $configuration + * @throws \Civi\DataProcessor\Exception\DataSourceNotFoundException + * @throws \Civi\DataProcessor\Exception\InvalidConfigurationException + * @throws \Civi\DataProcessor\Exception\FieldNotFoundException */ - abstract public function initialize($alias, $title, $is_required, $configuration); + abstract protected function doInitialization(); + + + /** + * Resets the filter + * + * @return void + */ + abstract public function resetFilter(); /** * @param array $filterParams @@ -49,14 +91,60 @@ abstract class AbstractFilterHandler { } + public function initialize($filter) { + if ($this->isInitialized()) { + return; + } + $this->filter = $filter; + $this->alias = $filter['name']; + $this->title = $filter['title']; + $this->configuration = $filter['configuration']; + $this->is_required = $filter['is_required']; + $this->is_exposed = $filter['is_exposed']; + $this->defaultFilterValues = $filter['filter_value']; + + $this->doInitialization(); + + if (!empty($this->defaultFilterValues)) { + $this->setFilter($this->defaultFilterValues); + } + $this->is_initialized = true; + } + + /** + * Return whether the filter is initialized + * + * @return bool + */ + public function isInitialized() { + return $this->is_initialized; + } + public function setDataProcessor(AbstractProcessorType $dataProcessor) { $this->data_processor = $dataProcessor; } + /** + * @return bool + */ public function isRequired() { return $this->is_required; } + /** + * @return bool + */ + public function isExposed() { + return $this->is_exposed; + } + + /** + * @return array + */ + public function getDefaultFilterValues() { + return $this->defaultFilterValues; + } + /** * Returns true when this filter has additional configuration * @@ -151,27 +239,26 @@ abstract class AbstractFilterHandler { public function applyFilterFromSubmittedFilterParams($submittedValues) { $isFilterSet = FALSE; $filterSpec = $this->getFieldSpecification(); - $filterName = $filterSpec->alias; if ($filterSpec->type == 'Date' || $filterSpec->type == 'Timestamp') { $isFilterSet = $this->applyDateFilter($submittedValues); } - elseif (isset($submittedValues[$filterName . '_op'])) { - switch ($submittedValues[$filterName . '_op']) { + elseif (isset($submittedValues['op'])) { + switch ($submittedValues['op']) { case 'IN': - if (isset($submittedValues[$filterName . '_value']) && $submittedValues[$filterName . '_value']) { + if (isset($submittedValues['value']) && $submittedValues['value']) { $filterParams = [ 'op' => 'IN', - 'value' => $submittedValues[$filterName . '_value'], + 'value' => $submittedValues['value'], ]; $this->setFilter($filterParams); $isFilterSet = TRUE; } break; case 'NOT IN': - if (isset($submittedValues[$filterName . '_value']) && $submittedValues[$filterName . '_value']) { + if (isset($submittedValues['value']) && $submittedValues['value']) { $filterParams = [ 'op' => 'NOT IN', - 'value' => $submittedValues[$filterName . '_value'], + 'value' => $submittedValues['value'], ]; $this->setFilter($filterParams); $isFilterSet = TRUE; @@ -183,50 +270,50 @@ abstract class AbstractFilterHandler { case '<': case '>=': case '<=': - if (isset($submittedValues[$filterName . '_value']) && $submittedValues[$filterName . '_value']) { + if (isset($submittedValues['value']) && $submittedValues['value']) { $filterParams = [ - 'op' => $submittedValues[$filterName . '_op'], - 'value' => $submittedValues[$filterName . '_value'], + 'op' => $submittedValues['op'], + 'value' => $submittedValues['value'], ]; $this->setFilter($filterParams); $isFilterSet = TRUE; } break; case 'has': - if (isset($submittedValues[$filterName . '_value']) && $submittedValues[$filterName . '_value']) { + if (isset($submittedValues['value']) && $submittedValues['value']) { $filterParams = [ 'op' => 'LIKE', - 'value' => '%' . $submittedValues[$filterName . '_value'] . '%', + 'value' => '%' . $submittedValues['value'] . '%', ]; $this->setFilter($filterParams); $isFilterSet = TRUE; } break; case 'nhas': - if (isset($submittedValues[$filterName . '_value']) && $submittedValues[$filterName . '_value']) { + if (isset($submittedValues['value']) && $submittedValues['value']) { $filterParams = [ 'op' => 'NOT LIKE', - 'value' => '%' . $submittedValues[$filterName . '_value'] . '%', + 'value' => '%' . $submittedValues['value'] . '%', ]; $this->setFilter($filterParams); $isFilterSet = TRUE; } break; case 'sw': - if (isset($submittedValues[$filterName . '_value']) && $submittedValues[$filterName . '_value']) { + if (isset($submittedValues['value']) && $submittedValues['value']) { $filterParams = [ 'op' => 'LIKE', - 'value' => $submittedValues[$filterName . '_value'] . '%', + 'value' => $submittedValues['value'] . '%', ]; $this->setFilter($filterParams); $isFilterSet = TRUE; } break; case 'ew': - if (isset($submittedValues[$filterName . '_value']) && $submittedValues[$filterName . '_value']) { + if (isset($submittedValues['value']) && $submittedValues['value']) { $filterParams = [ 'op' => 'LIKE', - 'value' => '%' . $submittedValues[$filterName . '_value'], + 'value' => '%' . $submittedValues['value'], ]; $this->setFilter($filterParams); $isFilterSet = TRUE; @@ -239,21 +326,65 @@ abstract class AbstractFilterHandler { } } + /** + * Process the submitted values to a filter value + * Which could then be processed by applyFilter function + * + * @param $submittedValues + * @return array + */ + public function processSubmittedValues($submittedValues) { + $return = array(); + $filterSpec = $this->getFieldSpecification(); + $alias = $filterSpec->alias; + if (isset($submittedValues[$alias.'_op'])) { + $return['op'] = $submittedValues[$alias . '_op']; + } + if (isset($submittedValues[$alias.'_value'])) { + $return['value'] = $submittedValues[$alias . '_value']; + } + if (isset($submittedValues[$alias.'_relative'])) { + $return['relative'] = $submittedValues[$alias . '_relative']; + } + if (isset($submittedValues[$alias.'_from'])) { + $return['from'] = $submittedValues[$alias . '_from']; + } + if (isset($submittedValues[$alias.'_to'])) { + $return['to'] = $submittedValues[$alias . '_to']; + } + if (isset($submittedValues[$alias.'_from_time'])) { + $return['from_time'] = $submittedValues[$alias.'_from_time']; + } + if (isset($submittedValues[$alias.'_to_time'])) { + $return['to_time'] = $submittedValues[$alias.'_to_time']; + } + if (isset($submittedValues[$alias.'_min'])) { + $return['min'] = $submittedValues[$alias.'_min']; + } + if (isset($submittedValues[$alias.'_max'])) { + $return['max'] = $submittedValues[$alias.'_max']; + } + return $return; + } + /** * Add the elements to the filter form. * * @param \CRM_Core_Form $form + * @param array $defaultFilterValue * @return array * Return variables belonging to this filter. */ - public function addToFilterForm(\CRM_Core_Form $form) { + public function addToFilterForm(\CRM_Core_Form $form, $defaultFilterValue) { static $count = 1; $types = \CRM_Utils_Type::getValidTypes(); $fieldSpec = $this->getFieldSpecification(); $operations = $this->getOperatorOptions($fieldSpec); $type = \CRM_Utils_Type::T_STRING; + $defaults = array(); $title = $fieldSpec->title; + $alias = $fieldSpec->alias; if ($this->isRequired()) { $title .= ' <span class="crm-marker">*</span>'; } @@ -262,34 +393,76 @@ abstract class AbstractFilterHandler { $type = $types[$fieldSpec->type]; } if ($fieldSpec->getOptions()) { - $form->addElement('select', "{$fieldSpec->alias}_op", E::ts('Operator:'), $operations); - $form->addElement('select', "{$fieldSpec->alias}_value", NULL, $fieldSpec->getOptions(), [ + $form->addElement('select', "{$alias}_op", E::ts('Operator:'), $operations); + $form->addElement('select', "{$alias}_value", NULL, $fieldSpec->getOptions(), [ 'style' => 'min-width:250px', 'class' => 'crm-select2 huge', 'multiple' => TRUE, 'placeholder' => E::ts('- select -'), ]); + if (isset($defaultFilterValue['op'])) { + $defaults[$alias . '_op'] = $defaultFilterValue['op']; + } + if (isset($defaultFilterValue['value'])) { + $defaults[$alias.'_value'] = $defaultFilterValue['value']; + } } else { switch ($type) { case \CRM_Utils_Type::T_DATE: case \CRM_Utils_Type::T_TIMESTAMP: - \CRM_Core_Form_Date::buildDateRange($form, $fieldSpec->alias, $count, '_from', '_to', E::ts('From:'), $this->isRequired(), $operations); + \CRM_Core_Form_Date::buildDateRange($form, $alias, $count, '_from', '_to', E::ts('From:'), $this->isRequired(), $operations); + if (isset($defaultFilterValue['op'])) { + $defaults[$alias . '_op'] = $defaultFilterValue['op']; + } + if (isset($defaultFilterValue['value'])) { + $defaults[$alias.'_value'] = $defaultFilterValue['value']; + } + if (isset($defaultFilterValue['relative'])) { + $defaults[$alias.'_relative'] = $defaultFilterValue['relative']; + } + if (isset($defaultFilterValue['from'])) { + $defaults[$alias.'_from'] = $defaultFilterValue['from']; + } + if (isset($defaultFilterValue['to'])) { + $defaults[$alias.'_to'] = $defaultFilterValue['to']; + } + if (isset($defaultFilterValue['from_time'])) { + $defaults[$alias.'_from_time'] = $defaultFilterValue['from_time']; + } + if (isset($defaultFilterValue['to_time'])) { + $defaults[$alias.'_to_time'] = $defaultFilterValue['to_time']; + } + $count ++; break; case \CRM_Utils_Type::T_INT: case \CRM_Utils_Type::T_FLOAT: // and a min value input box - $form->add('text', "{$fieldSpec->alias}_min", E::ts('Min')); + $form->add('text', "{$alias}_min", E::ts('Min')); // and a max value input box - $form->add('text', "{$fieldSpec->alias}_max", E::ts('Max')); + $form->add('text', "{$alias}_max", E::ts('Max')); + + if (isset($defaultFilterValue['min'])) { + $defaults[$alias.'_min'] = $defaultFilterValue['min']; + } + if (isset($defaultFilterValue['max'])) { + $defaults[$alias.'_max'] = $defaultFilterValue['max']; + } + default: // default type is string - $form->addElement('select', "{$fieldSpec->alias}_op", E::ts('Operator:'), $operations, - ['onchange' => "return showHideMaxMinVal( '$fieldSpec->alias', this.value );"] + $form->addElement('select', "{$alias}_op", E::ts('Operator:'), $operations, + ['onchange' => "return showHideMaxMinVal( '$alias', this.value );"] ); // we need text box for value input - $form->add('text', "{$fieldSpec->alias}_value", NULL, ['class' => 'huge']); + $form->add('text', "{$alias}_value", NULL, ['class' => 'huge']); + if (isset($defaultFilterValue['op'])) { + $defaults[$alias . '_op'] = $defaultFilterValue['op']; + } + if (isset($defaultFilterValue['value'])) { + $defaults[$alias.'_value'] = $defaultFilterValue['value']; + } break; } } @@ -297,6 +470,11 @@ abstract class AbstractFilterHandler { $filter['type'] = $fieldSpec->type; $filter['title'] = $title; + + if (count($defaults)) { + $form->setDefaults($defaults); + } + return $filter; } diff --git a/Civi/DataProcessor/FilterHandler/CaseRoleFilter.php b/Civi/DataProcessor/FilterHandler/CaseRoleFilter.php index 6b642419..8f34ae4b 100644 --- a/Civi/DataProcessor/FilterHandler/CaseRoleFilter.php +++ b/Civi/DataProcessor/FilterHandler/CaseRoleFilter.php @@ -7,26 +7,10 @@ namespace Civi\DataProcessor\FilterHandler; use Civi\DataProcessor\DataFlow\SqlDataFlow; -use Civi\DataProcessor\DataFlow\SqlTableDataFlow; -use Civi\DataProcessor\DataSpecification\CustomFieldSpecification; -use Civi\DataProcessor\DataSpecification\FieldSpecification; -use Civi\DataProcessor\Exception\DataSourceNotFoundException; -use Civi\DataProcessor\Exception\FieldNotFoundException; use Civi\DataProcessor\Exception\InvalidConfigurationException; -use Civi\DataProcessor\Source\SourceInterface; use CRM_Dataprocessor_ExtensionUtil as E; -class CaseRoleFilter extends AbstractFilterHandler { - - /** - * @var \Civi\DataProcessor\DataSpecification\FieldSpecification - */ - protected $fieldSpecification; - - /** - * @var SourceInterface - */ - protected $dataSource; +class CaseRoleFilter extends AbstractFieldFilterHandler { /** * @var array @@ -38,53 +22,33 @@ class CaseRoleFilter extends AbstractFilterHandler { } /** - * Initialize the processor + * Initialize the filter * - * @param String $alias - * @param String $title - * @param bool $is_required - * @param array $configuration + * @throws \Civi\DataProcessor\Exception\DataSourceNotFoundException + * @throws \Civi\DataProcessor\Exception\InvalidConfigurationException + * @throws \Civi\DataProcessor\Exception\FieldNotFoundException */ - public function initialize($alias, $title, $is_required, $configuration) { - if ($this->fieldSpecification) { - return; // Already initialized. - } - if (!isset($configuration['datasource']) || !isset($configuration['field'])) { - throw new InvalidConfigurationException(E::ts("Filter %1 requires a field to filter on. None given.", array(1=>$title))); - } - - $this->is_required = $is_required; - - $this->dataSource = $this->data_processor->getDataSourceByName($configuration['datasource']); - if (!$this->dataSource) { - throw new DataSourceNotFoundException(E::ts("Filter %1 requires data source '%2' which could not be found. Did you rename or deleted the data source?", array(1=>$title, 2=>$configuration['datasource']))); + protected function doInitialization() { + if (!isset($this->configuration['datasource']) || !isset($this->configuration['field'])) { + throw new InvalidConfigurationException(E::ts("Filter %1 requires a field to filter on. None given.", array(1=>$this->title))); } - $this->fieldSpecification = clone $this->dataSource->getAvailableFilterFields()->getFieldSpecificationByName($configuration['field']); - if (!$this->fieldSpecification) { - throw new FieldNotFoundException(E::ts("Filter %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", array( - 1 => $title, - 2 => $configuration['field'], - 3 => $configuration['datasource'] - ))); - } - $this->fieldSpecification->alias = $alias; - $this->fieldSpecification->title = $title; - + $this->initializeField($this->configuration['datasource'], $this->configuration['field']); - if (isset($configuration['relationship_types']) && is_array($configuration['relationship_types'])) { + if (isset($this->configuration['relationship_types']) && is_array($this->configuration['relationship_types'])) { $this->relationship_type_ids = array(); - foreach($configuration['relationship_types'] as $rel_type) { - $this->relationship_type_ids[] = civicrm_api3('RelationshipType', 'getvalue', array('return' => 'id', 'name_a_b' => $rel_type)); + foreach($this->configuration['relationship_types'] as $rel_type) { + try { + $this->relationship_type_ids[] = civicrm_api3('RelationshipType', 'getvalue', [ + 'return' => 'id', + 'name_a_b' => $rel_type + ]); + } catch (\CiviCRM_API3_Exception $e) { + // Do nothing + } }; } } - /** - * @return \Civi\DataProcessor\DataSpecification\FieldSpecification - */ - public function getFieldSpecification() { - return $this->fieldSpecification; - } /** * @param array $filter @@ -92,6 +56,8 @@ class CaseRoleFilter extends AbstractFilterHandler { * @return mixed */ public function setFilter($filter) { + $this->resetFilter(); + $dataFlow = $this->dataSource->ensureField($this->fieldSpecification->name); $cids = $filter['value']; if (!is_array($cids)) { @@ -108,7 +74,7 @@ class CaseRoleFilter extends AbstractFilterHandler { } if ($dataFlow && $dataFlow instanceof SqlDataFlow) { - $whereClause = new SqlDataFlow\InTableWhereClause( + $this->whereClause = new SqlDataFlow\InTableWhereClause( 'case_id', 'civicrm_relationship', $relationshipTableAlias, @@ -118,7 +84,7 @@ class CaseRoleFilter extends AbstractFilterHandler { $filter['op'] ); - $dataFlow->addWhereClause($whereClause); + $dataFlow->addWhereClause($this->whereClause); } } diff --git a/Civi/DataProcessor/FilterHandler/ContactFilter.php b/Civi/DataProcessor/FilterHandler/ContactFilter.php index 1570696f..71e73d02 100644 --- a/Civi/DataProcessor/FilterHandler/ContactFilter.php +++ b/Civi/DataProcessor/FilterHandler/ContactFilter.php @@ -6,88 +6,27 @@ namespace Civi\DataProcessor\FilterHandler; -use Civi\DataProcessor\DataFlow\SqlDataFlow; -use Civi\DataProcessor\DataFlow\SqlTableDataFlow; -use Civi\DataProcessor\DataSpecification\CustomFieldSpecification; -use Civi\DataProcessor\DataSpecification\FieldSpecification; -use Civi\DataProcessor\Exception\DataSourceNotFoundException; -use Civi\DataProcessor\Exception\FieldNotFoundException; use Civi\DataProcessor\Exception\InvalidConfigurationException; -use Civi\DataProcessor\Source\SourceInterface; use CRM_Dataprocessor_ExtensionUtil as E; -class ContactFilter extends AbstractFilterHandler { - - /** - * @var \Civi\DataProcessor\DataSpecification\FieldSpecification - */ - protected $fieldSpecification; - - /** - * @var SourceInterface - */ - protected $dataSource; +class ContactFilter extends AbstractFieldFilterHandler { public function __construct() { parent::__construct(); } /** - * Initialize the processor + * Initialize the filter * - * @param String $alias - * @param String $title - * @param bool $is_required - * @param array $configuration + * @throws \Civi\DataProcessor\Exception\DataSourceNotFoundException + * @throws \Civi\DataProcessor\Exception\InvalidConfigurationException + * @throws \Civi\DataProcessor\Exception\FieldNotFoundException */ - public function initialize($alias, $title, $is_required, $configuration) { - if ($this->fieldSpecification) { - return; // Already initialized. - } - if (!isset($configuration['datasource']) || !isset($configuration['field'])) { - throw new InvalidConfigurationException(E::ts("Filter %1 requires a field to filter on. None given.", array(1=>$title))); - } - - $this->is_required = $is_required; - - $this->dataSource = $this->data_processor->getDataSourceByName($configuration['datasource']); - if (!$this->dataSource) { - throw new DataSourceNotFoundException(E::ts("Filter %1 requires data source '%2' which could not be found. Did you rename or deleted the data source?", array(1=>$title, 2=>$configuration['datasource']))); - } - $this->fieldSpecification = clone $this->dataSource->getAvailableFilterFields()->getFieldSpecificationByName($configuration['field']); - if (!$this->fieldSpecification) { - throw new FieldNotFoundException(E::ts("Filter %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", array( - 1 => $title, - 2 => $configuration['field'], - 3 => $configuration['datasource'] - ))); - } - $this->fieldSpecification->alias = $alias; - $this->fieldSpecification->title = $title; - } - - /** - * @return \Civi\DataProcessor\DataSpecification\FieldSpecification - */ - public function getFieldSpecification() { - return $this->fieldSpecification; - } - - /** - * @param array $filter - * The filter settings - * @return mixed - */ - public function setFilter($filter) { - $dataFlow = $this->dataSource->ensureField($this->fieldSpecification->name); - if ($dataFlow && $dataFlow instanceof SqlDataFlow) { - $value = $filter['value']; - if (!is_array($value)) { - $value = explode(",", $value); - } - $whereClause = new SqlDataFlow\SimpleWhereClause($dataFlow->getName(), $this->fieldSpecification->name, $filter['op'], $value, $this->fieldSpecification->type); - $dataFlow->addWhereClause($whereClause); + protected function doInitialization() { + if (!isset($this->configuration['datasource']) || !isset($this->configuration['field'])) { + throw new InvalidConfigurationException(E::ts("Filter %1 requires a field to filter on. None given.", array(1=>$this->title))); } + $this->initializeField($this->configuration['datasource'], $this->configuration['field']); } /** @@ -154,26 +93,39 @@ class ContactFilter extends AbstractFilterHandler { * Add the elements to the filter form. * * @param \CRM_Core_Form $form + * @param array $defaultFilterValue * @return array * Return variables belonging to this filter. */ - public function addToFilterForm(\CRM_Core_Form $form) { + public function addToFilterForm(\CRM_Core_Form $form, $defaultFilterValue) { $fieldSpec = $this->getFieldSpecification(); $operations = $this->getOperatorOptions($fieldSpec); + $defaults = array(); $title = $fieldSpec->title; + $alias = $fieldSpec->alias; if ($this->isRequired()) { $title .= ' <span class="crm-marker">*</span>'; } - $form->addElement('select', "{$fieldSpec->alias}_op", E::ts('Operator:'), $operations); - $form->addEntityRef( "{$fieldSpec->alias}_value", NULL, array( + $form->addElement('select', "{$alias}_op", E::ts('Operator:'), $operations); + $form->addEntityRef( "{$alias}_value", NULL, array( 'placeholder' => E::ts('Select a contact'), 'entity' => 'Contact', 'create' => false, 'multiple' => true, )); + if (isset($defaultFilterValue['op'])) { + $defaults[$alias . '_op'] = $defaultFilterValue['op']; + } + if (isset($defaultFilterValue['value'])) { + $defaults[$alias.'_value'] = $defaultFilterValue['value']; + } + if (count($defaults)) { + $form->setDefaults($defaults); + } + $filter['type'] = $fieldSpec->type; $filter['title'] = $title; diff --git a/Civi/DataProcessor/FilterHandler/ContactInGroupFilter.php b/Civi/DataProcessor/FilterHandler/ContactInGroupFilter.php index 3c0f88d9..79be6aa3 100644 --- a/Civi/DataProcessor/FilterHandler/ContactInGroupFilter.php +++ b/Civi/DataProcessor/FilterHandler/ContactInGroupFilter.php @@ -7,86 +7,48 @@ namespace Civi\DataProcessor\FilterHandler; use Civi\DataProcessor\DataFlow\SqlDataFlow; -use Civi\DataProcessor\DataFlow\SqlTableDataFlow; -use Civi\DataProcessor\DataSpecification\CustomFieldSpecification; -use Civi\DataProcessor\DataSpecification\FieldSpecification; -use Civi\DataProcessor\Exception\DataSourceNotFoundException; -use Civi\DataProcessor\Exception\FieldNotFoundException; -use Civi\DataProcessor\Source\SourceInterface; +use Civi\DataProcessor\Exception\InvalidConfigurationException; use CRM_Dataprocessor_ExtensionUtil as E; -class ContactInGroupFilter extends AbstractFilterHandler { - - /** - * @var \Civi\DataProcessor\DataSpecification\FieldSpecification - */ - protected $fieldSpecification; - - /** - * @var SourceInterface - */ - protected $dataSource; +class ContactInGroupFilter extends AbstractFieldFilterHandler { /** * @var array */ protected $parent_group_id = false; - public function __construct() { - parent::__construct(); - } - /** - * Initialize the processor + * Initialize the filter * - * @param String $alias - * @param String $title - * @param bool $is_required - * @param array $configuration + * @throws \Civi\DataProcessor\Exception\DataSourceNotFoundException + * @throws \Civi\DataProcessor\Exception\InvalidConfigurationException + * @throws \Civi\DataProcessor\Exception\FieldNotFoundException */ - public function initialize($alias, $title, $is_required, $configuration) { - if ($this->fieldSpecification) { - return; // Already initialized. + protected function doInitialization() { + if (!isset($this->configuration['datasource']) || !isset($this->configuration['field'])) { + throw new InvalidConfigurationException(E::ts("Filter %1 requires a field to filter on. None given.", array(1=>$this->title))); } - if (!isset($configuration['datasource']) || !isset($configuration['field'])) { - throw new InvalidConfigurationException(E::ts("Filter %1 requires a field to filter on. None given.", array(1=>$title))); - } - - $this->is_required = $is_required; - - $this->dataSource = $this->data_processor->getDataSourceByName($configuration['datasource']); - if (!$this->dataSource) { - throw new DataSourceNotFoundException(E::ts("Filter %1 requires data source '%2' which could not be found. Did you rename or deleted the data source?", array(1=>$title, 2=>$configuration['datasource']))); - } - $this->fieldSpecification = clone $this->dataSource->getAvailableFilterFields()->getFieldSpecificationByName($configuration['field']); - if (!$this->fieldSpecification) { - throw new FieldNotFoundException(E::ts("Filter %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", array( - 1 => $title, - 2 => $configuration['field'], - 3 => $configuration['datasource'] - ))); - } - $this->fieldSpecification->alias = $alias; - $this->fieldSpecification->title = $title; - - if (isset($configuration['parent_group']) && $configuration['parent_group']) { - $this->parent_group_id = civicrm_api3('Group', 'getvalue', array('return' => 'id', 'name' => $configuration['parent_group'])); + $this->initializeField($this->configuration['datasource'], $this->configuration['field']); + + if (isset($this->configuration['parent_group']) && $this->configuration['parent_group']) { + try { + $this->parent_group_id = civicrm_api3('Group', 'getvalue', [ + 'return' => 'id', + 'name' => $this->configuration['parent_group'] + ]); + } catch (\CiviCRM_API3_Exception $e) { + // Do nothing + } } } - /** - * @return \Civi\DataProcessor\DataSpecification\FieldSpecification - */ - public function getFieldSpecification() { - return $this->fieldSpecification; - } - /** * @param array $filter * The filter settings * @return mixed */ public function setFilter($filter) { + $this->resetFilter(); $dataFlow = $this->dataSource->ensureField($this->fieldSpecification->name); $group_ids = $filter['value']; if (!is_array($group_ids)) { @@ -99,7 +61,7 @@ class ContactInGroupFilter extends AbstractFilterHandler { ); if ($dataFlow && $dataFlow instanceof SqlDataFlow) { - $whereClause = new SqlDataFlow\InTableWhereClause( + $this->whereClause = new SqlDataFlow\InTableWhereClause( 'contact_id', 'civicrm_group_contact', $groupTableAlias, @@ -109,7 +71,7 @@ class ContactInGroupFilter extends AbstractFilterHandler { $filter['op'] ); - $dataFlow->addWhereClause($whereClause); + $dataFlow->addWhereClause($this->whereClause); } } diff --git a/Civi/DataProcessor/FilterHandler/InApiFilter.php b/Civi/DataProcessor/FilterHandler/InApiFilter.php index ddb9a9f0..12f78ba8 100644 --- a/Civi/DataProcessor/FilterHandler/InApiFilter.php +++ b/Civi/DataProcessor/FilterHandler/InApiFilter.php @@ -6,103 +6,42 @@ namespace Civi\DataProcessor\FilterHandler; -use Civi\DataProcessor\DataFlow\SqlDataFlow; -use Civi\DataProcessor\DataFlow\SqlTableDataFlow; -use Civi\DataProcessor\DataSpecification\CustomFieldSpecification; -use Civi\DataProcessor\DataSpecification\FieldSpecification; -use Civi\DataProcessor\Exception\DataSourceNotFoundException; -use Civi\DataProcessor\Exception\FieldNotFoundException; use Civi\DataProcessor\Exception\InvalidConfigurationException; -use Civi\DataProcessor\Source\SourceInterface; use CRM_Dataprocessor_ExtensionUtil as E; -class InApiFilter extends AbstractFilterHandler { +class InApiFilter extends AbstractFieldFilterHandler { /** - * @var \Civi\DataProcessor\DataSpecification\FieldSpecification - */ - protected $fieldSpecification; - - /** - * @var SourceInterface - */ - protected $dataSource; - - public function __construct() { - parent::__construct(); - } - - /** - * Initialize the processor + * Initialize the filter * - * @param String $alias - * @param String $title - * @param bool $is_required - * @param array $configuration + * @throws \Civi\DataProcessor\Exception\DataSourceNotFoundException + * @throws \Civi\DataProcessor\Exception\InvalidConfigurationException + * @throws \Civi\DataProcessor\Exception\FieldNotFoundException */ - public function initialize($alias, $title, $is_required, $configuration) { - if ($this->fieldSpecification) { - return; // Already initialized. - } - if (!isset($configuration['datasource']) || !isset($configuration['field'])) { - throw new InvalidConfigurationException(E::ts("Filter %1 requires a field to filter on. None given.", array(1=>$title))); + protected function doInitialization() { + if (!isset($this->configuration['datasource']) || !isset($this->configuration['field'])) { + throw new InvalidConfigurationException(E::ts("Filter %1 requires a field to filter on. None given.", array(1=>$this->title))); } + $this->initializeField($this->configuration['datasource'], $this->configuration['field']); - $this->is_required = $is_required; - - $this->dataSource = $this->data_processor->getDataSourceByName($configuration['datasource']); - if (!$this->dataSource) { - throw new DataSourceNotFoundException(E::ts("Filter %1 requires data source '%2' which could not be found. Did you rename or deleted the data source?", array(1=>$title, 2=>$configuration['datasource']))); - } - $this->fieldSpecification = clone $this->dataSource->getAvailableFilterFields()->getFieldSpecificationByName($configuration['field']); - if (!$this->fieldSpecification) { - throw new FieldNotFoundException(E::ts("Filter %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", array( - 1 => $title, - 2 => $configuration['field'], - 3 => $configuration['datasource'] - ))); - } - $this->fieldSpecification->alias = $alias; - $this->fieldSpecification->title = $title; - - $apiEntity = $configuration['api_entity']; - $apiAction = $configuration['api_action']; - $apiParams = isset($configuration['api_params']) && is_array($configuration['api_params'])? $configuration['api_params'] : array(); - $id_field = $configuration['value_field']; - $label_field = $configuration['label_field']; + $apiEntity = $this->configuration['api_entity']; + $apiAction = $this->configuration['api_action']; + $apiParams = isset($this->configuration['api_params']) && is_array($this->configuration['api_params'])? $this->configuration['api_params'] : array(); + $id_field = $this->configuration['value_field']; + $label_field = $this->configuration['label_field']; $apiParams['return'] = array($id_field, $label_field); $apiParams['options']['limit'] = 0; unset($apiParams['options']['offset']); $this->fieldSpecification->options = array(); - $apiResult = civicrm_api3($apiEntity, $apiAction, $apiParams); - foreach ($apiResult['values'] as $option) { - if (isset($option[$id_field]) && isset($option[$label_field])) { - $this->fieldSpecification->options[$option[$id_field]] = $option[$label_field]; - } - } - } - - /** - * @return \Civi\DataProcessor\DataSpecification\FieldSpecification - */ - public function getFieldSpecification() { - return $this->fieldSpecification; - } - - /** - * @param array $filter - * The filter settings - * @return mixed - */ - public function setFilter($filter) { - $dataFlow = $this->dataSource->ensureField($this->fieldSpecification->name); - if ($dataFlow && $dataFlow instanceof SqlDataFlow) { - $value = $filter['value']; - if (!is_array($value)) { - $value = explode(",", $value); + try { + $apiResult = civicrm_api3($apiEntity, $apiAction, $apiParams); + foreach ($apiResult['values'] as $option) { + if (isset($option[$id_field]) && isset($option[$label_field])) { + $this->fieldSpecification->options[$option[$id_field]] = $option[$label_field]; + } } - $whereClause = new SqlDataFlow\SimpleWhereClause($dataFlow->getName(), $this->fieldSpecification->name, $filter['op'], $value, $this->fieldSpecification->type); - $dataFlow->addWhereClause($whereClause); + } catch (\CiviCRM_API3_Exception $e) { + // Do nothing } } diff --git a/Civi/DataProcessor/FilterHandler/SimpleSqlFilter.php b/Civi/DataProcessor/FilterHandler/SimpleSqlFilter.php index 17bc3b34..e2539e51 100644 --- a/Civi/DataProcessor/FilterHandler/SimpleSqlFilter.php +++ b/Civi/DataProcessor/FilterHandler/SimpleSqlFilter.php @@ -7,71 +7,24 @@ namespace Civi\DataProcessor\FilterHandler; use Civi\DataProcessor\DataFlow\SqlDataFlow; -use Civi\DataProcessor\DataFlow\SqlTableDataFlow; use Civi\DataProcessor\DataSpecification\CustomFieldSpecification; -use Civi\DataProcessor\DataSpecification\FieldSpecification; -use Civi\DataProcessor\Exception\DataSourceNotFoundException; -use Civi\DataProcessor\Exception\FieldNotFoundException; use Civi\DataProcessor\Exception\InvalidConfigurationException; -use Civi\DataProcessor\Source\SourceInterface; use CRM_Dataprocessor_ExtensionUtil as E; -class SimpleSqlFilter extends AbstractFilterHandler { +class SimpleSqlFilter extends AbstractFieldFilterHandler { /** - * @var \Civi\DataProcessor\DataSpecification\FieldSpecification - */ - protected $fieldSpecification; - - /** - * @var SourceInterface - */ - protected $dataSource; - - public function __construct() { - parent::__construct(); - } - - /** - * Initialize the processor + * Initialize the filter * - * @param String $alias - * @param String $title - * @param bool $is_required - * @param array $configuration + * @throws \Civi\DataProcessor\Exception\DataSourceNotFoundException + * @throws \Civi\DataProcessor\Exception\InvalidConfigurationException + * @throws \Civi\DataProcessor\Exception\FieldNotFoundException */ - public function initialize($alias, $title, $is_required, $configuration) { - if ($this->fieldSpecification) { - return; // Already initialized. - } - if (!isset($configuration['datasource']) || !isset($configuration['field'])) { - throw new InvalidConfigurationException(E::ts("Filter %1 requires a field to filter on. None given.", array(1=>$title))); + protected function doInitialization() { + if (!isset($this->configuration['datasource']) || !isset($this->configuration['field'])) { + throw new InvalidConfigurationException(E::ts("Filter %1 requires a field to filter on. None given.", array(1=>$this->title))); } - - $this->is_required = $is_required; - - $this->dataSource = $this->data_processor->getDataSourceByName($configuration['datasource']); - if (!$this->dataSource) { - throw new DataSourceNotFoundException(E::ts("Filter %1 requires data source '%2' which could not be found. Did you rename or deleted the data source?", array(1=>$title, 2=>$configuration['datasource']))); - } - $this->fieldSpecification = clone $this->dataSource->getAvailableFilterFields()->getFieldSpecificationByName($configuration['field']); - if (!$this->fieldSpecification) { - throw new FieldNotFoundException(E::ts("Filter %1 requires a field with the name '%2' in the data source '%3'. Did you change the data source type?", array( - 1 => $title, - 2 => $configuration['field'], - 3 => $configuration['datasource'] - ))); - } - $this->fieldSpecification->alias = $alias; - $this->fieldSpecification->title = $title; - - } - - /** - * @return \Civi\DataProcessor\DataSpecification\FieldSpecification - */ - public function getFieldSpecification() { - return $this->fieldSpecification; + $this->initializeField($this->configuration['datasource'], $this->configuration['field']); } /** @@ -80,14 +33,15 @@ class SimpleSqlFilter extends AbstractFilterHandler { * @return mixed */ public function setFilter($filter) { + $this->resetFilter(); $dataFlow = $this->dataSource->ensureField($this->fieldSpecification->name); if ($dataFlow && $dataFlow instanceof SqlDataFlow) { if ($this->isMultiValueField()) { - $whereClause = new SqlDataFlow\MultiValueFieldWhereClause($dataFlow->getName(), $this->fieldSpecification->name, $filter['op'], $filter['value'], $this->fieldSpecification->type); + $this->whereClause = new SqlDataFlow\MultiValueFieldWhereClause($dataFlow->getName(), $this->fieldSpecification->name, $filter['op'], $filter['value'], $this->fieldSpecification->type); } else { - $whereClause = new SqlDataFlow\SimpleWhereClause($dataFlow->getName(), $this->fieldSpecification->name, $filter['op'], $filter['value'], $this->fieldSpecification->type); + $this->whereClause = new SqlDataFlow\SimpleWhereClause($dataFlow->getName(), $this->fieldSpecification->name, $filter['op'], $filter['value'], $this->fieldSpecification->type); } - $dataFlow->addWhereClause($whereClause); + $dataFlow->addWhereClause($this->whereClause); } } diff --git a/Civi/DataProcessor/Output/Api.php b/Civi/DataProcessor/Output/Api.php index c6a5fb40..6b6307f6 100644 --- a/Civi/DataProcessor/Output/Api.php +++ b/Civi/DataProcessor/Output/Api.php @@ -227,7 +227,7 @@ class Api implements OutputInterface, API_ProviderInterface, EventSubscriberInte if (isset($types[$fieldSpec->type])) { $type = $types[$fieldSpec->type]; } - if (!$fieldSpec) { + if (!$fieldSpec || !$filterHandler->isExposed()) { continue; } $field = [ @@ -335,6 +335,9 @@ class Api implements OutputInterface, API_ProviderInterface, EventSubscriberInte protected function runDataProcessor(AbstractProcessorType $dataProcessorClass, $params, $isCount) { foreach($dataProcessorClass->getFilterHandlers() as $filter) { + if (!$filter->isExposed()) { + continue; + } $filterSpec = $filter->getFieldSpecification(); if ($filter->isRequired() && !isset($params[$filterSpec->alias])) { throw new \API_Exception('Field '.$filterSpec->alias.' is required'); diff --git a/api/v3/DataProcessorFilter.php b/api/v3/DataProcessorFilter.php index 65614292..7eef02f5 100644 --- a/api/v3/DataProcessorFilter.php +++ b/api/v3/DataProcessorFilter.php @@ -89,6 +89,11 @@ function civicrm_api3_data_processor_filter_get($params) { if (isset($value['configuration'])) { $return['values'][$id]['configuration'] = json_decode($value['configuration'], TRUE); } + if (isset($value['filter_value'])) { + $return['values'][$id]['filter_value'] = json_decode($value['filter_value'], TRUE); + } else { + $return['values'][$id]['filter_value'] = array(); + } } return $return; } diff --git a/info.xml b/info.xml index 7e8fde3e..2c720fa7 100644 --- a/info.xml +++ b/info.xml @@ -14,7 +14,7 @@ <url desc="Licensing">http://www.gnu.org/licenses/agpl-3.0.html</url> </urls> <releaseDate>2018-08-14</releaseDate> - <version>1.0</version> + <version>master</version> <develStage>alpha</develStage> <compatibility> <ver>4.7</ver> diff --git a/sql/auto_install.sql b/sql/auto_install.sql index 8de37b35..6f53f0f8 100644 --- a/sql/auto_install.sql +++ b/sql/auto_install.sql @@ -139,7 +139,9 @@ CREATE TABLE `civicrm_data_processor_filter` ( `title` varchar(255) NOT NULL , `type` varchar(255) NOT NULL , `is_required` tinyint NULL , - `configuration` text NULL + `is_exposed` tinyint NOT NULL DEFAULT 1 , + `configuration` text NULL , + `filter_value` text NULL , PRIMARY KEY (`id`) diff --git a/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Filters.tpl b/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Filters.tpl index 9d771a68..93188c8e 100644 --- a/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Filters.tpl +++ b/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Filters.tpl @@ -1,9 +1,10 @@ {crmScope extensionKey='dataprocessor'} - <h3>{ts}Exposed Filters{/ts}</h3> + <h3>{ts}Filters{/ts}</h3> <div class="crm-block crm-form-block crm-data-processor_source-block"> <table> <tr> <th>{ts}Title{/ts}</th> + <th>{ts}Exposed{/ts}</th> <th></th> <th></th> </tr> @@ -16,10 +17,18 @@ {/if} <br /> <span class="description">{$filter.name}</span> </td> + <td> + {if ($filter.is_exposed)}{ts}Yes{/ts}{else}{ts}No{/ts}{/if} + </td> <td style="width: 20%">{if ($filter.weight && !is_numeric($filter.weight))}{$filter.weight}{/if}</td> - <td style="width: 20%"> - <a href="{crmURL p="civicrm/dataprocessor/form/filter" q="reset=1&action=update&data_processor_id=`$filter.data_processor_id`&id=`$filter.id`"}">{ts}Edit{/ts}</a> - <a href="{crmURL p="civicrm/dataprocessor/form/filter" q="reset=1&action=delete&data_processor_id=`$filter.data_processor_id`&id=`$filter.id`"}">{ts}Remove{/ts}</a> + <td class="right nowrap"> + <span class="btn-slide crm-hover-button">{ts}Configure{/ts} + <ul class="panel"> + <li><a class="crm-hover-button" href="{crmURL p="civicrm/dataprocessor/form/filter" q="reset=1&action=update&data_processor_id=`$filter.data_processor_id`&id=`$filter.id`"}">{ts}Type & configuration{/ts}</a></li> + <li><a class="crm-hover-button" href="{crmURL p="civicrm/dataprocessor/form/filter_value" q="reset&action=update&data_processor_id=`$filter.data_processor_id`&id=`$filter.id`"}">{ts}Filter setting{/ts}</a></li> + <li><a class="crm-hover-button" href="{crmURL p="civicrm/dataprocessor/form/filter" q="reset=1&action=delete&data_processor_id=`$filter.data_processor_id`&id=`$filter.id`"}">{ts}Remove{/ts}</a></li> + </ul> + </span> </td> </tr> {/foreach} diff --git a/templates/CRM/Dataprocessor/Form/Filter.tpl b/templates/CRM/Dataprocessor/Form/Filter.tpl index 9a1b786f..26a2ef42 100644 --- a/templates/CRM/Dataprocessor/Form/Filter.tpl +++ b/templates/CRM/Dataprocessor/Form/Filter.tpl @@ -17,7 +17,7 @@ </div> {* block for rule data *} - <h3>{ts}Field{/ts}</h3> + <h3>{ts}Filter{/ts}</h3> <div class="crm-block crm-form-block crm-data-processor_filter-block"> <div class="crm-section"> <div class="label">{$form.type.label}</div> @@ -53,6 +53,11 @@ <div class="content">{$form.is_required.html}</div> <div class="clear"></div> </div> + <div class="crm-section"> + <div class="label">{$form.is_exposed.label}</div> + <div class="content">{$form.is_exposed.html}</div> + <div class="clear"></div> + </div> </div> <script type="text/javascript"> @@ -89,7 +94,7 @@ } }); - $('#type').change(); + //$('#type').change(); }); {/literal} </script> diff --git a/templates/CRM/Dataprocessor/Form/FilterValue.tpl b/templates/CRM/Dataprocessor/Form/FilterValue.tpl new file mode 100644 index 00000000..c9de51fb --- /dev/null +++ b/templates/CRM/Dataprocessor/Form/FilterValue.tpl @@ -0,0 +1,17 @@ +{crmScope extensionKey='dataprocessor'} + <h3>{ts}Default Filter Value{/ts}</h3> + <div class="crm-block crm-form-block crm-data-processor_filter_value-block"> + <table> + <tr> + <th>{ts}Name{/ts}</th> + <th>{ts}Operator{/ts}</th> + <th>{ts}Value{/ts}</th> + </tr> + {include file=$filter_template filterName=$filter.name filter=$filter} + </table> + </div> + + <div class="crm-submit-buttons"> + {include file="CRM/common/formButtons.tpl" location="bottom"} + </div> +{/crmScope} \ No newline at end of file diff --git a/xml/Menu/dataprocessor.xml b/xml/Menu/dataprocessor.xml index 8e58a520..73c5e0ae 100644 --- a/xml/Menu/dataprocessor.xml +++ b/xml/Menu/dataprocessor.xml @@ -42,6 +42,13 @@ <access_arguments>access CiviCRM</access_arguments> <access_arguments>administer CiviCRM</access_arguments> </item> + <item> + <path>civicrm/dataprocessor/form/filter_value</path> + <page_callback>CRM_Dataprocessor_Form_FilterValue</page_callback> + <title>DataProcessor</title> + <access_arguments>access CiviCRM</access_arguments> + <access_arguments>administer CiviCRM</access_arguments> + </item> <item> <path>civicrm/dataprocessor/form/aggregate_field</path> <page_callback>CRM_Dataprocessor_Form_AggregateField</page_callback> diff --git a/xml/schema/CRM/Dataprocessor/DataProcessorFilter.xml b/xml/schema/CRM/Dataprocessor/DataProcessorFilter.xml index 1fed632b..48f5ee87 100644 --- a/xml/schema/CRM/Dataprocessor/DataProcessorFilter.xml +++ b/xml/schema/CRM/Dataprocessor/DataProcessorFilter.xml @@ -59,6 +59,13 @@ <required>false</required> <length>255</length> </field> + <field> + <name>is_exposed</name> + <title>Is exposed</title> + <type>boolean</type> + <required>true</required> + <default>1</default> + </field> <field> <name>configuration</name> <title>Configuration</title> @@ -67,6 +74,14 @@ <length>255</length> <serialize>JSON</serialize> </field> + <field> + <name>filter_value</name> + <title>Default Filter Value</title> + <type>text</type> + <required>false</required> + <length>255</length> + <serialize>JSON</serialize> + </field> <foreignKey> <name>data_processor_id</name> <table>civicrm_data_processor</table> -- GitLab