Issues (3627)

EventListener/DynamicContentSubscriber.php (2 issues)

1
<?php
2
3
/*
4
 * @copyright   2016 Mautic Contributors. All rights reserved
5
 * @author      Mautic
6
 *
7
 * @link        http://mautic.org
8
 *
9
 * @license     GNU/GPLv3 http://www.gnu.org/licenses/gpl-3.0.html
10
 */
11
12
namespace Mautic\DynamicContentBundle\EventListener;
13
14
use DOMDocument;
15
use DOMXPath;
16
use Mautic\AssetBundle\Helper\TokenHelper as AssetTokenHelper;
17
use Mautic\CoreBundle\Event as MauticEvents;
18
use Mautic\CoreBundle\Model\AuditLogModel;
19
use Mautic\CoreBundle\Security\Permissions\CorePermissions;
20
use Mautic\DynamicContentBundle\DynamicContentEvents;
21
use Mautic\DynamicContentBundle\Entity\DynamicContent;
22
use Mautic\DynamicContentBundle\Event as Events;
23
use Mautic\DynamicContentBundle\Helper\DynamicContentHelper;
24
use Mautic\DynamicContentBundle\Model\DynamicContentModel;
25
use Mautic\EmailBundle\EventListener\MatchFilterForLeadTrait;
26
use Mautic\FormBundle\Helper\TokenHelper as FormTokenHelper;
27
use Mautic\LeadBundle\Entity\Lead;
28
use Mautic\LeadBundle\Helper\TokenHelper;
29
use Mautic\LeadBundle\Tracker\ContactTracker;
30
use Mautic\PageBundle\Entity\Trackable;
31
use Mautic\PageBundle\Event\PageDisplayEvent;
32
use Mautic\PageBundle\Helper\TokenHelper as PageTokenHelper;
33
use Mautic\PageBundle\Model\TrackableModel;
34
use Mautic\PageBundle\PageEvents;
35
use MauticPlugin\MauticFocusBundle\Helper\TokenHelper as FocusTokenHelper;
36
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
37
38
class DynamicContentSubscriber implements EventSubscriberInterface
39
{
40
    use MatchFilterForLeadTrait;
41
42
    /**
43
     * @var TrackableModel
44
     */
45
    private $trackableModel;
46
47
    /**
48
     * @var PageTokenHelper
49
     */
50
    private $pageTokenHelper;
51
52
    /**
53
     * @var AssetTokenHelper
54
     */
55
    private $assetTokenHelper;
56
57
    /**
58
     * @var FormTokenHelper
59
     */
60
    private $formTokenHelper;
61
62
    /**
63
     * @var FocusTokenHelper
64
     */
65
    private $focusTokenHelper;
66
67
    /**
68
     * @var AuditLogModel
69
     */
70
    private $auditLogModel;
71
72
    /**
73
     * @var DynamicContentHelper
74
     */
75
    private $dynamicContentHelper;
76
77
    /**
78
     * @var DynamicContentModel
79
     */
80
    private $dynamicContentModel;
81
82
    /**
83
     * @var CorePermissions
84
     */
85
    private $security;
86
87
    /**
88
     * @var ContactTracker
89
     */
90
    private $contactTracker;
91
92
    public function __construct(
93
        TrackableModel $trackableModel,
94
        PageTokenHelper $pageTokenHelper,
95
        AssetTokenHelper $assetTokenHelper,
96
        FormTokenHelper $formTokenHelper,
97
        FocusTokenHelper $focusTokenHelper,
98
        AuditLogModel $auditLogModel,
99
        DynamicContentHelper $dynamicContentHelper,
100
        DynamicContentModel $dynamicContentModel,
101
        CorePermissions $security,
102
        ContactTracker $contactTracker
103
    ) {
104
        $this->trackableModel       = $trackableModel;
105
        $this->pageTokenHelper      = $pageTokenHelper;
106
        $this->assetTokenHelper     = $assetTokenHelper;
107
        $this->formTokenHelper      = $formTokenHelper;
108
        $this->focusTokenHelper     = $focusTokenHelper;
109
        $this->auditLogModel        = $auditLogModel;
110
        $this->dynamicContentHelper = $dynamicContentHelper;
111
        $this->dynamicContentModel  = $dynamicContentModel;
112
        $this->security             = $security;
113
        $this->contactTracker       = $contactTracker;
114
    }
115
116
    /**
117
     * @return array
118
     */
119
    public static function getSubscribedEvents()
120
    {
121
        return [
122
            DynamicContentEvents::POST_SAVE         => ['onPostSave', 0],
123
            DynamicContentEvents::POST_DELETE       => ['onDelete', 0],
124
            DynamicContentEvents::TOKEN_REPLACEMENT => ['onTokenReplacement', 0],
125
            PageEvents::PAGE_ON_DISPLAY             => ['decodeTokens', 254],
126
        ];
127
    }
128
129
    /**
130
     * Add an entry to the audit log.
131
     */
132
    public function onPostSave(Events\DynamicContentEvent $event)
133
    {
134
        $entity = $event->getDynamicContent();
135
        if ($details = $event->getChanges()) {
136
            $log = [
137
                'bundle'   => 'dynamicContent',
138
                'object'   => 'dynamicContent',
139
                'objectId' => $entity->getId(),
140
                'action'   => ($event->isNew()) ? 'create' : 'update',
141
                'details'  => $details,
142
            ];
143
            $this->auditLogModel->writeToLog($log);
144
        }
145
    }
146
147
    /**
148
     * Add a delete entry to the audit log.
149
     */
150
    public function onDelete(Events\DynamicContentEvent $event)
151
    {
152
        $entity = $event->getDynamicContent();
153
        $log    = [
154
            'bundle'   => 'dynamicContent',
155
            'object'   => 'dynamicContent',
156
            'objectId' => $entity->deletedId,
157
            'action'   => 'delete',
158
            'details'  => ['name' => $entity->getName()],
159
        ];
160
        $this->auditLogModel->writeToLog($log);
161
    }
162
163
    public function onTokenReplacement(MauticEvents\TokenReplacementEvent $event)
164
    {
165
        /** @var Lead $lead */
166
        $lead         = $event->getLead();
167
        $content      = $event->getContent();
168
        $clickthrough = $event->getClickthrough();
169
170
        if ($content) {
171
            $tokens = array_merge(
172
                TokenHelper::findLeadTokens($content, $lead->getProfileFields()),
173
                $this->pageTokenHelper->findPageTokens($content, $clickthrough),
174
                $this->assetTokenHelper->findAssetTokens($content, $clickthrough),
175
                $this->formTokenHelper->findFormTokens($content),
176
                $this->focusTokenHelper->findFocusTokens($content)
177
            );
178
179
            list($content, $trackables) = $this->trackableModel->parseContentForTrackables(
180
                $content,
181
                $tokens,
182
                'dynamicContent',
183
                $clickthrough['dynamic_content_id']
184
            );
185
186
            $dwc     =  $this->dynamicContentModel->getEntity($clickthrough['dynamic_content_id']);
187
            $utmTags = [];
188
            if ($dwc && $dwc instanceof DynamicContent) {
189
                $utmTags = $dwc->getUtmTags();
190
            }
191
192
            /**
193
             * @var string
194
             * @var Trackable $trackable
195
             */
196
            foreach ($trackables as $token => $trackable) {
197
                $tokens[$token] = $this->trackableModel->generateTrackableUrl($trackable, $clickthrough, false, $utmTags);
198
            }
199
200
            $content = str_replace(array_keys($tokens), array_values($tokens), $content);
201
202
            $event->setContent($content);
203
        }
204
    }
205
206
    public function decodeTokens(PageDisplayEvent $event)
207
    {
208
        $lead = $this->security->isAnonymous() ? $this->contactTracker->getContact() : null;
209
        if (!$lead) {
210
            return;
211
        }
212
213
        $content = $event->getContent();
214
        if (empty($content)) {
215
            return;
216
        }
217
218
        $tokens    = $this->dynamicContentHelper->findDwcTokens($content, $lead);
219
        $leadArray = [];
220
        if ($lead instanceof Lead) {
0 ignored issues
show
$lead is always a sub-type of Mautic\LeadBundle\Entity\Lead.
Loading history...
221
            $leadArray = $this->dynamicContentHelper->convertLeadToArray($lead);
222
        }
223
        $result = [];
224
        foreach ($tokens as $token => $dwc) {
225
            $result[$token] = '';
226
            if ($this->matchFilterForLead($dwc['filters'], $leadArray)) {
227
                $result[$token] = $dwc['content'];
228
            }
229
        }
230
        $content = str_replace(array_keys($result), array_values($result), $content);
231
232
        // replace slots
233
        $dom = new DOMDocument('1.0', 'utf-8');
234
        $dom->loadHTML(mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_NOERROR);
0 ignored issues
show
It seems like mb_convert_encoding($con...TML-ENTITIES', 'UTF-8') can also be of type array; however, parameter $source of DOMDocument::loadHTML() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

234
        $dom->loadHTML(/** @scrutinizer ignore-type */ mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_NOERROR);
Loading history...
235
        $xpath = new DOMXPath($dom);
236
237
        $divContent = $xpath->query('//*[@data-slot="dwc"]');
238
        for ($i = 0; $i < $divContent->length; ++$i) {
239
            $slot = $divContent->item($i);
240
            if (!$slotName = $slot->getAttribute('data-param-slot-name')) {
241
                continue;
242
            }
243
244
            if (!$slotContent = $this->dynamicContentHelper->getDynamicContentForLead($slotName, $lead)) {
245
                continue;
246
            }
247
248
            $newnode = $dom->createDocumentFragment();
249
            $newnode->appendXML('<![CDATA['.mb_convert_encoding($slotContent, 'HTML-ENTITIES', 'UTF-8').']]>');
250
            $slot->parentNode->replaceChild($newnode, $slot);
251
        }
252
253
        $content = $dom->saveHTML();
254
255
        $event->setContent($content);
256
    }
257
}
258