Merge pull request #15790 from civicrm/5.20
[civicrm-core.git] / CRM / Core / Form / Search.php
index 7fab1da759fb27a1949f9767420c11dfa9982948..c4ea8a259940579fc321911d818dbf5880350c05 100644 (file)
@@ -3,7 +3,7 @@
  +--------------------------------------------------------------------+
  | CiviCRM version 5                                                  |
  +--------------------------------------------------------------------+
- | Copyright CiviCRM LLC (c) 2004-2019                                |
+ | Copyright CiviCRM LLC (c) 2004-2020                                |
  +--------------------------------------------------------------------+
  | This file is a part of CiviCRM.                                    |
  |                                                                    |
@@ -70,7 +70,7 @@ class CRM_Core_Form_Search extends CRM_Core_Form {
    *
    * @var string
    */
-  protected $_context = NULL;
+  protected $_context;
 
   /**
    * The list of tasks or actions that a searcher can perform on a result set.
@@ -124,15 +124,32 @@ class CRM_Core_Form_Search extends CRM_Core_Form {
     $this->searchFieldMetadata = array_merge($this->searchFieldMetadata, $searchFieldMetadata);
   }
 
+  /**
+   * Prepare for search by loading options from the url, handling force searches, retrieving form values.
+   *
+   * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
+   */
+  public function preProcess() {
+    $this->loadStandardSearchOptionsFromUrl();
+    if ($this->_force) {
+      $this->handleForcedSearch();
+    }
+    $this->_formValues = $this->getFormValues();
+  }
+
   /**
    * This virtual function is used to set the default values of various form elements.
    *
    * @return array|NULL
    *   reference to the array of default values
-   * @throws \Exception
+   * @throws \CRM_Core_Exception
    */
   public function setDefaultValues() {
-    $defaults = (array) $this->_formValues;
+    // Use the form values stored to the form. Ideally 'formValues'
+    // would remain 'pure' & another array would be wrangled.
+    // We don't do that - so we want the version of formValues stored early on.
+    $defaults = (array) $this->get('formValues');
     foreach (array_keys($this->getSearchFieldMetadata()) as $entity) {
       $defaults = array_merge($this->getEntityDefaults($entity), $defaults);
     }
@@ -145,17 +162,15 @@ class CRM_Core_Form_Search extends CRM_Core_Form {
    * @throws \Exception
    */
   protected function setFormValues() {
-    if (!empty($_POST) && !$this->_force) {
-      $this->_formValues = $this->controller->exportValues($this->_name);
-    }
-    elseif ($this->_force) {
-      $this->_formValues = $this->setDefaultValues();
-    }
+    $this->_formValues = $this->getFormValues();
+    $this->set('formValues', $this->_formValues);
     $this->convertTextStringsToUseLikeOperator();
   }
 
   /**
    * Common buildForm tasks required by all searches.
+   *
+   * @throws \CRM_Core_Exception
    */
   public function buildQuickForm() {
     CRM_Core_Resources::singleton()
@@ -181,6 +196,8 @@ class CRM_Core_Form_Search extends CRM_Core_Form {
    *
    * The goal is to describe all fields in metadata and handle from metadata rather
    * than existing ad hoc handling.
+   *
+   * @throws \CiviCRM_API3_Exception
    */
   public function addFormFieldsFromMetadata() {
     $this->addFormRule(['CRM_Core_Form_Search', 'formRule'], $this);
@@ -188,9 +205,9 @@ class CRM_Core_Form_Search extends CRM_Core_Form {
     foreach ($this->getSearchFieldMetadata() as $entity => $fields) {
       foreach ($fields as $fieldName => $fieldSpec) {
         $fieldType = $fieldSpec['type'] ?? '';
-        if ($fieldType === CRM_Utils_Type::T_DATE || $fieldType === (CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME)) {
+        if ($fieldType === CRM_Utils_Type::T_DATE || $fieldType === (CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME) || $fieldType === CRM_Utils_Type::T_TIMESTAMP) {
           $title = empty($fieldSpec['unique_title']) ? $fieldSpec['title'] : $fieldSpec['unique_title'];
-          $this->addDatePickerRange($fieldName, $title, ($fieldType === (CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME)));
+          $this->addDatePickerRange($fieldName, $title, ($fieldType === (CRM_Utils_Type::T_DATE + CRM_Utils_Type::T_TIME) || $fieldType === CRM_Utils_Type::T_TIMESTAMP));
         }
         else {
           // Not quite sure about moving to a mix of keying by entity vs permitting entity to
@@ -365,6 +382,8 @@ class CRM_Core_Form_Search extends CRM_Core_Form {
    *
    * Note that for translation purposes the full string works better than using 'prefix' hence we use override-able functions
    * to define the string.
+   *
+   * @throws \CiviCRM_API3_Exception
    */
   protected function addSortNameField() {
     $title = civicrm_api3('setting', 'getvalue', ['name' => 'includeEmailInName', 'group' => 'Search Preferences']) ? $this->getSortNameLabelWithEmail() : $this->getSortNameLabelWithOutEmail();
@@ -408,6 +427,8 @@ class CRM_Core_Form_Search extends CRM_Core_Form {
 
   /**
    * Add generic fields that specify the contact.
+   *
+   * @throws \CiviCRM_API3_Exception
    */
   protected function addContactSearchFields() {
     if (!$this->isFormInViewOrEditMode()) {
@@ -447,6 +468,8 @@ class CRM_Core_Form_Search extends CRM_Core_Form {
   /**
    * we allow the controller to set force/reset externally, useful when we are being
    * driven by the wizard framework
+   *
+   * @throws \CRM_Core_Exception
    */
   protected function loadStandardSearchOptionsFromUrl() {
     $this->_reset = CRM_Utils_Request::retrieve('reset', 'Boolean');
@@ -477,4 +500,61 @@ class CRM_Core_Form_Search extends CRM_Core_Form {
     }
   }
 
+  /**
+   * Get the form values.
+   *
+   * @todo consolidate with loadFormValues()
+   *
+   * @return array
+   *
+   * @throws \CRM_Core_Exception
+   */
+  protected function getFormValues() {
+    if (!empty($_POST) && !$this->_force) {
+      return $this->controller->exportValues($this->_name);
+    }
+    if ($this->_force) {
+      return $this->setDefaultValues();
+    }
+    return (array) $this->get('formValues');
+  }
+
+  /**
+   * Get the string processed to determine sort order.
+   *
+   * This looks like 'sort_name_u' for Sort name ascending.
+   *
+   * @return string|null
+   */
+  protected function getSortID() {
+    if ($this->get(CRM_Utils_Sort::SORT_ID)) {
+      return CRM_Utils_Sort::sortIDValue($this->get(CRM_Utils_Sort::SORT_ID),
+        $this->get(CRM_Utils_Sort::SORT_DIRECTION)
+      );
+    }
+    return NULL;
+  }
+
+  /**
+   * Set the metadata for the form.
+   *
+   * @throws \CiviCRM_API3_Exception
+   */
+  protected function setSearchMetadata() {}
+
+  /**
+   * Handle force=1 in the url.
+   *
+   * Search field metadata is normally added in buildForm but we are bypassing that in this flow
+   * (I've always found the flow kinda confusing & perhaps that is the problem but this mitigates)
+   *
+   * @throws \CiviCRM_API3_Exception
+   */
+  protected function handleForcedSearch() {
+    $this->setSearchMetadata();
+    $this->addContactSearchFields();
+    $this->postProcess();
+    $this->set('force', 0);
+  }
+
 }