Skip to content

Commit

Permalink
dev/core#2947 - Auto-enable {event.*} if participantId is present
Browse files Browse the repository at this point in the history
  • Loading branch information
totten committed Nov 12, 2021
1 parent ddd5619 commit 6cce990
Show file tree
Hide file tree
Showing 4 changed files with 147 additions and 0 deletions.
4 changes: 4 additions & 0 deletions Civi/Core/Container.php
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,10 @@ public function createContainer() {
[]
))->addTag('kernel.event_subscriber')->setPublic(TRUE);
}
$container->setDefinition('civi_token_impliedcontext', new Definition(
'Civi\Token\ImpliedContextSubscriber',
[]
))->addTag('kernel.event_subscriber')->setPublic(TRUE);
$container->setDefinition('crm_participant_tokens', new Definition(
'CRM_Event_ParticipantTokens',
[]
Expand Down
105 changes: 105 additions & 0 deletions Civi/Token/ImpliedContextSubscriber.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
<?php
namespace Civi\Token;

use Civi\Token\Event\TokenRegisterEvent;
use Civi\Token\Event\TokenValueEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

/**
* Suppose you have `$context['participantId']`. You can infer a
* corresponding `$context['eventId']`. The ImpliedContextSubscriber reads
* values like `participantId` and fills-in values like `eventId`.
*
* @package Civi\Token
*/
class ImpliedContextSubscriber implements EventSubscriberInterface {

public static function getSubscribedEvents() {
return [
'civi.token.list' => ['onRegisterTokens', 1000],
'civi.token.eval' => ['onEvaluateTokens', 1000],
];
}

/**
* When listing tokens, ensure that implied data is visible.
*
* Ex: If `$context['participantId']` is part of the schema, then
* `$context['eventId']` will also be part of the schema.
*
* This fires early during the `civi.token.list` process to ensure that
* other listeners see the updated schema.
*
* @param \Civi\Token\Event\TokenRegisterEvent $e
*/
public function onRegisterTokens(TokenRegisterEvent $e) {
$tokenProc = $e->getTokenProcessor();
foreach ($this->findRelevantMappings($tokenProc) as $mapping) {
$tokenProc->addSchema($mapping['destEntityId']);
}
}

/**
* When evaluating tokens, ensure that implied data is loaded.
*
* Ex: If `$context['participantId']` is supplied, then lookup the
* corresponding `$context['eventId']`.
*
* This fires early during the `civi.token.list` process to ensure that
* other listeners see the autoloaded values.
*
* @param \Civi\Token\Event\TokenValueEvent $e
*/
public function onEvaluateTokens(TokenValueEvent $e) {
$tokenProc = $e->getTokenProcessor();
foreach ($this->findRelevantMappings($tokenProc) as $mapping) {
$getSrcId = function($row) use ($mapping) {
return $row->context[$mapping['srcEntityId']] ?? $row->context[$mapping['srcEntityRec']]['id'] ?? NULL;
};

$ids = [];
foreach ($tokenProc->getRows() as $row) {
$ids[] = $getSrcId($row);
}
$ids = \array_diff(\array_unique($ids), [NULL]);
if (empty($ids)) {
continue;
}

[$srcTable, $fkColumn] = explode('.', $mapping['fk']);
$fks = \CRM_Utils_SQL_Select::from($srcTable)
->where('id in (#ids)', ['ids' => $ids])
->select(['id', $fkColumn])
->execute()
->fetchMap('id', $fkColumn);

$tokenProc->addSchema($mapping['destEntityId']);
foreach ($tokenProc->getRows() as $row) {
$srcId = $getSrcId($row);
if ($srcId && empty($row->context[$mapping['destEntityId']])) {
$row->context($mapping['destEntityId'], $fks[$srcId]);
}
}
}
}

private function findRelevantMappings(TokenProcessor $tokenProcessor) {
$schema = $tokenProcessor->context['schema'];
yield from [];
foreach ($this->getMappings() as $mapping) {
if (in_array($mapping['srcEntityRec'], $schema) || in_array($mapping['srcEntityId'], $schema)) {
yield $mapping;
}
}
}

private function getMappings(): iterable {
yield [
'srcEntityId' => 'participantId',
'srcEntityRec' => 'participant',
'fk' => 'civicrm_participant.event_id',
'destEntityId' => 'eventId',
];
}

}
11 changes: 11 additions & 0 deletions Civi/Token/TokenProcessor.php
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,17 @@ public function __construct($dispatcher, $context) {
$this->context = $context;
}

/**
* Add new elements to the field schema.
*
* @param string|string[] $fieldNames
* @return TokenProcessor
*/
public function addSchema($fieldNames) {
$this->context['schema'] = array_unique(array_merge($this->context['schema'], (array) $fieldNames));
return $this;
}

/**
* Register a string for which we'll need to merge in tokens.
*
Expand Down
27 changes: 27 additions & 0 deletions tests/phpunit/Civi/Token/ImpliedContextSubscriberTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php
namespace Civi\Token;

class ImpliedContextSubscriberTest extends \CiviUnitTestCase {

public function testParticipant_ImplicitEvent() {
$participantId = $this->participantCreate();

$messages = \CRM_Core_TokenSmarty::render(
['text' => 'Go to {event.title}!'],
['participantId' => $participantId]
);
$this->assertEquals('Go to Annual CiviCRM meet!', $messages['text']);
}

public function testParticipant_ExplicitEvent() {
$participantId = $this->participantCreate();
$otherEventId = $this->eventCreate(['title' => 'Alternate Event'])['id'];

$messages = \CRM_Core_TokenSmarty::render(
['text' => 'You may also like {event.title}!'],
['participantId' => $participantId, 'eventId' => $otherEventId]
);
$this->assertEquals('You may also like Alternate Event!', $messages['text']);
}

}

0 comments on commit 6cce990

Please sign in to comment.