Fix assigning of customGroup data to be more re-usable
authorEileen McNaughton <emcnaughton@wikimedia.org>
Thu, 21 Sep 2023 22:05:49 +0000 (10:05 +1200)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Thu, 21 Sep 2023 23:57:15 +0000 (11:57 +1200)
CRM/Core/BAO/CustomField.php
CRM/Event/Form/Participant.php
tests/phpunit/CRM/Event/Form/ParticipantTest.php

index 8d4bf6559e2d4171f5d41dc6cd2efce87b3dd03f..79ef2f16f7d35da691be6cb7e667a09c57ff9da2 100644 (file)
@@ -615,6 +615,7 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
         ->addSelect('custom_group_id.extends_entity_column_value')
         ->addSelect('custom_group_id.is_active')
         ->addSelect('custom_group_id.name')
+        ->addSelect('custom_group_id.title')
         ->addSelect('custom_group_id.table_name')
         ->addSelect('custom_group_id.is_public');
       if ($permissionType && !CRM_Core_Permission::customGroupAdmin()) {
@@ -808,6 +809,67 @@ class CRM_Core_BAO_CustomField extends CRM_Core_DAO_CustomField {
     return $field;
   }
 
+  /**
+   * Gets an array of custom fields that are public.
+   *
+   * @internal do not use from untested or external code - signature may change.
+   *
+   * This takes into account
+   *  - the is_public setting on the Custom Group
+   *  - the is_view permission on the field (these are generally suppressed).
+   *
+   * @param string $extends
+   * @param array $extendsEntity
+   *   Keyed by name values from CRM_Core_BAO_CustomGroup::getExtendsEntityColumnIdOptions()
+   *   Values can be an int or an array.
+   *   eg ['ParticipantRole' = [1], 'ParticipantEventType' => 2], ['ParticipantEventName' => 3]
+   * @param int $permissionType
+   *
+   * @return array
+   * @throws \CRM_Core_Exception
+   */
+  public static function getViewableCustomFields(string $extends, array $extendsEntity = [], $permissionType = CRM_Core_Permission::VIEW): array {
+    $entityFilters = [];
+    // Convert from ['ParticipantRole' = [1], 'ParticipantEventType' => 2], ['ParticipantEventName' => 3]
+    // to [1 => [1], 2 => [2], 3 = [3]
+    if (!empty($extendsEntity)) {
+      $entityColumns = CRM_Core_BAO_CustomGroup::getExtendsEntityColumnIdOptions();
+      foreach ($entityColumns as $entityColumn) {
+        if (isset($extendsEntity[$entityColumn['name']])) {
+          $entityFilters[(int) $entityColumn['id']] = (array) $extendsEntity[$entityColumn['name']];
+          foreach ($entityFilters[$entityColumn['id']] as &$value) {
+            // Cast to string because we don't want the calling function to have to worry
+            // but also the array intersect fails otherwise.
+            $value = (string) $value;
+          }
+        }
+      }
+    }
+    $cacheKey = $extends . $permissionType . CRM_Core_Config::domainID() . '_' . CRM_Core_I18n::getLocale() . substr(md5(json_encode($extendsEntity)), 0, 30);
+    if (!isset(\Civi::$statics[__CLASS__][__FUNCTION__][$cacheKey])) {
+      \Civi::$statics[__CLASS__][__FUNCTION__][$cacheKey] = [];
+      $allFields = CRM_Core_BAO_CustomField::getAllCustomFields($permissionType);
+      foreach ($allFields as $field) {
+        $entityValueMatches = array_intersect((array) $field['custom_group_id.extends_entity_column_value'], ($entityFilters[$field['custom_group_id.extends_entity_column_id']] ?? []));
+        if (
+          !$field['is_view']
+          && $field['custom_group_id.is_public']
+          && (
+            !empty($entityValueMatches)
+            || empty($field['custom_group_id.extends_entity_column_id'])
+          )
+          && (
+            $field['custom_group_id.extends'] === $extends
+            || ($field['extends'] === 'Contact' && in_array($extends, ['Individual', 'Organization', 'Household']))
+          )
+        ) {
+          \Civi::$statics[__CLASS__][__FUNCTION__][$cacheKey][(int) $field['id']] = $field;
+        }
+      }
+    }
+    return \Civi::$statics[__CLASS__][__FUNCTION__][$cacheKey];
+  }
+
   /**
    * Add a custom field to an existing form.
    *
index 2881e299b415c4d90f7efbef0ad326465a10e520..5c07b6d36a9c5c5ec1ee61b0ad42b2ab8cb4667d 100644 (file)
@@ -2090,6 +2090,7 @@ INNER JOIN civicrm_price_field_value value ON ( value.id = lineItem.price_field_
    *
    * @return array
    * @throws \CRM_Core_Exception
+   * @throws \Brick\Money\Exception\UnknownCurrencyException
    */
   protected function sendReceipts($params, array $participants, $lineItem, $additionalParticipantDetails): array {
     $sent = [];
@@ -2133,40 +2134,19 @@ INNER JOIN civicrm_price_field_value value ON ( value.id = lineItem.price_field_
     }
 
     $customGroup = [];
-    //format submitted data
-    $customFieldsRole = [];
-    foreach ($this->getSubmittedValue('role_id') as $roleKey) {
-      $customFieldsRole = CRM_Utils_Array::crmArrayMerge(CRM_Core_BAO_CustomField::getFields('Participant',
-        FALSE, FALSE, $roleKey, $this->_roleCustomDataTypeID), $customFieldsRole);
-    }
-    $customFieldsEvent = CRM_Core_BAO_CustomField::getFields('Participant',
-      FALSE,
-      FALSE,
-      $this->getEventID(),
-      $this->_eventNameCustomDataTypeID
-    );
-    $customFieldsEventType = CRM_Core_BAO_CustomField::getFields('Participant',
-      FALSE,
-      FALSE,
-      $this->_eventTypeId,
-      $this->_eventTypeCustomDataTypeID
-    );
-    $customFields = CRM_Utils_Array::crmArrayMerge($customFieldsRole,
-      CRM_Core_BAO_CustomField::getFields('Participant', FALSE, FALSE, NULL, NULL, TRUE)
-    );
-    $customFields = CRM_Utils_Array::crmArrayMerge($customFieldsEvent, $customFields);
-    $customFields = CRM_Utils_Array::crmArrayMerge($customFieldsEventType, $customFields);
+    $customFieldFilters = [
+      'ParticipantRole' => $this->getSubmittedValue('role_id'),
+      'ParticipantEventName' => $this->getEventID(),
+      'ParticipantEventType' => $this->getEventValue('event_type_id'),
+    ];
+    $customFields = CRM_Core_BAO_CustomField::getViewableCustomFields('Participant', $customFieldFilters);
     foreach ($params['custom'] as $fieldID => $values) {
       foreach ($values as $fieldValue) {
-        $isPublic = CRM_Core_DAO::getFieldValue('CRM_Core_DAO_CustomGroup', $fieldValue['custom_group_id'], 'is_public');
-        if ($isPublic) {
-          $customFields[$fieldID]['id'] = $fieldID;
-          $formattedValue = CRM_Core_BAO_CustomField::displayValue($fieldValue['value'], $fieldID, $participants[0]->id);
-          $customGroup[$customFields[$fieldID]['groupTitle']][$customFields[$fieldID]['label']] = str_replace('&nbsp;', '', $formattedValue);
-        }
+        $formattedValue = CRM_Core_BAO_CustomField::displayValue($fieldValue['value'], $fieldID, $participants[0]->id);
+        $customGroup[$customFields[$fieldID]['custom_group_id.title']][$customFields[$fieldID]['label']] = str_replace('&nbsp;', '', $formattedValue);
       }
     }
-
+    $this->assign('customGroup', $customGroup);
     foreach ($this->_contactIds as $num => $contactID) {
       // Retrieve the name and email of the contact - this will be the TO for receipt email
       [, $this->_contributorEmail, $this->_toDoNotEmail] = CRM_Contact_BAO_Contact::getContactDetails($contactID);
@@ -2177,7 +2157,6 @@ INNER JOIN civicrm_price_field_value value ON ( value.id = lineItem.price_field_
         $this->assign('isOnWaitlist', TRUE);
       }
 
-      $this->assign('customGroup', $customGroup);
       $this->assign('contactID', $contactID);
       $this->assign('participantID', $participants[$num]->id);
 
index 84eefb9dcec2c3714e88ad939667984d2204649e..d4832245ed0a706f2f51255df6d807b812934cce 100644 (file)
@@ -21,6 +21,7 @@ class CRM_Event_Form_ParticipantTest extends CiviUnitTestCase {
   use FormTrait;
   use CRMTraits_Financial_OrderTrait;
   use CRMTraits_Financial_PriceSetTrait;
+  use CRMTraits_Custom_CustomDataTrait;
 
   public function tearDown(): void {
     $this->quickCleanUpFinancialEntities();
@@ -57,6 +58,19 @@ class CRM_Event_Form_ParticipantTest extends CiviUnitTestCase {
     $this->assertStringContainsString('Volunteer, Speaker', $email);
   }
 
+  public function testSubmitWithCustomData(): void {
+    $this->createCustomGroupWithFieldOfType(['extends' => 'Participant', 'extends_entity_column_id' => 1, 'extends_entity_column_value' => CRM_Core_PseudoConstant::getKey('CRM_Event_BAO_Participant', 'role_id', 'Volunteer')]);
+    $email = $this->getForm([], [
+      'status_id' => 1,
+      'register_date' => date('Ymd'),
+      'send_receipt' => 1,
+      'from_email_address' => 'admin@email.com',
+      'role_id' => [CRM_Core_PseudoConstant::getKey('CRM_Event_BAO_Participant', 'role_id', 'Volunteer')],
+      $this->getCustomFieldName() => 'Random thing',
+    ])->postProcess()->getFirstMailBody();
+    $this->assertStringContainsStrings($email, ['Enter text here', 'Random thing', 'Group with field text']);
+  }
+
   /**
    * Test financial items pending transaction is later altered.
    *