From 60b9d8bec07bda450f0e7caf52bc9cf8dead8620 Mon Sep 17 00:00:00 2001 From: Jaap Jansma <jaap.jansma@civicoop.org> Date: Mon, 1 Jul 2019 17:32:13 +0200 Subject: [PATCH] velt issue 4576 WIP --- .../Selector/DataProcessorContactSearch.php | 191 ++++++++++++++++++ .../DataProcessorContactSearch.php | 92 +++++++++ .../Controller/ContactSearch.php | 16 +- .../Form/AbstractSearch.php | 1 + Civi/DataProcessor/DataFlow/SqlDataFlow.php | 7 + 5 files changed, 305 insertions(+), 2 deletions(-) create mode 100644 CRM/Contact/Selector/DataProcessorContactSearch.php create mode 100644 CRM/Contact/StateMachine/DataProcessorContactSearch.php diff --git a/CRM/Contact/Selector/DataProcessorContactSearch.php b/CRM/Contact/Selector/DataProcessorContactSearch.php new file mode 100644 index 00000000..55c29f7d --- /dev/null +++ b/CRM/Contact/Selector/DataProcessorContactSearch.php @@ -0,0 +1,191 @@ +<?php +/** + * @author Jaap Jansma <jaap.jansma@civicoop.org> + * @license AGPL-3.0 + */ + +class CRM_Contact_Selector_DataProcessorContactSearch { + /** + * @var \Civi\DataProcessor\ProcessorType\AbstractProcessorType; + */ + protected $dataProcessorClass; + + /** + * @var int + */ + protected $dataProcessorId; + + /** + * @var array + */ + protected $dataProcessor; + + /** + * @var \CRM_Dataprocessor_BAO_Output + */ + protected $dataProcessorOutput; + + public function __construct($customClass, $formValues=null, $params=null, $returnProperties=null) { + $this->loadDataProcessor(); + $sortFields = $this->getSortFields(); + $this->sort = new CRM_Utils_Sort($sortFields); + if (isset($formValues[CRM_Utils_Sort::SORT_ID])) { + $this->sort->initSortID($formValues[CRM_Utils_Sort::SORT_ID]); + } + + CRM_Dataprocessor_Form_Output_AbstractUIOutputForm::applyFilters($this->dataProcessorClass, $formValues); + // Set the sort + $sortDirection = 'ASC'; + if (!empty($this->sort->_vars[$this->sort->getCurrentSortID()])) { + $sortField = $this->sort->_vars[$this->sort->getCurrentSortID()]; + if ($this->sort->getCurrentSortDirection() == CRM_Utils_Sort::DESCENDING) { + $sortDirection = 'DESC'; + } + $this->dataProcessorClass->getDataFlow()->addSort($sortField['name'], $sortDirection); + } + } + + /** + * Add the headers for the columns + * + * @return array + * Array with all possible sort fields. + * + * @throws \Civi\DataProcessor\DataFlow\InvalidFlowException + */ + protected function getSortFields() { + $sortFields = array(); + $hiddenFields = $this->getHiddenFields(); + $sortColumnNr = 1; + foreach($this->dataProcessorClass->getDataFlow()->getOutputFieldHandlers() as $outputFieldHandler) { + $field = $outputFieldHandler->getOutputFieldSpecification(); + if (!in_array($field->alias, $hiddenFields)) { + if ($outputFieldHandler instanceof \Civi\DataProcessor\FieldOutputHandler\OutputHandlerSortable) { + $sortFields[$sortColumnNr] = array( + 'name' => $field->title, + 'sort' => $field->alias, + 'direction' => CRM_Utils_Sort::DONTCARE, + ); + $sortColumnNr++; + } + } + } + return $sortFields; + } + + /** + * Generate contact ID query. + * + * @param array $params + * @param $action + * @param int $sortID + * @param null $displayRelationshipType + * @param string $queryOperator + * + * @return Object + */ + public function contactIDQuery($params, $sortID, $displayRelationshipType = NULL, $queryOperator = 'AND') { + $dataFlow = $this->dataProcessorClass->getDataFlow(); + $cids = array(); + $contactIdFieldName = $this->dataProcessorOutput['configuration']['contact_id_field']; + try { + while($record = $dataFlow->nextRecord()) { + $cids[] = $record[$contactIdFieldName]->rawValue; + } + } catch (\Civi\DataProcessor\DataFlow\EndOfFlowException $e) { + // do nothing + } + return CRM_Core_DAO::executeQuery("SELECT id as contact_id from civicrm_contact WHERE id in (".implode(", ", $cids).")"); + } + + /** + * Return the data processor ID + * + * @return String + */ + protected function getDataProcessorName() { + $dataProcessorName = str_replace('civicrm/dataprocessor_contact_search/', '', CRM_Utils_System::getUrlPath()); + return $dataProcessorName; + } + + /** + * Retrieve the data processor and the output configuration + * + * @throws \Exception + */ + protected function loadDataProcessor() { + if (!$this->dataProcessorId) { + $dataProcessorName = $this->getDataProcessorName(); + $sql = " + SELECT civicrm_data_processor.id as data_processor_id, civicrm_data_processor_output.id AS output_id + FROM civicrm_data_processor + INNER JOIN civicrm_data_processor_output ON civicrm_data_processor.id = civicrm_data_processor_output.data_processor_id + WHERE is_active = 1 AND civicrm_data_processor.name = %1 AND civicrm_data_processor_output.type = %2 + "; + $params[1] = [$dataProcessorName, 'String']; + $params[2] = [$this->getOutputName(), 'String']; + $dao = CRM_Dataprocessor_BAO_DataProcessor::executeQuery($sql, $params, TRUE, 'CRM_Dataprocessor_BAO_DataProcessor'); + if (!$dao->fetch()) { + throw new \Exception('Could not find Data Processor "' . $dataProcessorName.'"'); + } + + $this->dataProcessor = civicrm_api3('DataProcessor', 'getsingle', array('id' => $dao->data_processor_id)); + $this->dataProcessorClass = \CRM_Dataprocessor_BAO_DataProcessor::dataProcessorToClass($this->dataProcessor); + $this->dataProcessorId = $dao->data_processor_id; + + $this->dataProcessorOutput = civicrm_api3('DataProcessorOutput', 'getsingle', array('id' => $dao->output_id)); + + if (!$this->isConfigurationValid()) { + throw new \Exception('Invalid configuration found of the data processor "' . $dataProcessorName . '"'); + } + } + } + + /** + * Checks whether the output has a valid configuration + * + * @return bool + */ + protected function isConfigurationValid() { + if (!isset($this->dataProcessorOutput['configuration']['contact_id_field'])) { + return false; + } + return true; + } + + /** + * Returns the name of the output for this search + * + * @return string + */ + protected function getOutputName() { + return 'contact_search'; + } + + /** + * Returns whether the ID field is Visible + * + * @return bool + */ + protected function isIdFieldVisible() { + if (isset($this->dataProcessorOutput['configuration']['hide_id_field']) && $this->dataProcessorOutput['configuration']['hide_id_field']) { + return false; + } + return true; + } + + /** + * Returns an array with hidden columns + * + * @return array + */ + protected function getHiddenFields() { + $hiddenFields = array(); + if (!$this->isIdFieldVisible()) { + $hiddenFields[] = $this->getIdFieldName(); + } + return $hiddenFields; + } + + +} \ No newline at end of file diff --git a/CRM/Contact/StateMachine/DataProcessorContactSearch.php b/CRM/Contact/StateMachine/DataProcessorContactSearch.php new file mode 100644 index 00000000..8aac209b --- /dev/null +++ b/CRM/Contact/StateMachine/DataProcessorContactSearch.php @@ -0,0 +1,92 @@ +<?php +/** + * @author Jaap Jansma <jaap.jansma@civicoop.org> + * @license AGPL-3.0 + */ + +class CRM_Contact_StateMachine_DataProcessorContactSearch extends CRM_Core_StateMachine { + + /** + * The task that the wizard is currently processing + * + * @var string + */ + protected $_task; + + /** + * Class constructor. + * + * @param object $controller + * @param \const|int $action + */ + public function __construct($controller, $action = CRM_Core_Action::NONE) { + parent::__construct($controller, $action); + + $this->_pages = array(); + $this->_pages['Basic'] = array( + 'className' => 'CRM_DataprocessorSearch_Form_ContactSearch', + ); + list($task, $result) = $this->taskName($controller); + $this->_task = $task; + if (is_array($task)) { + foreach ($task as $t) { + $this->_pages[$t] = NULL; + } + } + else { + $this->_pages[$task] = NULL; + } + + if ($result) { + $this->_pages['CRM_Contact_Form_Task_Result'] = NULL; + } + + $this->addSequentialPages($this->_pages, $action); + } + + /** + * Determine the form name based on the action. This allows us + * to avoid using conditional state machine, much more efficient + * and simpler + * + * @param CRM_Core_Controller $controller + * The controller object. + * + * @return array + * the name of the form that will handle the task + */ + public function taskName($controller) { + // total hack, check POST vars and then session to determine stuff + $value = CRM_Utils_Array::value('task', $_POST); + if (!isset($value)) { + $value = $controller->get('task'); + } + $this->_controller->set('task', $value); + + return CRM_Contact_Task::getTask($value); + } + + /** + * Return the form name of the task. + * + * @return string + */ + public function getTaskFormName() { + if (is_array($this->_task)) { + // return first page + return CRM_Utils_String::getClassName($this->_task[0]); + } + else { + return CRM_Utils_String::getClassName($this->_task); + } + } + + /** + * Since this is a state machine for search and we want to come back to the same state + * we dont want to issue a reset of the state session when we are done processing a task + */ + public function shouldReset() { + return FALSE; + } + +} diff --git a/CRM/DataprocessorSearch/Controller/ContactSearch.php b/CRM/DataprocessorSearch/Controller/ContactSearch.php index 362ed26a..15095c80 100644 --- a/CRM/DataprocessorSearch/Controller/ContactSearch.php +++ b/CRM/DataprocessorSearch/Controller/ContactSearch.php @@ -16,6 +16,18 @@ */ class CRM_DataprocessorSearch_Controller_ContactSearch extends CRM_Core_Controller { + protected $dataProcessor; + + /** + * Setter function to set the data porcessor + * + * @param $dataProcessorName + * @param $dataProcessor + */ + public function setDataProcessor($dataProcessorName, $dataProcessor) { + $this->dataProcessor = $dataProcessor; + } + /** * Class constructor. * @@ -26,7 +38,7 @@ class CRM_DataprocessorSearch_Controller_ContactSearch extends CRM_Core_Controll public function __construct($title = NULL, $modal = TRUE, $action = CRM_Core_Action::NONE) { parent::__construct($title, $modal); - $this->_stateMachine = new CRM_DataprocessorSearch_StateMachine_ContactSearch($this, $action); + $this->_stateMachine = new CRM_Contact_StateMachine_DataProcessorContactSearch($this, $action); // create and instantiate the pages $this->addPages($this->_stateMachine, $action); @@ -72,7 +84,7 @@ class CRM_DataprocessorSearch_Controller_ContactSearch extends CRM_Core_Controll * @return mixed */ public function selectorName() { - return $this->get('selectorName'); + return 'CRM_Contact_Selector_DataProcessorContactSearch'; } } diff --git a/CRM/DataprocessorSearch/Form/AbstractSearch.php b/CRM/DataprocessorSearch/Form/AbstractSearch.php index ceaa55a6..efbf0139 100644 --- a/CRM/DataprocessorSearch/Form/AbstractSearch.php +++ b/CRM/DataprocessorSearch/Form/AbstractSearch.php @@ -239,6 +239,7 @@ abstract class CRM_DataprocessorSearch_Form_AbstractSearch extends CRM_Dataproce $pagerParams['pageID'] = $pageId; $this->pager = new CRM_Utils_Pager($pagerParams); $this->assign('pager', $this->pager); + $this->controller->set('rowCount', $this->dataProcessorClass->getDataFlow()->recordCount()); $i=0; try { diff --git a/Civi/DataProcessor/DataFlow/SqlDataFlow.php b/Civi/DataProcessor/DataFlow/SqlDataFlow.php index f5142df0..60ddf3ed 100644 --- a/Civi/DataProcessor/DataFlow/SqlDataFlow.php +++ b/Civi/DataProcessor/DataFlow/SqlDataFlow.php @@ -277,4 +277,11 @@ abstract class SqlDataFlow extends AbstractDataFlow { ); } + /** + * @return \CRM_Core_DAO|null + */ + public function getDataObject() { + return $this->dao; + } + } \ No newline at end of file -- GitLab