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 +--------------------------------------------------------------------+
13 namespace Civi\WorkflowMessage
;
15 use Civi\Api4\Utils\ReflectionUtils
;
16 use Civi\Core\ClassScanner
;
17 use Civi\WorkflowMessage\Exception\WorkflowMessageException
;
20 * A WorkflowMessage describes the inputs to an automated email messages.
22 * These classes may be instantiated by either class-name or workflow-name.
24 * Ex: $msgWf = new \CRM_Foo_WorkflowMessage_MyAlert(['tplParams' => [...tplValues...]]);
25 * Ex: $msgWf = new \CRM_Foo_WorkflowMessage_MyAlert(['modelProps' => [...classProperties...]]);
26 * Ex: $msgWf = WorkflowMessage::create('my_alert_name', ['tplParams' => [...tplValues...]]);
27 * Ex: $msgWf = WorkflowMessage::create('my_alert_name', ['modelProps' => [...classProperties...]]);
29 * Instantiating by class-name will provide better hinting and inspection.
30 * However, some workflows may not have specific classes at the time of writing.
31 * Instantiating by workflow-name will work regardless of whether there is a specific class.
33 class WorkflowMessage
{
36 * Create a new instance of the workflow-message context.
38 * @param string $wfName
39 * Name of the workflow.
41 * @param array $imports
42 * List of data to use when populating the message.
44 * The parameters may be given in a mix of formats. This mix reflects two modes of operation:
46 * - (Informal/Generic) Traditionally, workflow-messages did not have formal parameters. Instead,
47 * they relied on a mix of un(der)documented/unverifiable inputs -- supplied as a mix of Smarty
48 * assignments, token-data, and sendTemplate() params.
49 * - (Formal) More recently, workflow-messages could be defined with a PHP class that lists the
52 * You may supply inputs using these keys:
54 * - `tplParams` (array): Smarty data. These values go to `$smarty->assign()`.
55 * - `tokenContext` (array): Token-processing data. These values go to `$tokenProcessor->context`.
56 * - `envelope` (array): Email delivery data. These values go to `sendTemplate(...)`
57 * - `modelProps` (array): Formal parameters defined by a class.
59 * Informal workflow-messages ONLY support 'tplParams', 'tokenContext', and/or 'envelope'.
60 * Formal workflow-messages accept any format.
62 * @return \Civi\WorkflowMessage\WorkflowMessageInterface
63 * If there is a workflow-message class, then it will return an instance of that class.
64 * Otherwise, it will return an instance of `GenericWorkflowMessage`.
65 * @throws \Civi\WorkflowMessage\Exception\WorkflowMessageException
67 public static function create(string $wfName, array $imports = []) {
68 $classMap = static::getWorkflowNameClassMap();
69 $class = $classMap[$wfName] ??
'Civi\WorkflowMessage\GenericWorkflowMessage';
70 $imports['envelope']['workflow'] = $wfName;
71 $model = new $class();
72 static::importAll($model, $imports);
77 * Import a batch of params, updating the $model.
79 * @param \Civi\WorkflowMessage\WorkflowMessageInterface $model
80 * @param array $params
81 * List of parameters, per MessageTemplate::sendTemplate().
82 * Ex: Initialize using adhoc data:
83 * ['tplParams' => [...], 'tokenContext' => [...]]
84 * Ex: Initialize using properties of the class-model
85 * ['modelProps' => [...]]
86 * @return \Civi\WorkflowMessage\WorkflowMessageInterface
88 * @throws \Civi\WorkflowMessage\Exception\WorkflowMessageException
90 public static function importAll(WorkflowMessageInterface
$model, array $params) {
91 // The $params format is defined to match the traditional format of CRM_Core_BAO_MessageTemplate::sendTemplate().
92 // At the top level, it is an "envelope", but it also has keys for other sections.
93 if (isset($params['model'])) {
94 if ($params['model'] !== $model) {
95 throw new WorkflowMessageException(sprintf("%s: Cannot apply mismatched model", get_class($model)));
97 unset($params['model']);
100 if (isset($params['tplParams'])) {
101 $model->import('tplParams', $params['tplParams']);
102 unset($params['tplParams']);
104 if (isset($params['tokenContext'])) {
105 $model->import('tokenContext', $params['tokenContext']);
106 unset($params['tokenContext']);
108 if (isset($params['modelProps'])) {
109 $model->import('modelProps', $params['modelProps']);
110 unset($params['modelProps']);
112 if (isset($params['envelope'])) {
113 $model->import('envelope', $params['envelope']);
114 unset($params['envelope']);
116 $model->import('envelope', $params);
121 * @param \Civi\WorkflowMessage\WorkflowMessageInterface $model
123 * List of parameters, per MessageTemplate::sendTemplate().
124 * Ex: ['tplParams' => [...], 'tokenContext' => [...]]
126 public static function exportAll(WorkflowMessageInterface
$model): array {
127 // The format is defined to match the traditional format of CRM_Core_BAO_MessageTemplate::sendTemplate().
128 // At the top level, it is an "envelope", but it also has keys for other sections.
129 $values = $model->export('envelope');
130 $values['tplParams'] = $model->export('tplParams');
131 $values['tokenContext'] = $model->export('tokenContext');
132 if (isset($values['tokenContext']['contactId'])) {
133 $values['contactId'] = $values['tokenContext']['contactId'];
140 * Array(string $workflowName => string $className).
141 * Ex: ["case_activity" => "CRM_Case_WorkflowMessage_CaseActivity"]
144 public static function getWorkflowNameClassMap() {
145 $cache = \Civi
::cache('long');
146 $cacheKey = 'WorkflowMessage-' . __FUNCTION__
;
147 $map = $cache->get($cacheKey);
150 foreach (ClassScanner
::get(['interface' => WorkflowMessageInterface
::class]) as $wfClass) {
151 $wfName = ($wfClass === GenericWorkflowMessage
::class) ?
'generic' : $wfClass::WORKFLOW
;
152 $map[$wfName] = $wfClass;
154 $cache->set($cacheKey, $map);
160 * Get general description of available workflow-messages.
163 * Array(string $workflowName => string $className).
164 * Ex: ["case_activity" => ["name" => "case_activity", "group" => "msg_workflow_case"]
167 public static function getWorkflowSpecs() {
168 $compute = function() {
169 $keys = ['name', 'group', 'class', 'description', 'comment', 'support'];
171 foreach (self
::getWorkflowNameClassMap() as $name => $class) {
174 'group' => \CRM_Utils_Constant
::value($class . '::GROUP'),
177 $list[$name] = \CRM_Utils_Array
::subset(
178 array_merge(ReflectionUtils
::getCodeDocs(new \
ReflectionClass($class)), $specs),
184 $cache = \Civi
::cache('long');
185 $cacheKey = 'WorkflowMessage-' . __FUNCTION__
;
186 $list = $cache->get($cacheKey);
187 if ($list === NULL) {
188 $cache->set($cacheKey, $list = $compute());