Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

M3: Refactor builder token helper #8204

Merged
merged 20 commits into from
Dec 19, 2019
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
13 changes: 8 additions & 5 deletions app/bundles/AssetBundle/Config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
* @license GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
*/

use Mautic\AssetBundle\EventListener\BuilderSubscriber;
use Mautic\AssetBundle\EventListener\ReportSubscriber;

return [
'routes' => [
'main' => [
Expand Down Expand Up @@ -86,19 +89,19 @@
],
],
'mautic.asset.reportbundle.subscriber' => [
'class' => \Mautic\AssetBundle\EventListener\ReportSubscriber::class,
'class' => ReportSubscriber::class,
'arguments' => [
'mautic.lead.model.company_report_data',
'mautic.asset.repository.download',
],
],
'mautic.asset.builder.subscriber' => [
'class' => \Mautic\AssetBundle\EventListener\BuilderSubscriber::class,
'class' => BuilderSubscriber::class,
'arguments' => [
'mautic.asset.helper.token',
'mautic.lead.model.lead',
'mautic.security',
'mautic.factory',
'mautic.asset.helper.token',
'mautic.tracker.contact',
'mautic.helper.token_builder.factory',
],
],
'mautic.asset.leadbundle.subscriber' => [
Expand Down
84 changes: 41 additions & 43 deletions app/bundles/AssetBundle/EventListener/BuilderSubscriber.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,11 @@

use Mautic\AssetBundle\Helper\TokenHelper;
use Mautic\CoreBundle\Event\BuilderEvent;
use Mautic\CoreBundle\Factory\MauticFactory;
use Mautic\CoreBundle\Helper\BuilderTokenHelper;
use Mautic\CoreBundle\Helper\BuilderTokenHelperFactory;
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
use Mautic\EmailBundle\EmailEvents;
use Mautic\EmailBundle\Event\EmailSendEvent;
use Mautic\LeadBundle\Model\LeadModel;
use Mautic\LeadBundle\Tracker\ContactTracker;
use Mautic\PageBundle\Event\PageDisplayEvent;
use Mautic\PageBundle\PageEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
Expand All @@ -31,37 +30,43 @@ class BuilderSubscriber implements EventSubscriberInterface
private $assetToken = '{assetlink=(.*?)}';

/**
* @var TokenHelper
* @var CorePermissions
*/
private $tokenHelper;
private $security;

/**
* @var LeadModel
* @var TokenHelper
*/
private $leadModel;
private $tokenHelper;

/**
* @var CorePermissions
* @var ContactTracker
*/
private $security;
protected $contactTracker;
mtshaw3 marked this conversation as resolved.
Show resolved Hide resolved

/**
* @var MauticFactory
* @var BuilderTokenHelperFactory
*/
private $factory;
protected $builderTokenHelperFactory;
mtshaw3 marked this conversation as resolved.
Show resolved Hide resolved

/**
* @param TokenHelper $tokenHelper
* @param LeadModel $leadModel
* @param CorePermissions $security
* @param MauticFactory $factory
* BuilderSubscriber constructor.
*
* @param CorePermissions $security
* @param TokenHelper $tokenHelper
* @param ContactTracker $contactTracker
* @param BuilderTokenHelperFactory $builderTokenHelperFactory
*/
public function __construct(TokenHelper $tokenHelper, LeadModel $leadModel, CorePermissions $security, MauticFactory $factory)
{
$this->tokenHelper = $tokenHelper;
$this->leadModel = $leadModel;
$this->security = $security;
$this->factory = $factory; // Temporary. @see https://github.com/mautic/mautic/issues/8088
public function __construct(
CorePermissions $security,
TokenHelper $tokenHelper,
ContactTracker $contactTracker,
BuilderTokenHelperFactory $builderTokenHelperFactory
) {
$this->security = $security;
$this->tokenHelper = $tokenHelper;
$this->contactTracker = $contactTracker;
$this->builderTokenHelperFactory = $builderTokenHelperFactory;
}

/**
Expand All @@ -84,7 +89,7 @@ public static function getSubscribedEvents()
public function onBuilderBuild(BuilderEvent $event)
{
if ($event->tokensRequested($this->assetToken)) {
$tokenHelper = new BuilderTokenHelper($this->factory, 'asset');
$tokenHelper = $this->builderTokenHelperFactory->getBuilderTokenHelper('asset');
$event->addTokensFromHelper($tokenHelper, $this->assetToken, 'title', 'id', true);
}
}
Expand All @@ -95,9 +100,9 @@ public function onBuilderBuild(BuilderEvent $event)
public function onEmailGenerate(EmailSendEvent $event)
{
$lead = $event->getLead();
$leadId = ($lead !== null) ? $lead['id'] : null;
$leadId = (int) ($lead !== null ? $lead['id'] : null);
$email = $event->getEmail();
$tokens = $this->generateTokensFromContent($event, $leadId, $event->getSource(), ($email === null) ? null : $email->getId());
$tokens = $this->generateTokensFromContent($event, $leadId, $event->getSource(), $email === null ? null : $email->getId());
$event->addTokens($tokens);
}

Expand All @@ -107,8 +112,8 @@ public function onEmailGenerate(EmailSendEvent $event)
public function onPageDisplay(PageDisplayEvent $event)
{
$page = $event->getPage();
$lead = ($this->security->isAnonymous()) ? $this->leadModel->getCurrentLead() : null;
$leadId = ($lead) ? $lead->getId() : null;
$lead = $this->security->isAnonymous() ? $this->contactTracker->getContact() : null;
$leadId = $lead ? $lead->getId() : null;
$tokens = $this->generateTokensFromContent($event, $leadId, ['page', $page->getId()]);
$content = $event->getContent();

Expand All @@ -119,30 +124,23 @@ public function onPageDisplay(PageDisplayEvent $event)
}

/**
* @param $event
* @param $leadId
* @param array $source
* @param null $emailId
* @param PageDisplayEvent|EmailSendEvent $event
* @param int $leadId
* @param array $source
* @param null $emailId
*
* @return array
*/
private function generateTokensFromContent($event, $leadId, $source = [], $emailId = null)
{
$content = $event->getContent();

$clickthrough = [];
if ($event instanceof PageDisplayEvent || ($event instanceof EmailSendEvent && $event->shouldAppendClickthrough())) {
$clickthrough = ['source' => $source];

if ($leadId !== null) {
$clickthrough['lead'] = $leadId;
}

if (!empty($emailId)) {
$clickthrough['email'] = $emailId;
}
$clickthrough = [
'source' => $source,
'lead' => $leadId ?? false,
'email' => $emailId ?? false,
];
}

return $this->tokenHelper->findAssetTokens($content, $clickthrough);
return $this->tokenHelper->findAssetTokens($event->getContent(), array_filter($clickthrough ?? []));
escopecz marked this conversation as resolved.
Show resolved Hide resolved
}
}
18 changes: 18 additions & 0 deletions app/bundles/CoreBundle/Config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,24 @@
'mautic.helper.core_parameters',
],
],
'mautic.helper.token_builder' => [
'class' => \Mautic\CoreBundle\Helper\BuilderTokenHelper::class,
'arguments' => [
'mautic.security',
'doctrine.orm.entity_manager',
'database_connection',
'mautic.helper.user',
],
],
'mautic.helper.token_builder.factory' => [
'class' => \Mautic\CoreBundle\Helper\BuilderTokenHelperFactory::class,
'arguments' => [
'mautic.security',
'doctrine.orm.entity_manager',
'database_connection',
'mautic.helper.user',
],
],
],
'menus' => [
'mautic.menu.main' => [
Expand Down
105 changes: 48 additions & 57 deletions app/bundles/CoreBundle/Helper/BuilderTokenHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,37 +11,57 @@

namespace Mautic\CoreBundle\Helper;

use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Query\Expression\CompositeExpression;
use Mautic\CoreBundle\Factory\MauticFactory;
use Doctrine\ORM\EntityManager;
use Mautic\CoreBundle\Security\Permissions\CorePermissions;

/**
* Class BuilderTokenHelper.
*/
class BuilderTokenHelper
{
protected $viewPermissionBase;
protected $modelName;
protected $langVar;
protected $bundleName;
private $isConfigured = false;

private $security;
private $entityManager;
private $connection;
private $userHelper;

protected $permissionSet;
protected $modelName;
protected $viewPermissionBase = null;
protected $langVar = null;
protected $bundleName = null;
dongilbert marked this conversation as resolved.
Show resolved Hide resolved

/**
* @var MauticFactory
*
* @deprecated 2.12 To be removed in 3.0 Inject dependencies in your constructor instead
* @param CorePermissions $security
* @param EntityManager $entityManager
* @param Connection $connection
* @param UserHelper $userHelper
*/
private $factory;
public function __construct(
CorePermissions $security,
EntityManager $entityManager,
Connection $connection,
UserHelper $userHelper
) {
$this->security = $security;
$this->entityManager = $entityManager;
$this->connection = $connection;
$this->userHelper = $userHelper;
}

/**
* @param MauticFactory $factory
* @param string $modelName Model name such as page
* @param string $viewPermissionBase Permission base such as page:pages or null to generate from $modelName
* @param string $bundleName Bundle name such as MauticPageBundle or null to generate from $modelName
* @param null $langVar Language base for filter such as page.page or leave blank to use $modelName
* This method must be called before the BuilderTokenHelper can be used.
*
* @param $modelName
* @param null $viewPermissionBase
* @param null $bundleName
* @param null $langVar
dongilbert marked this conversation as resolved.
Show resolved Hide resolved
*/
public function __construct(MauticFactory $factory, $modelName, $viewPermissionBase = null, $bundleName = null, $langVar = null)
public function configure($modelName, $viewPermissionBase = null, $bundleName = null, $langVar = null)
{
$this->factory = $factory;
$this->modelName = $modelName;
$this->viewPermissionBase = (!empty($viewPermissionBase)) ? $viewPermissionBase : "$modelName:{$modelName}s";
$this->bundleName = (!empty($bundleName)) ? $bundleName : 'Mautic'.ucfirst($modelName).'Bundle';
Expand All @@ -51,6 +71,8 @@ public function __construct(MauticFactory $factory, $modelName, $viewPermissionB
$this->viewPermissionBase.':viewown',
$this->viewPermissionBase.':viewother',
];

$this->isConfigured = true;
}

/**
Expand All @@ -61,6 +83,8 @@ public function __construct(MauticFactory $factory, $modelName, $viewPermissionB
* @param string $valueColumn The column that houses the value
* @param CompositeExpression $expr Use $factory->getDatabase()->getExpressionBuilder()->andX()
*
* @throws \Exception
*
* @return array|void
*/
public function getTokens(
Expand All @@ -70,8 +94,12 @@ public function getTokens(
$valueColumn = 'id',
CompositeExpression $expr = null
) {
if (!$this->isConfigured) {
throw new \Exception('You must call the "configure" method of this class first.');
dongilbert marked this conversation as resolved.
Show resolved Hide resolved
}

//set some permissions
$permissions = $this->factory->getSecurity()->isGranted(
$permissions = $this->security->isGranted(
$this->permissionSet,
'RETURN_ARRAY'
);
Expand All @@ -80,20 +108,20 @@ public function getTokens(
return;
}

$repo = $this->factory->getModel($this->modelName)->getRepository();
$repo = $this->entityManager->getRepository($this->modelName);
dongilbert marked this conversation as resolved.
Show resolved Hide resolved
$prefix = $repo->getTableAlias();
if (!empty($prefix)) {
$prefix .= '.';
}

$exprBuilder = $this->factory->getDatabase()->getExpressionBuilder();
$exprBuilder = $this->connection->getExpressionBuilder();
if ($expr == null) {
$expr = $exprBuilder->andX();
}

if (isset($permissions[$this->viewPermissionBase.':viewother']) && !$permissions[$this->viewPermissionBase.':viewother']) {
$expr->add(
$exprBuilder->eq($prefix.'created_by', $this->factory->getUser()->getId())
$exprBuilder->eq($prefix.'created_by', $this->userHelper->getUser()->getId())
);
}

Expand Down Expand Up @@ -130,43 +158,6 @@ public function setPermissionSet(array $permissions)
$this->permissionSet = $permissions;
}

/**
* Prevent tokens in URLs from being converted to visual tokens by encoding the brackets.
*
* @deprecated 2.2.1 - to be removed in 3.0
*
* @param string $content
* @param array $tokenKeys
*/
public static function encodeUrlTokens(&$content, array $tokenKeys)
{
$processMatches = function ($matches) use (&$content, $tokenKeys) {
foreach ($matches as $link) {
// There may be more than one leadfield token in the URL
preg_match_all('/{['.implode('|', $tokenKeys).'].*?}/i', $link, $tokens);
$newLink = $link;
foreach ($tokens as $token) {
// Encode brackets
$encodedToken = str_replace(['{', '}'], ['%7B', '%7D'], $token);
$newLink = str_replace($token, $encodedToken, $newLink);
}
$content = str_replace($link, $newLink, $content);
}
};

// Special handling for leadfield tokens in URLs
$foundMatches = preg_match_all('/<a.*?href=["\'].*?({['.implode('|', $tokenKeys).'].*?}).*?["\']/i', $content, $matches);
if ($foundMatches) {
$processMatches($matches[0]);
}

// Special handling for leadfield tokens in image src
$foundMatches = preg_match_all('/<img.*?src=["\'].*?({['.implode('|', $tokenKeys).'].*?}).*?["\']/i', $content, $matches);
if ($foundMatches) {
$processMatches($matches[0]);
}
}

/**
* @deprecated 2.6.0 to be removed in 3.0
*
Expand Down