From 44a4d897381a3ff2476c3c3acac823c68bbc7d80 Mon Sep 17 00:00:00 2001 From: Jaap Jansma <jaap.jansma@civicoop.org> Date: Fri, 21 Sep 2018 19:04:10 +0200 Subject: [PATCH] Added filters --- CRM/Dataprocessor/BAO/DataProcessor.php | 38 ++++ CRM/Dataprocessor/BAO/Filter.php | 180 ++++++++++++++++++ CRM/Dataprocessor/DAO/Filter.php | 102 ++++++++++ CRM/Dataprocessor/Form/DataProcessor.php | 12 ++ CRM/Dataprocessor/Form/Filter.php | 165 ++++++++++++++++ CRM/Dataprocessor/Utils/Importer.php | 6 + .../Event/FilterHandlerEvent.php | 37 ++++ Civi/DataProcessor/Factory.php | 10 + .../AbstractFieldOutputHandler.php | 2 - .../RawFieldOutputHandler.php | 1 - .../FilterHandler/AbstractFilterHandler.php | 81 ++++++++ .../FilterHandler/SimpleSqlFilter.php | 61 ++++++ Civi/DataProcessor/Output/Api.php | 91 ++++++++- .../ProcessorType/AbstractProcessorType.php | 35 ++++ .../Source/AbstractCivicrmEntitySource.php | 38 ++-- api/v3/DataProcessorFilter/Create.php | 67 +++++++ api/v3/DataProcessorFilter/Delete.php | 37 ++++ api/v3/DataProcessorFilter/Get.php | 30 +++ sql/create_civicrm_data_processor.sql | 11 ++ .../CRM/Dataprocessor/Form/DataProcessor.tpl | 1 + .../DataProcessorBlocks/AggregateFields.tpl | 1 + .../Form/DataProcessorBlocks/Fields.tpl | 1 + .../Form/DataProcessorBlocks/Filters.tpl | 39 ++++ .../Form/DataProcessorBlocks/Outputs.tpl | 3 +- .../Form/DataProcessorBlocks/Sources.tpl | 1 + templates/CRM/Dataprocessor/Form/Filter.tpl | 43 +++++ xml/Menu/dataprocessor.xml | 7 + 27 files changed, 1082 insertions(+), 18 deletions(-) create mode 100644 CRM/Dataprocessor/BAO/Filter.php create mode 100644 CRM/Dataprocessor/DAO/Filter.php create mode 100644 CRM/Dataprocessor/Form/Filter.php create mode 100644 Civi/DataProcessor/Event/FilterHandlerEvent.php create mode 100644 Civi/DataProcessor/FilterHandler/AbstractFilterHandler.php create mode 100644 Civi/DataProcessor/FilterHandler/SimpleSqlFilter.php create mode 100644 api/v3/DataProcessorFilter/Create.php create mode 100644 api/v3/DataProcessorFilter/Delete.php create mode 100644 api/v3/DataProcessorFilter/Get.php create mode 100644 templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Filters.tpl create mode 100644 templates/CRM/Dataprocessor/Form/Filter.tpl diff --git a/CRM/Dataprocessor/BAO/DataProcessor.php b/CRM/Dataprocessor/BAO/DataProcessor.php index fe003050..14e09256 100644 --- a/CRM/Dataprocessor/BAO/DataProcessor.php +++ b/CRM/Dataprocessor/BAO/DataProcessor.php @@ -249,6 +249,16 @@ class CRM_Dataprocessor_BAO_DataProcessor extends CRM_Dataprocessor_DAO_DataProc } } + $filters = CRM_Dataprocessor_BAO_Filter::getValues(array('data_processor_id' => $this->id)); + $filterHandlers = $dataProcessor->getAvailableFilterHandlers(); + foreach($filters as $filter) { + if (isset($filterHandlers[$filter['type']])) { + $filterHandler = $filterHandlers[$filter['type']]; + $filterHandler->initialize($filter['name'], $filter['title'], $filter['is_required'], $filter['configuration']); + $dataProcessor->addFilterHandler($filterHandler); + } + } + $fields = CRM_Dataprocessor_BAO_Field::getValues(array('data_processor_id' => $this->id)); $outputHandlers = $dataProcessor->getAvailableOutputHandlers(); foreach($fields as $field) { @@ -285,6 +295,29 @@ class CRM_Dataprocessor_BAO_DataProcessor extends CRM_Dataprocessor_DAO_DataProc return $dataProcessor->getAvailableOutputHandlers(); } + public static function getAvailableFilterHandlers($data_processor_id) { + $dao = new CRM_Dataprocessor_BAO_DataProcessor(); + $dao->id = $data_processor_id; + $dao->find(true); + $factory = dataprocessor_get_factory(); + $dataProcessor = $factory->getDataProcessorTypeByName($dao->type); + $sources = CRM_Dataprocessor_BAO_Source::getValues(array('data_processor_id' => $dao->id)); + foreach($sources as $sourceDao) { + $source = $factory->getDataSourceByName($sourceDao['type']); + $source->setSourceName($sourceDao['name']); + $source->setSourceTitle($sourceDao['title']); + $source->initialize($sourceDao['configuration']); + $join = null; + if ($sourceDao['join_type']) { + $join = $factory->getJoinByName($sourceDao['join_type']); + $join->initialize($sourceDao['join_configuration'], $dao->id); + } + $dataProcessor->addDataSource($source, $join); + } + + return $dataProcessor->getAvailableFilterHandlers(); + } + public static function getAvailableAggregationFields($data_processor_id) { $availableAggregationFields = array(); $factory = dataprocessor_get_factory(); @@ -373,6 +406,11 @@ class CRM_Dataprocessor_BAO_DataProcessor extends CRM_Dataprocessor_DAO_DataProc unset($dataProcessor['data_sources'][$i]['id']); unset($dataProcessor['data_sources'][$i]['data_processor_id']); } + $dataProcessor['filters'] = CRM_Dataprocessor_BAO_Filter::getValues(array('data_processor_id' => $id)); + foreach($dataProcessor['filters'] as $i => $field) { + unset($dataProcessor['filters'][$i]['id']); + unset($dataProcessor['filters'][$i]['data_processor_id']); + } $dataProcessor['fields'] = CRM_Dataprocessor_BAO_Field::getValues(array('data_processor_id' => $id)); foreach($dataProcessor['fields'] as $i => $field) { unset($dataProcessor['fields'][$i]['id']); diff --git a/CRM/Dataprocessor/BAO/Filter.php b/CRM/Dataprocessor/BAO/Filter.php new file mode 100644 index 00000000..d649e5eb --- /dev/null +++ b/CRM/Dataprocessor/BAO/Filter.php @@ -0,0 +1,180 @@ +<?php +/** + * @author Jaap Jansma <jaap.jansma@civicoop.org> + * @license AGPL-3.0 + */ + +class CRM_Dataprocessor_BAO_Filter extends CRM_Dataprocessor_DAO_Filter { + + /** + * Function to get values + * + * @return array $result found rows with data + * @access public + * @static + */ + public static function getValues($params) { + $factory = dataprocessor_get_factory(); + $types = $factory->getDataSources(); + + $result = array(); + $filter = new CRM_Dataprocessor_DAO_Filter(); + if (!empty($params)) { + $filters = self::fields(); + foreach ($params as $key => $value) { + if (isset($filters[$key])) { + $filter->$key = $value; + } + } + } + $filter->find(); + while ($filter->fetch()) { + $row = array(); + self::storeValues($filter, $row); + + if (isset($types[$row['type']])) { + $row['type_name'] = $types[$row['type']]; + } else { + $row['type_name'] = ''; + } + if (!empty($row['configuration'])) { + $row['configuration'] = json_decode($row['configuration'], true); + } else { + $row['configuration'] = array(); + } + if (!empty($row['join_configuration'])) { + $row['join_configuration'] = json_decode($row['join_configuration'], true); + } else { + $row['join_configuration'] = array(); + } + + $result[$row['id']] = $row; + } + return $result; + } + + /** + * Function to add or update a DataProcessor + * + * @param array $params + * @return array $result + * @access public + * @throws Exception when params is empty + * @static + */ + public static function add($params) { + $result = array(); + if (empty($params)) { + throw new Exception('Params can not be empty when adding or updating a data processor filter'); + } + + if (!empty($params['id'])) { + CRM_Utils_Hook::pre('edit', 'DataProcessorFilter', $params['id'], $params); + } + else { + CRM_Utils_Hook::pre('create', 'DataProcessorFilter', NULL, $params); + } + + $filter = new CRM_Dataprocessor_DAO_Filter(); + $filters = self::fields(); + foreach ($params as $key => $value) { + if (isset($filters[$key])) { + $filter->$key = $value; + } + } + if (isset($filter->configuration) && is_array($filter->configuration)) { + $filter->configuration = json_encode($filter->configuration); + } + + $filter->save(); + self::storeValues($filter, $result); + + if (!empty($params['id'])) { + CRM_Utils_Hook::post('edit', 'DataProcessorFilter', $filter->id, $filter); + } + else { + CRM_Utils_Hook::post('create', 'DataProcessorFilter', $filter->id, $filter); + } + + return $result; + } + + /** + * Public function to generate name from title + * + * @param $label + * @return string + * @access public + * @static + */ + public static function buildNameFromTitle($title) { + return preg_replace('@[^a-z0-9_]+@','_', strtolower($title)); + } + + /** + * Returns whether the name is valid or not + * + * @param string $name + * @param int $data_procssor_id, + * @param int $id optional + * @return bool + * @static + */ + public static function isNameValid($name, $data_procssor_id, $id=null) { + $sql = "SELECT COUNT(*) FROM `civicrm_data_processor_filter` WHERE `name` = %1 AND `data_processor_id` = %2"; + $params[1] = array($name, 'String'); + $params[2] = array($data_procssor_id, 'Integer'); + if ($id) { + $sql .= " AND `id` != %3"; + $params[3] = array($id, 'Integer'); + } + $count = CRM_Core_DAO::singleValueQuery($sql, $params); + return ($count > 0) ? false : true; + } + + /** + * Function to delete a Data Processor Filter with id + * + * @param int $id + * @throws Exception when $id is empty + * @access public + * @static + */ + public static function deleteWithId($id) { + if (empty($id)) { + throw new Exception('id can not be empty when attempting to delete a data processor filter'); + } + + CRM_Utils_Hook::pre('delete', 'DataProcessorFilter', $id, CRM_Core_DAO::$_nullArray); + + $filter = new CRM_Dataprocessor_DAO_Filter(); + $filter->id = $id; + $filter->delete(); + + CRM_Utils_Hook::post('delete', 'DataProcessorFilter', $id, CRM_Core_DAO::$_nullArray); + + return; + } + + /** + * Function to delete a Data Processor Filter with id + * + * @param int $id + * @throws Exception when $id is empty + * @access public + * @static + */ + public static function deleteWithDataProcessorId($id) { + if (empty($id)) { + throw new Exception('id can not be empty when attempting to delete a data processor filter'); + } + + $filter = new CRM_Dataprocessor_DAO_Filter(); + $filter->data_processor_id = $id; + $filter->find(FALSE); + while ($filter->fetch()) { + self::deleteWithId($filter->id); + } + } + +} \ No newline at end of file diff --git a/CRM/Dataprocessor/DAO/Filter.php b/CRM/Dataprocessor/DAO/Filter.php new file mode 100644 index 00000000..6be51fce --- /dev/null +++ b/CRM/Dataprocessor/DAO/Filter.php @@ -0,0 +1,102 @@ +<?php + +use CRM_Dataprocessor_ExtensionUtil as E; + +/** + * @author Jaap Jansma (CiviCooP) <jaap.jansma@civicoop.org> + * @license http://www.gnu.org/licenses/agpl-3.0.html + */ +class CRM_Dataprocessor_DAO_Filter extends CRM_Core_DAO { + /** + * static instance to hold the field values + * + * @var array + * @static + */ + static $_fields = null; + static $_export = null; + /** + * empty definition for virtual function + */ + static function getTableName() { + return 'civicrm_data_processor_filter'; + } + /** + * returns all the column names of this table + * + * @access public + * @return array + */ + public static function &fields() { + if (!(self::$_fields)) { + self::$_fields = array( + 'id' => array( + 'name' => 'id', + 'title' => E::ts('ID'), + 'type' => CRM_Utils_Type::T_INT, + 'required' => true + ) , + 'data_processor_id' => array( + 'name' => 'data_processor_id', + 'title' => E::ts('Data Processor ID'), + 'type' => CRM_Utils_Type::T_INT, + 'required' => true, + 'FKApiName' => 'DataProcessor', + ), + 'is_required' => array( + 'name' => 'is_required', + 'title' => E::ts('Is required'), + 'type' => CRM_Utils_Type::T_INT, + ), + 'type' => array( + 'name' => 'type', + 'title' => E::ts('Type'), + 'type' => CRM_Utils_Type::T_STRING, + 'maxlength' => 80, + 'required' => true, + ), + 'name' => array( + 'name' => 'name', + 'title' => E::ts('Name'), + 'type' => CRM_Utils_Type::T_STRING, + 'maxlength' => 128, + 'required' => true + ), + 'title' => array( + 'name' => 'title', + 'title' => E::ts('Title'), + 'type' => CRM_Utils_Type::T_STRING, + 'maxlength' => 128, + 'required' => true + ), + 'configuration' => array( + 'name' => 'configuration', + 'title' => E::ts('Configuration'), + 'type' => CRM_Utils_Type::T_TEXT, + ), + ); + } + return self::$_fields; + } + /** + * Returns an array containing, for each field, the array key used for that + * field in self::$_fields. + * + * @access public + * @return array + */ + public static function &fieldKeys() { + if (!(self::$_fieldKeys)) { + self::$_fieldKeys = array( + 'id' => 'id', + 'data_processor_id' => 'data_processor_id', + 'is_required' => 'is_required', + 'type' => 'type', + 'name' => 'name', + 'title' => 'title', + 'configuration' => 'configuration', + ); + } + return self::$_fieldKeys; + } +} \ No newline at end of file diff --git a/CRM/Dataprocessor/Form/DataProcessor.php b/CRM/Dataprocessor/Form/DataProcessor.php index 66ee1bbe..2815ae7e 100644 --- a/CRM/Dataprocessor/Form/DataProcessor.php +++ b/CRM/Dataprocessor/Form/DataProcessor.php @@ -45,15 +45,18 @@ class CRM_Dataprocessor_Form_DataProcessor extends CRM_Core_Form { if ($this->dataProcessorId) { $this->addSources(); $this->addFields(); + $this->addFilters(); $this->addAggregateFields(); $this->assign('outputs', CRM_Dataprocessor_BAO_Output::getValues(array('data_processor_id' => $this->dataProcessorId))); $dataSourceAddUrl = CRM_Utils_System::url('civicrm/dataprocessor/form/source', 'reset=1&action=add&data_processor_id='.$this->dataProcessorId, TRUE); $addAggregateFieldUrl = CRM_Utils_System::url('civicrm/dataprocessor/form/aggregate_field', 'reset=1&action=add&id='.$this->dataProcessorId, TRUE); $addFieldUrl = CRM_Utils_System::url('civicrm/dataprocessor/form/field', 'reset=1&action=add&data_processor_id='.$this->dataProcessorId, TRUE); + $addFilterUrl = CRM_Utils_System::url('civicrm/dataprocessor/form/filter', 'reset=1&action=add&data_processor_id='.$this->dataProcessorId, TRUE); $outputAddUrl = CRM_Utils_System::url('civicrm/dataprocessor/form/output', 'reset=1&action=add&data_processor_id='.$this->dataProcessorId, TRUE); $this->assign('addDataSourceUrl', $dataSourceAddUrl); $this->assign('addAggregateFieldUrl', $addAggregateFieldUrl); $this->assign('addFieldUrl', $addFieldUrl); + $this->assign('addFilterUrl', $addFilterUrl); $this->assign('addOutputUrl', $outputAddUrl); } } @@ -84,6 +87,15 @@ class CRM_Dataprocessor_Form_DataProcessor extends CRM_Core_Form { $this->assign('fields', $fields); } + protected function addFilters() { + $filters = CRM_Dataprocessor_BAO_Filter::getValues(array('data_processor_id' => $this->dataProcessorId)); + foreach($filters as $idx => $filter) { + $filters[$idx]['is_required'] = $filter['is_required'] ? E::ts('Yes') : E::ts('No'); + $filters[$idx]['configuration_link'] = ''; + } + $this->assign('filters', $filters); + } + protected function addAggregateFields() { $fields = array(); $aggregationFields = CRM_Dataprocessor_BAO_DataProcessor::getAvailableAggregationFields($this->dataProcessorId); diff --git a/CRM/Dataprocessor/Form/Filter.php b/CRM/Dataprocessor/Form/Filter.php new file mode 100644 index 00000000..4105eb38 --- /dev/null +++ b/CRM/Dataprocessor/Form/Filter.php @@ -0,0 +1,165 @@ +<?php + +use CRM_Dataprocessor_ExtensionUtil as E; + +/** + * Form controller class + * + * @see https://wiki.civicrm.org/confluence/display/CRMDOC/QuickForm+Reference + */ +class CRM_Dataprocessor_Form_Filter extends CRM_Core_Form { + + private $dataProcessorId; + + private $id; + + /** + * Function to perform processing before displaying form (overrides parent function) + * + * @access public + */ + function preProcess() { + $session = CRM_Core_Session::singleton(); + $this->dataProcessorId = CRM_Utils_Request::retrieve('data_processor_id', 'Integer'); + $this->assign('data_processor_id', $this->dataProcessorId); + + $this->id = CRM_Utils_Request::retrieve('id', 'Integer'); + $this->assign('id', $this->id); + + if ($this->id) { + $filter = CRM_Dataprocessor_BAO_Filter::getValues(array('id' => $this->id)); + $this->assign('filter', $filter[$this->id]); + } + + $title = E::ts('Data Processor Filter'); + CRM_Utils_System::setTitle($title); + + $url = CRM_Utils_System::url('civicrm/dataprocessor/form/edit', array('id' => $this->dataProcessorId, 'action' => 'update', 'reset' => 1)); + $session->pushUserContext($url); + } + + public function buildQuickForm() { + $this->add('hidden', 'data_processor_id'); + $this->add('hidden', 'id'); + if ($this->_action != CRM_Core_Action::DELETE) { + $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); + + $filterHandlers = CRM_Dataprocessor_BAO_DataProcessor::getAvailableFilterHandlers($this->dataProcessorId); + $filterHandlersSelect = array(E::ts('- Select -')); + foreach($filterHandlers as $filterHandler) { + $filterHandlersSelect[$filterHandler->getName()] = $filterHandler->getTitle(); + } + + $this->add('select', 'type', E::ts('Select Filter'), $filterHandlersSelect, true, array('class' => 'crm-select2 crm-huge40')); + + $this->add('checkbox', 'is_required', E::ts('Is required')); + } + if ($this->_action == CRM_Core_Action::ADD) { + $this->addButtons(array( + array('type' => 'next', 'name' => E::ts('Next'), 'isDefault' => TRUE,), + array('type' => 'cancel', 'name' => E::ts('Cancel')))); + } elseif ($this->_action == CRM_Core_Action::DELETE) { + $this->addButtons(array( + array('type' => 'next', 'name' => E::ts('Delete'), 'isDefault' => TRUE,), + array('type' => 'cancel', 'name' => E::ts('Cancel')))); + } else { + $this->addButtons(array( + array('type' => 'next', 'name' => E::ts('Save'), 'isDefault' => TRUE,), + array('type' => 'cancel', 'name' => E::ts('Cancel')))); + } + parent::buildQuickForm(); + } + + function setDefaultValues() { + $defaults = []; + $defaults['data_processor_id'] = $this->dataProcessorId; + $defaults['id'] = $this->id; + + $filter = CRM_Dataprocessor_BAO_Filter::getValues(array('id' => $this->id)); + if (isset($filter[$this->id]['type'])) { + $defaults['type'] = $filter[$this->id]['type']; + } + if (isset($filter[$this->id]['is_required'])) { + $defaults['is_required'] = $filter[$this->id]['is_required']; + } + if (isset($filter[$this->id]['title'])) { + $defaults['title'] = $filter[$this->id]['title']; + } + if (isset($filter[$this->id]['name'])) { + $defaults['name'] = $filter[$this->id]['name']; + } + return $defaults; + } + + public function postProcess() { + $session = CRM_Core_Session::singleton(); + $redirectUrl = $session->readUserContext(); + if ($this->_action == CRM_Core_Action::DELETE) { + CRM_Dataprocessor_BAO_Filter::deleteWithId($this->id); + $session->setStatus(E::ts('Filter removed'), E::ts('Removed'), 'success'); + CRM_Utils_System::redirect($redirectUrl); + } + + $values = $this->exportValues(); + if (!empty($values['name'])) { + $params['name'] = $values['name']; + } else { + $params['name'] = CRM_Dataprocessor_BAO_Filter::buildNameFromTitle($values['title']); + } + $params['title'] = $values['title']; + $params['type'] = $values['type']; + $params['is_required'] = $values['is_required']; + if ($this->dataProcessorId) { + $params['data_processor_id'] = $this->dataProcessorId; + } + if ($this->id) { + $params['id'] = $this->id; + } + + $result = CRM_Dataprocessor_BAO_Filter::add($params); + + CRM_Utils_System::redirect($redirectUrl); + parent::postProcess(); + } + + /** + * Function to add validation rules (overrides parent function) + * + * @access public + */ + function addRules() { + if ($this->_action != CRM_Core_Action::DELETE) { + $this->addFormRule(array( + 'CRM_Dataprocessor_Form_Filter', + 'validateName' + )); + } + } + + /** + * Function to validate if rule label already exists + * + * @param array $fields + * @return array|bool + * @access static + */ + static function validateName($fields) { + /* + * if id not empty, edit mode. Check if changed before check if exists + */ + $id = false; + if (!empty($fields['id'])) { + $id = $fields['id']; + } + if (empty($fields['name'])) { + $fields['name'] = CRM_Dataprocessor_BAO_Filter::buildNameFromTitle($fields['title']); + } + if (!CRM_Dataprocessor_BAO_Filter::isNameValid($fields['name'], $fields['data_processor_id'], $id)) { + $errors['name'] = E::ts('There is already a filter with this name'); + return $errors; + } + return TRUE; + } + +} \ No newline at end of file diff --git a/CRM/Dataprocessor/Utils/Importer.php b/CRM/Dataprocessor/Utils/Importer.php index f30181f2..c1a3842d 100644 --- a/CRM/Dataprocessor/Utils/Importer.php +++ b/CRM/Dataprocessor/Utils/Importer.php @@ -70,6 +70,7 @@ class CRM_Dataprocessor_Utils_Importer { // Clear all existing data sources and outputs CRM_Dataprocessor_BAO_Source::deleteWithDataProcessorId($id); + CRM_Dataprocessor_BAO_Filter::deleteWithDataProcessorId($id); CRM_Dataprocessor_BAO_Field::deleteWithDataProcessorId($id); CRM_Dataprocessor_BAO_Output::deleteWithDataProcessorId($id); @@ -78,6 +79,11 @@ class CRM_Dataprocessor_Utils_Importer { $params['data_processor_id'] = $id; $result = CRM_Dataprocessor_BAO_Source::add($params); } + foreach($data['filters'] as $filter) { + $params = $filter; + $params['data_processor_id'] = $id; + $result = CRM_Dataprocessor_BAO_Filter::add($params); + } foreach($data['fields'] as $field) { $params = $field; $params['data_processor_id'] = $id; diff --git a/Civi/DataProcessor/Event/FilterHandlerEvent.php b/Civi/DataProcessor/Event/FilterHandlerEvent.php new file mode 100644 index 00000000..1774c658 --- /dev/null +++ b/Civi/DataProcessor/Event/FilterHandlerEvent.php @@ -0,0 +1,37 @@ +<?php +/** + * @author Jaap Jansma <jaap.jansma@civicoop.org> + * @license AGPL-3.0 + */ + +namespace Civi\DataProcessor\Event; + +use Civi\DataProcessor\DataSpecification\FieldSpecification; +use Civi\DataProcessor\Source\SourceInterface; +use Symfony\Component\EventDispatcher\Event; + +class FilterHandlerEvent extends Event { + + const NAME = 'dataprocessor.filterhandler'; + + /** + * @var FieldSpecification + */ + public $fieldSpecification; + + /** + * @var SourceInterface + */ + public $dataSource; + + /** + * @var array + */ + public $handlers = array(); + + public function __construct(FieldSpecification $field, SourceInterface $source) { + $this->fieldSpecification = $field; + $this->dataSource = $source; + } + +} \ No newline at end of file diff --git a/Civi/DataProcessor/Factory.php b/Civi/DataProcessor/Factory.php index 6d478c12..dac494ce 100644 --- a/Civi/DataProcessor/Factory.php +++ b/Civi/DataProcessor/Factory.php @@ -7,8 +7,10 @@ namespace Civi\DataProcessor; use Civi\DataProcessor\DataSpecification\FieldSpecification; +use Civi\DataProcessor\Event\FilterHandlerEvent; use Civi\DataProcessor\Event\OutputHandlerEvent; use Civi\DataProcessor\FieldOutputHandler\RawFieldOutputHandler; +use Civi\DataProcessor\FilterHandler\SimpleSqlFilter; use Civi\DataProcessor\ProcessorType\AbstractProcessorType; use Civi\DataProcessor\Source\SourceInterface; use Symfony\Component\EventDispatcher\EventDispatcher; @@ -201,4 +203,12 @@ class Factory { return $event->handlers; } + public function getFilterHandlers(FieldSpecification $field, SourceInterface $source) { + $event = new FilterHandlerEvent($field, $source); + $handler = new SimpleSqlFilter($field, $source); + $event->handlers[$handler->getName()] = $handler; + $this->dispatcher->dispatch(OutputHandlerEvent::NAME, $event); + return $event->handlers; + } + } \ No newline at end of file diff --git a/Civi/DataProcessor/FieldOutputHandler/AbstractFieldOutputHandler.php b/Civi/DataProcessor/FieldOutputHandler/AbstractFieldOutputHandler.php index 168f5539..f9d6c0da 100644 --- a/Civi/DataProcessor/FieldOutputHandler/AbstractFieldOutputHandler.php +++ b/Civi/DataProcessor/FieldOutputHandler/AbstractFieldOutputHandler.php @@ -7,8 +7,6 @@ namespace Civi\DataProcessor\FieldOutputHandler; use Civi\DataProcessor\DataSpecification\FieldSpecification; -use Civi\DataProcessor\ProcessorType\AbstractProcessorType; -use Civi\DataProcessor\Source\SourceInterface; abstract class AbstractFieldOutputHandler { diff --git a/Civi/DataProcessor/FieldOutputHandler/RawFieldOutputHandler.php b/Civi/DataProcessor/FieldOutputHandler/RawFieldOutputHandler.php index f305db25..30fed2b7 100644 --- a/Civi/DataProcessor/FieldOutputHandler/RawFieldOutputHandler.php +++ b/Civi/DataProcessor/FieldOutputHandler/RawFieldOutputHandler.php @@ -9,7 +9,6 @@ namespace Civi\DataProcessor\FieldOutputHandler; use CRM_Dataprocessor_ExtensionUtil as E; use Civi\DataProcessor\Source\SourceInterface; use Civi\DataProcessor\DataSpecification\FieldSpecification; -use Civi\DataProcessor\ProcessorType\AbstractProcessorType; class RawFieldOutputHandler extends AbstractFieldOutputHandler { diff --git a/Civi/DataProcessor/FilterHandler/AbstractFilterHandler.php b/Civi/DataProcessor/FilterHandler/AbstractFilterHandler.php new file mode 100644 index 00000000..c454c955 --- /dev/null +++ b/Civi/DataProcessor/FilterHandler/AbstractFilterHandler.php @@ -0,0 +1,81 @@ +<?php +/** + * @author Jaap Jansma <jaap.jansma@civicoop.org> + * @license AGPL-3.0 + */ + +namespace Civi\DataProcessor\FilterHandler; + +use Civi\DataProcessor\DataSpecification\FieldSpecification; + +abstract class AbstractFilterHandler { + + /** + * @var \Civi\DataProcessor\DataSpecification\FieldSpecification + */ + protected $fieldSpecification; + + /** + * @var bool + */ + protected $is_required; + + /** + * Returns the name of the handler type. + * + * @return String + */ + abstract public function getName(); + + /** + * Returns the title of the handler type. + * + * @return String + */ + abstract public function getTitle(); + + /** + * Returns the data type of this field + * + * @return String + */ + abstract protected function getType(); + + /** + * @param array $filterParams + * The filter settings + * @return mixed + */ + abstract public function setFilter($filterParams); + + public function __construct() { + $this->fieldSpecification = new FieldSpecification($this->getName(), $this->getType(), $this->getName()); + } + + /** + * Initialize the processor + * + * @param String $alias + * @param String $title + * @param bool $is_required + * @param array $configuration + */ + public function initialize($alias, $title, $is_required, $configuration) { + // Override this in child classes. + $this->fieldSpecification->title = $title; + $this->fieldSpecification->alias = $alias; + $this->is_required = $is_required; + } + + public function isRequired() { + return $this->is_required; + } + + /** + * @return \Civi\DataProcessor\DataSpecification\FieldSpecification + */ + public function getFieldSpecification() { + return $this->fieldSpecification; + } + +} \ No newline at end of file diff --git a/Civi/DataProcessor/FilterHandler/SimpleSqlFilter.php b/Civi/DataProcessor/FilterHandler/SimpleSqlFilter.php new file mode 100644 index 00000000..8c58a109 --- /dev/null +++ b/Civi/DataProcessor/FilterHandler/SimpleSqlFilter.php @@ -0,0 +1,61 @@ +<?php +/** + * @author Jaap Jansma <jaap.jansma@civicoop.org> + * @license AGPL-3.0 + */ + +namespace Civi\DataProcessor\FilterHandler; + +use Civi\DataProcessor\DataSpecification\FieldSpecification; +use Civi\DataProcessor\Source\SourceInterface; +use CRM_Dataprocessor_ExtensionUtil as E; + +class SimpleSqlFilter extends AbstractFilterHandler { + + /** + * @var \Civi\DataProcessor\Source\SourceInterface + */ + protected $dataSource; + + public function __construct(FieldSpecification $filterField, SourceInterface $dataSource) { + $this->dataSource = $dataSource; + $this->fieldSpecification = $filterField; + } + + /** + * Returns the name of the handler type. + * + * @return String + */ + public function getName() { + return 'simple_filter_'.$this->fieldSpecification->alias; + } + + /** + * Returns the data type of this field + * + * @return String + */ + protected function getType() { + return $this->fieldSpecification->type; + } + + /** + * Returns the title of this field + * + * @return String + */ + public function getTitle() { + return E::ts('%1::%2 (Simple Filter)', array(1 => $this->dataSource->getSourceTitle(), 2 => $this->fieldSpecification->title)); + } + + /** + * @param array $filter + * The filter settings + * @return mixed + */ + public function setFilter($filter) { + $this->dataSource->addFilter($this->fieldSpecification->name, $filter); + } + +} \ No newline at end of file diff --git a/Civi/DataProcessor/Output/Api.php b/Civi/DataProcessor/Output/Api.php index 3c9ec137..a9a2039e 100644 --- a/Civi/DataProcessor/Output/Api.php +++ b/Civi/DataProcessor/Output/Api.php @@ -35,7 +35,7 @@ class Api implements OutputInterface, API_ProviderInterface, EventSubscriberInte // if so do our getfields stuff there. return array( Events::RESOLVE => array('onApiResolve'), - //Events::RESPOND => array('onGetFieldsRepsonse'), // we use this method to add our field definition to the getFields action. + Events::RESPOND => array('onGetFieldsRepsonse'), // we use this method to add our field definition to the getFields action. ); } @@ -46,6 +46,72 @@ class Api implements OutputInterface, API_ProviderInterface, EventSubscriberInte } } + /** + * Event listener on the ResponddEvent to handle the getfields actions. + * So the fields defined by the user are availble in the api explorer for example. + */ + public function onGetFieldsRepsonse(RespondEvent $event) { + $apiRequest = $event->getApiRequest(); + $params = $apiRequest['params']; + $result = $event->getResponse(); + + // First check whether the entity is dataprocessorapi and the action is getfields. + // If not return this function. + if (strtolower($apiRequest['entity']) != 'dataprocessorapi' || strtolower($apiRequest['action']) != 'getfields') { + return; + } + // Now check whether the action param is set. With the action param we can find the data processor. + if (isset($params['action'])) { + if (stripos($params['action'], 'getcount') === 0) { + return; // Is a get count action. + } + // Find the data processor + try { + $dataProcessor = \CRM_Dataprocessor_BAO_DataProcessor::getDataProcessorByOutputTypeAndName('api', $params['action']); + + foreach ($dataProcessor->getDataFlow()->getDataSpecification()->getFields() as $fieldSpec) { + $field = [ + 'name' => $fieldSpec->alias, + 'title' => $fieldSpec->title, + 'description' => '', + 'type' => $fieldSpec->type, + 'api.required' => FALSE, + 'api.aliases' => [], + ]; + if ($fieldSpec->getOptions()) { + $field['options'] = $fieldSpec->getOptions(); + } + $result['values'][$fieldSpec->alias] = $field; + } + foreach($dataProcessor->getFilterHandlers() as $filterHandler) { + $fieldSpec = $filterHandler->getFieldSpecification(); + $field = [ + 'name' => $fieldSpec->alias, + 'title' => $fieldSpec->title, + 'description' => '', + 'type' => $fieldSpec->type, + 'api.required' => $filterHandler->isRequired(), + 'api.aliases' => [], + ]; + if ($fieldSpec->getOptions()) { + $field['options'] = $fieldSpec->getOptions(); + } + if (!isset($result['values'][$fieldSpec->alias])) { + $result['values'][$fieldSpec->alias] = $field; + } else { + $result['values'][$fieldSpec->alias] = array_merge($result['values'][$fieldSpec->alias], $field); + } + } + } catch(\Exception $e) { + // Do nothing. + } + + $result['count'] = count($result['values']); + $event->setResponse($result); + } + } + + /** * @param array $apiRequest * The full description of the API request. @@ -68,6 +134,29 @@ class Api implements OutputInterface, API_ProviderInterface, EventSubscriberInte throw new \API_Exception('Could not find a form processor'); } + $params = $apiRequest['params']; + foreach($dataProcessor->getFilterHandlers() as $filter) { + $filterSpec = $filter->getFieldSpecification(); + if ($filter->isRequired() && !isset($params[$filterSpec->alias])) { + throw new \API_Exception('Field '.$filterSpec->alias.' is required'); + } + if (isset($params[$filterSpec->alias])) { + if (!is_array($params[$filterSpec->alias])) { + $filterParams = [ + 'op' => '=', + 'value' => $params[$filterSpec->alias], + ]; + } else { + $value = reset($params[$filterSpec->alias]); + $filterParams = [ + 'op' => key($params[$filterSpec->alias]), + 'value' => $value, + ]; + } + $filter->setFilter($filterParams); + } + } + if ($isCountAction) { $count = $dataProcessor->getDataFlow()->recordCount(); return array('count' => $count, 'is_error' => 0); diff --git a/Civi/DataProcessor/ProcessorType/AbstractProcessorType.php b/Civi/DataProcessor/ProcessorType/AbstractProcessorType.php index f8a0c75d..c977750b 100644 --- a/Civi/DataProcessor/ProcessorType/AbstractProcessorType.php +++ b/Civi/DataProcessor/ProcessorType/AbstractProcessorType.php @@ -15,6 +15,7 @@ use Civi\DataProcessor\DataFlow\MultipleDataFlows\JoinSpecification; use Civi\DataProcessor\DataFlow\SqlDataFlow; use Civi\DataProcessor\DataSpecification\FieldSpecification; use Civi\DataProcessor\FieldOutputHandler\AbstractFieldOutputHandler; +use Civi\DataProcessor\FilterHandler\AbstractFilterHandler; use Civi\DataProcessor\Storage\StorageInterface; abstract class AbstractProcessorType { @@ -44,6 +45,11 @@ abstract class AbstractProcessorType { */ protected $outputFieldHandlers; + /** + * @var \Civi\DataProcessor\FilterHandler\AbstractFilterHandler[] + */ + protected $filterHandlers; + /** * Add a data source to the processor * @param \Civi\DataProcessor\Source\SourceInterface $datasource @@ -88,6 +94,21 @@ abstract class AbstractProcessorType { return $handlers; } + /** + * @return \Civi\DataProcessor\FieldOutputHandler\AbstractFilterOutputHandler[] + */ + public function getAvailableFilterHandlers() { + $factory = dataprocessor_get_factory(); + $handlers = array(); + foreach($this->dataSources as $dataSource) { + foreach($dataSource['datasource']->getAvailableFilterFields()->getFields() as $field) { + $fieldHandlers = $factory->getFilterHandlers($field, $dataSource['datasource']); + $handlers = array_merge($handlers, $fieldHandlers); + } + } + return $handlers; + } + /** * @param \Civi\DataProcessor\FieldOutputHandler\AbstractFieldOutputHandler $outputFieldHandler */ @@ -95,6 +116,20 @@ abstract class AbstractProcessorType { $this->outputFieldHandlers[] = $outputFieldHandler; } + /** + * @param \Civi\DataProcessor\FilterHandler\AbstractFilterHandler $filterHandler + */ + public function addFilterHandler(AbstractFilterHandler $filterHandler) { + $this->filterHandlers[] = $filterHandler; + } + + /** + * @return \Civi\DataProcessor\FilterHandler\AbstractFilterHandler[] + */ + public function getFilterHandlers() { + return $this->filterHandlers; + } + public function ensureFieldInDataSource(FieldSpecification $fieldSpecification) { foreach($this->dataSources as $dataSource) { $dataSource['datasource']->ensureFieldInSource($fieldSpecification); diff --git a/Civi/DataProcessor/Source/AbstractCivicrmEntitySource.php b/Civi/DataProcessor/Source/AbstractCivicrmEntitySource.php index 87632b78..dca26b3f 100644 --- a/Civi/DataProcessor/Source/AbstractCivicrmEntitySource.php +++ b/Civi/DataProcessor/Source/AbstractCivicrmEntitySource.php @@ -201,19 +201,31 @@ abstract class AbstractCivicrmEntitySource implements SourceInterface { protected function addFilters($configuration) { if (isset($configuration['filter']) && is_array($configuration['filter'])) { foreach($configuration['filter'] as $filter_alias => $filter_field) { - if ($this->getAvailableFilterFields()->doesFieldExist($filter_alias)) { - $spec = $this->getAvailableFilterFields()->getFieldSpecificationByName($filter_alias); - if ($spec instanceof CustomFieldSpecification) { - $customGroupDataFlowDescription = $this->ensureCustomGroup($spec->customGroupTableName, $spec->customGroupName); - $customGroupTableAlias = $customGroupDataFlowDescription->getDataFlow() - ->getTableAlias(); - $customGroupDataFlowDescription->getDataFlow()->addWhereClause( - new SimpleWhereClause($customGroupTableAlias, $spec->customFieldColumnName, $filter_field['op'], $filter_field['value']) - ); - } else { - $this->primaryDataFlow->addWhereClause(new SimpleWhereClause($this->primaryDataFlow->getTableAlias(), $spec->name, $filter_field['op'], $filter_field['value'])); - } - } + $this->addFilter($filter_alias, $filter_field); + } + } + } + + /** + * Adds an inidvidual filter to the data source + * + * @param $filter_alias + * @param $filter + * + * @throws \Exception + */ + public function addFilter($filter_alias, $filter) { + if ($this->getAvailableFilterFields()->doesFieldExist($filter_alias)) { + $spec = $this->getAvailableFilterFields()->getFieldSpecificationByName($filter_alias); + if ($spec instanceof CustomFieldSpecification) { + $customGroupDataFlowDescription = $this->ensureCustomGroup($spec->customGroupTableName, $spec->customGroupName); + $customGroupTableAlias = $customGroupDataFlowDescription->getDataFlow() + ->getTableAlias(); + $customGroupDataFlowDescription->getDataFlow()->addWhereClause( + new SimpleWhereClause($customGroupTableAlias, $spec->customFieldColumnName, $filter['op'], $filter['value']) + ); + } else { + $this->primaryDataFlow->addWhereClause(new SimpleWhereClause($this->primaryDataFlow->getTableAlias(), $spec->name, $filter['op'], $filter['value'])); } } } diff --git a/api/v3/DataProcessorFilter/Create.php b/api/v3/DataProcessorFilter/Create.php new file mode 100644 index 00000000..f20d05d4 --- /dev/null +++ b/api/v3/DataProcessorFilter/Create.php @@ -0,0 +1,67 @@ +<?php + +use CRM_Dataprocessor_ExtensionUtil as E; + +/** + * DataProcessorFilter.Create API specification (optional) + * This is used for documentation and validation. + * + * @param array $spec description of filters supported by this API call + * @return void + * @see http://wiki.civicrm.org/confluence/display/CRM/API+Architecture+Standards + */ +function _civicrm_api3_data_processor_filter_create_spec(&$spec) { + $spec['id'] = array( + 'title' => E::ts('ID'), + 'type' => CRM_Utils_Type::T_INT, + 'api.required' => false + ); + $spec['data_processor_id'] = array( + 'title' => E::ts('Data Processor ID'), + 'type' => CRM_Utils_Type::T_INT, + 'api.required' => true, + ); + $spec['is_required'] = array( + 'title' => E::ts('Is required'), + 'type' => CRM_Utils_Type::T_BOOLEAN, + 'api.required' => true, + 'api.default' => true, + ); + $spec['type'] = array( + 'title' => E::ts('Type'), + 'type' => CRM_Utils_Type::T_STRING, + 'api.required' => true + ); + $spec['name'] = array( + 'title' => E::ts('Name'), + 'type' => CRM_Utils_Type::T_STRING, + 'api.required' => true + ); + $spec['title'] = array( + 'title' => E::ts('Title'), + 'type' => CRM_Utils_Type::T_STRING, + 'api.required' => true + ); + $spec['configuration'] = array( + 'title' => E::ts('Configuration'), + 'type' => CRM_Utils_Type::T_TEXT, + 'api.required' => false, + ); +} + +/** + * DataProcessorFilter.Create API + * + * @param array $params + * @return array API result descriptor + * @see civicrm_api3_create_success + * @see civicrm_api3_create_error + * + * + */ +function civicrm_api3_data_processor_filter_create($params) { + $returnValue = CRM_Dataprocessor_BAO_Filter::add($params); + $returnValues[$returnValue['id']] = $returnValue; + return civicrm_api3_create_success($returnValues, $params, 'DataProcessorFilter', 'Create'); +} + diff --git a/api/v3/DataProcessorFilter/Delete.php b/api/v3/DataProcessorFilter/Delete.php new file mode 100644 index 00000000..fdcb3341 --- /dev/null +++ b/api/v3/DataProcessorFilter/Delete.php @@ -0,0 +1,37 @@ +<?php + +use CRM_Dataprocessor_ExtensionUtil as E; + +/** + * DataProcessorFilter.Delete API specification (optional) + * This is used for documentation and validation. + * + * @param array $spec description of fields supported by this API call + * @return void + * @see http://wiki.civicrm.org/confluence/display/CRMDOC/API+Architecture+Standards + */ +function _civicrm_api3_data_processor_filter_Delete_spec(&$spec) { + $spec['id'] = array( + 'title' => E::ts('ID'), + 'type' => CRM_Utils_Type::T_INT, + 'api.required' => true + ); +} + +/** + * DataProcessorFilter.Delete API + * + * @param array $params + * @return array API result descriptor + * @see civicrm_api3_create_success + * @see civicrm_api3_create_error + * @throws API_Exception + */ +function civicrm_api3_data_processor_filter_Delete($params) { + if (!array_key_exists('id', $params) || empty($params['id'])) { + throw new API_Exception('Parameter id is mandatory and can not be empty in ' . __METHOD__, 0010); + } else { + return civicrm_api3_create_success(CRM_Dataprocessor_BAO_DataProcessorFilter::deleteWithId($params['id']), $params, 'DataProcessorFilter', 'Delete'); + } +} + diff --git a/api/v3/DataProcessorFilter/Get.php b/api/v3/DataProcessorFilter/Get.php new file mode 100644 index 00000000..dbdfd8a5 --- /dev/null +++ b/api/v3/DataProcessorFilter/Get.php @@ -0,0 +1,30 @@ +<?php +/** + * DataProcessorFilter.Get API + * + * @param array $params + * @return array API result descriptor + * @see civicrm_api3_create_success + * @see civicrm_api3_create_error + * @throws API_Exception + */ +function civicrm_api3_data_processor_filter_get($params) { + $returnValues = CRM_Dataprocessor_BAO_Filter::getValues($params); + return civicrm_api3_create_success($returnValues, $params, 'DataProcessorFilter', 'Get'); +} + +/** + * DataProcessorFilter.Get API specification (optional) + * This is used for documentation and validation. + * + * @param array $spec description of fields supported by this API call + * @return void + * @see http://wiki.civicrm.org/confluence/display/CRM/API+Architecture+Standards + */ +function _civicrm_api3_data_processor_filter_get_spec(&$spec) { + $fields = CRM_Dataprocessor_BAO_Filter::fields(); + foreach($fields as $fieldname => $field) { + $spec[$fieldname] = $field; + } +} + diff --git a/sql/create_civicrm_data_processor.sql b/sql/create_civicrm_data_processor.sql index 2186f248..287fae8a 100644 --- a/sql/create_civicrm_data_processor.sql +++ b/sql/create_civicrm_data_processor.sql @@ -26,6 +26,17 @@ CREATE TABLE IF NOT EXISTS `civicrm_data_processor_source` ( PRIMARY KEY (`id`) ) ENGINE = InnoDB; +CREATE TABLE IF NOT EXISTS `civicrm_data_processor_filter` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `data_processor_id` INT UNSIGNED NOT NULL, + `name` VARCHAR(128) NOT NULL, + `type` VARCHAR(128) NOT NULL, + `title` VARCHAR(128) NOT NULL, + `is_required` TINYINT NULL DEFAULT 1, + `configuration` TEXT NULL, + PRIMARY KEY (`id`) +) ENGINE = InnoDB; + CREATE TABLE IF NOT EXISTS `civicrm_data_processor_field` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `data_processor_id` INT UNSIGNED NOT NULL, diff --git a/templates/CRM/Dataprocessor/Form/DataProcessor.tpl b/templates/CRM/Dataprocessor/Form/DataProcessor.tpl index 6bb449cf..4d72c2c1 100644 --- a/templates/CRM/Dataprocessor/Form/DataProcessor.tpl +++ b/templates/CRM/Dataprocessor/Form/DataProcessor.tpl @@ -50,6 +50,7 @@ {if $data_processor_id} {include file="CRM/Dataprocessor/Form/DataProcessorBlocks/Sources.tpl"} {include file="CRM/Dataprocessor/Form/DataProcessorBlocks/AggregateFields.tpl"} + {include file="CRM/Dataprocessor/Form/DataProcessorBlocks/Filters.tpl"} {include file="CRM/Dataprocessor/Form/DataProcessorBlocks/Fields.tpl"} {include file="CRM/Dataprocessor/Form/DataProcessorBlocks/Outputs.tpl"} {/if} diff --git a/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/AggregateFields.tpl b/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/AggregateFields.tpl index 49cd0981..dfab0011 100644 --- a/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/AggregateFields.tpl +++ b/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/AggregateFields.tpl @@ -1,4 +1,5 @@ {crmScope extensionKey='dataprocessor'} +<h3>{ts}Aggregation{/ts}</h3> <div class="crm-block crm-form-block crm-data-processor_source-block"> <table> <tr> diff --git a/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Fields.tpl b/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Fields.tpl index 91737000..92e1d151 100644 --- a/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Fields.tpl +++ b/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Fields.tpl @@ -1,4 +1,5 @@ {crmScope extensionKey='dataprocessor'} +<h3>{ts}Fields{/ts}</h3> <div class="crm-block crm-form-block crm-data-processor_source-block"> <table> <tr> diff --git a/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Filters.tpl b/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Filters.tpl new file mode 100644 index 00000000..bc9f673a --- /dev/null +++ b/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Filters.tpl @@ -0,0 +1,39 @@ +{crmScope extensionKey='dataprocessor'} + <h3>{ts}Exposed Filters{/ts}</h3> + <div class="crm-block crm-form-block crm-data-processor_source-block"> + <table> + <tr> + <th>{ts}Title{/ts}</th> + <th>{ts}Name{/ts}</th> + <th>{ts}Required{/ts}</th> + <th></th> + <th></th> + <th></th> + <th></th> + </tr> + {foreach from=$filters item=filter} + <tr> + <td>{$filter.title}</td> + <td>{$filter.name}</td> + <td>{$filter.is_required}</td> + <td> + {if $filter.configuration_link} + <a href="{$filter.configuration_link}">{ts}Configure Filter{/ts}</a> + {/if} + </td> + <td> + <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> + </td> + <td> + <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> + </tr> + {/foreach} + </table> + + <div class="crm-submit-buttons"> + <a class="add button" title="{ts}Add Filter{/ts}" href="{$addFilterUrl}"> + <span><div class="icon add-icon ui-icon-circle-plus"></div>{ts}Add Filter{/ts}</span></a> + </div> + </div> +{/crmScope} \ No newline at end of file diff --git a/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Outputs.tpl b/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Outputs.tpl index f2b3cca1..dfce9c8b 100644 --- a/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Outputs.tpl +++ b/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Outputs.tpl @@ -1,5 +1,6 @@ {crmScope extensionKey='dataprocessor'} -<div class="crm-block crm-form-block crm-data-processor_outputs-block"> + <h3>{ts}Output{/ts}</h3> + <div class="crm-block crm-form-block crm-data-processor_outputs-block"> <table> <tr> <th>{ts}Output{/ts}</th> diff --git a/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Sources.tpl b/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Sources.tpl index 320bf351..bdc57fe0 100644 --- a/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Sources.tpl +++ b/templates/CRM/Dataprocessor/Form/DataProcessorBlocks/Sources.tpl @@ -1,4 +1,5 @@ {crmScope extensionKey='dataprocessor'} + <h3>{ts}Data Sources{/ts}</h3> <div class="crm-block crm-form-block crm-data-processor_source-block"> <table> <tr> diff --git a/templates/CRM/Dataprocessor/Form/Filter.tpl b/templates/CRM/Dataprocessor/Form/Filter.tpl new file mode 100644 index 00000000..b3afbc13 --- /dev/null +++ b/templates/CRM/Dataprocessor/Form/Filter.tpl @@ -0,0 +1,43 @@ +{crmScope extensionKey='dataprocessor'} +<div class="crm-submit-buttons"> + {include file="CRM/common/formButtons.tpl" location="top"} +</div> + +{if $action eq 8} + {* Are you sure to delete form *} + <h3>{ts}Delete Field{/ts}</h3> + <div class="crm-block crm-form-block crm-data-processor_label-block"> + <div class="crm-section">{ts 1=$filter->title}Are you sure to delete filter '%1'?{/ts}</div> + </div> +{else} + + {* block for rule data *} + <h3>{ts}Field{/ts}</h3> + <div class="crm-block crm-form-block crm-data-processor_source-block"> + <div class="crm-section"> + <div class="label">{$form.type.label}</div> + <div class="content">{$form.type.html}</div> + <div class="clear"></div> + </div> + <div class="crm-section"> + <div class="label">{$form.title.label}</div> + <div class="content">{$form.title.html}</div> + <div class="clear"></div> + </div> + <div class="crm-section"> + <div class="label">{$form.name.label}</div> + <div class="content">{$form.name.html}</div> + <div class="clear"></div> + </div> + <div class="crm-section"> + <div class="label">{$form.is_required.label}</div> + <div class="content">{$form.is_required.html}</div> + <div class="clear"></div> + </div> + </div> +{/if} + +<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 6b2419d3..a50142d7 100644 --- a/xml/Menu/dataprocessor.xml +++ b/xml/Menu/dataprocessor.xml @@ -21,6 +21,13 @@ <access_arguments>access CiviCRM</access_arguments> <access_arguments>administer CiviCRM</access_arguments> </item> + <item> + <path>civicrm/dataprocessor/form/filter</path> + <page_callback>CRM_Dataprocessor_Form_Filter</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> -- GitLab