diff --git a/Config/config.php b/Config/config.php index 85b1c8b70..5e1ff6a39 100644 --- a/Config/config.php +++ b/Config/config.php @@ -863,10 +863,12 @@ 'custom_object.query.filter.factory', 'mautic.custom.model.object', 'mautic.custom.model.item', + 'mautic.custom.model.field', 'custom_object.token.parser', 'mautic.campaign.model.event', 'event_dispatcher', 'custom_object.helper.token_formatter', + '%mautic.custom_item_fetch_limit_per_lead%', ], ], 'custom_object.segments.decorator_delegate.subscriber' => [ @@ -1188,10 +1190,11 @@ ], ], 'parameters' => [ - ConfigProvider::CONFIG_PARAM_ENABLED => true, - ConfigProvider::CONFIG_PARAM_ITEM_VALUE_TO_CONTACT_RELATION_LIMIT => ConfigProvider::CONFIG_PARAM_ITEM_VALUE_TO_CONTACT_RELATION_DEFAULT_LIMIT, - 'custom_item_export_dir' => '%kernel.root_dir%/../media/files/temp', - 'custom_object_merge_filter' => false, + ConfigProvider::CONFIG_PARAM_ENABLED => true, + ConfigProvider::CONFIG_PARAM_ITEM_VALUE_TO_CONTACT_RELATION_LIMIT => ConfigProvider::CONFIG_PARAM_ITEM_VALUE_TO_CONTACT_RELATION_DEFAULT_LIMIT, + 'custom_item_export_dir' => '%kernel.root_dir%/../media/files/temp', + 'custom_object_merge_filter' => false, + 'custom_item_fetch_limit_per_lead' => 15, ], ]; diff --git a/EventListener/TokenSubscriber.php b/EventListener/TokenSubscriber.php index 4ed2bc3ce..b0b3d774d 100644 --- a/EventListener/TokenSubscriber.php +++ b/EventListener/TokenSubscriber.php @@ -7,11 +7,14 @@ use Mautic\CampaignBundle\Entity\Event; use Mautic\CampaignBundle\Model\EventModel; use Mautic\CoreBundle\Event\BuilderEvent; +use Mautic\CoreBundle\Event\TokenReplacementEvent; use Mautic\EmailBundle\EmailEvents; use Mautic\EmailBundle\Entity\Email; use Mautic\EmailBundle\Event\EmailSendEvent; use Mautic\EmailBundle\EventListener\MatchFilterForLeadTrait; use Mautic\LeadBundle\Entity\LeadList; +use Mautic\LeadBundle\Exception\OperatorsNotFoundException; +use Mautic\LeadBundle\Segment\OperatorOptions; use MauticPlugin\CustomObjectsBundle\CustomItemEvents; use MauticPlugin\CustomObjectsBundle\CustomObjectEvents; use MauticPlugin\CustomObjectsBundle\DTO\TableConfig; @@ -29,6 +32,7 @@ use MauticPlugin\CustomObjectsBundle\Helper\QueryFilterHelper; use MauticPlugin\CustomObjectsBundle\Helper\TokenFormatter; use MauticPlugin\CustomObjectsBundle\Helper\TokenParser; +use MauticPlugin\CustomObjectsBundle\Model\CustomFieldModel; use MauticPlugin\CustomObjectsBundle\Model\CustomItemModel; use MauticPlugin\CustomObjectsBundle\Model\CustomObjectModel; use MauticPlugin\CustomObjectsBundle\Provider\ConfigProvider; @@ -69,6 +73,11 @@ class TokenSubscriber implements EventSubscriberInterface */ private $customItemModel; + /** + * @var CustomFieldModel + */ + private $customFieldModel; + /** * @var TokenParser */ @@ -89,26 +98,35 @@ class TokenSubscriber implements EventSubscriberInterface */ private $tokenFormatter; + /** + * @var int + */ + private $leadCustomItemFetchLimit; + public function __construct( ConfigProvider $configProvider, QueryFilterHelper $queryFilterHelper, QueryFilterFactory $queryFilterFactory, CustomObjectModel $customObjectModel, CustomItemModel $customItemModel, + CustomFieldModel $customFieldModel, TokenParser $tokenParser, EventModel $eventModel, EventDispatcherInterface $eventDispatcher, - TokenFormatter $tokenFormatter + TokenFormatter $tokenFormatter, + int $leadCustomItemFetchLimit ) { - $this->configProvider = $configProvider; - $this->queryFilterHelper = $queryFilterHelper; - $this->queryFilterFactory = $queryFilterFactory; - $this->customObjectModel = $customObjectModel; - $this->customItemModel = $customItemModel; - $this->tokenParser = $tokenParser; - $this->eventModel = $eventModel; - $this->eventDispatcher = $eventDispatcher; - $this->tokenFormatter = $tokenFormatter; + $this->configProvider = $configProvider; + $this->queryFilterHelper = $queryFilterHelper; + $this->queryFilterFactory = $queryFilterFactory; + $this->customObjectModel = $customObjectModel; + $this->customItemModel = $customItemModel; + $this->customFieldModel = $customFieldModel; + $this->tokenParser = $tokenParser; + $this->eventModel = $eventModel; + $this->eventDispatcher = $eventDispatcher; + $this->tokenFormatter = $tokenFormatter; + $this->leadCustomItemFetchLimit = $leadCustomItemFetchLimit; } /** @@ -121,6 +139,7 @@ public static function getSubscribedEvents() EmailEvents::EMAIL_ON_SEND => ['decodeTokens', 0], EmailEvents::EMAIL_ON_DISPLAY => ['decodeTokens', 0], CustomItemEvents::ON_CUSTOM_ITEM_LIST_DBAL_QUERY => ['onListQuery', -1], + EmailEvents::TOKEN_REPLACEMENT => ['onTokenReplacement', 100], ]; } @@ -323,4 +342,321 @@ private function getCustomFieldValues(CustomObject $customObject, Token $token, return $fieldValues; } + + public function onTokenReplacement(TokenReplacementEvent $event): void + { + $clickthrough = $event->getClickthrough(); + + if (!array_key_exists('dynamicContent', $clickthrough)) { + return; + } + + $lead = $event->getLead(); + $tokenData = $clickthrough['dynamicContent']; + + foreach ($tokenData as $data) { + $filterContent = $data['content']; + + $isCustomObject = false; + foreach ($data['filters'] as $filter) { + $customFieldValues = $this->getCustomFieldDataForLead($filter['filters'], (string) $lead['id']); + + if (!empty($customFieldValues)) { + $isCustomObject = true; + $lead = array_merge($lead, $customFieldValues); + } + + if ($isCustomObject && $this->matchFilterForLeadInCustomObject($filter['filters'], $lead)) { + $filterContent = $filter['content']; + break; + } + } + + if ($isCustomObject) { + $event->addToken('{dynamiccontent="'.$data['tokenName'].'"}', $filterContent); + } + } + } + + /** + * @param array $filters + * + * @return array + */ + private function getCustomFieldDataForLead(array $filters, string $leadId): array + { + $customFieldValues = $cachedCustomItems = []; + + foreach ($filters as $condition) { + try { + if ('custom_object' !== $condition['object']) { + continue; + } + + if ('cmf_' === substr($condition['field'], 0, 4)) { + $customField = $this->customFieldModel->fetchEntity( + (int) explode('cmf_', $condition['field'])[1] + ); + $customObject = $customField->getCustomObject(); + $fieldAlias = $customField->getAlias(); + } elseif ('cmo_' === substr($condition['field'], 0, 4)) { + $customObject = $this->customObjectModel->fetchEntity( + (int) explode('cmo_', $condition['field'])[1] + ); + $fieldAlias = 'name'; + } else { + continue; + } + + $key = $customObject->getId().'-'.$leadId; + if (!isset($cachedCustomItems[$key])) { + $cachedCustomItems[$key] = $this->getCustomItems($customObject, $leadId); + } + + $result = $this->getCustomFieldValue($customObject, $fieldAlias, $cachedCustomItems[$key]); + + $customFieldValues[$condition['field']] = $result; + } catch (NotFoundException|InvalidCustomObjectFormatListException $e) { + continue; + } + } + + return $customFieldValues; + } + + /** + * @param array $customItems + * + * @return array + */ + private function getCustomFieldValue( + CustomObject $customObject, + string $customFieldAlias, + array $customItems + ): array { + $fieldValues = []; + + foreach ($customItems as $customItemData) { + // Name is known from the CI data array. + if ('name' === $customFieldAlias) { + $fieldValues[] = $customItemData['name']; + + continue; + } + + // Custom Field values are handled like this. + $customItem = new CustomItem($customObject); + $customItem->populateFromArray($customItemData); + $customItem = $this->customItemModel->populateCustomFields($customItem); + + try { + $fieldValue = $customItem->findCustomFieldValueForFieldAlias($customFieldAlias); + // If the CO item doesn't have a value, get the default value + if (empty($fieldValue->getValue())) { + $fieldValue->setValue($fieldValue->getCustomField()->getDefaultValue()); + } + + if (in_array($fieldValue->getCustomField()->getType(), ['multiselect', 'select'])) { + $fieldValues[] = $fieldValue->getValue(); + } else { + $fieldValues[] = $fieldValue->getCustomField()->getTypeObject()->valueToString($fieldValue); + } + } catch (NotFoundException $e) { + // Custom field not found. + } + } + + return $fieldValues; + } + + /** + * @return array + */ + private function getCustomItems(CustomObject $customObject, string $leadId): array + { + $orderBy = CustomItem::TABLE_ALIAS.'.id'; + $orderDir = 'DESC'; + + $tableConfig = new TableConfig($this->leadCustomItemFetchLimit, 1, $orderBy, $orderDir); + $tableConfig->addParameter('customObjectId', $customObject->getId()); + $tableConfig->addParameter('filterEntityType', 'contact'); + $tableConfig->addParameter('filterEntityId', $leadId); + + return $this->customItemModel->getArrayTableData($tableConfig); + } + + // We have a similar function in MatchFilterForLeadTrait since we are unable to alter anything in Mautic 4.4, + // hence there is some duplication of code. + + /** + * @param array $filter + * @param array $lead + * + * @throws OperatorsNotFoundException + */ + protected function matchFilterForLeadInCustomObject(array $filter, array $lead): bool + { + if (empty($lead['id'])) { + // Lead in generated for preview with faked data + return false; + } + + $groups = []; + $groupNum = 0; + + foreach ($filter as $data) { + if (!array_key_exists($data['field'], $lead)) { + continue; + } + + /* + * Split the filters into groups based on the glue. + * The first filter and any filters whose glue is + * "or" will start a new group. + */ + if (0 === $groupNum || 'or' === $data['glue']) { + ++$groupNum; + $groups[$groupNum] = null; + } + + /* + * If the group has been marked as false, there + * is no need to continue checking the others + * in the group. + */ + if (false === $groups[$groupNum]) { + continue; + } + + /* + * If we are checking the first filter in a group + * assume that the group will not match. + */ + if (null === $groups[$groupNum]) { + $groups[$groupNum] = false; + } + + $leadValues = $lead[$data['field']]; + $leadValues = 'custom_object' === $data['object'] ? $leadValues : [$leadValues]; + $filterVal = $data['filter']; + $subgroup = null; + + if (is_array($leadValues)) { + foreach ($leadValues as $leadVal) { + if ($subgroup) { + break; + } + + switch ($data['type']) { + case 'boolean': + if (null !== $leadVal) { + $leadVal = (bool) $leadVal; + } + + if (null !== $filterVal) { + $filterVal = (bool) $filterVal; + } + break; + case 'datetime': + case 'time': + if (!is_null($leadVal) && !is_null($filterVal)) { + $leadValCount = substr_count($leadVal, ':'); + $filterValCount = substr_count($filterVal, ':'); + + if (2 === $leadValCount && 1 === $filterValCount) { + $filterVal .= ':00'; + } + } + break; + case 'tags': + case 'select': + case 'multiselect': + if (!is_array($leadVal) && !empty($leadVal)) { + $leadVal = explode('|', $leadVal); + } + if (!is_null($filterVal) && !is_array($filterVal)) { + $filterVal = explode('|', $filterVal); + } + break; + case 'number': + $leadVal = (int) $leadVal; + $filterVal = (int) $filterVal; + break; + } + + switch ($data['operator']) { + case '=': + if ('boolean' === $data['type']) { + $groups[$groupNum] = $leadVal === $filterVal; + } else { + $groups[$groupNum] = $leadVal == $filterVal; + } + break; + case '!=': + if ('boolean' === $data['type']) { + $groups[$groupNum] = $leadVal !== $filterVal; + } else { + $groups[$groupNum] = $leadVal != $filterVal; + } + break; + case 'gt': + $groups[$groupNum] = $leadVal > $filterVal; + break; + case 'gte': + $groups[$groupNum] = $leadVal >= $filterVal; + break; + case 'lt': + $groups[$groupNum] = $leadVal < $filterVal; + break; + case 'lte': + $groups[$groupNum] = $leadVal <= $filterVal; + break; + case 'empty': + $groups[$groupNum] = empty($leadVal); + break; + case '!empty': + $groups[$groupNum] = !empty($leadVal); + break; + case 'like': + $matchVal = str_replace(['.', '*', '%'], ['\.', '\*', '.*'], $filterVal); + $groups[$groupNum] = 1 === preg_match('/'.$matchVal.'/', $leadVal); + break; + case '!like': + $matchVal = str_replace(['.', '*'], ['\.', '\*'], $filterVal); + $matchVal = str_replace('%', '.*', $matchVal); + $groups[$groupNum] = 1 !== preg_match('/'.$matchVal.'/', $leadVal); + break; + case OperatorOptions::IN: + $groups[$groupNum] = $this->checkLeadValueIsInFilter($leadVal, $filterVal, false); + break; + case OperatorOptions::NOT_IN: + $groups[$groupNum] = $this->checkLeadValueIsInFilter($leadVal, $filterVal, true); + break; + case 'regexp': + $groups[$groupNum] = 1 === preg_match('/'.$filterVal.'/i', $leadVal); + break; + case '!regexp': + $groups[$groupNum] = 1 !== preg_match('/'.$filterVal.'/i', $leadVal); + break; + case 'startsWith': + $groups[$groupNum] = 0 === strncmp($leadVal, $filterVal, strlen($filterVal)); + break; + case 'endsWith': + $endOfString = substr($leadVal, strlen($leadVal) - strlen($filterVal)); + $groups[$groupNum] = 0 === strcmp($endOfString, $filterVal); + break; + case 'contains': + $groups[$groupNum] = false !== strpos((string) $leadVal, (string) $filterVal); + break; + default: + throw new OperatorsNotFoundException('Operator is not defined or invalid operator found.'); + } + + $subgroup = $groups[$groupNum]; + } + } + } + + return in_array(true, $groups); + } } diff --git a/Tests/Functional/Token/EmailWithCustomObjectDynamicContentFunctionalTest.php b/Tests/Functional/Token/EmailWithCustomObjectDynamicContentFunctionalTest.php new file mode 100644 index 000000000..14bab135c --- /dev/null +++ b/Tests/Functional/Token/EmailWithCustomObjectDynamicContentFunctionalTest.php @@ -0,0 +1,329 @@ + + */ + private $customItems; + + /** + * @var array + */ + private $customFieldValues; + + protected function setUp(): void + { + parent::setUp(); + + $this->customItemModel = self::$container->get('mautic.custom.model.item'); + $this->customFieldValueModel = self::$container->get('mautic.custom.model.field.value'); + + $this->customObject = $this->createCustomObjectWithAllFields(self::$container, 'Car'); + $this->customItems['nexon'] = new CustomItem($this->customObject); + + $this->customItems['nexon']->setName('Nexon'); + $this->customFieldValueModel->createValuesForItem($this->customItems['nexon']); + + $this->customFieldValues['nexon-text'] = $this->customItems['nexon']->findCustomFieldValueForFieldAlias('text-test-field'); + $this->customFieldValues['nexon-text']->setValue('Tata'); + + $this->customFieldValues['nexon-datetime'] = $this->customItems['nexon']->findCustomFieldValueForFieldAlias('datetime-test-field'); + $this->customFieldValues['nexon-datetime']->setValue('2024-07-01 13:29:43'); + + $this->customFieldValues['nexon-multiselect'] = $this->customItems['nexon']->findCustomFieldValueForFieldAlias('multiselect-test-field'); + $this->customFieldValues['nexon-multiselect']->setValue('option_a'); + + $this->customFieldValues['nexon-select'] = $this->customItems['nexon']->findCustomFieldValueForFieldAlias('select-test-field'); + + $this->customFieldValues['nexon-url'] = $this->customItems['nexon']->findCustomFieldValueForFieldAlias('url-test-field'); + $this->customFieldValues['nexon-url']->setValue('https://test.mautic.fr'); + + $this->customFieldValues['nexon-number'] = $this->customItems['nexon']->findCustomFieldValueForFieldAlias('number-test-field'); + $this->customFieldValues['nexon-number']->setValue(10); + + $this->customItems['nexon'] = $this->customItemModel->save($this->customItems['nexon']); + + $this->customItems['fortuner'] = new CustomItem($this->customObject); + + $this->customItems['fortuner']->setName('Fortuner'); + $this->customFieldValueModel->createValuesForItem($this->customItems['fortuner']); + + $this->customFieldValues['fortuner-text'] = $this->customItems['fortuner']->findCustomFieldValueForFieldAlias('text-test-field'); + $this->customFieldValues['fortuner-text']->setValue('Toyota'); + + $this->customItems['fortuner'] = $this->customItemModel->save($this->customItems['fortuner']); + + $this->customItems['city'] = new CustomItem($this->customObject); + + $this->customItems['city']->setName('City'); + $this->customFieldValueModel->createValuesForItem($this->customItems['city']); + + $this->customFieldValues['city-text'] = $this->customItems['city']->findCustomFieldValueForFieldAlias('text-test-field'); + $this->customFieldValues['city-text']->setValue('Honda'); + + $this->customItems['city'] = $this->customItemModel->save($this->customItems['city']); + } + + public function testDynamicContentEmail(): void + { + foreach ([ + [ + 'nexonlikeurl@acquia.com', + $this->buildDynamicContentArray([ + ['nexon-url', 'tic.f', 'like'], + ]), + 'Custom Object Dynamic Content', + ], + [ + 'nexonselect@acquia.com', + $this->buildDynamicContentArray([ + ['nexon-select', null, 'empty', 'select'], + ]), + 'Custom Object Dynamic Content', + ], + [ + 'nexonmultiselect@acquia.com', + $this->buildDynamicContentArray([ + ['nexon-datetime', null, '!empty', 'datetime'], + ['nexon-number', 12, 'lt', 'number'], + ]), + 'Custom Object Dynamic Content', + ], [ + 'nexonmultiselect@acquia.com', + $this->buildDynamicContentArray([ + ['nexon-text', 'Tata', '='], + ['nexon-datetime', '2024-07-01 00:00', 'gte', 'datetime'], + ['nexon-multiselect', 'option_a', 'in', 'multiselect'], + ]), + 'Custom Object Dynamic Content', + ], [ + 'nexondatetime@acquia.com', + $this->buildDynamicContentArray([ + ['nexon-text', 'Tata', '='], + ['nexon-datetime', '2024-07-01', 'gte'], + ]), + 'Custom Object Dynamic Content', + ], [ + 'nexonequal@acquia.com', + $this->buildDynamicContentArray([ + ['nexon-text', 'Tata', '='], + ]), + 'Custom Object Dynamic Content', + ], [ + 'nexonnotequal@acquia.com', + $this->buildDynamicContentArray([['nexon-text', 'Toyota', '!=']]), + 'Custom Object Dynamic Content', + ], [ + 'nexonempty@acquia.com', + $this->buildDynamicContentArray([['nexon-text', '', 'empty']]), + 'Default Dynamic Content', + ], [ + 'nexonnotempty@acquia.com', + $this->buildDynamicContentArray([['nexon-text', '', '!empty']]), + 'Custom Object Dynamic Content', + ], [ + 'nexonlike@acquia.com', + $this->buildDynamicContentArray([['nexon-text', 'at', 'like']]), + 'Custom Object Dynamic Content', + ], [ + 'nexonnotlike@acquia.com', + $this->buildDynamicContentArray([['nexon-text', 'Toyota', '!like']]), + 'Custom Object Dynamic Content', + ], [ + 'nexonstartsWith@acquia.com', + $this->buildDynamicContentArray([['nexon-text', 'Ta', 'startsWith']]), + 'Custom Object Dynamic Content', + ], [ + 'nexonendsWith@acquia.com', + $this->buildDynamicContentArray([['nexon-text', 'ta', 'endsWith']]), + 'Custom Object Dynamic Content', + ], [ + 'nexonendsWith@acquia.com', + $this->buildDynamicContentArray([['nexon-text', 'at', 'contains']]), + 'Custom Object Dynamic Content', + ], + ] as $item) { + $this->emailWithCustomObjectDynamicContent($item[0], $item[1], $item[2]); + } + } + + /** + * @param array $inputs + * + * @return array + */ + private function buildDynamicContentArray(array $inputs): array + { + return [ + [ + 'tokenName' => 'Dynamic Content 1', + 'content' => 'Default Dynamic Content', + 'filters' => [ + [ + 'content' => null, + 'filters' => [ + ], + ], + ], + ], + [ + 'tokenName' => 'Dynamic Content 2', + 'content' => 'Default Dynamic Content', + 'filters' => [ + [ + 'content' => 'Custom Object Dynamic Content', + 'filters' => array_merge( + array_map(function ($input) { + return [ + 'glue' => 'and', + 'field' => 'cmf_'.$this->customFieldValues[$input[0]]->getCustomField()->getId(), + 'object' => 'custom_object', + 'type' => $input[3] ?? 'text', + 'filter' => $input[1], + 'display' => $this->customObject->getName().':'.$this->customFieldValues[$input[0]]->getCustomField()->getLabel(), + 'operator' => $input[2], + ]; + }, $inputs), + [ + [ + 'glue' => 'and', + 'field' => 'email', + 'object' => 'lead', + 'type' => 'email', + 'filter' => null, + 'operator' => '!empty', + ], + ] + ), + ], + ], + ], + ]; + } + + /** + * @param array $dynamicContent + */ + private function emailWithCustomObjectDynamicContent(string $emailAddress, array $dynamicContent, string $assertText): void + { + $lead = $this->createLead($emailAddress); + $email = $this->createEmail(); + $email->setDynamicContent($dynamicContent); + $this->em->persist($email); + $this->em->flush(); + + $this->customItemModel->linkEntity($this->customItems['nexon'], 'contact', (int) $lead->getId()); + $this->customItemModel->linkEntity($this->customItems['fortuner'], 'contact', (int) $lead->getId()); + $this->customItemModel->linkEntity($this->customItems['city'], 'contact', (int) $lead->getId()); + + $this->sendAndAssetText($email, $lead, $assertText); + } + + private function createEmail(bool $publicPreview = true): Email + { + $email = new Email(); + $email->setDateAdded(new DateTime()); + $email->setName('Email name'); + $email->setSubject('Email subject'); + $email->setTemplate('Blank'); + $email->setPublicPreview($publicPreview); + $email->setCustomHtml('{dynamiccontent="Dynamic Content 2"}'); + $this->em->persist($email); + + return $email; + } + + private function createLead(string $email): Lead + { + $lead = new Lead(); + $lead->setEmail($email); + $this->em->persist($lead); + + return $lead; + } + + private function sendAndAssetText(Email $email, Lead $lead, string $matchText): void + { + /** @var EmailModel $emailModel */ + $emailModel = self::$container->get('mautic.email.model.email'); + $emailModel->sendEmail( + $email, + [ + [ + 'id' => $lead->getId(), + 'email' => $lead->getEmail(), + 'firstname' => $lead->getFirstname(), + 'lastname' => $lead->getLastname(), + ], + ] + ); + + /** @var StatRepository $emailStatRepository */ + $emailStatRepository = $this->em->getRepository(Stat::class); + + /** @var Stat|null $emailStat */ + $emailStat = $emailStatRepository->findOneBy( + [ + 'email' => $email->getId(), + 'lead' => $lead->getId(), + ] + ); + + Assert::assertNotNull($emailStat); + + $crawler = $this->client->request(Request::METHOD_GET, "/email/view/{$emailStat->getTrackingHash()}"); + + $body = $crawler->filter('body'); + + // Remove the tracking tags that are causing troubles with different Mautic configurations. + $body->filter('a,img,div')->each(function (Crawler $crawler) { + foreach ($crawler as $node) { + $node->parentNode->removeChild($node); + } + }); + + Assert::assertStringContainsString( + $matchText, + $body->html() + ); + } +} diff --git a/Tests/Unit/EventListener/TokenSubscriberTest.php b/Tests/Unit/EventListener/TokenSubscriberTest.php index 62f2821be..04cfe28da 100644 --- a/Tests/Unit/EventListener/TokenSubscriberTest.php +++ b/Tests/Unit/EventListener/TokenSubscriberTest.php @@ -29,6 +29,7 @@ use MauticPlugin\CustomObjectsBundle\Helper\QueryFilterHelper; use MauticPlugin\CustomObjectsBundle\Helper\TokenFormatter; use MauticPlugin\CustomObjectsBundle\Helper\TokenParser; +use MauticPlugin\CustomObjectsBundle\Model\CustomFieldModel; use MauticPlugin\CustomObjectsBundle\Model\CustomItemModel; use MauticPlugin\CustomObjectsBundle\Model\CustomObjectModel; use MauticPlugin\CustomObjectsBundle\Provider\ConfigProvider; @@ -65,6 +66,11 @@ class TokenSubscriberTest extends TestCase */ private $customItemModel; + /** + * @var CustomFieldModel|MockObject + */ + private $customFieldModel; + /** * @var TokenParser|MockObject */ @@ -114,6 +120,7 @@ protected function setUp(): void $this->queryFilterFactory = $this->createMock(QueryFilterFactory::class); $this->customObjectModel = $this->createMock(CustomObjectModel::class); $this->customItemModel = $this->createMock(CustomItemModel::class); + $this->customFieldModel = $this->createMock(CustomFieldModel::class); $this->tokenParser = $this->createMock(TokenParser::class); $this->eventModel = $this->createMock(EventModel::class); $this->eventDispatcher = $this->createMock(EventDispatcher::class); @@ -124,10 +131,12 @@ protected function setUp(): void $this->queryFilterFactory, $this->customObjectModel, $this->customItemModel, + $this->customFieldModel, $this->tokenParser, $this->eventModel, $this->eventDispatcher, - $this->tokenFormatter + $this->tokenFormatter, + 15 ); $this->builderEvent = $this->createMock(BuilderEvent::class); @@ -143,6 +152,7 @@ public function testGetSubscribedEvents(): void EmailEvents::EMAIL_ON_SEND => ['decodeTokens', 0], EmailEvents::EMAIL_ON_DISPLAY => ['decodeTokens', 0], CustomItemEvents::ON_CUSTOM_ITEM_LIST_DBAL_QUERY => ['onListQuery', -1], + EmailEvents::TOKEN_REPLACEMENT => ['onTokenReplacement', 100], ], TokenSubscriber::getSubscribedEvents() ); @@ -844,10 +854,12 @@ private function constructWithDependencies(): void $this->queryFilterFactory, $this->customObjectModel, $this->customItemModel, + $this->customFieldModel, $this->tokenParser, $this->eventModel, $this->eventDispatcher, - new TokenFormatter() + new TokenFormatter(), + 15 ); } }