dev/core#866, dev/core#1318 Fix failure to import checkboxes for activities
authoreileen <emcnaughton@wikimedia.org>
Fri, 4 Dec 2020 04:22:29 +0000 (17:22 +1300)
committereileen <emcnaughton@wikimedia.org>
Tue, 12 Jan 2021 07:30:49 +0000 (20:30 +1300)
This seeks to address a long-standing issue whereby checkboxes cannot be imported onto
activities if the name and value match.

Since this code was written the create was switched from a BAO create to the
activity api create. Simply removing the handling to prepare for the BAO create
allows the api to do it correctly

CRM/Activity/Import/Parser/Activity.php
CRM/Core/Session.php
api/v3/CustomField.php
tests/phpunit/CRM/Activity/Import/Parser/ActivityTest.php [new file with mode: 0644]
tests/phpunit/CRM/Core/BAO/CustomFieldTest.php
tests/phpunit/CRMTraits/Custom/CustomDataTrait.php

index e38432cff8e266bdeed869cd99239f2a4b76d259..86186d256cee6367f23ff512b4ca9d2baad62108 100644 (file)
@@ -278,11 +278,6 @@ class CRM_Activity_Import_Parser_Activity extends CRM_Activity_Import_Parser {
       return CRM_Import_Parser::ERROR;
     }
 
-    $params['custom'] = CRM_Core_BAO_CustomField::postProcess($params,
-      NULL,
-      'Activity'
-    );
-
     if ($this->_contactIdIndex < 0) {
 
       // Retrieve contact id using contact dedupe rule.
@@ -393,36 +388,12 @@ class CRM_Activity_Import_Parser_Activity extends CRM_Activity_Import_Parser {
     $fields = CRM_Activity_DAO_Activity::fields();
     _civicrm_api3_store_values($fields, $params, $values);
 
-    require_once 'CRM/Core/OptionGroup.php';
-    $customFields = CRM_Core_BAO_CustomField::getFields('Activity');
-
     foreach ($params as $key => $value) {
       // ignore empty values or empty arrays etc
       if (CRM_Utils_System::isNull($value)) {
         continue;
       }
 
-      //Handling Custom Data
-      if ($customFieldID = CRM_Core_BAO_CustomField::getKeyID($key)) {
-        $values[$key] = $value;
-        $type = $customFields[$customFieldID]['html_type'];
-        if (CRM_Core_BAO_CustomField::isSerialized($customFields[$customFieldID])) {
-          $values[$key] = CRM_Import_Parser::unserializeCustomValue($customFieldID, $value, $type);
-        }
-        elseif ($type == 'Select' || $type == 'Radio') {
-          $customOption = CRM_Core_BAO_CustomOption::getCustomOption($customFieldID, TRUE);
-          foreach ($customOption as $customFldID => $customValue) {
-            $val = $customValue['value'] ?? NULL;
-            $label = $customValue['label'] ?? NULL;
-            $label = strtolower($label);
-            $value = strtolower(trim($value));
-            if (($value == $label) || ($value == strtolower($val))) {
-              $values[$key] = $val;
-            }
-          }
-        }
-      }
-
       if ($key == 'target_contact_id') {
         if (!CRM_Utils_Rule::integer($value)) {
           return civicrm_api3_create_error("contact_id not valid: $value");
index 037efb1115b57af76c192a859c779c2b0e189b89..7ca6a46ead2587973f289598fc769743869e9f17 100644 (file)
@@ -52,9 +52,9 @@ class CRM_Core_Session {
    * We only need one instance of this object. So we use the singleton
    * pattern and cache the instance in this variable
    *
-   * @var object
+   * @var \CRM_Core_Session
    */
-  static private $_singleton = NULL;
+  static private $_singleton;
 
   /**
    * Constructor.
index 346a1ec2be98be0ed25717e7e9a7b196036c68fc..f8652419bee97ca74bfab766dd24175dc4d0e033 100644 (file)
@@ -69,6 +69,9 @@ function civicrm_api3_custom_field_create($params) {
  * Flush static caches in functions that might have stored available custom fields.
  */
 function _civicrm_api3_custom_field_flush_static_caches() {
+  if (isset(\Civi::$statics['CRM_Core_BAO_OptionGroup']['titles_by_name'])) {
+    unset(\Civi::$statics['CRM_Core_BAO_OptionGroup']['titles_by_name']);
+  }
   civicrm_api('CustomField', 'getfields', ['version' => 3, 'cache_clear' => 1]);
   CRM_Core_BAO_UFField::getAvailableFieldsFlat(TRUE);
 }
diff --git a/tests/phpunit/CRM/Activity/Import/Parser/ActivityTest.php b/tests/phpunit/CRM/Activity/Import/Parser/ActivityTest.php
new file mode 100644 (file)
index 0000000..99df179
--- /dev/null
@@ -0,0 +1,108 @@
+<?php
+
+/**
+ *  File for the TestActivityType class
+ *
+ *  (PHP 5)
+ *
+ * @package   CiviCRM
+ *
+ *   This file is part of CiviCRM
+ *
+ *   CiviCRM is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU Affero General Public License
+ *   as published by the Free Software Foundation; either version 3 of
+ *   the License, or (at your option) any later version.
+ *
+ *   CiviCRM is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU Affero General Public License for more details.
+ *
+ *   You should have received a copy of the GNU Affero General Public
+ *   License along with this program.  If not, see
+ *   <http://www.gnu.org/licenses/>.
+ */
+
+/**
+ *  Test CRM/Member/BAO Membership Log add , delete functions
+ *
+ * @package   CiviCRM
+ * @group headless
+ */
+class CRM_Activity_Import_Parser_ActivityTest extends CiviUnitTestCase {
+  use CRMTraits_Custom_CustomDataTrait;
+
+  /**
+   * Prepare for tests.
+   */
+  public function setUp():void {
+    parent::setUp();
+    $this->createLoggedInUser();
+  }
+
+  /**
+   * Clean up after test.
+   *
+   * @throws \CRM_Core_Exception
+   */
+  public function tearDown():void {
+    $this->quickCleanup(['civicrm_contact', 'civicrm_activity'], TRUE);
+    parent::tearDown();
+  }
+
+  /**
+   *  Test Import.
+   *
+   * So far this is just testing the class constructor & preparing for more
+   * tests.
+   *
+   * @throws \API_Exception
+   * @throws \CRM_Core_Exception
+   * @throws \CiviCRM_API3_Exception
+   */
+  public function testImport(): void {
+    $this->createCustomGroupWithFieldOfType(['extends' => 'Activity'], 'checkbox');
+    $values = [
+      'activity_detail' => 'fascinating',
+      'activity_type_id' => 1,
+      'activity_date_time' => '2010-01-06',
+      'target_contact_id' => $this->individualCreate(),
+      'subject' => 'riveting stuff',
+      $this->getCustomFieldName('checkbox') => 'L',
+    ];
+    $this->importValues($values);
+    $this->callAPISuccessGetSingle('Activity', [$this->getCustomFieldName('checkbox') => 'L']);
+  }
+
+  /**
+   * Create an import object.
+   *
+   * @param array $fields
+   *
+   * @return \CRM_Activity_Import_Parser_Activity
+   */
+  protected function createImportObject(array $fields): \CRM_Activity_Import_Parser_Activity {
+    $fieldMapper = [];
+    foreach ($fields as $index => $field) {
+      $fieldMapper[] = $field;
+    }
+    $importer = new CRM_Activity_Import_Parser_Activity($fieldMapper);
+    $importer->init();
+    return $importer;
+  }
+
+  /**
+   * Run the importer.
+   *
+   * @param array $values
+   * @param int $expectedOutcome
+   */
+  protected function importValues(array $values, $expectedOutcome = 1): void {
+    $importer = $this->createImportObject(array_keys($values));
+    $params = array_values($values);
+    CRM_Core_Session::singleton()->set('dateTypes', 1);
+    $this->assertEquals($expectedOutcome, $importer->import(NULL, $params));
+  }
+
+}
index 8ea4dad0cdbbeb2ce4021086b3104d98710cbe2b..8f543c95d0df33602793624acfe3e0402525932d 100644 (file)
@@ -618,7 +618,7 @@ class CRM_Core_BAO_CustomFieldTest extends CiviUnitTestCase {
         'extends_entity_column_id' => NULL,
         'is_view' => '0',
         'is_multiple' => '0',
-        'option_group_id' => $this->callAPISuccessGetValue('CustomField', ['id' => $this->getCustomFieldID('select_string'), 'return' => 'option_group_id']),
+        'option_group_id' => $this->getOptionGroupID('select_string'),
         'date_format' => NULL,
         'time_format' => NULL,
         'is_required' => 0,
@@ -877,6 +877,44 @@ class CRM_Core_BAO_CustomFieldTest extends CiviUnitTestCase {
           'callback' => 'CRM_Core_SelectValues::boolean',
         ],
       ],
+      $this->getCustomFieldName('checkbox') => [
+        'name' => $this->getCustomFieldName('checkbox'),
+        'custom_field_id' => $this->getCustomFieldID('checkbox'),
+        'id' => $this->getCustomFieldID('checkbox'),
+        'groupTitle' => 'Custom Group',
+        'default_value' => NULL,
+        'option_group_id' => $this->getOptionGroupID('checkbox'),
+        'custom_group_id' => $customGroupID,
+        'extends' => 'Contact',
+        'extends_entity_column_value' => NULL,
+        'extends_entity_column_id' => NULL,
+        'is_view' => '0',
+        'is_multiple' => '0',
+        'date_format' => NULL,
+        'time_format' => NULL,
+        'is_required' => 0,
+        'table_name' => 'civicrm_value_custom_group_' . $customGroupID,
+        'column_name' => $this->getCustomFieldColumnName('checkbox'),
+        'where' => 'civicrm_value_custom_group_' . $customGroupID . '.' . $this->getCustomFieldColumnName('checkbox'),
+        'extends_table' => 'civicrm_contact',
+        'search_table' => 'contact_a',
+        'import' => 1,
+        'label' => 'Pick Shade',
+        'headerPattern' => '//',
+        'title' => 'Pick Shade',
+        'data_type' => 'String',
+        'type' => 2,
+        'html_type' => 'CheckBox',
+        'text_length' => NULL,
+        'options_per_line' => NULL,
+        'is_search_range' => '0',
+        'serialize' => '1',
+        'pseudoconstant' => [
+          'optionGroupName' => $this->getOptionGroupName('checkbox'),
+          'optionEditPath' => 'civicrm/admin/options/' . $this->getOptionGroupName('checkbox'),
+
+        ],
+      ],
     ];
     $this->assertEquals($expected, CRM_Core_BAO_CustomField::getFieldsForImport());
   }
index 1e9ef5e4bfd33b14bd9bcd07e8aa98810eb94532..95cbb922f561ec904f87c45329bdfa0e4740ccc3 100644 (file)
@@ -92,10 +92,9 @@ trait CRMTraits_Custom_CustomDataTrait {
    *
    * @throws \API_Exception
    * @throws \CRM_Core_Exception
-   * @throws \Civi\API\Exception\UnauthorizedException
    */
-  public function createCustomGroupWithFieldOfType($groupParams = [], $customFieldType = 'text', $identifier = NULL, $fieldParams = []) {
-    $supported = ['text', 'select', 'date', 'int', 'contact_reference', 'radio', 'multi_country'];
+  public function createCustomGroupWithFieldOfType($groupParams = [], $customFieldType = 'text', $identifier = NULL, $fieldParams = []): void {
+    $supported = ['text', 'select', 'date', 'checkbox', 'int', 'contact_reference', 'radio', 'multi_country'];
     if (!in_array($customFieldType, $supported, TRUE)) {
       throw new CRM_Core_Exception('we have not yet extracted other custom field types from createCustomFieldsOfAllTypes, Use consistent syntax when you do');
     }
@@ -113,6 +112,10 @@ trait CRMTraits_Custom_CustomDataTrait {
         $reference = $this->createSelectCustomField($fieldParams)['id'];
         return;
 
+      case 'checkbox':
+        $reference = $this->createStringCheckboxCustomField($fieldParams)['id'];
+        return;
+
       case 'int':
         $reference = $this->createIntCustomField($fieldParams)['id'];
         return;
@@ -154,6 +157,7 @@ trait CRMTraits_Custom_CustomDataTrait {
     $ids['state'] = (int) $this->createStateCustomField(['custom_group_id' => $customGroupID])['id'];
     $ids['multi_state'] = (int) $this->createMultiStateCustomField(['custom_group_id' => $customGroupID])['id'];
     $ids['boolean'] = (int) $this->createBooleanCustomField(['custom_group_id' => $customGroupID])['id'];
+    $ids['checkbox'] = (int) $this->createStringCheckboxCustomField(['custom_group_id' => $customGroupID])['id'];
     return $ids;
   }
 
@@ -206,6 +210,34 @@ trait CRMTraits_Custom_CustomDataTrait {
     return $this->ids['CustomField'][$key];
   }
 
+  /**
+   * Get the option group id of the created field.
+   *
+   * @param string $key
+   *
+   * @return string
+   */
+  protected function getOptionGroupID(string $key): string {
+    return (string) $this->callAPISuccessGetValue('CustomField', [
+      'id' => $this->getCustomFieldID($key),
+      'return' => 'option_group_id',
+    ]);
+  }
+
+  /**
+   * Get the option group id of the created field.
+   *
+   * @param string $key
+   *
+   * @return string
+   */
+  protected function getOptionGroupName(string $key): string {
+    return (string) $this->callAPISuccessGetValue('CustomField', [
+      'id' => $this->getCustomFieldID($key),
+      'return' => 'option_group_id.name',
+    ]);
+  }
+
   /**
    * Create a custom text fields.
    *
@@ -374,6 +406,18 @@ trait CRMTraits_Custom_CustomDataTrait {
     return $this->callAPISuccess('custom_field', 'create', $params)['values'][0];
   }
 
+  /**
+   * Create a custom field of  type radio with integer values.
+   *
+   * @param array $params
+   *
+   * @return array
+   */
+  protected function createStringCheckboxCustomField(array $params): array {
+    $params = array_merge($this->getFieldsValuesByType('String', 'CheckBox'), $params);
+    return $this->callAPISuccess('custom_field', 'create', $params)['values'][0];
+  }
+
   /**
    * Create a custom field of  type radio with integer values.
    *
@@ -480,30 +524,36 @@ trait CRMTraits_Custom_CustomDataTrait {
           ],
         ],
         'CheckBox' => [
-          'label' => 'Pick Color',
+          'label' => 'Pick Shade',
           'html_type' => 'CheckBox',
           'data_type' => 'String',
           'text_length' => '',
           'default_value' => '',
           'option_values' => [
             [
-              'label' => 'Red',
-              'value' => 'R',
+              'label' => 'Lilac',
+              'value' => 'L',
               'weight' => 1,
               'is_active' => 1,
             ],
             [
-              'label' => 'Yellow',
-              'value' => 'Y',
+              'label' => 'Purple',
+              'value' => 'P',
               'weight' => 2,
               'is_active' => 1,
             ],
             [
-              'label' => 'Green',
-              'value' => 'G',
+              'label' => 'Mauve',
+              'value' => 'M',
               'weight' => 3,
               'is_active' => 1,
             ],
+            [
+              'label' => 'Violet',
+              'value' => 'V',
+              'weight' => 4,
+              'is_active' => 1,
+            ],
           ],
         ],
         'Multi-Select' => [