Update icons
[civicrm-core.git] / Civi / Token / TokenRow.php
CommitLineData
8adcd073
TO
1<?php
2namespace Civi\Token;
3use Civi\Token\Event\TokenRenderEvent;
4
5/**
6 * Class TokenRow
7 * @package Civi\Token
8 *
9 * A TokenRow is a helper providing simplified access to the
10 * TokenProcessor.
11 *
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.
15 *
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.
20 *
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.
25 *
26 * @code
27 * $row
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');
32 *
33 * echo $row->context['contact_id'];
34 * echo $row->tokens['profile']['viewUrl'];
35 *
36 * $row->tokens('profile', array(
37 * 'viewUrl' => 'http://example.com/view/' . urlencode($row->context['contact_id'];
38 * ));
39 * @endcode
40 */
41class TokenRow {
42
43 /**
44 * @var TokenProcessor
45 */
46 public $tokenProcessor;
47
48 public $tokenRow;
49
50 public $format;
51
52 /**
09c2328a 53 * @var array|\ArrayAccess
8adcd073
TO
54 * List of token values.
55 * Ex: array('contact' => array('display_name' => 'Alice')).
56 */
57 public $tokens;
58
59 /**
09c2328a 60 * @var array|\ArrayAccess
8adcd073
TO
61 * List of context values.
62 * Ex: array('controller' => 'CRM_Foo_Bar').
63 */
64 public $context;
65
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);
71 }
72
73 /**
74 * @param string $format
75 * @return $this
76 */
77 public function format($format) {
78 $this->format = $format;
79 $this->tokens = &$this->tokenProcessor->rowValues[$this->tokenRow][$format];
80 return $this;
81 }
82
83 /**
84 * Update the value of a context element.
85 *
86 * @param string|array $a
87 * @param mixed $b
88 * @return $this
89 */
90 public function context($a = NULL, $b = NULL) {
91 if (is_array($a)) {
92 \CRM_Utils_Array::extend($this->tokenProcessor->rowContexts[$this->tokenRow], $a);
93 }
94 elseif (is_array($b)) {
95 \CRM_Utils_Array::extend($this->tokenProcessor->rowContexts[$this->tokenRow][$a], $b);
96 }
97 else {
98 $this->tokenProcessor->rowContexts[$this->tokenRow][$a] = $b;
99 }
100 return $this;
101 }
102
103 /**
104 * Update the value of a token.
105 *
106 * @param string|array $a
107 * @param string|array $b
108 * @param mixed $c
109 * @return $this
110 */
111 public function tokens($a = NULL, $b = NULL, $c = NULL) {
112 if (is_array($a)) {
113 \CRM_Utils_Array::extend($this->tokens, $a);
114 }
115 elseif (is_array($b)) {
116 \CRM_Utils_Array::extend($this->tokens[$a], $b);
117 }
118 elseif (is_array($c)) {
119 \CRM_Utils_Array::extend($this->tokens[$a][$b], $c);
120 }
121 elseif ($c === NULL) {
122 $this->tokens[$a] = $b;
123 }
124 else {
125 $this->tokens[$a][$b] = $c;
126 }
127 return $this;
128 }
129
2045389a
TO
130 /**
131 * Update the value of a token. Apply formatting based on DB schema.
132 *
133 * @param string $tokenEntity
134 * @param string $tokenField
135 * @param string $baoName
136 * @param array $baoField
137 * @param mixed $fieldValue
138 */
139 public function dbToken($tokenEntity, $tokenField, $baoName, $baoField, $fieldValue) {
140 if ($fieldValue === NULL || $fieldValue === '') {
141 return $this->tokens($tokenEntity, $tokenField, '');
142 }
143
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]);
148 }
149
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));
153
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));
158
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);
164
165 }
166
167 throw new \CRM_Core_Exception("Cannot format token for field '$baoField' in '$baoName'");
168 }
169
8adcd073
TO
170 /**
171 * Auto-convert between different formats
54957108 172 *
173 * @param string $format
174 *
175 * @return $this
8adcd073
TO
176 */
177 public function fill($format = NULL) {
178 if ($format === NULL) {
179 $format = $this->format;
180 }
181
182 if (!isset($this->tokenProcessor->rowValues[$this->tokenRow]['text/html'])) {
183 $this->tokenProcessor->rowValues[$this->tokenRow]['text/html'] = array();
184 }
185 if (!isset($this->tokenProcessor->rowValues[$this->tokenRow]['text/plain'])) {
186 $this->tokenProcessor->rowValues[$this->tokenRow]['text/plain'] = array();
187 }
188
189 $htmlTokens = &$this->tokenProcessor->rowValues[$this->tokenRow]['text/html'];
190 $textTokens = &$this->tokenProcessor->rowValues[$this->tokenRow]['text/plain'];
191
192 switch ($format) {
193 case 'text/html':
194 // Plain => HTML.
195 foreach ($textTokens as $entity => $values) {
196 foreach ($values as $field => $value) {
197 if (!isset($htmlTokens[$entity][$field])) {
198 $htmlTokens[$entity][$field] = htmlentities($value);
199 }
200 }
201 }
202 break;
203
204 case 'text/plain':
205 // HTML => Plain.
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));
210 }
211 }
212 }
213 break;
214
215 default:
216 throw new \RuntimeException("Invalid format");
217 }
218
219 return $this;
220 }
221
222 /**
223 * Render a message.
224 *
225 * @param string $name
226 * The name previously registered with TokenProcessor::addMessage.
227 * @return string
228 * Fully rendered message, with tokens merged.
229 */
230 public function render($name) {
231 return $this->tokenProcessor->render($name, $this);
232 }
233
234}
235
236/**
237 * Class TokenRowContext
238 * @package Civi\Token
239 *
240 * Combine the row-context and general-context into a single array-like facade.
241 */
242class TokenRowContext implements \ArrayAccess, \IteratorAggregate, \Countable {
243
244 /**
245 * @var TokenProcessor
246 */
247 protected $tokenProcessor;
248
249 protected $tokenRow;
250
251 /**
e8e8f3ad 252 * Class constructor.
253 *
254 * @param array $tokenProcessor
255 * @param array $tokenRow
8adcd073
TO
256 */
257 public function __construct($tokenProcessor, $tokenRow) {
258 $this->tokenProcessor = $tokenProcessor;
259 $this->tokenRow = $tokenRow;
260 }
261
e8e8f3ad 262 /**
263 * Does offset exist.
264 *
265 * @param mixed $offset
266 *
267 * @return bool
268 */
8adcd073
TO
269 public function offsetExists($offset) {
270 return
271 isset($this->tokenProcessor->rowContexts[$this->tokenRow][$offset])
272 || isset($this->tokenProcessor->context[$offset]);
273 }
274
e8e8f3ad 275 /**
276 * Get offset.
277 *
278 * @param string $offset
279 *
280 * @return string
281 */
8adcd073
TO
282 public function &offsetGet($offset) {
283 if (isset($this->tokenProcessor->rowContexts[$this->tokenRow][$offset])) {
284 return $this->tokenProcessor->rowContexts[$this->tokenRow][$offset];
285 }
286 if (isset($this->tokenProcessor->context[$offset])) {
287 return $this->tokenProcessor->context[$offset];
288 }
289 $val = NULL;
290 return $val;
291 }
292
e8e8f3ad 293 /**
294 * Set offset.
295 *
296 * @param string $offset
297 * @param mixed $value
298 */
8adcd073
TO
299 public function offsetSet($offset, $value) {
300 $this->tokenProcessor->rowContexts[$this->tokenRow][$offset] = $value;
301 }
302
e8e8f3ad 303 /**
304 * Unset offset.
305 *
306 * @param mixed $offset
307 */
8adcd073
TO
308 public function offsetUnset($offset) {
309 unset($this->tokenProcessor->rowContexts[$this->tokenRow][$offset]);
310 }
311
e8e8f3ad 312 /**
313 * Get iterator.
314 *
315 * @return \ArrayIterator
316 */
8adcd073
TO
317 public function getIterator() {
318 return new \ArrayIterator($this->createMergedArray());
319 }
320
e8e8f3ad 321 /**
322 * Count.
323 *
324 * @return int
325 */
8adcd073
TO
326 public function count() {
327 return count($this->createMergedArray());
328 }
329
e8e8f3ad 330 /**
331 * Create merged array.
332 *
333 * @return array
334 */
8adcd073
TO
335 protected function createMergedArray() {
336 return array_merge(
337 $this->tokenProcessor->rowContexts[$this->tokenRow],
338 $this->tokenProcessor->context
339 );
340 }
341
342}