-
-
Notifications
You must be signed in to change notification settings - Fork 825
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #21338 from totten/master-msgtpl-api
(dev/mail#83) Introduce WorkflowMessage APIs with CaseActivity example
- Loading branch information
Showing
22 changed files
with
1,511 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
<?php | ||
|
||
/* | ||
+--------------------------------------------------------------------+ | ||
| Copyright CiviCRM LLC. All rights reserved. | | ||
| | | ||
| This work is published under the GNU AGPLv3 license with some | | ||
| permitted exceptions and without any warranty. For full license | | ||
| and copyright information, see https://civicrm.org/licensing | | ||
+--------------------------------------------------------------------+ | ||
*/ | ||
|
||
namespace Civi\Api4\Action\ExampleData; | ||
|
||
use Civi\Api4\Generic\BasicGetAction; | ||
use Civi\Api4\Generic\Result; | ||
use Civi\Test\ExampleDataLoader; | ||
|
||
/** | ||
* Get a list of example data-sets. | ||
* | ||
* Examples are generated by scanning `*.ex.php` files. The scanner caches | ||
* metadata fields (`name`, `title`, `tags`, `file`) to avoid extraneous scanning, but | ||
* substantive fields (`data`) are computed as-needed. | ||
* | ||
* FIXME: When we have an update for dev-docs, include a `@link` here. | ||
*/ | ||
class Get extends BasicGetAction { | ||
|
||
public function _run(Result $result) { | ||
if ($this->select !== [] && !in_array('name', $this->select)) { | ||
$this->select[] = 'name'; | ||
} | ||
parent::_run($result); | ||
} | ||
|
||
protected function getRecords() { | ||
return \Civi\Test::examples()->getMetas(); | ||
} | ||
|
||
protected function selectArray($values) { | ||
$result = parent::selectArray($values); | ||
|
||
$heavyFields = array_intersect( | ||
explode(',', ExampleDataLoader::HEAVY_FIELDS), | ||
$this->select ?: [] | ||
); | ||
if (!empty($heavyFields)) { | ||
foreach ($result as &$item) { | ||
$heavy = \Civi\Test::examples()->getFull($item['name']); | ||
$item = array_merge($item, \CRM_Utils_Array::subset($heavy, $heavyFields)); | ||
} | ||
} | ||
|
||
return $result; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
<?php | ||
|
||
namespace Civi\Api4\Action\WorkflowMessage; | ||
|
||
/** | ||
* Class GetTemplateFields | ||
* @package Civi\Api4\Action\WorkflowMessage | ||
* | ||
* @method $this setWorkflow(string $workflow) | ||
* @method string getWorkflow() | ||
* @method $this setFormat(string $workflow) | ||
* @method string getFormat() | ||
*/ | ||
class GetTemplateFields extends \Civi\Api4\Generic\BasicGetAction { | ||
|
||
/** | ||
* @var string | ||
* @required | ||
*/ | ||
public $workflow; | ||
|
||
/** | ||
* Controls the return format. | ||
* - 'metadata': Return the fields as an array of metadata | ||
* - 'example': Return the fields as an example record (a basis for passing into Render::$values). | ||
* | ||
* @var string | ||
* @options metadata,example | ||
*/ | ||
protected $format = 'metadata'; | ||
|
||
protected function getRecords() { | ||
$item = \Civi\WorkflowMessage\WorkflowMessage::create($this->workflow); | ||
/** @var \Civi\WorkflowMessage\FieldSpec[] $fields */ | ||
$fields = $item->getFields(); | ||
$array = []; | ||
$genericExamples = [ | ||
'string[]' => ['example-string1', 'example-string2...'], | ||
'string' => 'example-string', | ||
'int[]' => [1, 2, 3], | ||
'int' => 123, | ||
'double[]' => [1.23, 4.56], | ||
'double' => 1.23, | ||
'array' => [], | ||
]; | ||
|
||
switch ($this->format) { | ||
case 'metadata': | ||
foreach ($fields as $name => $field) { | ||
$array[$name] = $field->toArray(); | ||
} | ||
return $array; | ||
|
||
case 'example': | ||
foreach ($fields as $name => $field) { | ||
$array[$name] = NULL; | ||
foreach (array_intersect(array_keys($genericExamples), $field->getType()) as $ex) { | ||
$array[$name] = $genericExamples[$ex]; | ||
} | ||
} | ||
ksort($array); | ||
return [$array]; | ||
|
||
default: | ||
throw new \RuntimeException("Unrecognized format"); | ||
} | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
<?php | ||
|
||
/* | ||
+--------------------------------------------------------------------+ | ||
| Copyright CiviCRM LLC. All rights reserved. | | ||
| | | ||
| This work is published under the GNU AGPLv3 license with some | | ||
| permitted exceptions and without any warranty. For full license | | ||
| and copyright information, see https://civicrm.org/licensing | | ||
+--------------------------------------------------------------------+ | ||
*/ | ||
|
||
namespace Civi\Api4\Action\WorkflowMessage; | ||
|
||
use Civi\Api4\Event\ValidateValuesEvent; | ||
use Civi\WorkflowMessage\WorkflowMessage; | ||
|
||
/** | ||
* Render a message. | ||
* | ||
* @method $this setValues(array $rows) Set the list of records to be rendered. | ||
* @method array getValues() | ||
* @method $this setMessageTemplate(array|null $fragments) Set of messages to be rendered. | ||
* @method array|null getMessageTemplate() | ||
* @method $this setMessageTemplateId(int|null $id) Set of messages to be rendered. | ||
* @method int|null getMessageTemplateId() | ||
* @method $this setWorkflow(string $workflow) | ||
* @method string getWorkflow() | ||
* @method $this setErrorLevel(string $workflow) | ||
* @method string getErrorLevel() | ||
*/ | ||
class Render extends \Civi\Api4\Generic\AbstractAction { | ||
|
||
/** | ||
* Abort if the validator finds any issues at this error level. | ||
* | ||
* @var string | ||
* @options error,warning,info | ||
*/ | ||
protected $errorLevel = 'error'; | ||
|
||
/** | ||
* Symbolic name of the workflow step for which we need a message. | ||
* @var string | ||
* @required | ||
*/ | ||
protected $workflow; | ||
|
||
/** | ||
* @var array | ||
*/ | ||
protected $values = []; | ||
|
||
/** | ||
* Load and render a specific message template (by ID). | ||
* | ||
* @var int|null | ||
*/ | ||
protected $messageTemplateId; | ||
|
||
/** | ||
* Use a draft message template. | ||
* | ||
* @var array|null | ||
* - `subject`: Message template (eg `Hello {contact.first_name}!`) | ||
* - `text`: Message template (eg `Hello {contact.first_name}!`) | ||
* - `html`: Message template (eg `<p>Hello {contact.first_name}!</p>`) | ||
*/ | ||
protected $messageTemplate; | ||
|
||
/** | ||
* @var \Civi\WorkflowMessage\WorkflowMessageInterface | ||
*/ | ||
protected $_model; | ||
|
||
public function _run(\Civi\Api4\Generic\Result $result) { | ||
$this->validateValues(); | ||
|
||
$r = \CRM_Core_BAO_MessageTemplate::renderTemplate([ | ||
'model' => $this->_model, | ||
'messageTemplate' => $this->getMessageTemplate(), | ||
'messageTemplateId' => $this->getMessageTemplateId(), | ||
]); | ||
|
||
$result[] = \CRM_Utils_Array::subset($r, ['subject', 'html', 'text']); | ||
} | ||
|
||
/** | ||
* The token-processor supports a range of context parameters. We enforce different security rules for each kind of input. | ||
* | ||
* Broadly, this distinguishes between a few values: | ||
* - Autoloaded data (e.g. 'contactId', 'activityId'). We need to ensure that the specific records are visible and extant. | ||
* - Inputted data (e.g. 'contact'). We merely ensure that these are type-correct. | ||
* - Prohibited/extended options, e.g. 'smarty' | ||
*/ | ||
protected function validateValues() { | ||
$rows = [$this->getValues()]; | ||
$e = new ValidateValuesEvent($this, $rows, new \CRM_Utils_LazyArray(function () use ($rows) { | ||
return array_map( | ||
function ($row) { | ||
return ['old' => NULL, 'new' => $row]; | ||
}, | ||
$rows | ||
); | ||
})); | ||
$this->onValidateValues($e); | ||
\Civi::dispatcher()->dispatch('civi.api4.validate', $e); | ||
if (!empty($e->errors)) { | ||
throw $e->toException(); | ||
} | ||
} | ||
|
||
protected function onValidateValues(ValidateValuesEvent $e) { | ||
$errorWeightMap = \CRM_Core_Error_Log::getMap(); | ||
$errorWeight = $errorWeightMap[$this->getErrorLevel()]; | ||
|
||
if (count($e->records) !== 1) { | ||
throw new \CRM_Core_Exception("Expected exactly one record to validate"); | ||
} | ||
foreach ($e->records as $recordKey => $record) { | ||
/** @var \Civi\WorkflowMessage\WorkflowMessageInterface $w */ | ||
$w = $this->_model = WorkflowMessage::create($this->getWorkflow(), [ | ||
'modelProps' => $record, | ||
]); | ||
$fields = $w->getFields(); | ||
|
||
$unknowns = array_diff(array_keys($record), array_keys($fields)); | ||
foreach ($unknowns as $fieldName) { | ||
$e->addError($recordKey, $fieldName, 'unknown_field', ts('Unknown field (%1). Templates may only be executed with supported fields.', [ | ||
1 => $fieldName, | ||
])); | ||
} | ||
|
||
// Merge intrinsic validations | ||
foreach ($w->validate() as $issue) { | ||
if ($errorWeightMap[$issue['severity']] < $errorWeight) { | ||
$e->addError($recordKey, $issue['fields'], $issue['name'], $issue['message']); | ||
} | ||
} | ||
|
||
// Add checks which don't fit in WFM::validate | ||
foreach ($fields as $fieldName => $fieldSpec) { | ||
$fieldValue = $record[$fieldName] ?? NULL; | ||
if ($fieldSpec->getFkEntity() && !empty($fieldValue)) { | ||
if (!empty($params['check_permissions']) && !\Civi\Api4\Utils\CoreUtil::checkAccessDelegated($fieldSpec->getFkEntity(), 'get', ['id' => $fieldValue], CRM_Core_Session::getLoggedInContactID() ?: 0)) { | ||
$e->addError($recordKey, $fieldName, 'nonexistent_id', ts('Referenced record does not exist or is not visible (%1).', [ | ||
1 => $this->getWorkflow() . '::' . $fieldName, | ||
])); | ||
} | ||
} | ||
} | ||
} | ||
} | ||
|
||
public function fields() { | ||
return []; | ||
// We don't currently get the name of the workflow. But if we do... | ||
//$item = \Civi\WorkflowMessage\WorkflowMessage::create($this->workflow); | ||
///** @var \Civi\WorkflowMessage\FieldSpec[] $fields */ | ||
//$fields = $item->getFields(); | ||
//$array = []; | ||
//foreach ($fields as $name => $field) { | ||
// $array[$name] = $field->toArray(); | ||
//} | ||
//return $array; | ||
} | ||
|
||
} |
Oops, something went wrong.