dev/core#2850 add comment
[civicrm-core.git] / CRM / Core / EntityTokens.php
index 6ded3ebec5ace00d59b497641a2305e6bdb465ef..cc9350bb1374a9f9a1021c230b1c2e80d080ac68 100644 (file)
@@ -11,6 +11,7 @@
  */
 
 use Civi\Token\AbstractTokenSubscriber;
+use Civi\Token\Event\TokenValueEvent;
 use Civi\Token\TokenRow;
 use Civi\ActionSchedule\Event\MailingQueryEvent;
 use Civi\Token\TokenProcessor;
@@ -39,11 +40,24 @@ 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)) {
+        // If it's set here it has already been loaded in pre-fetch.
+        return $row->format('text/plain')->tokens($entity, $field, (string) $fieldValue);
+      }
+      // Once prefetch is fully standardised we can remove this - as long
+      // as tests pass we should be fine as tests cover this.
       $split = explode(':', $field);
       return $row->tokens($entity, $field, $this->getPseudoValue($split[0], $split[1], $this->getFieldValue($row, $split[0])));
     }
+    if ($this->isCustomField($field)) {
+      return $row->customToken($entity, \CRM_Core_BAO_CustomField::getKeyID($field), $this->getFieldValue($row, 'id'));
+    }
     if ($this->isMoneyField($field)) {
       return $row->format('text/plain')->tokens($entity, $field,
         \CRM_Utils_Money::format($fieldValue, $this->getCurrency($row)));
@@ -51,12 +65,7 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
     if ($this->isDateField($field)) {
       return $row->format('text/plain')->tokens($entity, $field, \CRM_Utils_Date::customFormat($fieldValue));
     }
-    if ($this->isCustomField($field)) {
-      $row->customToken($entity, \CRM_Core_BAO_CustomField::getKeyID($field), $this->getFieldValue($row, 'id'));
-    }
-    else {
-      $row->format('text/plain')->tokens($entity, $field, (string) $fieldValue);
-    }
+    $row->format('text/plain')->tokens($entity, $field, (string) $fieldValue);
   }
 
   /**
@@ -118,9 +127,33 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
    * Get all the tokens supported by this processor.
    *
    * @return array|string[]
+   * @throws \API_Exception
+   */
+  protected function getAllTokens(): array {
+    $basicTokens = $this->getBasicTokens();
+    foreach (array_keys($basicTokens) as $fieldName) {
+      // The goal is to be able to render more complete tokens
+      // (eg. actual booleans, field names, raw ids) for a more
+      // advanced audiences - ie those using conditionals
+      // and to specify that audience in the api that retrieves.
+      // But, for now, let's not advertise, given that most of these fields
+      // aren't really needed even once...
+      if ($this->isBooleanField($fieldName)) {
+        unset($basicTokens[$fieldName]);
+      }
+    }
+    return array_merge($basicTokens, $this->getPseudoTokens(), CRM_Utils_Token::getCustomFieldTokens($this->getApiEntityName()));
+  }
+
+  /**
+   * Is the given field a boolean field.
+   *
+   * @param string $fieldName
+   *
+   * @return bool
    */
-  public function getAllTokens(): array {
-    return array_merge($this->getBasicTokens(), $this->getPseudoTokens(), CRM_Utils_Token::getCustomFieldTokens('Contribution'));
+  public function isBooleanField(string $fieldName): bool {
+    return $this->getFieldMetadata()[$fieldName]['data_type'] === 'Boolean';
   }
 
   /**
@@ -131,7 +164,7 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
    * @return bool
    */
   public function isDateField(string $fieldName): bool {
-    return $this->getFieldMetadata()[$fieldName]['data_type'] === 'Timestamp';
+    return in_array($this->getFieldMetadata()[$fieldName]['data_type'], ['Timestamp', 'Date'], TRUE);
   }
 
   /**
@@ -200,6 +233,9 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
         $return[$fieldName . ':label'] = $fieldLabel;
         $return[$fieldName . ':name'] = ts('Machine name') . ': ' . $fieldLabel;
       }
+      if ($this->isBooleanField($fieldName)) {
+        $return[$fieldName . ':label'] = $this->getFieldMetadata()[$fieldName]['title'];
+      }
     }
     return $return;
   }
@@ -261,6 +297,11 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
    * @return string|int
    */
   protected function getFieldValue(TokenRow $row, string $field) {
+    $entityName = $this->getEntityName();
+    if (isset($row->context[$entityName][$field])) {
+      return $row->context[$entityName][$field];
+    }
+
     $actionSearchResult = $row->context['actionSearchResult'];
     $aliasedField = $this->getEntityAlias() . $field;
     if (isset($actionSearchResult->{$aliasedField})) {
@@ -321,7 +362,12 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
   public function getBasicTokens(): array {
     $return = [];
     foreach ($this->getExposedFields() as $fieldName) {
-      $return[$fieldName] = $this->getFieldMetadata()[$fieldName]['title'];
+      // Custom fields are still added v3 style - we want to keep v4 naming 'unpoluted'
+      // for now to allow us to consider how to handle names vs labels vs values
+      // and other raw vs not raw options.
+      if ($this->getFieldMetadata()[$fieldName]['type'] !== 'Custom') {
+        $return[$fieldName] = $this->getFieldMetadata()[$fieldName]['title'];
+      }
     }
     return $return;
   }
@@ -348,7 +394,10 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
    * @return string[]
    */
   public function getSkippedFields(): array {
-    $fields = ['contact_id'];
+    // tags is offered in 'case' & is one of the only fields that is
+    // 'not a real field' offered up by case - seems like an oddity
+    // we should skip at the top level for now.
+    $fields = ['contact_id', 'tags'];
     if (!CRM_Campaign_BAO_Campaign::isCampaignEnable()) {
       $fields[] = 'campaign_id';
     }
@@ -362,11 +411,11 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
     return CRM_Core_DAO_AllCoreTables::convertEntityNameToLower($this->getApiEntityName());
   }
 
-  public function getEntityIDField() {
+  public function getEntityIDField(): string {
     return $this->getEntityName() . 'Id';
   }
 
-  public function prefetch(\Civi\Token\Event\TokenValueEvent $e): ?array {
+  public function prefetch(TokenValueEvent $e): ?array {
     $entityIDs = $e->getTokenProcessor()->getContextValues($this->getEntityIDField());
     if (empty($entityIDs)) {
       return [];
@@ -399,8 +448,38 @@ class CRM_Core_EntityTokens extends AbstractTokenSubscriber {
     return CRM_Core_Config::singleton()->defaultCurrency;
   }
 
-  public function getPrefetchFields(\Civi\Token\Event\TokenValueEvent $e): array {
-    return array_intersect($this->getActiveTokens($e), $this->getCurrencyFieldName(), array_keys($this->getAllTokens()));
+  /**
+   * Get the fields required to prefetch the entity.
+   *
+   * @param \Civi\Token\Event\TokenValueEvent $e
+   *
+   * @return array
+   * @throws \API_Exception
+   */
+  public function getPrefetchFields(TokenValueEvent $e): array {
+    $allTokens = array_keys($this->getAllTokens());
+    $requiredFields = array_intersect($this->getActiveTokens($e), $allTokens);
+    if (empty($requiredFields)) {
+      return [];
+    }
+    $requiredFields = array_merge($requiredFields, array_intersect($allTokens, array_merge(['id'], $this->getCurrencyFieldName())));
+    foreach ($this->getDependencies() as $field => $required) {
+      if (in_array($field, $this->getActiveTokens($e), TRUE)) {
+        foreach ((array) $required as $key) {
+          $requiredFields[] = $key;
+        }
+      }
+    }
+    return $requiredFields;
+  }
+
+  /**
+   * Get fields which need to be returned to render another token.
+   *
+   * @return array
+   */
+  public function getDependencies(): array {
+    return [];
   }
 
 }