dev/core#3498 Fix mishandled option values
authorEileen McNaughton <emcnaughton@wikimedia.org>
Thu, 9 Jun 2022 03:18:48 +0000 (15:18 +1200)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Thu, 9 Jun 2022 04:21:01 +0000 (16:21 +1200)
CRM/Custom/Import/Parser/Api.php
CRM/Import/Parser.php
tests/phpunit/CRM/Contribute/Import/Parser/ContributionTest.php
tests/phpunit/CRM/Custom/Import/Parser/ApiTest.php [new file with mode: 0644]
tests/phpunit/CRMTraits/Import/ParserTrait.php
tests/phpunit/CiviTest/CiviUnitTestCase.php

index 0ab33abf73b0575f261c87d2e6297a50d0324604..a8a002084062aaed38bbf5bfedd4e3aa3b9fa3bc 100644 (file)
@@ -1,5 +1,6 @@
 <?php
 
+use Civi\Api4\CustomField;
 use Civi\Api4\CustomGroup;
 
 /**
@@ -175,35 +176,40 @@ class CRM_Custom_Import_Parser_Api extends CRM_Import_Parser {
    *
    * @return array
    *
+   * @noinspection PhpDocMissingThrowsInspection
+   * @noinspection PhpUnhandledExceptionInspection
    */
-  private function getGroupFieldsForImport($customGroupID) {
+  private function getGroupFieldsForImport(int $customGroupID): array {
     $importableFields = [];
-    $params = ['custom_group_id' => $customGroupID];
-    $group = CustomGroup::get(FALSE)->addSelect('extends')->addWhere('id', '=', $customGroupID)->execute()->first();
-    $allFields = civicrm_api3('custom_field', 'get', $params);
-    $fields = $allFields['values'];
-    foreach ($fields as $id => $values) {
+    $fields = (array) CustomField::get(FALSE)
+      ->addSelect('*', 'custom_group_id.is_multiple', 'custom_group_id.name', 'custom_group_id.extends')
+      ->addWhere('custom_group_id', '=', $customGroupID)->execute();
+
+    foreach ($fields as $values) {
       $datatype = $values['data_type'] ?? NULL;
       if ($datatype === 'File') {
         continue;
       }
       /* generate the key for the fields array */
-      $key = "custom_$id";
-      $regexp = preg_replace('/[.,;:!?]/', '', CRM_Utils_Array::value(0, $values));
+      $key = 'custom_' . $values['id'];
+      $regexp = preg_replace('/[.,;:!?]/', '', $values['label']);
       $importableFields[$key] = [
         'name' => $key,
         'title' => $values['label'] ?? NULL,
         'headerPattern' => '/' . preg_quote($regexp, '/') . '/',
         'import' => 1,
-        'custom_field_id' => $id,
-        'options_per_line' => $values['options_per_line'] ?? NULL,
-        'data_type' => $values['data_type'] ?? NULL,
-        'html_type' => $values['html_type'] ?? NULL,
+        'custom_field_id' => $values['id'],
+        'options_per_line' => $values['options_per_line'],
+        'data_type' => $values['data_type'],
+        'html_type' => $values['html_type'],
         'type' => CRM_Core_BAO_CustomField::dataToType()[$values['data_type']],
-        'is_search_range' => $values['is_search_range'] ?? NULL,
-        'date_format' => $values['date_format'] ?? NULL,
-        'time_format' => $values['time_format'] ?? NULL,
-        'extends' => $group['extends'],
+        'is_search_range' => $values['is_search_range'],
+        'date_format' => $values['date_format'],
+        'time_format' => $values['time_format'],
+        'extends' => $values['custom_group_id.extends'],
+        'custom_group_id' => $customGroupID,
+        'custom_group_id.name' => $values['custom_group_id.name'],
+        'is_multiple' => $values['custom_group_id.is_multiple'],
       ];
     }
     return $importableFields;
index 3f84b93ef7ae557c51061ec0c4c9c7b63da3147f..099e0afd5d1df29441f17c6339ff613732e3f713 100644 (file)
@@ -1441,19 +1441,28 @@ abstract class CRM_Import_Parser {
       }
       $optionFieldName = empty($fieldMap[$fieldName]) ? $fieldMetadata['name'] : $fieldName;
 
-      if (!empty($fieldMetadata['custom_group_id'])) {
-        $customField = CustomField::get(FALSE)
-          ->addWhere('id', '=', $fieldMetadata['custom_field_id'])
-          ->addSelect('name', 'custom_group_id.name')
-          ->execute()
-          ->first();
-        $optionFieldName = $customField['custom_group_id.name'] . '.' . $customField['name'];
-      }
-      $options = civicrm_api4($this->getFieldEntity($fieldName), 'getFields', [
-        'loadOptions' => ['id', 'name', 'label', 'abbr'],
-        'where' => [['name', '=', $optionFieldName]],
-        'select' => ['options'],
-      ])->first()['options'];
+      if (!empty($fieldMetadata['custom_field_id']) && !empty($fieldMetadata['is_multiple'])) {
+        $options = civicrm_api4('Custom_' . $fieldMetadata['custom_group_id.name'], 'getFields', [
+          'loadOptions' => ['id', 'name', 'label', 'abbr'],
+          'where' => [['custom_field_id', '=', $fieldMetadata['custom_field_id']]],
+          'select' => ['options'],
+        ])->first()['options'];
+      }
+      else {
+        if (!empty($fieldMetadata['custom_group_id'])) {
+          $customField = CustomField::get(FALSE)
+            ->addWhere('id', '=', $fieldMetadata['custom_field_id'])
+            ->addSelect('name', 'custom_group_id.name')
+            ->execute()
+            ->first();
+          $optionFieldName = $customField['custom_group_id.name'] . '.' . $customField['name'];
+        }
+        $options = civicrm_api4($this->getFieldEntity($fieldName), 'getFields', [
+          'loadOptions' => ['id', 'name', 'label', 'abbr'],
+          'where' => [['name', '=', $optionFieldName]],
+          'select' => ['options'],
+        ])->first()['options'];
+      }
       if (is_array($options)) {
         // We create an array of the possible variants - notably including
         // name AND label as either might be used. We also lower case before checking
index 19c11d37dc967d7fc518637f404ac68d101bea31..88cb41466116a0c6d0915f97f4092de75dbd2dfd 100644 (file)
@@ -26,11 +26,6 @@ class CRM_Contribute_Import_Parser_ContributionTest extends CiviUnitTestCase {
    */
   protected $entity = 'Contribution';
 
-  /**
-   * @var int
-   */
-  protected $userJobID;
-
   /**
    * Cleanup function.
    *
@@ -363,21 +358,49 @@ class CRM_Contribute_Import_Parser_ContributionTest extends CiviUnitTestCase {
   }
 
   /**
-   * @param array $mappings
+   * Get the import's datasource form.
    *
-   * @return array
+   * Defaults to contribution - other classes should override.
+   *
+   * @param array $submittedValues
+   *
+   * @return \CRM_Contribute_Import_Form_DataSource|\CRM_Core_Form|\CRM_Custom_Import_Form_DataSource
+   * @noinspection PhpUnnecessaryLocalVariableInspection
    */
-  protected function getMapperFromFieldMappings(array $mappings): array {
-    $mapper = [];
-    foreach ($mappings as $mapping) {
-      $fieldInput = [$mapping['name']];
-      if (!empty($mapping['soft_credit_type_id'])) {
-        $fieldInput[1] = $mapping['soft_credit_match_field'];
-        $fieldInput[2] = $mapping['soft_credit_type_id'];
-      }
-      $mapper[] = $fieldInput;
-    }
-    return $mapper;
+  protected function getDataSourceForm(array $submittedValues) {
+    return $this->getFormObject('CRM_Contribute_Import_Form_DataSource', $submittedValues);
+  }
+
+  /**
+   * Get the import's mapField form.
+   *
+   * Defaults to contribution - other classes should override.
+   *
+   * @param array $submittedValues
+   *
+   * @return \CRM_Contribute_Import_Form_MapField
+   * @noinspection PhpUnnecessaryLocalVariableInspection
+   */
+  protected function getMapFieldForm(array $submittedValues): CRM_Contribute_Import_Form_MapField {
+    /* @var \CRM_Contribute_Import_Form_MapField $form */
+    $form = $this->getFormObject('CRM_Contribute_Import_Form_MapField', $submittedValues);
+    return $form;
+  }
+
+  /**
+   * Get the import's preview form.
+   *
+   * Defaults to contribution - other classes should override.
+   *
+   * @param array $submittedValues
+   *
+   * @return \CRM_Contribute_Import_Form_Preview
+   * @noinspection PhpUnnecessaryLocalVariableInspection
+   */
+  protected function getPreviewForm(array $submittedValues): CRM_Contribute_Import_Form_Preview {
+    /* @var CRM_Contribute_Import_Form_Preview $form */
+    $form = $this->getFormObject('CRM_Contribute_Import_Form_Preview', $submittedValues);
+    return $form;
   }
 
 }
diff --git a/tests/phpunit/CRM/Custom/Import/Parser/ApiTest.php b/tests/phpunit/CRM/Custom/Import/Parser/ApiTest.php
new file mode 100644 (file)
index 0000000..05d261b
--- /dev/null
@@ -0,0 +1,90 @@
+<?php
+/**
+ * @file
+ * File for the CRM_Custom_Import_Parser_ContributionTest class.
+ */
+
+/**
+ *  Test Contribution import parser.
+ *
+ * @package CiviCRM
+ * @group headless
+ */
+class CRM_Custom_Import_Parser_ApiTest extends CiviUnitTestCase {
+
+  use CRMTraits_Custom_CustomDataTrait;
+  use CRMTraits_Import_ParserTrait;
+
+  /**
+   * Test the full form-flow import.
+   */
+  public function testImport(): void {
+    $this->individualCreate();
+    $this->createCustomGroupWithFieldOfType(['is_multiple' => TRUE, 'extends' => 'Contact'], 'select', 'level');
+    $customGroupID = $this->ids['CustomGroup']['level'];
+    $dateFieldID = $this->createDateCustomField(['date_format' => 'yy', 'custom_group_id' => $customGroupID])['id'];
+    $this->importCSV('custom_data_date_select.csv', [
+      ['name' => 'contact_id'],
+      ['name' => $this->getCustomFieldName('levelselect')],
+      ['name' => 'do_not_import'],
+      ['name' => 'custom_' . $dateFieldID],
+    ], ['multipleCustomData' => $customGroupID]);
+    $dataSource = new CRM_Import_DataSource_CSV($this->userJobID);
+    $row = $dataSource->getRow();
+    $this->assertEquals('IMPORTED', $row['_status']);
+    $row = $dataSource->getRow();
+    $this->assertEquals('IMPORTED', $row['_status']);
+    $row = $dataSource->getRow();
+    $this->assertEquals('ERROR', $row['_status']);
+  }
+
+
+  /**
+   * Get the import's datasource form.
+   *
+   * Defaults to contribution - other classes should override.
+   *
+   * @param array $submittedValues
+   *
+   * @return \CRM_Custom_Import_Form_DataSource
+   * @noinspection PhpUnnecessaryLocalVariableInspection
+   */
+  protected function getDataSourceForm(array $submittedValues): CRM_Custom_Import_Form_DataSource {
+    /* @var \CRM_Custom_Import_Form_DataSource $form */
+    $form = $this->getFormObject('CRM_Custom_Import_Form_DataSource', $submittedValues);
+    return $form;
+  }
+
+  /**
+   * Get the import's mapField form.
+   *
+   * Defaults to contribution - other classes should override.
+   *
+   * @param array $submittedValues
+   *
+   * @return \CRM_Custom_Import_Form_MapField
+   * @noinspection PhpUnnecessaryLocalVariableInspection
+   */
+  protected function getMapFieldForm(array $submittedValues): CRM_Custom_Import_Form_MapField {
+    /* @var \CRM_Custom_Import_Form_MapField $form */
+    $form = $this->getFormObject('CRM_Custom_Import_Form_MapField', $submittedValues);
+    return $form;
+  }
+
+  /**
+   * Get the import's preview form.
+   *
+   * Defaults to contribution - other classes should override.
+   *
+   * @param array $submittedValues
+   *
+   * @return \CRM_Custom_Import_Form_Preview
+   * @noinspection PhpUnnecessaryLocalVariableInspection
+   */
+  protected function getPreviewForm(array $submittedValues): CRM_Custom_Import_Form_Preview {
+    /* @var CRM_Custom_Import_Form_Preview $form */
+    $form = $this->getFormObject('CRM_Custom_Import_Form_Preview', $submittedValues);
+    return $form;
+  }
+
+}
index 4531d74ccb71b1199f2c99798947bc8c1b48ba68..8ed18f0d93ef70ef196b1107e820eb88b2e68360 100644 (file)
  */
 trait CRMTraits_Import_ParserTrait {
 
+  /**
+   * @var int
+   */
+  protected $userJobID;
+
   /**
    * Import the csv file values.
    *
@@ -41,9 +46,13 @@ trait CRMTraits_Import_ParserTrait {
       'groups' => [],
     ], $submittedValues);
     $form = $this->getDataSourceForm($submittedValues);
+    $values = $_SESSION['_' . $form->controller->_name . '_container']['values'];
     $form->buildForm();
     $form->postProcess();
     $this->userJobID = $form->getUserJobID();
+    // This gets reset in DataSource so re-do....
+    $_SESSION['_' . $form->controller->_name . '_container']['values'] = $values;
+
     $form = $this->getMapFieldForm($submittedValues);
     $form->setUserJobID($this->userJobID);
     $form->buildForm();
@@ -57,51 +66,21 @@ trait CRMTraits_Import_ParserTrait {
   }
 
   /**
-   * Get the import's datasource form.
-   *
-   * Defaults to contribution - other classes should override.
-   *
-   * @param array $submittedValues
-   *
-   * @return \CRM_Contribute_Import_Form_DataSource
-   * @noinspection PhpUnnecessaryLocalVariableInspection
-   */
-  protected function getDataSourceForm(array $submittedValues): CRM_Contribute_Import_Form_DataSource {
-    /* @var \CRM_Contribute_Import_Form_DataSource $form */
-    $form = $this->getFormObject('CRM_Contribute_Import_Form_DataSource', $submittedValues);
-    return $form;
-  }
-
-  /**
-   * Get the import's mapField form.
-   *
-   * Defaults to contribution - other classes should override.
-   *
-   * @param array $submittedValues
-   *
-   * @return \CRM_Contribute_Import_Form_MapField
-   * @noinspection PhpUnnecessaryLocalVariableInspection
-   */
-  protected function getMapFieldForm(array $submittedValues): CRM_Contribute_Import_Form_MapField {
-    /* @var \CRM_Contribute_Import_Form_MapField $form */
-    $form = $this->getFormObject('CRM_Contribute_Import_Form_MapField', $submittedValues);
-    return $form;
-  }
-
-  /**
-   * Get the import's preview form.
-   *
-   * Defaults to contribution - other classes should override.
-   *
-   * @param array $submittedValues
+   * @param array $mappings
    *
-   * @return \CRM_Contribute_Import_Form_Preview
-   * @noinspection PhpUnnecessaryLocalVariableInspection
+   * @return array
    */
-  protected function getPreviewForm(array $submittedValues): CRM_Contribute_Import_Form_Preview {
-    /* @var CRM_Contribute_Import_Form_Preview $form */
-    $form = $this->getFormObject('CRM_Contribute_Import_Form_Preview', $submittedValues);
-    return $form;
+  protected function getMapperFromFieldMappings(array $mappings): array {
+    $mapper = [];
+    foreach ($mappings as $mapping) {
+      $fieldInput = [$mapping['name']];
+      if (!empty($mapping['soft_credit_type_id'])) {
+        $fieldInput[1] = $mapping['soft_credit_match_field'];
+        $fieldInput[2] = $mapping['soft_credit_type_id'];
+      }
+      $mapper[] = $fieldInput;
+    }
+    return $mapper;
   }
 
 }
index dca1ecf07bc36f6d737dbf93de12b2254b2863d1..07baf4ed80e2d196f46a176e885193b938f914eb 100644 (file)
@@ -3279,6 +3279,18 @@ class CiviUnitTestCase extends PHPUnit\Framework\TestCase {
         $_SESSION['_' . $form->controller->_name . '_container']['values']['Preview'] = $formValues;
         return $form;
 
+      case 'CRM_Custom_Import_Form_DataSource':
+      case 'CRM_Custom_Import_Form_MapField':
+      case 'CRM_Custom_Import_Form_Preview':
+        $form->controller = new CRM_Custom_Import_Controller();
+        $form->controller->setStateMachine(new CRM_Core_StateMachine($form->controller));
+        // The submitted values should be set on one or the other of the forms in the flow.
+        // For test simplicity we set on all rather than figuring out which ones go where....
+        $_SESSION['_' . $form->controller->_name . '_container']['values']['DataSource'] = $formValues;
+        $_SESSION['_' . $form->controller->_name . '_container']['values']['MapField'] = $formValues;
+        $_SESSION['_' . $form->controller->_name . '_container']['values']['Preview'] = $formValues;
+        return $form;
+
       case strpos($class, '_Form_') !== FALSE:
         $form->controller = new CRM_Core_Controller_Simple($class, $pageName);
         break;