From 24a37dccebcebe2e28d3b1d0d0865d4c5c41a75a Mon Sep 17 00:00:00 2001
From: Jaap Jansma <jaap.jansma@civicoop.org>
Date: Thu, 6 Apr 2023 11:28:15 +0200
Subject: [PATCH] Fixed regression bug in contact search.

---
 CHANGELOG.md                                  |   1 +
 .../Controller/DataProcessorContactSearch.php | 102 +++---------------
 .../Form/DataProcessorContactSearch.php       |   1 +
 .../DataProcessorContactSearch.php            |  92 ----------------
 .../Form/OutputExport.php                     |   4 +-
 .../Form/AbstractSearch.php                   |   2 +-
 .../Form/Task/SearchActionDesigner.php        |   2 +-
 7 files changed, 22 insertions(+), 182 deletions(-)
 delete mode 100644 CRM/Contact/StateMachine/DataProcessorContactSearch.php

diff --git a/CHANGELOG.md b/CHANGELOG.md
index e25e6068..1d394845 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,7 @@
 # Version 1.67 (not yet released)
 
 * Added filter Total Contribution Amount in Period.
+* Fixed regression bug in contact search.
 
 # Version 1.66
 
diff --git a/CRM/Contact/Controller/DataProcessorContactSearch.php b/CRM/Contact/Controller/DataProcessorContactSearch.php
index d68ae278..63e2e256 100644
--- a/CRM/Contact/Controller/DataProcessorContactSearch.php
+++ b/CRM/Contact/Controller/DataProcessorContactSearch.php
@@ -14,71 +14,16 @@
  *
  * The second form is used to process search results with the associated actions.
  */
-class CRM_Contact_Controller_DataProcessorContactSearch extends CRM_Core_Controller {
-
-  protected $dataProcessor;
+class CRM_Contact_Controller_DataProcessorContactSearch extends CRM_DataprocessorSearch_Controller_Search {
 
   /**
-   * Setter function to set the data porcessor
-   *
-   * @param $dataProcessorName
-   * @param $dataProcessor
-   */
-  public function setDataProcessor($dataProcessorName, $dataProcessor) {
-    $this->dataProcessor = $dataProcessor;
-  }
-
-  /**
-   * Class constructor.
-   *
-   * @param string $title
+   * @param string|null $title
    * @param bool $modal
-   * @param int|mixed|null $action
+   * @param int|mixed $action
    */
   public function __construct($title = NULL, $modal = TRUE, $action = CRM_Core_Action::NONE) {
-    parent::__construct($title, $modal);
-
-    $this->_stateMachine = new CRM_Contact_StateMachine_DataProcessorContactSearch($this, $action);
-
-    // create and instantiate the pages
-    $this->addPages($this->_stateMachine, $action);
+    parent::__construct($title, $modal, $action);
     $this->set('entity', 'Contact');
-
-    // add all the actions
-    $this->addActions();
-  }
-
-  /**
-   * Process the request, overrides the default QFC run method
-   * This routine actually checks if the QFC is modal and if it
-   * is the first invalid page, if so it call the requested action
-   * if not, it calls the display action on the first invalid page
-   * avoids the issue of users hitting the back button and getting
-   * a broken page
-   *
-   * This run is basically a composition of the original run and the
-   * jump action
-   *
-   * @return mixed
-   */
-  public function run() {
-
-    $actionName = $this->getActionName();
-    list($pageName, $action) = $actionName;
-    // Hack to replace to userContext for redirecting after a Task has been completed.
-    // We want the redirect
-    if (!$this->_pages[$pageName] instanceof CRM_DataprocessorSearch_Form_ContactSearch) {
-      CRM_DataprocessorSearch_Form_Search_Custom_DataprocessorSmartGroupIntegration::setDataProcessorAndFormValues('contact_search', $this->get('formValues'), CRM_Utils_System::currentPath());
-      $qfKey = CRM_Utils_Request::retrieve('qfKey', 'String', $this);
-      $urlPath = CRM_Utils_System::currentPath();
-      $urlParams = 'force=1';
-      if ($qfKey) {
-        $urlParams .= "&qfKey=$qfKey";
-      }
-      $this->setDestination(CRM_Utils_System::url($urlPath, $urlParams));
-    }
-
-    return parent::run();
   }
 
   /**
@@ -88,37 +33,22 @@ class CRM_Contact_Controller_DataProcessorContactSearch extends CRM_Core_Control
     return 'CRM_Contact_Selector_DataProcessorContactSearch';
   }
 
-  /**
-   * @param string $name
-   * @param bool $addSequence
-   * @param bool $ignoreKey
-   *
-   * @return mixed|string
-   */
-  public function key($name, $addSequence = FALSE, $ignoreKey = FALSE) {
-    $config = CRM_Core_Config::singleton();
 
-    if (
-      $ignoreKey ||
-      (isset($config->keyDisable) && $config->keyDisable)
-    ) {
-      return NULL;
-    }
 
-    $key = CRM_Utils_Array::value('qfKey', $_REQUEST, NULL);
-    if (!$key && $_SERVER['REQUEST_METHOD'] === 'GET') {
-      $key = CRM_Core_Key::get($name, $addSequence);
-    }
-    else if (!$_SERVER['REQUEST_METHOD'] === 'POST' || !isset($_REQUEST['ssId'])) {
-      $_key = CRM_Core_Key::validate($key, $name, $addSequence);
-    }
-    if (!$key) {
-      $this->invalidKey();
-    }
+  public function getSearchName(): string {
+    return 'Basic';
+  }
 
-    $this->_key = $key;
+  public function getSearchClass(): string {
+    return 'CRM_Contact_Form_DataProcessorContactSearch';
+  }
 
-    return $key;
+  public function getTaskClass(string $selectedTask):? string {
+    [$task] = CRM_Contact_Task::getTask($selectedTask);
+    if ($task) {
+      return $task;
+    }
+    return null;
   }
 
 }
diff --git a/CRM/Contact/Form/DataProcessorContactSearch.php b/CRM/Contact/Form/DataProcessorContactSearch.php
index e593e48e..4e5f9046 100644
--- a/CRM/Contact/Form/DataProcessorContactSearch.php
+++ b/CRM/Contact/Form/DataProcessorContactSearch.php
@@ -156,4 +156,5 @@ class CRM_Contact_Form_DataProcessorContactSearch extends CRM_DataprocessorSearc
     return $this->_taskList;
   }
 
+
 }
diff --git a/CRM/Contact/StateMachine/DataProcessorContactSearch.php b/CRM/Contact/StateMachine/DataProcessorContactSearch.php
deleted file mode 100644
index bc1b6794..00000000
--- a/CRM/Contact/StateMachine/DataProcessorContactSearch.php
+++ /dev/null
@@ -1,92 +0,0 @@
-<?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_Contact_Form_DataProcessorContactSearch',
-    );
-    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/DataprocessorOutputExport/Form/OutputExport.php b/CRM/DataprocessorOutputExport/Form/OutputExport.php
index 3617237b..f5bddc09 100644
--- a/CRM/DataprocessorOutputExport/Form/OutputExport.php
+++ b/CRM/DataprocessorOutputExport/Form/OutputExport.php
@@ -63,7 +63,7 @@ class CRM_DataprocessorOutputExport_Form_OutputExport extends CRM_Core_Form {
 
   protected function setDataProcessorSearchForm(CRM_DataprocessorSearch_Form_AbstractSearch $form) {
     $this->searchForm = $form;
-    $this->searchValues = $this->controller->exportValues('Search');
+    $this->searchValues = $this->controller->exportValues($this->controller->getSearchName());
     $this->dataProcessorClass = $form->getDataProcessorClass();
     $this->dataProcessor = $form->getDataProcessor();
     $this->idFieldName = $form->getIdFieldName();
@@ -101,7 +101,7 @@ class CRM_DataprocessorOutputExport_Form_OutputExport extends CRM_Core_Form {
     $export_id = $this->controller->get('export_id');
     $factory = dataprocessor_get_factory();
 
-    $dataProcessorSearchForm = $this->controller->getPage('Search');
+    $dataProcessorSearchForm = $this->controller->getPage($this->controller->getSearchName());
     if ($dataProcessorSearchForm instanceof CRM_DataprocessorSearch_Form_AbstractSearch) {
       $this->setDataProcessorSearchForm($dataProcessorSearchForm);
       try {
diff --git a/CRM/DataprocessorSearch/Form/AbstractSearch.php b/CRM/DataprocessorSearch/Form/AbstractSearch.php
index 5b3bdfb9..0bda42ec 100644
--- a/CRM/DataprocessorSearch/Form/AbstractSearch.php
+++ b/CRM/DataprocessorSearch/Form/AbstractSearch.php
@@ -628,7 +628,7 @@ abstract class CRM_DataprocessorSearch_Form_AbstractSearch extends CRM_Dataproce
    */
   public function getSelectionCount(): int {
     // We use ajax to handle selections only if the search results component_mode is set to "contacts"
-    if ($this->usePrevNextCache()) {
+    if ($this->usePrevNextCache() && (!isset($this->_formValues['radio_ts']) || $this->_formValues['radio_ts'] != 'ts_all')) {
       return count($this->getSelectedIds());
     } else {
       if (isset($this->_formValues['radio_ts']) && $this->_formValues['radio_ts'] == 'ts_sel') {
diff --git a/CRM/DataprocessorSearch/Form/Task/SearchActionDesigner.php b/CRM/DataprocessorSearch/Form/Task/SearchActionDesigner.php
index becef4bc..fee2c54f 100644
--- a/CRM/DataprocessorSearch/Form/Task/SearchActionDesigner.php
+++ b/CRM/DataprocessorSearch/Form/Task/SearchActionDesigner.php
@@ -22,7 +22,7 @@ class CRM_DataprocessorSearch_Form_Task_SearchActionDesigner extends CRM_Searcha
     $session->replaceUserContext($url);
 
     $selectionCount = 0;
-    $dataProcessorSearchForm = $this->controller->getPage('Search');
+    $dataProcessorSearchForm = $this->controller->getPage($this->controller->getSearchName());
     if ($dataProcessorSearchForm instanceof CRM_DataprocessorSearch_Form_AbstractSearch) {
       $this->searchForm = $dataProcessorSearchForm;
       $selectionCount = $this->searchForm->getSelectionCount();
-- 
GitLab