4 +--------------------------------------------------------------------+
5 | Copyright CiviCRM LLC. All rights reserved. |
7 | This work is published under the GNU AGPLv3 license with some |
8 | permitted exceptions and without any warranty. For full license |
9 | and copyright information, see https://civicrm.org/licensing |
10 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
18 use Civi\Token\Event\TokenValueEvent
;
19 use Civi\Token\TokenRow
;
22 * Class CRM_Member_Tokens
24 * Generate "activity.*" tokens.
26 * This TokenSubscriber was originally produced by refactoring the code from the
27 * scheduled-reminder system with the goal of making that system
28 * more flexible. The current implementation is still coupled to
29 * scheduled-reminders. It would be good to figure out a more generic
30 * implementation which is not tied to scheduled reminders, although
31 * that is outside the current scope.
33 * This has been enhanced to work with PDF/letter merge
35 class CRM_Activity_Tokens
extends CRM_Core_EntityTokens
{
37 use CRM_Core_TokenTrait
;
40 * Get the entity name for api v4 calls.
44 protected function getApiEntityName(): string {
51 private function getEntityTableName(): string {
52 return 'civicrm_activity';
58 private function getEntityContextSchema(): string {
63 * Mapping from tokenName to api return field
64 * Using arrays allows more complex tokens to be handled that require more than one API field.
65 * For example, an address token might want ['street_address', 'city', 'postal_code']
69 private static $fieldMapping = [
70 'activity_id' => ['id'],
71 'activity_type' => ['activity_type_id'],
72 'status' => ['status_id'],
73 'campaign' => ['campaign_id'],
79 public function alterActionScheduleQuery(\Civi\ActionSchedule\Event\MailingQueryEvent
$e): void
{
80 if ($e->mapping
->getEntity() !== $this->getEntityTableName()) {
84 // The joint expression for activities needs some extra nuance to handle.
85 // Multiple revisions of the activity.
86 // Q: Could we simplify & move the extra AND clauses into `where(...)`?
87 $e->query
->param('casEntityJoinExpr', 'e.id = reminder.entity_id AND e.is_current_revision = 1 AND e.is_deleted = 0');
88 $e->query
->select('e.id AS tokenContext_' . $this->getEntityContextSchema());
92 * Evaluate the content of a single token.
94 * @param \Civi\Token\TokenRow $row
95 * The record for which we want token values.
96 * @param string $entity
97 * The name of the token entity.
98 * @param string $field
99 * The name of the token field.
100 * @param mixed $prefetch
101 * Any data that was returned by the prefetch().
103 * @throws \CRM_Core_Exception
105 public function evaluateToken(TokenRow
$row, $entity, $field, $prefetch = NULL) {
106 $activityId = $row->context
[$this->getEntityContextSchema()];
108 if (!empty($this->getDeprecatedTokens()[$field])) {
109 $realField = $this->getDeprecatedTokens()[$field];
110 parent
::evaluateToken($row, $entity, $realField, $prefetch);
111 $row->format('text/plain')->tokens($entity, $field, $row->tokens
['activity'][$realField]);
113 elseif (in_array($field, ['case_id'])) {
114 // An activity can be linked to multiple cases so case_id is always an array.
115 // We just return the first case ID for the token.
116 // this weird hack might exist because apiv3 is weird &
117 $caseID = CRM_Core_DAO
::singleValueQuery('SELECT case_id FROM civicrm_case_activity WHERE activity_id = %1 LIMIT 1', [1 => [$activityId, 'Integer']]);
118 $row->tokens($entity, $field, $caseID ??
'');
121 parent
::evaluateToken($row, $entity, $field, $prefetch);
126 * Get all the tokens supported by this processor.
128 * @return array|string[]
129 * @throws \API_Exception
131 protected function getAllTokens(): array {
132 $tokens = parent
::getAllTokens();
133 if (array_key_exists('CiviCase', CRM_Core_Component
::getEnabledComponents())) {
134 $tokens['case_id'] = ts('Activity Case ID');
140 * Get the basic tokens provided.
142 * @return array token name => token label
144 public function getBasicTokens(): array {
145 if (!isset($this->basicTokens
)) {
146 $this->basicTokens
= [
147 'id' => ts('Activity ID'),
148 'subject' => ts('Activity Subject'),
149 'details' => ts('Activity Details'),
150 'activity_date_time' => ts('Activity Date-Time'),
151 'created_date' => ts('Activity Created Date'),
152 'modified_date' => ts('Activity Modified Date'),
153 'activity_type_id' => ts('Activity Type ID'),
154 'status_id' => ts('Activity Status ID'),
155 'location' => ts('Activity Location'),
156 'duration' => ts('Activity Duration'),
158 if (CRM_Campaign_BAO_Campaign
::isCampaignEnable()) {
159 $this->basicTokens
['campaign_id'] = ts('Campaign ID');
162 return $this->basicTokens
;
168 public function getActiveTokens(TokenValueEvent
$e) {
169 $messageTokens = $e->getTokenProcessor()->getMessageTokens();
170 if (!isset($messageTokens[$this->entity
])) {
175 // if message token contains '_\d+_', then treat as '_N_'
176 foreach ($messageTokens[$this->entity
] as $msgToken) {
177 if (array_key_exists($msgToken, $this->tokenNames
)) {
178 $activeTokens[] = $msgToken;
180 elseif (in_array($msgToken, ['campaign', 'activity_id', 'status', 'activity_type', 'case_id'])) {
181 $activeTokens[] = $msgToken;
184 $altToken = preg_replace('/_\d+_/', '_N_', $msgToken);
185 if (array_key_exists($altToken, $this->tokenNames
)) {
186 $activeTokens[] = $msgToken;
190 return array_unique($activeTokens);
193 public function getPrefetchFields(TokenValueEvent
$e): array {
194 $tokens = parent
::getPrefetchFields($e);
195 $active = $this->getActiveTokens($e);
196 foreach ($this->getDeprecatedTokens() as $old => $new) {
197 if (in_array($old, $active, TRUE) && !in_array($new, $active, TRUE)) {
205 * These tokens still work but we don't advertise them.
207 * We will actively remove from the following places
208 * - scheduled reminders
209 * - add to 'blocked' on pdf letter & email
211 * & then at some point start issuing warnings for them.
215 protected function getDeprecatedTokens(): array {
217 'activity_id' => 'id',
218 'activity_type' => 'activity_type_id:label',
219 'status' => 'status_id:label',
220 'campaign' => 'campaign_id:label',