Issues (3627)

CoreBundle/EventListener/CoreSubscriber.php (4 issues)

1
<?php
2
3
/*
4
 * @copyright   2014 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\CoreBundle\EventListener;
13
14
use Mautic\CoreBundle\Controller\MauticController;
15
use Mautic\CoreBundle\CoreEvents;
16
use Mautic\CoreBundle\Event\IconEvent;
17
use Mautic\CoreBundle\Event\MenuEvent;
18
use Mautic\CoreBundle\Event\RouteEvent;
19
use Mautic\CoreBundle\Factory\MauticFactory;
20
use Mautic\CoreBundle\Helper\BundleHelper;
21
use Mautic\CoreBundle\Helper\CoreParametersHelper;
22
use Mautic\CoreBundle\Helper\UserHelper;
23
use Mautic\CoreBundle\Menu\MenuHelper;
24
use Mautic\CoreBundle\Service\FlashBag;
25
use Mautic\CoreBundle\Templating\Helper\AssetsHelper;
26
use Mautic\FormBundle\Entity\FormRepository;
27
use Mautic\UserBundle\Entity\User;
28
use Mautic\UserBundle\Event\LoginEvent;
29
use Mautic\UserBundle\Model\UserModel;
30
use Mautic\UserBundle\UserEvents;
31
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
32
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
33
use Symfony\Component\HttpFoundation\RequestStack;
34
use Symfony\Component\HttpKernel\Event\FilterControllerEvent;
35
use Symfony\Component\HttpKernel\KernelEvents;
36
use Symfony\Component\Routing\Route;
37
use Symfony\Component\Routing\RouteCollection;
38
use Symfony\Component\Security\Core\Authorization\AuthorizationChecker;
39
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
40
use Symfony\Component\Security\Http\SecurityEvents;
41
use Symfony\Component\Translation\TranslatorInterface;
42
43
class CoreSubscriber implements EventSubscriberInterface
44
{
45
    /**
46
     * @var BundleHelper
47
     */
48
    private $bundleHelper;
49
50
    /**
51
     * @var MenuHelper
52
     */
53
    private $menuHelper;
54
55
    /**
56
     * @var UserHelper
57
     */
58
    private $userHelper;
59
60
    /**
61
     * @var AssetsHelper
62
     */
63
    private $assetsHelper;
64
65
    /**
66
     * @var AuthorizationChecker
67
     */
68
    private $securityContext;
69
70
    /**
71
     * @var UserModel
72
     */
73
    private $userModel;
74
75
    /**
76
     * @var CoreParametersHelper
77
     */
78
    private $coreParametersHelper;
79
80
    /**
81
     * @var EventDispatcherInterface
82
     */
83
    private $dispatcher;
84
85
    /**
86
     * @var TranslatorInterface
87
     */
88
    private $translator;
89
90
    /**
91
     * @var RequestStack
92
     */
93
    private $requestStack;
94
95
    /**
96
     * @var FormRepository
97
     */
98
    private $formRepository;
99
100
    /**
101
     * @var MauticFactory
102
     */
103
    private $factory;
104
105
    /**
106
     * @var FlashBag
107
     */
108
    private $flashBag;
109
110
    public function __construct(
111
        BundleHelper $bundleHelper,
112
        MenuHelper $menuHelper,
113
        UserHelper $userHelper,
114
        AssetsHelper $assetsHelper,
115
        CoreParametersHelper $coreParametersHelper,
116
        AuthorizationChecker $securityContext,
117
        UserModel $userModel,
118
        EventDispatcherInterface $dispatcher,
119
        TranslatorInterface $translator,
120
        RequestStack $requestStack,
121
        FormRepository $formRepository,
122
        MauticFactory $factory,
123
        FlashBag $flashBag
124
    ) {
125
        $this->bundleHelper         = $bundleHelper;
126
        $this->menuHelper           = $menuHelper;
127
        $this->userHelper           = $userHelper;
128
        $this->assetsHelper         = $assetsHelper;
129
        $this->securityContext      = $securityContext;
130
        $this->userModel            = $userModel;
131
        $this->coreParametersHelper = $coreParametersHelper;
132
        $this->dispatcher           = $dispatcher;
133
        $this->translator           = $translator;
134
        $this->requestStack         = $requestStack;
135
        $this->formRepository       = $formRepository;
136
        $this->factory              = $factory;
137
        $this->flashBag             = $flashBag;
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143
    public static function getSubscribedEvents()
144
    {
145
        return [
146
            KernelEvents::CONTROLLER => [
147
                ['onKernelController', 0],
148
                ['onKernelRequestAddGlobalJS', 0],
149
            ],
150
            CoreEvents::BUILD_MENU            => ['onBuildMenu', 9999],
151
            CoreEvents::BUILD_ROUTE           => ['onBuildRoute', 0],
152
            CoreEvents::FETCH_ICONS           => ['onFetchIcons', 9999],
153
            SecurityEvents::INTERACTIVE_LOGIN => ['onSecurityInteractiveLogin', 0],
154
        ];
155
    }
156
157
    /**
158
     * Add mauticForms in js script tag for Froala.
159
     */
160
    public function onKernelRequestAddGlobalJS(FilterControllerEvent $event)
161
    {
162
        if (defined('MAUTIC_INSTALLER') || $this->userHelper->getUser()->isGuest() || !$event->isMasterRequest()) {
163
            return;
164
        }
165
166
        $list        = $this->formRepository->getSimpleList();
167
        $mauticForms = json_encode($list, JSON_FORCE_OBJECT | JSON_PRETTY_PRINT);
168
169
        $this->assetsHelper->addScriptDeclaration("var mauticForms = {$mauticForms};");
170
    }
171
172
    /**
173
     * Set vars on login.
174
     */
175
    public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
176
    {
177
        if (defined('MAUTIC_INSTALLER')) {
178
            return;
179
        }
180
181
        $session = $event->getRequest()->getSession();
182
        if ($this->securityContext->isGranted('IS_AUTHENTICATED_FULLY') || $this->securityContext->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
183
            $user = $event->getAuthenticationToken()->getUser();
184
185
            //set a session var for filemanager to know someone is logged in
186
            $session->set('mautic.user', $user->getId());
187
188
            //mark the user as last logged in
189
            $user = $this->userHelper->getUser();
190
            if ($user instanceof User) {
191
                $this->userModel->getRepository()->setLastLogin($user);
192
193
                // Set the timezone and locale in session while we have it since Symfony dispatches the onKernelRequest prior to the
194
                // firewall setting the known user
195
                $tz = $user->getTimezone();
196
                if (empty($tz)) {
197
                    $tz = $this->coreParametersHelper->get('default_timezone');
198
                }
199
                $session->set('_timezone', $tz);
200
201
                $locale = $user->getLocale();
202
                if (empty($locale)) {
203
                    $locale = $this->coreParametersHelper->get('locale');
204
                }
205
                $session->set('_locale', $locale);
206
            }
207
208
            //dispatch on login events
209
            if ($this->dispatcher->hasListeners(UserEvents::USER_LOGIN)) {
210
                $loginEvent = new LoginEvent($this->userHelper->getUser());
211
                $this->dispatcher->dispatch(UserEvents::USER_LOGIN, $loginEvent);
212
            }
213
        } else {
214
            $session->remove('mautic.user');
215
        }
216
    }
217
218
    /**
219
     * Populates namespace, bundle, controller, and action into request to be used throughout application.
220
     */
221
    public function onKernelController(FilterControllerEvent $event)
222
    {
223
        $controller = $event->getController();
224
225
        if (!is_array($controller)) {
226
            return;
227
        }
228
229
        //only affect Mautic controllers
230
        if ($controller[0] instanceof MauticController) {
231
            $request = $event->getRequest();
232
233
            //also set the request for easy access throughout controllers
234
            $controller[0]->setRequest($request);
235
236
            // set the factory for easy use access throughout the controllers
237
            // @deprecated To be removed in 3.0
238
            $controller[0]->setFactory($this->factory);
239
240
            // set the user as well
241
            $controller[0]->setUser($this->userHelper->getUser());
0 ignored issues
show
The method setUser() does not exist on Mautic\CoreBundle\Controller\MauticController. Since it exists in all sub-types, consider adding an abstract or default implementation to Mautic\CoreBundle\Controller\MauticController. ( Ignorable by Annotation )

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

241
            $controller[0]->/** @scrutinizer ignore-call */ 
242
                            setUser($this->userHelper->getUser());
Loading history...
242
243
            // and the core parameters helper
244
            $controller[0]->setCoreParametersHelper($this->coreParametersHelper);
0 ignored issues
show
The method setCoreParametersHelper() does not exist on Mautic\CoreBundle\Controller\MauticController. Since it exists in all sub-types, consider adding an abstract or default implementation to Mautic\CoreBundle\Controller\MauticController. ( Ignorable by Annotation )

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

244
            $controller[0]->/** @scrutinizer ignore-call */ 
245
                            setCoreParametersHelper($this->coreParametersHelper);
Loading history...
245
246
            // and the dispatcher
247
            $controller[0]->setDispatcher($this->dispatcher);
0 ignored issues
show
The method setDispatcher() does not exist on Mautic\CoreBundle\Controller\MauticController. Since it exists in all sub-types, consider adding an abstract or default implementation to Mautic\CoreBundle\Controller\MauticController. ( Ignorable by Annotation )

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

247
            $controller[0]->/** @scrutinizer ignore-call */ 
248
                            setDispatcher($this->dispatcher);
Loading history...
248
249
            // and the translator
250
            $controller[0]->setTranslator($this->translator);
0 ignored issues
show
The method setTranslator() does not exist on Mautic\CoreBundle\Controller\MauticController. Since it exists in all sub-types, consider adding an abstract or default implementation to Mautic\CoreBundle\Controller\MauticController. ( Ignorable by Annotation )

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

250
            $controller[0]->/** @scrutinizer ignore-call */ 
251
                            setTranslator($this->translator);
Loading history...
251
252
            // and the flash bag
253
            $controller[0]->setFlashBag($this->flashBag);
254
255
            //run any initialize functions
256
            $controller[0]->initialize($event);
257
        }
258
    }
259
260
    public function onBuildMenu(MenuEvent $event)
261
    {
262
        $name    = $event->getType();
263
        $bundles = $this->bundleHelper->getMauticBundles(true);
264
        foreach ($bundles as $bundle) {
265
            if (!empty($bundle['config']['menu'][$name])) {
266
                $menu = $bundle['config']['menu'][$name];
267
                $event->addMenuItems(
268
                    [
269
                        'priority' => !isset($menu['priority']) ? 9999 : $menu['priority'],
270
                        'items'    => !isset($menu['items']) ? $menu : $menu['items'],
271
                    ]
272
                );
273
            }
274
        }
275
    }
276
277
    public function onBuildRoute(RouteEvent $event)
278
    {
279
        $type       = $event->getType();
280
        $bundles    = $this->bundleHelper->getMauticBundles(true);
281
        $collection = $event->getCollection();
282
283
        foreach ($bundles as $bundle) {
284
            if (!empty($bundle['config']['routes'][$type])) {
285
                foreach ($bundle['config']['routes'][$type] as $name => $details) {
286
                    if ('api' == $type && !empty($details['standard_entity'])) {
287
                        $standards = [
288
                            'getall' => [
289
                                'action' => 'getEntities',
290
                                'method' => 'GET',
291
                                'path'   => '',
292
                            ],
293
                            'getone' => [
294
                                'action' => 'getEntity',
295
                                'method' => 'GET',
296
                                'path'   => '/{id}',
297
                            ],
298
                            'new' => [
299
                                'action' => 'newEntity',
300
                                'method' => 'POST',
301
                                'path'   => '/new',
302
                            ],
303
                            'newbatch' => [
304
                                'action' => 'newEntities',
305
                                'method' => 'POST',
306
                                'path'   => '/batch/new',
307
                            ],
308
                            'editbatchput' => [
309
                                'action' => 'editEntities',
310
                                'method' => 'PUT',
311
                                'path'   => '/batch/edit',
312
                            ],
313
                            'editbatchpatch' => [
314
                                'action' => 'editEntities',
315
                                'method' => 'PATCH',
316
                                'path'   => '/batch/edit',
317
                            ],
318
                            'editput' => [
319
                                'action' => 'editEntity',
320
                                'method' => 'PUT',
321
                                'path'   => '/{id}/edit',
322
                            ],
323
                            'editpatch' => [
324
                                'action' => 'editEntity',
325
                                'method' => 'PATCH',
326
                                'path'   => '/{id}/edit',
327
                            ],
328
                            'deletebatch' => [
329
                                'action' => 'deleteEntities',
330
                                'method' => 'DELETE',
331
                                'path'   => '/batch/delete',
332
                            ],
333
                            'delete' => [
334
                                'action' => 'deleteEntity',
335
                                'method' => 'DELETE',
336
                                'path'   => '/{id}/delete',
337
                            ],
338
                        ];
339
340
                        foreach (['name', 'path', 'controller'] as $required) {
341
                            if (empty($details[$required])) {
342
                                throw new \InvalidArgumentException("$bundle.$name must have $required defined");
343
                            }
344
                        }
345
346
                        $routeName  = 'mautic_api_'.$details['name'].'_';
347
                        $pathBase   = $details['path'];
348
                        $controller = $details['controller'];
349
                        foreach ($standards as $standardName => $standardDetails) {
350
                            if (!empty($details['supported_endpoints']) && !in_array($standardName, $details['supported_endpoints'])) {
351
                                // Not supported so ignore
352
                                continue;
353
                            }
354
355
                            $routeDetails = array_merge(
356
                                $standardDetails,
357
                                [
358
                                    'path'       => $pathBase.$standardDetails['path'],
359
                                    'controller' => $controller.':'.$standardDetails['action'],
360
                                    'method'     => $standardDetails['method'],
361
                                ]
362
                            );
363
                            $this->addRouteToCollection($collection, $type, $routeName.$standardName, $routeDetails);
364
                        }
365
                    } else {
366
                        $this->addRouteToCollection($collection, $type, $name, $details);
367
                    }
368
                }
369
            }
370
        }
371
    }
372
373
    public function onFetchIcons(IconEvent $event)
374
    {
375
        $session = $this->requestStack->getCurrentRequest()->getSession();
376
        $icons   = $session->get('mautic.menu.icons', []);
377
378
        if (empty($icons)) {
379
            $bundles = $this->bundleHelper->getMauticBundles(true);
380
381
            foreach ($bundles as $bundle) {
382
                if (!empty($bundle['config']['menu']['main'])) {
383
                    $items = (!isset($bundle['config']['menu']['main']['items']) ? $bundle['config']['menu']['main'] : $bundle['config']['menu']['main']['items']);
384
                }
385
386
                if (!empty($items)) {
387
                    $this->menuHelper->createMenuStructure($items);
388
                    foreach ($items as $item) {
389
                        if (isset($item['iconClass']) && isset($item['id'])) {
390
                            $id = explode('_', $item['id']);
391
                            if (isset($id[1])) {
392
                                // some bundle names are in plural, create also singular item
393
                                if ('s' == substr($id[1], -1)) {
394
                                    $event->addIcon(rtrim($id[1], 's'), $item['iconClass']);
395
                                }
396
                                $event->addIcon($id[1], $item['iconClass']);
397
                            }
398
                        }
399
                    }
400
                }
401
            }
402
            unset($bundles);
403
404
            $icons = $event->getIcons();
405
            $session->set('mautic.menu.icons', $icons);
406
        } else {
407
            $event->setIcons($icons);
408
        }
409
    }
410
411
    /**
412
     * @param $type
413
     * @param $name
414
     * @param $details
415
     */
416
    private function addRouteToCollection(RouteCollection $collection, $type, $name, $details)
417
    {
418
        // Set defaults and controller
419
        $defaults = (!empty($details['defaults'])) ? $details['defaults'] : [];
420
        if (isset($details['controller'])) {
421
            $defaults['_controller'] = $details['controller'];
422
        }
423
        if (isset($details['format'])) {
424
            $defaults['_format'] = $details['format'];
425
        } elseif ('api' == $type) {
426
            $defaults['_format'] = 'json';
427
        }
428
        $method = '';
429
        if (isset($details['method'])) {
430
            $method = $details['method'];
431
        } elseif ('api' === $type) {
432
            $method = 'GET';
433
        }
434
        // Set requirements
435
        $requirements = (!empty($details['requirements'])) ? $details['requirements'] : [];
436
437
        // Set some very commonly used defaults and requirements
438
        if (false !== strpos($details['path'], '{page}')) {
439
            if (!isset($defaults['page'])) {
440
                $defaults['page'] = 0;
441
            }
442
            if (!isset($requirements['page'])) {
443
                $requirements['page'] = '\d+';
444
            }
445
        }
446
        if (false !== strpos($details['path'], '{objectId}')) {
447
            if (!isset($defaults['objectId'])) {
448
                // Set default to 0 for the "new" actions
449
                $defaults['objectId'] = 0;
450
            }
451
            if (!isset($requirements['objectId'])) {
452
                // Only allow alphanumeric and _- for objectId
453
                $requirements['objectId'] = '[a-zA-Z0-9_-]+';
454
            }
455
        }
456
        if ('api' == $type) {
457
            if (false !== strpos($details['path'], '{id}')) {
458
                if (!isset($requirements['page'])) {
459
                    $requirements['id'] = '\d+';
460
                }
461
            }
462
463
            if (preg_match_all('/\{(.*?Id)\}/', $details['path'], $matches)) {
464
                // Force digits for IDs
465
                foreach ($matches[1] as $match) {
466
                    if (!isset($requirements[$match])) {
467
                        $requirements[$match] = '\d+';
468
                    }
469
                }
470
            }
471
        }
472
473
        // Add the route
474
        $collection->add($name, new Route($details['path'], $defaults, $requirements, [], '', [], $method));
475
    }
476
}
477