diff --git a/CHANGELOG.md b/CHANGELOG.md index efaabadccf89f72022cd31eb733a417579367c15..f220498db318df5c0f52c28bbc52d83cd3613339 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Added field outputs for simple calculations (substract and total). * Added escaped output to search screens. * Replaced the value separator in the raw field with a comma. +* Added filter to search text in multiple fields. # Version 1.0.7 diff --git a/Civi/DataProcessor/Factory.php b/Civi/DataProcessor/Factory.php index 2c96837ee36bf860a25fda52c5e4b199b0963c27..0c6e1907bc0024cd4b7ea209d4adf157a20b8610 100644 --- a/Civi/DataProcessor/Factory.php +++ b/Civi/DataProcessor/Factory.php @@ -128,6 +128,7 @@ class Factory { $this->addOutput('participant_search', 'CRM_DataprocessorSearch_ParticipantSearch', E::ts('Participant Search')); $this->addOutput('export_csv', 'CRM_DataprocessorOutputExport_CSV', E::ts('CSV Export')); $this->addFilter('simple_sql_filter', 'Civi\DataProcessor\FilterHandler\SimpleSqlFilter', E::ts('Field filter')); + $this->addFilter('multiple_field_filter', 'Civi\DataProcessor\FilterHandler\MultipleFieldFilter', E::ts('Text in multiple fields Filter')); $this->addFilter('activity_filter', 'Civi\DataProcessor\FilterHandler\ActivityFilter', E::ts('Activity filter')); $this->addFilter('contact_filter', 'Civi\DataProcessor\FilterHandler\ContactFilter', E::ts('Contact filter')); $this->addFilter('contact_in_group_filter', 'Civi\DataProcessor\FilterHandler\ContactInGroupFilter', E::ts('Contact in Group filter')); diff --git a/Civi/DataProcessor/FilterHandler/AbstractFilterHandler.php b/Civi/DataProcessor/FilterHandler/AbstractFilterHandler.php index e6b3a1d492c44a867fb2ed1c1fc386911bdd06f9..3b8ba3dc506902f4a2454ff80ae726c2118432c0 100644 --- a/Civi/DataProcessor/FilterHandler/AbstractFilterHandler.php +++ b/Civi/DataProcessor/FilterHandler/AbstractFilterHandler.php @@ -528,7 +528,7 @@ abstract class AbstractFilterHandler { if (isset($defaultFilterValue['op']) && $defaultFilterValue['op']) { $defaults[$alias . '_op'] = $defaultFilterValue['op']; } else { - $defaults[$alias . '_op'] = key($operations); + $defaults[$alias . '_op'] = 'has'; // Contains } if (isset($defaultFilterValue['value'])) { $defaults[$alias.'_value'] = $defaultFilterValue['value']; diff --git a/Civi/DataProcessor/FilterHandler/MultipleFieldFilter.php b/Civi/DataProcessor/FilterHandler/MultipleFieldFilter.php new file mode 100644 index 0000000000000000000000000000000000000000..98c17cd46150618006c612bb326cbb7e4534cfff --- /dev/null +++ b/Civi/DataProcessor/FilterHandler/MultipleFieldFilter.php @@ -0,0 +1,205 @@ +<?php +/** + * @author Jaap Jansma <jaap.jansma@civicoop.org> + * @license AGPL-3.0 + */ + +namespace Civi\DataProcessor\FilterHandler; + +use Civi\DataProcessor\DataFlow\SqlDataFlow; +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 CRM_Dataprocessor_ExtensionUtil as E; + +class MultipleFieldFilter extends AbstractFilterHandler { + + /** + * @var \Civi\DataProcessor\DataSpecification\FieldSpecification + */ + protected $fieldSpecification; + + /** + * @var \Civi\DataProcessor\DataFlow\SqlDataFlow\WhereClauseInterface + */ + protected $whereClause; + + /** + * Initialize the filter + * + * @throws \Civi\DataProcessor\Exception\DataSourceNotFoundException + * @throws \Civi\DataProcessor\Exception\InvalidConfigurationException + * @throws \Civi\DataProcessor\Exception\FieldNotFoundException + */ + protected function doInitialization() { + if (!isset($this->configuration['fields']) || !is_array($this->configuration['fields']) || !count($this->configuration['fields'])) { + throw new InvalidConfigurationException(E::ts("Filter %1 requires at least one field to filter on. None given.", array(1=>$this->title))); + } + foreach($this->configuration['fields'] as $fieldAndDataSource) { + list($dataSource, $field) = explode("::", $fieldAndDataSource); + $this->initializeField($dataSource, $field); + } + + $this->fieldSpecification = new FieldSpecification($this->alias, 'String', $this->title, null, $this->alias); + } + + /** + * @param $datasource_name + * @param $field_name + * + * @throws \Civi\DataProcessor\Exception\DataSourceNotFoundException + * @throws \Civi\DataProcessor\Exception\FieldNotFoundException + */ + protected function initializeField($datasource_name, $field_name) { + $dataSource = $this->data_processor->getDataSourceByName($datasource_name); + if (!$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))); + } + if (!$dataSource->getAvailableFilterFields()->getFieldSpecificationByName($field_name)) { + 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 + ))); + } + $fieldSpecification = clone $dataSource->getAvailableFilterFields()->getFieldSpecificationByName($field_name); + if (!$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 + ))); + } + } + + /** + * Returns true when this filter has additional configuration + * + * @return bool + */ + public function hasConfiguration() { + return true; + } + + /** + * When this filter type has additional configuration you can add + * the fields on the form with this function. + * + * @param \CRM_Core_Form $form + * @param array $filter + */ + public function buildConfigurationForm(\CRM_Core_Form $form, $filter=array()) { + $fieldSelect = \CRM_Dataprocessor_Utils_DataSourceFields::getAvailableFilterFieldsInDataSources($filter['data_processor_id'], [$this, 'filterFieldsForConfiguration']); + + $form->add('select', 'fields', E::ts('Fields'), $fieldSelect, true, array( + 'style' => 'min-width:250px', + 'class' => 'crm-select2 huge data-processor-field-for-name', + 'placeholder' => E::ts('- select -'), + 'multiple' => true, + )); + if (isset($filter['configuration'])) { + $configuration = $filter['configuration']; + if (isset($configuration['fields'])) { + $defaults['fields'] = $configuration['fields']; + $form->setDefaults($defaults); + } + } + } + + /** + * When this filter type has configuration specify the template file name + * for the configuration form. + * + * @return false|string + */ + public function getConfigurationTemplateFileName() { + return "CRM/Dataprocessor/Form/Filter/Configuration/MultipleFieldFilter.tpl"; + } + + + /** + * Process the submitted values and create a configuration array + * + * @param $submittedValues + * @return array + */ + public function processConfiguration($submittedValues) { + $configuration['fields'] = $submittedValues['fields']; + return $configuration; + } + + /** + * @param \Civi\DataProcessor\DataSpecification\FieldSpecification $field + * @return bool + */ + public function filterFieldsForConfiguration(FieldSpecification $field) { + switch ($field->type) { + case 'String': + return true; + break; + } + return false; + } + + /** + * @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->data_processor->getDataFlow(); + if ($dataFlow && $dataFlow instanceof SqlDataFlow && $this->whereClause) { + $dataFlow->removeWhereClause($this->whereClause); + unset($this->whereClause); + } + } + + /** + * @param array $filterParams + * The filter settings + * + * @return mixed + */ + public function setFilter($filterParams) { + $this->resetFilter(); + $clauses = array(); + foreach($this->configuration['fields'] as $fieldAndDataSource) { + list($datasource_name, $field_name) = explode("::", $fieldAndDataSource); + $dataSource = $this->data_processor->getDataSourceByName($datasource_name); + $dataFlow = $dataSource->ensureField($field_name); + if ($dataFlow && $dataFlow instanceof SqlDataFlow) { + $clauses[] = new SqlDataFlow\SimpleWhereClause($dataFlow->getName(), $field_name, $filterParams['op'], $filterParams['value'], $this->fieldSpecification->type); + } + } + if (count($clauses)) { + switch ($filterParams['op']) { + case 'IS NULL': + case 'NOT LIKE': + case '!=': + $this->whereClause = new SqlDataFlow\AndClause($clauses); + default: + $this->whereClause = new SqlDataFlow\OrClause($clauses); + break; + } + $dataFlow = $this->data_processor->getDataFlow(); + if ($dataFlow && $dataFlow instanceof SqlDataFlow) { + $dataFlow->addWhereClause($this->whereClause); + } + } + } + + +} diff --git a/templates/CRM/Dataprocessor/Form/Filter/Configuration/MultipleFieldFilter.tpl b/templates/CRM/Dataprocessor/Form/Filter/Configuration/MultipleFieldFilter.tpl new file mode 100644 index 0000000000000000000000000000000000000000..dff8e51b458e0a8cf0317c034f4d090597b7e4a0 --- /dev/null +++ b/templates/CRM/Dataprocessor/Form/Filter/Configuration/MultipleFieldFilter.tpl @@ -0,0 +1,7 @@ +{crmScope extensionKey='dataprocessor'} +<div class="crm-section"> + <div class="label">{$form.fields.label}</div> + <div class="content">{$form.fields.html}</div> + <div class="clear"></div> +</div> +{/crmScope}