3fe10f3ede2ff96dce671184f38316cb4f09d507
[civicrm-core.git] / CRM / Contribute / Tokens.php
1 <?php
2
3 /*
4 +--------------------------------------------------------------------+
5 | Copyright CiviCRM LLC. All rights reserved. |
6 | |
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 +--------------------------------------------------------------------+
11 */
12
13 use Civi\ActionSchedule\Event\MailingQueryEvent;
14 use Civi\Token\AbstractTokenSubscriber;
15 use Civi\Token\TokenProcessor;
16 use Civi\Token\TokenRow;
17
18 /**
19 * Class CRM_Contribute_Tokens
20 *
21 * Generate "contribution.*" tokens.
22 *
23 * At time of writing, we don't have any particularly special tokens -- we just
24 * do some basic formatting based on the corresponding DB field.
25 */
26 class CRM_Contribute_Tokens extends AbstractTokenSubscriber {
27
28 /**
29 * @return string
30 */
31 protected function getEntityName(): string {
32 return 'contribution';
33 }
34
35 /**
36 * Get the relevant bao name.
37 */
38 public function getBAOName(): string {
39 return CRM_Core_DAO_AllCoreTables::getFullName(ucfirst($this->getEntityName()));
40 }
41
42 /**
43 * Metadata about the entity fields.
44 *
45 * @var array
46 */
47 protected $fieldMetadata = [];
48
49 /**
50 * Get a list of tokens whose name and title match the DB fields.
51 * @return array
52 */
53 protected function getPassthruTokens(): array {
54 return [
55 'contribution_page_id',
56 'source',
57 'id',
58 'receive_date',
59 'total_amount',
60 'fee_amount',
61 'net_amount',
62 'trxn_id',
63 'invoice_id',
64 'currency',
65 'cancel_date',
66 'receipt_date',
67 'thankyou_date',
68 'tax_amount',
69 'contribution_status_id',
70 ];
71 }
72
73 /**
74 * Get alias tokens.
75 *
76 * @return array
77 */
78 protected function getAliasTokens(): array {
79 return [
80 'payment_instrument' => 'payment_instrument_id',
81 'type' => 'financial_type_id',
82 ];
83 }
84
85 /**
86 * Get tokens supporting the syntax we are migrating to.
87 *
88 * In general these are tokens that were not previously supported
89 * so we can add them in the preferred way or that we have
90 * undertaken some, as yet to be written, db update.
91 *
92 * See https://lab.civicrm.org/dev/core/-/issues/2650
93 *
94 * @return string[]
95 */
96 protected function getBasicTokens(): array {
97 return ['contribution_status_id' => ts('Contribution Status ID')];
98 }
99
100 /**
101 * Get pseudoTokens - it tokens that reflect the name or label of a pseudoconstant.
102 *
103 * @internal - this function will likely be made protected soon.
104 *
105 * @return array
106 */
107 public function getPseudoTokens(): array {
108 $return = [];
109 foreach (array_keys($this->getBasicTokens()) as $fieldName) {
110 if (!empty($this->fieldMetadata[$fieldName]['pseudoconstant'])) {
111 $return[$fieldName . ':label'] = $this->fieldMetadata[$fieldName]['html']['label'];
112 $return[$fieldName . ':name'] = ts('Machine name') . ': ' . $this->fieldMetadata[$fieldName]['html']['label'];
113 }
114 }
115 return $return;
116 }
117
118 /**
119 * Class constructor.
120 */
121 public function __construct() {
122 $tokens = CRM_Utils_Array::subset(
123 CRM_Utils_Array::collect('title', $this->getFieldMetadata()),
124 $this->getPassthruTokens()
125 );
126 $tokens['id'] = ts('Contribution ID');
127 $tokens['payment_instrument'] = ts('Payment Instrument');
128 $tokens['source'] = ts('Contribution Source');
129 $tokens['type'] = ts('Financial Type');
130 $tokens = array_merge($tokens, $this->getPseudoTokens(), CRM_Utils_Token::getCustomFieldTokens('Contribution'));
131 parent::__construct('contribution', $tokens);
132 }
133
134 /**
135 * Check if the token processor is active.
136 *
137 * @param \Civi\Token\TokenProcessor $processor
138 *
139 * @return bool
140 */
141 public function checkActive(TokenProcessor $processor) {
142 return !empty($processor->context['actionMapping'])
143 && $processor->context['actionMapping']->getEntity() === 'civicrm_contribution';
144 }
145
146 /**
147 * Alter action schedule query.
148 *
149 * @param \Civi\ActionSchedule\Event\MailingQueryEvent $e
150 */
151 public function alterActionScheduleQuery(MailingQueryEvent $e): void {
152 if ($e->mapping->getEntity() !== 'civicrm_contribution') {
153 return;
154 }
155
156 $fields = $this->getFieldMetadata();
157 foreach ($this->getPassthruTokens() as $token) {
158 $e->query->select("e." . $fields[$token]['name'] . " AS contrib_{$token}");
159 }
160 foreach (array_keys($this->getPseudoTokens()) as $token) {
161 $split = explode(':', $token);
162 $e->query->select("e." . $fields[$split[0]]['name'] . " AS contrib_{$split[0]}");
163 }
164 foreach ($this->getAliasTokens() as $alias => $orig) {
165 $e->query->select('e.' . $fields[$orig]['name'] . " AS contrib_{$alias}");
166 }
167 }
168
169 /**
170 * @inheritDoc
171 */
172 public function evaluateToken(TokenRow $row, $entity, $field, $prefetch = NULL) {
173 $actionSearchResult = $row->context['actionSearchResult'];
174 $fieldValue = $actionSearchResult->{"contrib_$field"} ?? NULL;
175
176 $aliasTokens = $this->getAliasTokens();
177 if (in_array($field, ['total_amount', 'fee_amount', 'net_amount'])) {
178 return $row->format('text/plain')->tokens($entity, $field,
179 \CRM_Utils_Money::format($fieldValue, $actionSearchResult->contrib_currency));
180 }
181 if (isset($aliasTokens[$field])) {
182 $row->dbToken($entity, $field, 'CRM_Contribute_BAO_Contribution', $aliasTokens[$field], $fieldValue);
183 }
184 elseif ($cfID = \CRM_Core_BAO_CustomField::getKeyID($field)) {
185 $row->customToken($entity, $cfID, $actionSearchResult->entity_id);
186 }
187 elseif (array_key_exists($field, $this->getPseudoTokens())) {
188 $split = explode(':', $field);
189 $row->tokens($entity, $field, $this->getPseudoValue($split[0], $split[1], $actionSearchResult->{"contrib_$split[0]"} ?? NULL));
190 }
191 elseif (in_array($field, array_keys($this->getBasicTokens()))) {
192 $row->tokens($entity, $field, $fieldValue);
193 }
194 elseif (!array_key_exists($field, CRM_Contribute_BAO_Contribution::fields())) {
195 if ($this->isDateField($field)) {
196 $row->format('text/plain')->tokens($entity, $field, \CRM_Utils_Date::customFormat($fieldValue));
197 }
198 else {
199 $row->format('text/plain')->tokens($entity, $field, $fieldValue);
200 }
201 }
202 else {
203 $row->dbToken($entity, $field, 'CRM_Contribute_BAO_Contribution', $field, $fieldValue);
204 }
205 }
206
207 /**
208 * Is the given field a date field.
209 *
210 * @param string $fieldName
211 *
212 * @return bool
213 */
214 public function isDateField($fieldName): bool {
215 return $this->getFieldMetadata()[$fieldName]['type'] === (\CRM_Utils_Type::T_DATE + \CRM_Utils_Type::T_TIME);
216 }
217
218 /**
219 * Get the value for the relevant pseudo field.
220 *
221 * @param string $realField e.g contribution_status_id
222 * @param string $pseudoKey e.g name
223 * @param int|string $fieldValue e.g 1
224 *
225 * @return string
226 * Eg. 'Completed' in the example above.
227 *
228 * @internal function will likely be protected soon.
229 */
230 public function getPseudoValue(string $realField, string $pseudoKey, $fieldValue): string {
231 if ($pseudoKey === 'name') {
232 $fieldValue = (string) CRM_Core_PseudoConstant::getName($this->getBAOName(), $realField, $fieldValue);
233 }
234 if ($pseudoKey === 'label') {
235 $fieldValue = (string) CRM_Core_PseudoConstant::getLabel($this->getBAOName(), $realField, $fieldValue);
236 }
237 return (string) $fieldValue;
238 }
239
240 /**
241 * Get the metadata for the available fields.
242 *
243 * @return array
244 */
245 protected function getFieldMetadata(): array {
246 if (empty($this->fieldMetadata)) {
247 $baoName = $this->getBAOName();
248 $fields = (array) $baoName::fields();
249 // re-index by real field name. I originally wanted to use apiv4
250 // getfields - but it returns different stuff for 'type' and
251 // does not return 'pseudoconstant' as a key so for now...
252 foreach ($fields as $details) {
253 $this->fieldMetadata[$details['name']] = $details;
254 }
255 }
256 return $this->fieldMetadata;
257 }
258
259 }