3 +--------------------------------------------------------------------+
4 | Copyright CiviCRM LLC. All rights reserved. |
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
9 +--------------------------------------------------------------------+
15 * @copyright CiviCRM LLC https://civicrm.org/licensing
17 class CRM_Upgrade_Incremental_MessageTemplates
{
20 * Version we are upgrading to.
24 protected $upgradeVersion;
29 public function getUpgradeVersion() {
30 return $this->upgradeVersion
;
34 * @param string $upgradeVersion
36 public function setUpgradeVersion($upgradeVersion) {
37 $this->upgradeVersion
= $upgradeVersion;
41 * CRM_Upgrade_Incremental_MessageTemplates constructor.
43 * @param string $upgradeVersion
45 public function __construct($upgradeVersion) {
46 $this->setUpgradeVersion($upgradeVersion);
50 * Get any templates that have been updated.
54 protected function getTemplateUpdates() {
57 'version' => '5.4.alpha1',
58 'upgrade_descriptor' => ts('Use email greeting at top where available'),
60 ['name' => 'membership_online_receipt', 'type' => 'text'],
61 ['name' => 'membership_online_receipt', 'type' => 'html'],
62 ['name' => 'contribution_online_receipt', 'type' => 'text'],
63 ['name' => 'contribution_online_receipt', 'type' => 'html'],
64 ['name' => 'event_online_receipt', 'type' => 'text'],
65 ['name' => 'event_online_receipt', 'type' => 'html'],
66 ['name' => 'event_online_receipt', 'type' => 'subject'],
70 'version' => '5.7.alpha1',
71 'upgrade_descriptor' => ts('Fix invoice number (human readable) instead of id (reference)'),
72 'label' => ts('Contributions - Invoice'),
74 ['name' => 'contribution_invoice_receipt', 'type' => 'html'],
78 'version' => '5.10.alpha1',
79 'upgrade_descriptor' => ts('Show recurring cancel/update URLs in receipt based on payment processor capabilities'),
80 'label' => ts('Receipts - cancel/update subscription URLs'),
82 ['name' => 'contribution_online_receipt', 'type' => 'text'],
83 ['name' => 'contribution_online_receipt', 'type' => 'html'],
84 ['name' => 'contribution_recurring_notify', 'type' => 'text'],
85 ['name' => 'contribution_recurring_notify', 'type' => 'html'],
86 ['name' => 'membership_online_receipt', 'type' => 'text'],
87 ['name' => 'membership_online_receipt', 'type' => 'html'],
91 'version' => '5.12.alpha1',
92 'upgrade_descriptor' => ts('Update payment notification to remove print text, use email greeting'),
93 'label' => ts('Payment notification'),
95 ['name' => 'payment_or_refund_notification', 'type' => 'text'],
96 ['name' => 'payment_or_refund_notification', 'type' => 'html'],
100 'version' => '5.15.alpha1',
101 'upgrade_descriptor' => ts('Use email greeting and fix capitalization'),
102 'label' => ts('Pledge acknowledgement'),
104 ['name' => 'pledge_acknowledge', 'type' => 'text'],
105 ['name' => 'pledge_acknowledge', 'type' => 'html'],
109 'version' => '5.20.alpha1',
110 'upgrade_descriptor' => ts('Fix missing Email greetings'),
112 ['name' => 'contribution_dupalert', 'type' => 'subject'],
113 ['name' => 'contribution_invoice_receipt', 'type' => 'subject'],
114 ['name' => 'contribution_offline_receipt', 'type' => 'html'],
115 ['name' => 'contribution_offline_receipt', 'type' => 'subject'],
116 ['name' => 'contribution_offline_receipt', 'type' => 'text'],
117 ['name' => 'contribution_online_receipt', 'type' => 'subject'],
118 ['name' => 'contribution_online_receipt', 'type' => 'html'],
119 ['name' => 'contribution_recurring_billing', 'type' => 'html'],
120 ['name' => 'contribution_recurring_billing', 'type' => 'subject'],
121 ['name' => 'contribution_recurring_billing', 'type' => 'text'],
122 ['name' => 'contribution_recurring_cancelled', 'type' => 'html'],
123 ['name' => 'contribution_recurring_cancelled', 'type' => 'subject'],
124 ['name' => 'contribution_recurring_cancelled', 'type' => 'text'],
125 ['name' => 'contribution_recurring_edit', 'type' => 'html'],
126 ['name' => 'contribution_recurring_edit', 'type' => 'subject'],
127 ['name' => 'contribution_recurring_edit', 'type' => 'text'],
128 ['name' => 'contribution_recurring_notify', 'type' => 'html'],
129 ['name' => 'contribution_recurring_notify', 'type' => 'subject'],
130 ['name' => 'contribution_recurring_notify', 'type' => 'text'],
131 ['name' => 'event_offline_receipt', 'type' => 'html'],
132 ['name' => 'event_offline_receipt', 'type' => 'subject'],
133 ['name' => 'event_offline_receipt', 'type' => 'text'],
134 ['name' => 'event_online_receipt', 'type' => 'html'],
135 ['name' => 'event_online_receipt', 'type' => 'subject'],
136 ['name' => 'event_online_receipt', 'type' => 'text'],
137 ['name' => 'event_registration_receipt', 'type' => 'html'],
138 ['name' => 'event_registration_receipt', 'type' => 'subject'],
139 ['name' => 'event_registration_receipt', 'type' => 'text'],
140 ['name' => 'membership_autorenew_billing', 'type' => 'html'],
141 ['name' => 'membership_autorenew_billing', 'type' => 'subject'],
142 ['name' => 'membership_autorenew_billing', 'type' => 'text'],
143 ['name' => 'membership_autorenew_cancelled', 'type' => 'html'],
144 ['name' => 'membership_autorenew_cancelled', 'type' => 'subject'],
145 ['name' => 'membership_autorenew_cancelled', 'type' => 'text'],
146 ['name' => 'membership_offline_receipt', 'type' => 'html'],
147 ['name' => 'membership_offline_receipt', 'type' => 'subject'],
148 ['name' => 'membership_offline_receipt', 'type' => 'text'],
149 ['name' => 'membership_online_receipt', 'type' => 'subject'],
150 ['name' => 'membership_online_receipt', 'type' => 'html'],
151 ['name' => 'participant_cancelled', 'type' => 'html'],
152 ['name' => 'participant_cancelled', 'type' => 'subject'],
153 ['name' => 'participant_cancelled', 'type' => 'text'],
154 ['name' => 'participant_confirm', 'type' => 'html'],
155 ['name' => 'participant_confirm', 'type' => 'subject'],
156 ['name' => 'participant_confirm', 'type' => 'text'],
157 ['name' => 'participant_expired', 'type' => 'html'],
158 ['name' => 'participant_expired', 'type' => 'subject'],
159 ['name' => 'participant_expired', 'type' => 'text'],
160 ['name' => 'participant_transferred', 'type' => 'html'],
161 ['name' => 'participant_transferred', 'type' => 'subject'],
162 ['name' => 'participant_transferred', 'type' => 'text'],
163 ['name' => 'payment_or_refund_notification', 'type' => 'html'],
164 ['name' => 'payment_or_refund_notification', 'type' => 'subject'],
165 ['name' => 'payment_or_refund_notification', 'type' => 'text'],
166 ['name' => 'pcp_notify', 'type' => 'subject'],
167 ['name' => 'pcp_owner_notify', 'type' => 'html'],
168 ['name' => 'pcp_owner_notify', 'type' => 'subject'],
169 ['name' => 'pcp_owner_notify', 'type' => 'text'],
170 ['name' => 'pcp_status_change', 'type' => 'subject'],
171 ['name' => 'pcp_status_change', 'type' => 'html'],
172 ['name' => 'pcp_supporter_notify', 'type' => 'html'],
173 ['name' => 'pcp_supporter_notify', 'type' => 'subject'],
174 ['name' => 'pcp_supporter_notify', 'type' => 'text'],
175 ['name' => 'petition_confirmation_needed', 'type' => 'html'],
176 ['name' => 'petition_confirmation_needed', 'type' => 'subject'],
177 ['name' => 'petition_confirmation_needed', 'type' => 'text'],
178 ['name' => 'petition_sign', 'type' => 'html'],
179 ['name' => 'petition_sign', 'type' => 'subject'],
180 ['name' => 'petition_sign', 'type' => 'text'],
181 ['name' => 'pledge_acknowledge', 'type' => 'subject'],
182 ['name' => 'pledge_acknowledge', 'type' => 'html'],
183 ['name' => 'pledge_acknowledge', 'type' => 'text'],
184 ['name' => 'pledge_reminder', 'type' => 'html'],
185 ['name' => 'pledge_reminder', 'type' => 'subject'],
186 ['name' => 'pledge_reminder', 'type' => 'text'],
187 ['name' => 'uf_notify', 'type' => 'subject'],
188 ['name' => 'uf_notify', 'type' => 'html'],
189 ['name' => 'case_activity', 'type' => 'html'],
190 ['name' => 'contribution_dupalert', 'type' => 'html'],
191 ['name' => 'friend', 'type' => 'html'],
192 ['name' => 'test_preview', 'type' => 'html'],
196 'version' => '5.21.beta1',
197 'upgrade_descriptor' => ts('Fix Membership Receipt'),
199 ['name' => 'membership_online_receipt', 'type' => 'html'],
203 'version' => '5.23.alpha1',
204 'upgrade_descriptor' => ts('Add Contributor Name to Offline Contribution receipts; fix bad event self-service URL'),
206 ['name' => 'contribution_offline_receipt', 'type' => 'text'],
207 ['name' => 'contribution_offline_receipt', 'type' => 'html'],
208 ['name' => 'participant_confirm', 'type' => 'html'],
212 'version' => '5.24.alpha1',
213 'upgrade_descriptor' => ts('Layout fixes for the Contribution templates'),
215 ['name' => 'contribution_invoice_receipt', 'type' => 'html'],
219 'version' => '5.30.alpha1',
220 'upgrade_descriptor' => ts('Support negative hours for cancellation/transfer'),
222 ['name' => 'participant_confirm', 'type' => 'html'],
223 ['name' => 'participant_confirm', 'type' => 'text'],
224 ['name' => 'event_online_receipt', 'type' => 'html'],
225 ['name' => 'event_online_receipt', 'type' => 'text'],
229 'version' => '5.30.beta1',
230 'upgrade_descriptor' => ts('Ensure that amount paid is shown even when fully paid'),
232 ['name' => 'contribution_invoice_receipt', 'type' => 'html'],
236 'version' => '5.38.alpha1',
237 'upgrade_descriptor' => ts('Fix Petition Confirmation email having a blank space at the end of url'),
239 ['name' => 'petition_confirmation_needed', 'type' => 'html'],
243 'version' => '5.38.alpha1',
244 'upgrade_descriptor' => ts('Fix Pledge and PCP urls to go to the front end site rather than backend site'),
246 ['name' => 'pcp_notify', 'type' => 'html'],
247 ['name' => 'pcp_notify', 'type' => 'text'],
248 ['name' => 'pledge_reminder', 'type' => 'html'],
249 ['name' => 'pledge_reminder', 'type' => 'text'],
256 * Get any required template updates.
260 public function getTemplatesToUpdate() {
261 $templates = $this->getTemplateUpdates();
263 foreach ($templates as $templateArray) {
264 if ($templateArray['version'] === $this->getUpgradeVersion()) {
265 foreach ($templateArray['templates'] as $template) {
266 $return[$template['name'] . '_' . $template['type']] = array_merge($template, $templateArray);
274 * Replace a token with the new preferred option.
276 * @param string $workflowName
280 public function replaceTokenInTemplate(string $workflowName, string $old, string $new): void
{
281 $oldToken = '{' . $old . '}';
282 $newToken = '{' . $new . '}';
283 CRM_Core_DAO
::executeQuery("UPDATE civicrm_msg_template
285 msg_text = REPLACE(msg_text, '$oldToken', '$newToken'),
286 msg_subject = REPLACE(msg_subject, '$oldToken', '$newToken'),
287 msg_html = REPLACE(msg_html, '$oldToken', '$newToken')
288 WHERE workflow_name = '$workflowName'
293 * Get warnings for users if the replaced string is still present.
295 * This might be the case when used in an IF and for now we will recommend
296 * manual intervention.
298 * @param string $workflowName
304 public function getMessageTemplateWarning(string $workflowName, string $old, string $new) {
305 if (CRM_Core_DAO
::singleValueQuery("
307 FROM civicrm_msg_template
308 WHERE workflow_name = '$workflowName'
310 msg_html LIKE '%$old%'
311 OR msg_subject LIKE '%$old%'
312 OR civicrm_msg_template.msg_text LIKE '%$old%'
315 return ts('Please review your %1 message template and remove references to the token %2 as it has been replaced by %3', [
317 2 => '{' . $old . '}',
318 3 => '{' . $new . '}',
325 * Get the upgrade messages.
327 public function getUpgradeMessages() {
328 $updates = $this->getTemplatesToUpdate();
331 foreach ($updates as $key => $value) {
333 $templateLabel = civicrm_api3('OptionValue', 'getvalue', [
335 'name' => $value['name'],
336 'options' => ['limit' => 1],
339 catch (Exception
$e) {
340 if (!empty($value['label'])) {
341 $templateLabel = $value['label'];
344 $messages[$templateLabel] = $value['upgrade_descriptor'];
350 * Update message templates.
352 public function updateTemplates() {
353 $templates = $this->getTemplatesToUpdate();
354 foreach ($templates as $template) {
355 $workFlowID = CRM_Core_DAO
::singleValueQuery("SELECT MAX(id) as id FROM civicrm_option_value WHERE name = %1", [
356 1 => [$template['name'], 'String'],
358 $content = file_get_contents(\Civi
::paths()->getPath('[civicrm.root]/xml/templates/message_templates/' . $template['name'] . '_' . $template['type'] . '.tpl'));
359 $templatesToUpdate = [];
360 if (!empty($workFlowID)) {
361 // This could be empty if the template was deleted. It should not happen,
362 // but has been seen in the wild (ex: marketing/civicrm-website#163).
363 $id = CRM_Core_DAO
::singleValueQuery("SELECT id FROM civicrm_msg_template WHERE workflow_id = $workFlowID AND is_reserved = 1");
365 $templatesToUpdate[] = $id;
367 $defaultTemplateID = CRM_Core_DAO
::singleValueQuery("
368 SELECT default_template.id FROM civicrm_msg_template reserved
369 LEFT JOIN civicrm_msg_template default_template
370 ON reserved.workflow_id = default_template.workflow_id
371 WHERE reserved.workflow_id = $workFlowID
372 AND reserved.is_reserved = 1 AND default_template.is_default = 1 AND reserved.id <> default_template.id
373 AND reserved.msg_{$template['type']} = default_template.msg_{$template['type']}
375 if ($defaultTemplateID) {
376 $templatesToUpdate[] = $defaultTemplateID;
379 if (!empty($templatesToUpdate)) {
380 CRM_Core_DAO
::executeQuery("
381 UPDATE civicrm_msg_template SET msg_{$template['type']} = %1 WHERE id IN (" . implode(',', $templatesToUpdate) . ")", [
382 1 => [$content, 'String'],