Merge pull request #20856 from eileenmcnaughton/paypal
[civicrm-core.git] / Civi / WorkflowMessage / WorkflowMessage.php
CommitLineData
92f656cb
TO
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
13namespace Civi\WorkflowMessage;
14
15use Civi\WorkflowMessage\Exception\WorkflowMessageException;
16
17/**
18 * A WorkflowMessage describes the inputs to an automated email messages.
19 *
20 * These classes may be instantiated by either class-name or workflow-name.
21 *
22 * Ex: $msgWf = new \CRM_Foo_WorkflowMessage_MyAlert(['tplParams' => [...tplValues...]]);
23 * Ex: $msgWf = new \CRM_Foo_WorkflowMessage_MyAlert(['modelProps' => [...classProperties...]]);
24 * Ex: $msgWf = WorkflowMessage::create('my_alert_name', ['tplParams' => [...tplValues...]]);
25 * Ex: $msgWf = WorkflowMessage::create('my_alert_name', ['modelProps' => [...classProperties...]]);
26 *
27 * Instantiating by class-name will provide better hinting and inspection.
28 * However, some workflows may not have specific classes at the time of writing.
29 * Instantiating by workflow-name will work regardless of whether there is a specific class.
30 */
31class WorkflowMessage {
32
33 /**
34 * Create a new instance of the workflow-message context.
35 *
36 * @param string $wfName
37 * Name of the workflow.
38 * Ex: 'case_activity'
39 * @param array $imports
40 * List of data to use when populating the message.
41 *
42 * The parameters may be given in a mix of formats. This mix reflects two modes of operation:
43 *
44 * - (Informal/Generic) Traditionally, workflow-messages did not have formal parameters. Instead,
45 * they relied on a mix of un(der)documented/unverifiable inputs -- supplied as a mix of Smarty
46 * assignments, token-data, and sendTemplate() params.
47 * - (Formal) More recently, workflow-messages could be defined with a PHP class that lists the
48 * inputs explicitly.
49 *
50 * You may supply inputs using these keys:
51 *
52 * - `tplParams` (array): Smarty data. These values go to `$smarty->assign()`.
53 * - `tokenContext` (array): Token-processing data. These values go to `$tokenProcessor->context`.
54 * - `envelope` (array): Email delivery data. These values go to `sendTemplate(...)`
55 * - `modelProps` (array): Formal parameters defined by a class.
56 *
57 * Informal workflow-messages ONLY support 'tplParams', 'tokenContext', and/or 'envelope'.
58 * Formal workflow-messages accept any format.
59 *
60 * @return \Civi\WorkflowMessage\WorkflowMessageInterface
61 * If there is a workflow-message class, then it will return an instance of that class.
62 * Otherwise, it will return an instance of `GenericWorkflowMessage`.
63 * @throws \Civi\WorkflowMessage\Exception\WorkflowMessageException
64 */
65 public static function create(string $wfName, array $imports = []) {
66 $classMap = static::getWorkflowNameClassMap();
67 $class = $classMap[$wfName] ?? 'Civi\WorkflowMessage\GenericWorkflowMessage';
68 $imports['envelope']['valueName'] = $wfName;
69 $model = new $class();
70 static::importAll($model, $imports);
71 return $model;
72 }
73
74 /**
75 * Import a batch of params, updating the $model.
76 *
77 * @param \Civi\WorkflowMessage\WorkflowMessageInterface $model
78 * @param array $params
79 * List of parameters, per MessageTemplate::sendTemplate().
80 * Ex: Initialize using adhoc data:
81 * ['tplParams' => [...], 'tokenContext' => [...]]
82 * Ex: Initialize using properties of the class-model
83 * ['modelProps' => [...]]
84 * @return \Civi\WorkflowMessage\WorkflowMessageInterface
85 * The updated model.
86 * @throws \Civi\WorkflowMessage\Exception\WorkflowMessageException
87 */
88 public static function importAll(WorkflowMessageInterface $model, array $params) {
89 // The $params format is defined to match the traditional format of CRM_Core_BAO_MessageTemplate::sendTemplate().
90 // At the top level, it is an "envelope", but it also has keys for other sections.
91 if (isset($params['model'])) {
92 if ($params['model'] !== $model) {
93 throw new WorkflowMessageException(sprintf("%s: Cannot apply mismatched model", get_class($model)));
94 }
95 unset($params['model']);
96 }
97
98 \CRM_Utils_Array::pathMove($params, ['contactId'], ['tokenContext', 'contactId']);
99
100 // Core#644 - handle Email ID passed as "From".
101 if (isset($params['from'])) {
102 $params['from'] = \CRM_Utils_Mail::formatFromAddress($params['from']);
103 }
104
105 if (isset($params['tplParams'])) {
106 $model->import('tplParams', $params['tplParams']);
107 unset($params['tplParams']);
108 }
109 if (isset($params['tokenContext'])) {
110 $model->import('tokenContext', $params['tokenContext']);
111 unset($params['tokenContext']);
112 }
113 if (isset($params['modelProps'])) {
114 $model->import('modelProps', $params['modelProps']);
115 unset($params['modelProps']);
116 }
117 if (isset($params['envelope'])) {
118 $model->import('envelope', $params['envelope']);
119 unset($params['envelope']);
120 }
121 $model->import('envelope', $params);
122 return $model;
123 }
124
125 /**
126 * @param \Civi\WorkflowMessage\WorkflowMessageInterface $model
127 * @return array
128 * List of parameters, per MessageTemplate::sendTemplate().
129 * Ex: ['tplParams' => [...], 'tokenContext' => [...]]
130 */
131 public static function exportAll(WorkflowMessageInterface $model): array {
132 // The format is defined to match the traditional format of CRM_Core_BAO_MessageTemplate::sendTemplate().
133 // At the top level, it is an "envelope", but it also has keys for other sections.
134 $values = $model->export('envelope');
135 $values['tplParams'] = $model->export('tplParams');
136 $values['tokenContext'] = $model->export('tokenContext');
137 if (isset($values['tokenContext']['contactId'])) {
138 $values['contactId'] = $values['tokenContext']['contactId'];
139 }
140 return $values;
141 }
142
143 /**
144 * @return array
145 * Array(string $workflowName => string $className).
146 * Ex: ["case_activity" => "CRM_Case_WorkflowMessage_CaseActivity"]
147 * @internal
148 */
149 public static function getWorkflowNameClassMap() {
150 $cache = \Civi::cache('long');
151 $cacheKey = 'WorkflowMessage-' . __FUNCTION__;
152 $map = $cache->get($cacheKey);
153 if ($map === NULL) {
154 $map = [];
155 $map['generic'] = GenericWorkflowMessage::class;
156 $baseDirs = explode(PATH_SEPARATOR, get_include_path());
157 foreach ($baseDirs as $baseDir) {
158 $baseDir = \CRM_Utils_File::addTrailingSlash($baseDir);
159 $glob = (array) glob($baseDir . 'CRM/*/WorkflowMessage/*.php');
160 $glob = preg_grep('/\.ex\.php$/', $glob, PREG_GREP_INVERT);
161 foreach ($glob as $file) {
162 $class = strtr(preg_replace('/\.php$/', '', \CRM_Utils_File::relativize($file, $baseDir)), ['/' => '_', '\\' => '_']);
163 if (class_exists($class) && (new \ReflectionClass($class))->implementsInterface(WorkflowMessageInterface::class)) {
164 $map[$class::WORKFLOW] = $class;
165 }
166 }
167 }
168 $cache->set($cacheKey, $map);
169 }
170 return $map;
171 }
172
173}