drupal-civicrm/sites/all/modules/civicrm/Civi/Token/TokenRow.php
2018-01-14 13:10:16 +00:00

375 lines
9.7 KiB
PHP

<?php
namespace Civi\Token;
/**
* Class TokenRow
* @package Civi\Token
*
* A TokenRow is a helper providing simplified access to the
* TokenProcessor.
*
* A TokenRow combines two elements:
* - context: This is backend data provided by the controller.
* - tokens: This is frontend data that can be mail-merged.
*
* The context and tokens can be accessed using either methods
* or attributes. The methods are appropriate for updates
* (and generally accept a mix of arrays), and the attributes
* are appropriate for reads.
*
* To update the context or the tokens, use the methods.
* Note that the methods are fairly flexible about accepting
* single values or arrays. If given an array, the values
* will be merged recursively.
*
* @code
* $row
* ->context('contact_id', 123)
* ->context(array('contact_id' => 123))
* ->tokens('profile', array('viewUrl' => 'http://example.com'))
* ->tokens('profile', 'viewUrl, 'http://example.com');
*
* echo $row->context['contact_id'];
* echo $row->tokens['profile']['viewUrl'];
*
* $row->tokens('profile', array(
* 'viewUrl' => 'http://example.com/view/' . urlencode($row->context['contact_id'];
* ));
* @endcode
*/
class TokenRow {
/**
* @var TokenProcessor
*/
public $tokenProcessor;
public $tokenRow;
public $format;
/**
* @var array|\ArrayAccess
* List of token values.
* Ex: array('contact' => array('display_name' => 'Alice')).
*/
public $tokens;
/**
* @var array|\ArrayAccess
* List of context values.
* Ex: array('controller' => 'CRM_Foo_Bar').
*/
public $context;
public function __construct(TokenProcessor $tokenProcessor, $key) {
$this->tokenProcessor = $tokenProcessor;
$this->tokenRow = $key;
$this->format('text/plain'); // Set a default.
$this->context = new TokenRowContext($tokenProcessor, $key);
}
/**
* @param string $format
* @return TokenRow
*/
public function format($format) {
$this->format = $format;
$this->tokens = &$this->tokenProcessor->rowValues[$this->tokenRow][$format];
return $this;
}
/**
* Update the value of a context element.
*
* @param string|array $a
* @param mixed $b
* @return TokenRow
*/
public function context($a = NULL, $b = NULL) {
if (is_array($a)) {
\CRM_Utils_Array::extend($this->tokenProcessor->rowContexts[$this->tokenRow], $a);
}
elseif (is_array($b)) {
\CRM_Utils_Array::extend($this->tokenProcessor->rowContexts[$this->tokenRow][$a], $b);
}
else {
$this->tokenProcessor->rowContexts[$this->tokenRow][$a] = $b;
}
return $this;
}
/**
* Update the value of a token.
*
* @param string|array $a
* @param string|array $b
* @param mixed $c
* @return TokenRow
*/
public function tokens($a = NULL, $b = NULL, $c = NULL) {
if (is_array($a)) {
\CRM_Utils_Array::extend($this->tokens, $a);
}
elseif (is_array($b)) {
\CRM_Utils_Array::extend($this->tokens[$a], $b);
}
elseif (is_array($c)) {
\CRM_Utils_Array::extend($this->tokens[$a][$b], $c);
}
elseif ($c === NULL) {
$this->tokens[$a] = $b;
}
else {
$this->tokens[$a][$b] = $c;
}
return $this;
}
/**
* Update the value of a custom field token.
*
* @param string $entity
* @param int $customFieldID
* @param int $entityID
* @return TokenRow
*/
public function customToken($entity, $customFieldID, $entityID) {
$customFieldName = "custom_" . $customFieldID;
$fieldValue = civicrm_api3($entity, 'getvalue', array(
'return' => $customFieldName,
'id' => $entityID,
));
// format the raw custom field value into proper display value
if ($fieldValue) {
$fieldValue = \CRM_Core_BAO_CustomField::displayValue($fieldValue, $customFieldID);
}
return $this->tokens($entity, $customFieldName, $fieldValue);
}
/**
* Update the value of a token. Apply formatting based on DB schema.
*
* @param string $tokenEntity
* @param string $tokenField
* @param string $baoName
* @param array $baoField
* @param mixed $fieldValue
* @return TokenRow
* @throws \CRM_Core_Exception
*/
public function dbToken($tokenEntity, $tokenField, $baoName, $baoField, $fieldValue) {
if ($fieldValue === NULL || $fieldValue === '') {
return $this->tokens($tokenEntity, $tokenField, '');
}
$fields = $baoName::fields();
if (!empty($fields[$baoField]['pseudoconstant'])) {
$options = $baoName::buildOptions($baoField, 'get');
return $this->format('text/plain')->tokens($tokenEntity, $tokenField, $options[$fieldValue]);
}
switch ($fields[$baoField]['type']) {
case \CRM_Utils_Type::T_DATE + \CRM_Utils_Type::T_TIME:
return $this->format('text/plain')->tokens($tokenEntity, $tokenField, \CRM_Utils_Date::customFormat($fieldValue));
case \CRM_Utils_Type::T_MONEY:
// Is this something you should ever use? Seems like you need more context
// to know which currency to use.
return $this->format('text/plain')->tokens($tokenEntity, $tokenField, \CRM_Utils_Money::format($fieldValue));
case \CRM_Utils_Type::T_STRING:
case \CRM_Utils_Type::T_BOOLEAN:
case \CRM_Utils_Type::T_INT:
case \CRM_Utils_Type::T_TEXT:
return $this->format('text/plain')->tokens($tokenEntity, $tokenField, $fieldValue);
}
throw new \CRM_Core_Exception("Cannot format token for field '$baoField' in '$baoName'");
}
/**
* Auto-convert between different formats
*
* @param string $format
*
* @return TokenRow
*/
public function fill($format = NULL) {
if ($format === NULL) {
$format = $this->format;
}
if (!isset($this->tokenProcessor->rowValues[$this->tokenRow]['text/html'])) {
$this->tokenProcessor->rowValues[$this->tokenRow]['text/html'] = array();
}
if (!isset($this->tokenProcessor->rowValues[$this->tokenRow]['text/plain'])) {
$this->tokenProcessor->rowValues[$this->tokenRow]['text/plain'] = array();
}
$htmlTokens = &$this->tokenProcessor->rowValues[$this->tokenRow]['text/html'];
$textTokens = &$this->tokenProcessor->rowValues[$this->tokenRow]['text/plain'];
switch ($format) {
case 'text/html':
// Plain => HTML.
foreach ($textTokens as $entity => $values) {
foreach ($values as $field => $value) {
if (!isset($htmlTokens[$entity][$field])) {
// CRM-18420 - Activity Details Field are enclosed within <p>,
// hence if $body_text is empty, htmlentities will lead to
// conversion of these tags resulting in raw HTML.
if ($entity == 'activity' && $field == 'details') {
$htmlTokens[$entity][$field] = $value;
}
else {
$htmlTokens[$entity][$field] = htmlentities($value);
}
}
}
}
break;
case 'text/plain':
// HTML => Plain.
foreach ($htmlTokens as $entity => $values) {
foreach ($values as $field => $value) {
if (!isset($textTokens[$entity][$field])) {
$textTokens[$entity][$field] = html_entity_decode(strip_tags($value));
}
}
}
break;
default:
throw new \RuntimeException("Invalid format");
}
return $this;
}
/**
* Render a message.
*
* @param string $name
* The name previously registered with TokenProcessor::addMessage.
* @return string
* Fully rendered message, with tokens merged.
*/
public function render($name) {
return $this->tokenProcessor->render($name, $this);
}
}
/**
* Class TokenRowContext
* @package Civi\Token
*
* Combine the row-context and general-context into a single array-like facade.
*/
class TokenRowContext implements \ArrayAccess, \IteratorAggregate, \Countable {
/**
* @var TokenProcessor
*/
protected $tokenProcessor;
protected $tokenRow;
/**
* Class constructor.
*
* @param array $tokenProcessor
* @param array $tokenRow
*/
public function __construct($tokenProcessor, $tokenRow) {
$this->tokenProcessor = $tokenProcessor;
$this->tokenRow = $tokenRow;
}
/**
* Does offset exist.
*
* @param mixed $offset
*
* @return bool
*/
public function offsetExists($offset) {
return
isset($this->tokenProcessor->rowContexts[$this->tokenRow][$offset])
|| isset($this->tokenProcessor->context[$offset]);
}
/**
* Get offset.
*
* @param string $offset
*
* @return string
*/
public function &offsetGet($offset) {
if (isset($this->tokenProcessor->rowContexts[$this->tokenRow][$offset])) {
return $this->tokenProcessor->rowContexts[$this->tokenRow][$offset];
}
if (isset($this->tokenProcessor->context[$offset])) {
return $this->tokenProcessor->context[$offset];
}
$val = NULL;
return $val;
}
/**
* Set offset.
*
* @param string $offset
* @param mixed $value
*/
public function offsetSet($offset, $value) {
$this->tokenProcessor->rowContexts[$this->tokenRow][$offset] = $value;
}
/**
* Unset offset.
*
* @param mixed $offset
*/
public function offsetUnset($offset) {
unset($this->tokenProcessor->rowContexts[$this->tokenRow][$offset]);
}
/**
* Get iterator.
*
* @return \ArrayIterator
*/
public function getIterator() {
return new \ArrayIterator($this->createMergedArray());
}
/**
* Count.
*
* @return int
*/
public function count() {
return count($this->createMergedArray());
}
/**
* Create merged array.
*
* @return array
*/
protected function createMergedArray() {
return array_merge(
$this->tokenProcessor->rowContexts[$this->tokenRow],
$this->tokenProcessor->context
);
}
}