3 use Civi\Token\Event\TokenRenderEvent
;
9 * A TokenRow is a helper providing simplified access to the
12 * A TokenRow combines two elements:
13 * - context: This is backend data provided by the controller.
14 * - tokens: This is frontend data that can be mail-merged.
16 * The context and tokens can be accessed using either methods
17 * or attributes. The methods are appropriate for updates
18 * (and generally accept a mix of arrays), and the attributes
19 * are appropriate for reads.
21 * To update the context or the tokens, use the methods.
22 * Note that the methods are fairly flexible about accepting
23 * single values or arrays. If given an array, the values
24 * will be merged recursively.
28 * ->context('contact_id', 123)
29 * ->context(array('contact_id' => 123))
30 * ->tokens('profile', array('viewUrl' => 'http://example.com'))
31 * ->tokens('profile', 'viewUrl, 'http://example.com');
33 * echo $row->context['contact_id'];
34 * echo $row->tokens['profile']['viewUrl'];
36 * $row->tokens('profile', array(
37 * 'viewUrl' => 'http://example.com/view/' . urlencode($row->context['contact_id'];
46 public $tokenProcessor;
53 * @var array|\ArrayAccess
54 * List of token values.
55 * Ex: array('contact' => array('display_name' => 'Alice')).
60 * @var array|\ArrayAccess
61 * List of context values.
62 * Ex: array('controller' => 'CRM_Foo_Bar').
66 public function __construct(TokenProcessor
$tokenProcessor, $key) {
67 $this->tokenProcessor
= $tokenProcessor;
68 $this->tokenRow
= $key;
69 $this->format('text/plain'); // Set a default.
70 $this->context
= new TokenRowContext($tokenProcessor, $key);
74 * @param string $format
77 public function format($format) {
78 $this->format
= $format;
79 $this->tokens
= &$this->tokenProcessor
->rowValues
[$this->tokenRow
][$format];
84 * Update the value of a context element.
86 * @param string|array $a
90 public function context($a = NULL, $b = NULL) {
92 \CRM_Utils_Array
::extend($this->tokenProcessor
->rowContexts
[$this->tokenRow
], $a);
94 elseif (is_array($b)) {
95 \CRM_Utils_Array
::extend($this->tokenProcessor
->rowContexts
[$this->tokenRow
][$a], $b);
98 $this->tokenProcessor
->rowContexts
[$this->tokenRow
][$a] = $b;
104 * Update the value of a token.
106 * @param string|array $a
107 * @param string|array $b
111 public function tokens($a = NULL, $b = NULL, $c = NULL) {
113 \CRM_Utils_Array
::extend($this->tokens
, $a);
115 elseif (is_array($b)) {
116 \CRM_Utils_Array
::extend($this->tokens
[$a], $b);
118 elseif (is_array($c)) {
119 \CRM_Utils_Array
::extend($this->tokens
[$a][$b], $c);
121 elseif ($c === NULL) {
122 $this->tokens
[$a] = $b;
125 $this->tokens
[$a][$b] = $c;
131 * Update the value of a token. Apply formatting based on DB schema.
133 * @param string $tokenEntity
134 * @param string $tokenField
135 * @param string $baoName
136 * @param array $baoField
137 * @param mixed $fieldValue
139 public function dbToken($tokenEntity, $tokenField, $baoName, $baoField, $fieldValue) {
140 if ($fieldValue === NULL ||
$fieldValue === '') {
141 return $this->tokens($tokenEntity, $tokenField, '');
144 $fields = $baoName::fields();
145 if (!empty($fields[$baoField]['pseudoconstant'])) {
146 $options = $baoName::buildOptions($baoField, 'get');
147 return $this->format('text/plain')->tokens($tokenEntity, $tokenField, $options[$fieldValue]);
150 switch ($fields[$baoField]['type']) {
151 case \CRM_Utils_Type
::T_DATE + \CRM_Utils_Type
::T_TIME
:
152 return $this->format('text/plain')->tokens($tokenEntity, $tokenField, \CRM_Utils_Date
::customFormat($fieldValue));
154 case \CRM_Utils_Type
::T_MONEY
:
155 // Is this something you should ever use? Seems like you need more context
156 // to know which currency to use.
157 return $this->format('text/plain')->tokens($tokenEntity, $tokenField, \CRM_Utils_Money
::format($fieldValue));
159 case \CRM_Utils_Type
::T_STRING
:
160 case \CRM_Utils_Type
::T_BOOLEAN
:
161 case \CRM_Utils_Type
::T_INT
:
162 case \CRM_Utils_Type
::T_TEXT
:
163 return $this->format('text/plain')->tokens($tokenEntity, $tokenField, $fieldValue);
167 throw new \
CRM_Core_Exception("Cannot format token for field '$baoField' in '$baoName'");
171 * Auto-convert between different formats
173 * @param string $format
177 public function fill($format = NULL) {
178 if ($format === NULL) {
179 $format = $this->format
;
182 if (!isset($this->tokenProcessor
->rowValues
[$this->tokenRow
]['text/html'])) {
183 $this->tokenProcessor
->rowValues
[$this->tokenRow
]['text/html'] = array();
185 if (!isset($this->tokenProcessor
->rowValues
[$this->tokenRow
]['text/plain'])) {
186 $this->tokenProcessor
->rowValues
[$this->tokenRow
]['text/plain'] = array();
189 $htmlTokens = &$this->tokenProcessor
->rowValues
[$this->tokenRow
]['text/html'];
190 $textTokens = &$this->tokenProcessor
->rowValues
[$this->tokenRow
]['text/plain'];
195 foreach ($textTokens as $entity => $values) {
196 foreach ($values as $field => $value) {
197 if (!isset($htmlTokens[$entity][$field])) {
198 $htmlTokens[$entity][$field] = htmlentities($value);
206 foreach ($htmlTokens as $entity => $values) {
207 foreach ($values as $field => $value) {
208 if (!isset($textTokens[$entity][$field])) {
209 $textTokens[$entity][$field] = html_entity_decode(strip_tags($value));
216 throw new \
RuntimeException("Invalid format");
225 * @param string $name
226 * The name previously registered with TokenProcessor::addMessage.
228 * Fully rendered message, with tokens merged.
230 public function render($name) {
231 return $this->tokenProcessor
->render($name, $this);
237 * Class TokenRowContext
238 * @package Civi\Token
240 * Combine the row-context and general-context into a single array-like facade.
242 class TokenRowContext
implements \ArrayAccess
, \IteratorAggregate
, \Countable
{
245 * @var TokenProcessor
247 protected $tokenProcessor;
252 * @param $tokenProcessor
255 public function __construct($tokenProcessor, $tokenRow) {
256 $this->tokenProcessor
= $tokenProcessor;
257 $this->tokenRow
= $tokenRow;
260 public function offsetExists($offset) {
262 isset($this->tokenProcessor
->rowContexts
[$this->tokenRow
][$offset])
263 ||
isset($this->tokenProcessor
->context
[$offset]);
266 public function &offsetGet($offset) {
267 if (isset($this->tokenProcessor
->rowContexts
[$this->tokenRow
][$offset])) {
268 return $this->tokenProcessor
->rowContexts
[$this->tokenRow
][$offset];
270 if (isset($this->tokenProcessor
->context
[$offset])) {
271 return $this->tokenProcessor
->context
[$offset];
277 public function offsetSet($offset, $value) {
278 $this->tokenProcessor
->rowContexts
[$this->tokenRow
][$offset] = $value;
281 public function offsetUnset($offset) {
282 unset($this->tokenProcessor
->rowContexts
[$this->tokenRow
][$offset]);
285 public function getIterator() {
286 return new \
ArrayIterator($this->createMergedArray());
289 public function count() {
290 return count($this->createMergedArray());
293 protected function createMergedArray() {
295 $this->tokenProcessor
->rowContexts
[$this->tokenRow
],
296 $this->tokenProcessor
->context