123])`). * 2. Defining tokens/entities/data-loaders: You need to listen for TokenProcessor * events; if any of your tokens/entities are used, then load the batch of data. * * Each use-case is presented with examples in the Developer Guide: * * @link https://docs.civicrm.org/dev/en/latest/framework/token/ */ class TokenProcessor { /** * @var array * Description of the context in which the tokens are being processed. * Ex: Array('class'=>'CRM_Core_BAO_ActionSchedule', 'schedule' => $dao, 'mapping' => $dao). * Ex: Array('class'=>'CRM_Mailing_BAO_MailingJob', 'mailing' => $dao). * * For lack of a better place, here's a list of known/intended context values: * * - controller: string, the class which is managing the mail-merge. * - smarty: bool, whether to enable smarty support. * - contactId: int, the main person/org discussed in the message. * - contact: array, the main person/org discussed in the message. * (Optional for performance tweaking; if omitted, will load * automatically from contactId.) * - actionSchedule: DAO, the rule which triggered the mailing * [for CRM_Core_BAO_ActionScheduler]. * - schema: array, a list of fields that will be provided for each row. * This is automatically populated with any general context * keys, but you may need to add extra keys for token-row data. * ex: ['contactId', 'activityId']. */ public $context; /** * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface */ protected $dispatcher; /** * @var array * Each message is an array with keys: * - string: Unprocessed message (eg "Hello, {display_name}."). * - format: Media type (eg "text/plain"). * - tokens: List of tokens which are actually used in this message. */ protected $messages; /** * DO NOT access field this directly. Use TokenRow. This is * marked as public only to benefit TokenRow. * * @var array * Array(int $pos => array $keyValues); */ public $rowContexts; /** * DO NOT access field this directly. Use TokenRow. This is * marked as public only to benefit TokenRow. * * @var array * Ex: $rowValues[$rowPos][$format][$entity][$field] = 'something'; * Ex: $rowValues[3]['text/plain']['contact']['display_name'] = 'something'; */ public $rowValues; /** * A list of available tokens * @var array * Array(string $dottedName => array('entity'=>string, 'field'=>string, 'label'=>string)). */ protected $tokens = NULL; /** * A list of available tokens formatted for display * @var array * Array('{' . $dottedName . '}' => 'labelString') */ protected $listTokens = NULL; protected $next = 0; /** * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher * @param array $context */ public function __construct($dispatcher, $context) { $context['schema'] = isset($context['schema']) ? array_unique(array_merge($context['schema'], array_keys($context))) : array_keys($context); $this->dispatcher = $dispatcher; $this->context = $context; } /** * Register a string for which we'll need to merge in tokens. * * @param string $name * Ex: 'subject', 'body_html'. * @param string $value * Ex: '
Hello {contact.name}
'. * @param string $format * Ex: 'text/html'. * @return TokenProcessor */ public function addMessage($name, $value, $format) { $this->messages[$name] = [ 'string' => $value, 'format' => $format, 'tokens' => \CRM_Utils_Token::getTokens($value), ]; return $this; } /** * Add a row of data. * * @param array|NULL $context * Optionally, initialize the context for this row. * Ex: ['contact_id' => 123]. * @return TokenRow */ public function addRow($context = NULL) { $key = $this->next++; $this->rowContexts[$key] = []; $this->rowValues[$key] = [ 'text/plain' => [], 'text/html' => [], ]; $row = new TokenRow($this, $key); if ($context !== NULL) { $row->context($context); } return $row; } /** * Add several rows. * * @param array $contexts * List of rows to add. * Ex: [['contact_id'=>123], ['contact_id'=>456]] * @return TokenRow[] * List of row objects */ public function addRows($contexts) { $rows = []; foreach ($contexts as $context) { $row = $this->addRow($context); $rows[$row->tokenRow] = $row; } return $rows; } /** * @param array $params * Array with keys: * - entity: string, e.g. "profile". * - field: string, e.g. "viewUrl". * - label: string, e.g. "Default Profile URL (View Mode)". * @return TokenProcessor */ public function addToken($params) { $key = $params['entity'] . '.' . $params['field']; $this->tokens[$key] = $params; return $this; } /** * @param string $name * @return array * Keys: * - string: Unprocessed message (eg "Hello, {display_name}."). * - format: Media type (eg "text/plain"). */ public function getMessage($name) { return $this->messages[$name]; } /** * Get a list of all tokens used in registered messages. * * @return array * The list of activated tokens, indexed by object/entity. * Array(string $entityName => string[] $fieldNames) * * Ex: If a message says 'Hello {contact.first_name} {contact.last_name}!', * then $result['contact'] would be ['first_name', 'last_name']. */ public function getMessageTokens() { $tokens = []; foreach ($this->messages as $message) { $tokens = \CRM_Utils_Array::crmArrayMerge($tokens, $message['tokens']); } foreach (array_keys($tokens) as $e) { $tokens[$e] = array_unique($tokens[$e]); sort($tokens[$e]); } return $tokens; } /** * Get a specific row (i.e. target or recipient). * * Ex: echo $p->getRow(2)->context['contact_id']; * Ex: $p->getRow(3)->token('profile', 'viewUrl', 'http://example.com/profile?cid=3'); * * @param int $key * The row ID * @return \Civi\Token\TokenRow * The row is presented with a fluent, OOP facade. * @see TokenRow */ public function getRow($key) { return new TokenRow($this, $key); } /** * Get the list of rows (i.e. targets/recipients to generate). * * @see TokenRow * @return \Traversable