Convert event badges to use token processor
authorEileen McNaughton <emcnaughton@wikimedia.org>
Sat, 18 Sep 2021 22:49:29 +0000 (10:49 +1200)
committerEileen McNaughton <emcnaughton@wikimedia.org>
Mon, 20 Sep 2021 23:21:21 +0000 (11:21 +1200)
This adds the token processor class for participants and
switches the one place where we currently resolve participant
tokens to use it.

In the process I upgrade the badge tokens with a syntax change and
drop a few more that are actually
a) weird / seemingly accidentally exposed and
b) not really part of the participant entity

ie event template title & default role id

With participant tokens we have been fortunate not
to let a non-standard implementation go out
so it's only the event badges which are
a bit obscure / less used / less likely to be
blindly used. I have added token replacement
but I think it will be all very edge case

CRM/Badge/BAO/Badge.php
CRM/Core/EntityTokens.php
CRM/Core/SelectValues.php
CRM/Event/ParticipantTokens.php [new file with mode: 0644]
CRM/Upgrade/Incremental/php/FiveFortyThree.php
Civi/Core/Container.php
tests/phpunit/CRM/Event/Form/Task/BadgeTest.php
tests/phpunit/CRM/Utils/TokenConsistencyTest.php

index e501e95ff7515f35933e7b46b94769b61760276c..adb8dd791c220a5bfb6a33b94a7d0f596916b08c 100644 (file)
@@ -15,6 +15,8 @@
  * @copyright CiviCRM LLC https://civicrm.org/licensing
  */
 
+use Civi\Token\TokenProcessor;
+
 /**
  * Class CRM_Badge_Format_Badge.
  *
@@ -87,7 +89,7 @@ class CRM_Badge_BAO_Badge {
         if ($element) {
           $value = $row[$element];
           // hack to fix date field display format
-          if (strpos($element, '_date')) {
+          if (in_array($element, ['event_start_date', 'event_end_date'], TRUE)) {
             $value = CRM_Utils_Date::customFormat($value, "%B %E%f");
           }
         }
@@ -369,7 +371,7 @@ class CRM_Badge_BAO_Badge {
     $this->imgRes = 300;
 
     if ($img) {
-      list($w, $h) = self::getImageProperties($img, $this->imgRes, $w, $h);
+      [$w, $h] = self::getImageProperties($img, $this->imgRes, $w, $h);
       $this->pdf->Image($img, $x, $y, $w, $h, '', '', '', FALSE, 72, '', FALSE,
         FALSE, $this->debug, FALSE, FALSE, FALSE);
     }
@@ -402,39 +404,36 @@ class CRM_Badge_BAO_Badge {
   public static function buildBadges(&$params, &$form) {
     // get name badge layout info
     $layoutInfo = CRM_Badge_BAO_Layout::buildLayout($params);
-
+    $tokenProcessor = new TokenProcessor(\Civi::dispatcher(), ['schema' => ['participantId'], 'smarty' => FALSE]);
     // split/get actual field names from token and individual contact image URLs
-    $returnProperties = [];
+    $returnProperties = $processorTokens = [];
     if (!empty($layoutInfo['data']['token'])) {
       foreach ($layoutInfo['data']['token'] as $index => $value) {
-        $element = '';
         if ($value) {
           $token = CRM_Utils_Token::getTokens($value);
-          if (key($token) == 'contact') {
-            $element = $token['contact'][0];
-          }
-          elseif (key($token) == 'event') {
+          if (strpos($value, '{event.') === 0) {
             $element = $token['event'][0];
             //FIX ME - we need to standardize event token names
             if (substr($element, 0, 6) != 'event_') {
+              // legacy style.
               $element = 'event_' . $element;
+              $returnProperties[$element] = 1;
+              // add actual field name to row element
+              $layoutInfo['data']['rowElements'][$index] = $element;
             }
           }
-          elseif (key($token) == 'participant') {
-            $element = $token['participant'][0];
+          else {
+            $tokenName = str_replace(['}', '{contact.', '{participant.'], '', $value);
+            $tokenProcessor->addMessage($tokenName, $value, 'text/plain');
+            $processorTokens[] = $tokenName;
+            $layoutInfo['data']['rowElements'][$index] = $tokenName;
           }
-
-          // build returnproperties for query
-          $returnProperties[$element] = 1;
         }
-
-        // add actual field name to row element
-        $layoutInfo['data']['rowElements'][$index] = $element;
       }
     }
 
     // add additional required fields for query execution
-    $additionalFields = ['participant_register_date', 'participant_id', 'event_id', 'contact_id', 'image_URL'];
+    $additionalFields = ['participant_id', 'event_id', 'contact_id'];
     foreach ($additionalFields as $field) {
       $returnProperties[$field] = 1;
     }
@@ -450,7 +449,7 @@ class CRM_Badge_BAO_Badge {
       CRM_Contact_BAO_Query::MODE_EVENT
     );
 
-    list($select, $from, $where, $having) = $query->query();
+    [$select, $from, $where, $having] = $query->query();
     if (empty($where)) {
       $where = "WHERE {$form->_componentClause}";
     }
@@ -469,16 +468,19 @@ class CRM_Badge_BAO_Badge {
 
     $dao = CRM_Core_DAO::executeQuery($queryString);
     $rows = [];
+
     while ($dao->fetch()) {
-      $query->convertToPseudoNames($dao);
+      $tokenProcessor->addRow(['contactId' => $dao->contact_id, 'participantId' => $dao->participant_id]);
       $rows[$dao->participant_id] = [];
       foreach ($returnProperties as $key => $dontCare) {
-        $value = $dao->$key ?? NULL;
-        // Format custom fields
-        if (strstr($key, 'custom_') && isset($value)) {
-          $value = CRM_Core_BAO_CustomField::displayValue($value, substr($key, 7), $dao->contact_id);
-        }
-        $rows[$dao->participant_id][$key] = $value;
+        // we are now only resolving the 4 event tokens here.
+        $rows[$dao->participant_id][$key] = $dao->$key ?? NULL;
+      }
+    }
+    $tokenProcessor->evaluate();
+    foreach ($tokenProcessor->getRows() as $row) {
+      foreach ($processorTokens as $processorToken) {
+        $rows[$row->context['participantId']][$processorToken] = $row->render($processorToken);
       }
     }
 
index d879efdc73c711efa3eeea4917acd1b5a6bbe11f..cc9350bb1374a9f9a1021c230b1c2e80d080ac68 100644 (file)
@@ -40,6 +40,10 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
   public function evaluateToken(TokenRow $row, $entity, $field, $prefetch = NULL) {
     $this->prefetch = (array) $prefetch;
     $fieldValue = $this->getFieldValue($row, $field);
+    if (is_array($fieldValue)) {
+      // eg. role_id for participant would be an array here.
+      $fieldValue = implode(',', $fieldValue);
+    }
 
     if ($this->isPseudoField($field)) {
       if (!empty($fieldValue)) {
index 2acad7d0b5fe78bf84fa9bd4c72b598797075464..b0e5f24d51ef024711c3d463180b1e2085a5c9d8 100644 (file)
@@ -594,18 +594,16 @@ class CRM_Core_SelectValues {
    */
   public static function participantTokens(): array {
     $tokens = [
-      '{participant.participant_status_id}' => 'Status ID',
-      '{participant.participant_role_id}' => 'Participant Role (ID)',
-      '{participant.participant_register_date}' => 'Register date',
-      '{participant.participant_source}' => 'Participant Source',
-      '{participant.participant_fee_level}' => 'Fee level',
-      '{participant.participant_fee_amount}' => 'Fee Amount',
-      '{participant.participant_registered_by_id}' => 'Registered By Participant ID',
+      '{participant.status_id}' => 'Status ID',
+      '{participant.role_id}' => 'Participant Role (ID)',
+      '{participant.register_date}' => 'Register date',
+      '{participant.source}' => 'Participant Source',
+      '{participant.fee_level}' => 'Fee level',
+      '{participant.fee_amount}' => 'Fee Amount',
+      '{participant.registered_by_id}' => 'Registered By Participant ID',
       '{participant.transferred_to_contact_id}' => 'Transferred to Contact ID',
-      '{participant.participant_role}' => 'Participant Role (label)',
+      '{participant.role_id:label}' => 'Participant Role (label)',
       '{participant.fee_label}' => 'Fee Label',
-      '{participant.default_role_id}' => 'Default Role',
-      '{participant.template_title}' => 'Event Template Title',
     ];
     $customFields = CRM_Core_BAO_CustomField::getFields('Participant');
 
diff --git a/CRM/Event/ParticipantTokens.php b/CRM/Event/ParticipantTokens.php
new file mode 100644 (file)
index 0000000..717b117
--- /dev/null
@@ -0,0 +1,36 @@
+<?php
+
+/*
+ +--------------------------------------------------------------------+
+ | Copyright CiviCRM LLC. All rights reserved.                        |
+ |                                                                    |
+ | This work is published under the GNU AGPLv3 license with some      |
+ | permitted exceptions and without any warranty. For full license    |
+ | and copyright information, see https://civicrm.org/licensing       |
+ +--------------------------------------------------------------------+
+ */
+
+/**
+ * Class CRM_Event_ParticipantTokens
+ *
+ * Generate "participant.*" tokens.
+ */
+class CRM_Event_ParticipantTokens extends CRM_Core_EntityTokens {
+
+  /**
+   * Get the entity name for api v4 calls.
+   *
+   * @return string
+   */
+  protected function getApiEntityName(): string {
+    return 'Participant';
+  }
+
+  /**
+   * @return array
+   */
+  public function getCurrencyFieldName(): array {
+    return ['fee_currency'];
+  }
+
+}
index 624174ff0ee67ef070f625334aebf020ae42d303..7d022f943738f17ec758c95f44a758d56056b1e0 100644 (file)
@@ -83,6 +83,31 @@ class CRM_Upgrade_Incremental_php_FiveFortyThree extends CRM_Upgrade_Incremental
     $this->addTask('Replace duplicate event end date token in event badges',
       'updatePrintLabelToken', 'participant.event_end_date', 'event.end_date', $rev
     );
+    $this->addTask('Update participant status id token in event badges',
+      'updatePrintLabelToken', 'participant.participant_status_id', 'participant.status_id', $rev
+    );
+    $this->addTask('Update participant role id token in event badges',
+      'updatePrintLabelToken', 'participant.participant_role_id', 'participant.role_id', $rev
+    );
+    $this->addTask('Update participant role label token in event badges',
+      'updatePrintLabelToken', 'participant.participant_role', 'participant.role_id:label', $rev
+    );
+    $this->addTask('Update participant register date token in event badges',
+      'updatePrintLabelToken', 'participant.participant_register_date', 'participant.register_date', $rev
+    );
+    $this->addTask('Update participant source token in event badges',
+      'updatePrintLabelToken', 'participant.participant_source', 'participant.source', $rev
+    );
+    $this->addTask('Update participant fee level token in event badges',
+      'updatePrintLabelToken', 'participant.participant_fee_level', 'participant.fee_level', $rev
+    );
+    $this->addTask('Update participant fee amount token in event badges',
+      'updatePrintLabelToken', 'participant.participant_fee_amount', 'participant.fee_amount', $rev
+    );
+    $this->addTask('Update participant registered by id token in event badges',
+      'updatePrintLabelToken', 'participant.participant_registered_by_id', 'participant.registered_by_id', $rev
+    );
+
   }
 
   /**
index 5c74d402bc60f9c2d01da3a547d1b685721036aa..69f05d1bec1718f1ff4bc40633ce1bc5bba34f2b 100644 (file)
@@ -333,6 +333,10 @@ class Container {
         []
       ))->addTag('kernel.event_subscriber')->setPublic(TRUE);
     }
+    $container->setDefinition('crm_participant_tokens', new Definition(
+      'CRM_Event_ParticipantTokens',
+      []
+    ))->addTag('kernel.event_subscriber')->setPublic(TRUE);
     $container->setDefinition('crm_contribution_recur_tokens', new Definition(
       'CRM_Contribute_RecurTokens',
       []
index aa0c19763f87c747cd6f19697d89365487ff655e..8e0d4db5b25bd3e484b20f47f99876554a80993b 100644 (file)
@@ -84,27 +84,24 @@ class CRM_Event_Form_Task_BadgeTest extends CiviUnitTestCase {
    * @return string[]
    */
   protected function getAvailableTokens(): array {
-    $tokens = [
+    return [
       '{event.title}' => 'Annual CiviCRM meet',
       '{contact.display_name}' => 'Mr. Anthony Anderson II',
       '{contact.current_employer}' => 'Default Organization',
       '{event.start_date}' => 'October 21st',
-      '{participant.participant_status_id}' => 2,
-      '{participant.participant_role_id}' => 1,
-      '{participant.participant_register_date}' => 'February 19th',
-      '{participant.participant_source}' => 'Wimbeldon',
-      '{participant.participant_fee_level}' => 'low',
-      '{participant.participant_fee_amount}' => NULL,
-      '{participant.participant_registered_by_id}' => NULL,
+      '{participant.status_id}' => 2,
+      '{participant.role_id}' => 1,
+      '{participant.register_date}' => 'February 19th, 2007 12:00 AM',
+      '{participant.source}' => 'Wimbeldon',
+      '{participant.fee_level}' => 'low',
+      '{participant.fee_amount}' => NULL,
+      '{participant.registered_by_id}' => NULL,
       '{participant.transferred_to_contact_id}' => NULL,
-      '{participant.participant_role}' => 'Attendee',
+      '{participant.role_id:label}' => 'Attendee',
       '{participant.fee_label}' => NULL,
-      '{participant.default_role_id}' => 1,
-      '{participant.template_title}' => NULL,
       '{event.end_date}' => 'October 23rd',
       '{event.id}' => 1,
     ];
-    return $tokens;
   }
 
 }
index d13406b264fdfca4d058932957836af11bc16e3e..0a0ab45571112070dcb4499b322d9d4dc7ef8009 100644 (file)
@@ -483,18 +483,16 @@ December 21st, 2007
    */
   public function getParticipantTokens(): array {
     return [
-      '{participant.participant_status_id}' => 'Status ID',
-      '{participant.participant_role_id}' => 'Participant Role (ID)',
-      '{participant.participant_register_date}' => 'Register date',
-      '{participant.participant_source}' => 'Participant Source',
-      '{participant.participant_fee_level}' => 'Fee level',
-      '{participant.participant_fee_amount}' => 'Fee Amount',
-      '{participant.participant_registered_by_id}' => 'Registered By Participant ID',
+      '{participant.status_id}' => 'Status ID',
+      '{participant.role_id}' => 'Participant Role (ID)',
+      '{participant.register_date}' => 'Register date',
+      '{participant.source}' => 'Participant Source',
+      '{participant.fee_level}' => 'Fee level',
+      '{participant.fee_amount}' => 'Fee Amount',
+      '{participant.registered_by_id}' => 'Registered By Participant ID',
       '{participant.transferred_to_contact_id}' => 'Transferred to Contact ID',
-      '{participant.participant_role}' => 'Participant Role (label)',
+      '{participant.role_id:label}' => 'Participant Role (label)',
       '{participant.fee_label}' => 'Fee Label',
-      '{participant.default_role_id}' => 'Default Role',
-      '{participant.template_title}' => 'Event Template Title',
       '{participant.' . $this->getCustomFieldName('text') . '}' => 'Enter text here :: Group with field text',
     ];
   }