Completed
Push — master ( 8fc13e...c19aa3 )
by Axel
06:21
created

ExtensionController::installAction()   C

Complexity

Conditions 15
Paths 9

Size

Total Lines 71
Code Lines 43

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 15
eloc 43
nc 9
nop 9
dl 0
loc 71
rs 5.9166
c 2
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Zikula package.
7
 *
8
 * Copyright Zikula Foundation - https://ziku.la/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Zikula\ExtensionsModule\Controller;
15
16
use RuntimeException;
17
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
18
use Symfony\Component\HttpFoundation\RedirectResponse;
19
use Symfony\Component\HttpFoundation\Request;
20
use Symfony\Component\HttpFoundation\Response;
21
use Symfony\Component\Routing\Annotation\Route;
22
use Symfony\Component\Routing\RouterInterface;
23
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
24
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
25
use Zikula\BlocksModule\Entity\RepositoryInterface\BlockRepositoryInterface;
26
use Zikula\Bundle\CoreBundle\CacheClearer;
27
use Zikula\Bundle\CoreBundle\Composer\MetaData;
28
use Zikula\Bundle\CoreBundle\Controller\AbstractController;
29
use Zikula\Bundle\CoreBundle\Event\GenericEvent;
30
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaHttpKernelInterface;
31
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaKernel;
32
use Zikula\Bundle\FormExtensionBundle\Form\Type\DeletionType;
33
use Zikula\Component\SortableColumns\Column;
34
use Zikula\Component\SortableColumns\SortableColumns;
35
use Zikula\ExtensionsModule\AbstractExtension;
36
use Zikula\ExtensionsModule\Constant;
37
use Zikula\ExtensionsModule\Entity\ExtensionEntity;
38
use Zikula\ExtensionsModule\Entity\RepositoryInterface\ExtensionRepositoryInterface;
39
use Zikula\ExtensionsModule\Event\ExtensionStateEvent;
40
use Zikula\ExtensionsModule\ExtensionEvents;
41
use Zikula\ExtensionsModule\Form\Type\ExtensionInstallType;
42
use Zikula\ExtensionsModule\Form\Type\ExtensionModifyType;
43
use Zikula\ExtensionsModule\Helper\BundleSyncHelper;
44
use Zikula\ExtensionsModule\Helper\ExtensionDependencyHelper;
45
use Zikula\ExtensionsModule\Helper\ExtensionHelper;
46
use Zikula\ExtensionsModule\Helper\ExtensionStateHelper;
47
use Zikula\PermissionsModule\Annotation\PermissionCheck;
48
use Zikula\ThemeModule\Engine\Annotation\Theme;
49
use Zikula\ThemeModule\Engine\Engine;
50
51
/**
52
 * Class ExtensionController
53
 *
54
 * @Route("")
55
 */
56
class ExtensionController extends AbstractController
57
{
58
    private const NEW_ROUTES_AVAIL = 'new.routes.avail';
59
60
    /**
61
     * @Route("/list/{pos}")
62
     * @PermissionCheck("admin")
63
     * @Theme("admin")
64
     * @Template("@ZikulaExtensionsModule/Extension/list.html.twig")
65
     */
66
    public function listAction(
67
        Request $request,
68
        EventDispatcherInterface $eventDispatcher,
69
        ExtensionRepositoryInterface $extensionRepository,
70
        BundleSyncHelper $bundleSyncHelper,
71
        RouterInterface $router,
72
        int $pos = 1
73
    ): array {
74
        $modulesJustInstalled = $request->query->get('justinstalled');
75
        if (!empty($modulesJustInstalled)) {
76
            // notify the event dispatcher that new routes are available (ids of modules just installed avail as args)
77
            $event = new GenericEvent(null, json_decode($modulesJustInstalled));
78
            $eventDispatcher->dispatch($event, self::NEW_ROUTES_AVAIL);
79
        }
80
81
        $sortableColumns = new SortableColumns($router, 'zikulaextensionsmodule_extension_list');
82
        $sortableColumns->addColumns([new Column('displayname'), new Column('state')]);
83
        $sortableColumns->setOrderByFromRequest($request);
84
85
        $upgradedExtensions = [];
86
        $vetoEvent = new GenericEvent();
87
        $eventDispatcher->dispatch($vetoEvent, ExtensionEvents::REGENERATE_VETO);
88
        if (1 === $pos && !$vetoEvent->isPropagationStopped()) {
89
            // regenerate the extension list only when viewing the first page
90
            $extensionsInFileSystem = $bundleSyncHelper->scanForBundles();
91
            $upgradedExtensions = $bundleSyncHelper->syncExtensions($extensionsInFileSystem);
92
        }
93
94
        $pagedResult = $extensionRepository->getPagedCollectionBy([], [
95
            $sortableColumns->getSortColumn()->getName() => $sortableColumns->getSortDirection()
96
        ], $this->getVar('itemsperpage'), $pos);
0 ignored issues
show
Bug introduced by
It seems like $this->getVar('itemsperpage') can also be of type false; however, parameter $limit of Zikula\ExtensionsModule\...:getPagedCollectionBy() does only seem to accept integer, 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

96
        ], /** @scrutinizer ignore-type */ $this->getVar('itemsperpage'), $pos);
Loading history...
97
98
        return [
99
            'sort' => $sortableColumns->generateSortableColumns(),
100
            'pager' => [
101
                'limit' => $this->getVar('itemsperpage'),
102
                'count' => count($pagedResult)
103
            ],
104
            'extensions' => $pagedResult,
105
            'upgradedExtensions' => $upgradedExtensions
106
        ];
107
    }
108
109
    /**
110
     * @Route("/activate/{id}/{token}", methods = {"GET"}, requirements={"id" = "^[1-9]\d*$"})
111
     * @PermissionCheck("admin")
112
     *
113
     * Activate an extension.
114
     *
115
     * @throws AccessDeniedException Thrown if the CSRF token is invalid
116
     */
117
    public function activateAction(
118
        int $id,
119
        string $token,
120
        ExtensionRepositoryInterface $extensionRepository,
121
        ExtensionStateHelper $extensionStateHelper,
122
        CacheClearer $cacheClearer
123
    ): RedirectResponse {
124
        if (!$this->isCsrfTokenValid('activate-extension', $token)) {
125
            throw new AccessDeniedException();
126
        }
127
128
        /** @var ExtensionEntity $extension */
129
        $extension = $extensionRepository->find($id);
130
        if (Constant::STATE_NOTALLOWED === $extension->getState()) {
131
            $this->addFlash('error', $this->trans('Error! Activation of %name% not allowed.', ['%name%' => $extension->getName()]));
132
        } else {
133
            // Update state
134
            $extensionStateHelper->updateState($id, Constant::STATE_ACTIVE);
135
            $cacheClearer->clear('symfony');
136
            $this->addFlash('status', $this->trans('Done! Activated %name%.', ['%name%' => $extension->getName()]));
137
        }
138
139
        return $this->redirectToRoute('zikulaextensionsmodule_extension_list');
140
    }
141
142
    /**
143
     * @Route("/deactivate/{id}/{token}", methods = {"GET"}, requirements={"id" = "^[1-9]\d*$"})
144
     * @PermissionCheck("admin")
145
     *
146
     * Deactivate an extension
147
     *
148
     * @throws AccessDeniedException Thrown if the CSRF token is invalid
149
     */
150
    public function deactivateAction(
151
        int $id,
152
        string $token,
153
        ExtensionRepositoryInterface $extensionRepository,
154
        ExtensionStateHelper $extensionStateHelper,
155
        CacheClearer $cacheClearer
156
    ): RedirectResponse {
157
        if (!$this->isCsrfTokenValid('deactivate-extension', $token)) {
158
            throw new AccessDeniedException();
159
        }
160
161
        /** @var ExtensionEntity $extension */
162
        $extension = $extensionRepository->find($id);
163
        if (null !== $extension) {
164
            if (ZikulaKernel::isCoreExtension($extension->getName())) {
165
                $this->addFlash('error', $this->trans('Error! You cannot deactivate the %name%. It is required by the system.', ['%name%' => $extension->getName()]));
166
            } else {
167
                // Update state
168
                $extensionStateHelper->updateState($id, Constant::STATE_INACTIVE);
169
                $cacheClearer->clear('symfony');
170
                $this->addFlash('status', $this->trans('Done! Deactivated %name%.', ['%name%' => $extension->getName()]));
171
            }
172
        }
173
174
        return $this->redirectToRoute('zikulaextensionsmodule_extension_list');
175
    }
176
177
    /**
178
     * @Route("/modify/{id}/{forceDefaults}", requirements={"id" = "^[1-9]\d*$", "forceDefaults" = "0|1"})
179
     * @Theme("admin")
180
     * @Template("@ZikulaExtensionsModule/Extension/modify.html.twig")
181
     *
182
     * Modify a module.
183
     *
184
     * @return array|RedirectResponse
185
     * @throws AccessDeniedException Thrown if the user doesn't have admin permissions for modifying the extension
186
     */
187
    public function modifyAction(
188
        Request $request,
189
        ZikulaHttpKernelInterface $kernel,
190
        ExtensionEntity $extension,
191
        CacheClearer $cacheClearer,
192
        bool $forceDefaults = false
193
    ) {
194
        if (!$this->hasPermission('ZikulaExtensionsModule::modify', $extension->getName() . '::' . $extension->getId(), ACCESS_ADMIN)) {
195
            throw new AccessDeniedException();
196
        }
197
198
        /** @var AbstractExtension $extensionBundle */
199
        $extensionBundle = $kernel->getBundle($extension->getName());
200
        $metaData = $extensionBundle->getMetaData()->getFilteredVersionInfoArray();
201
202
        if ($forceDefaults) {
203
            $extension->setName($metaData['name']);
204
            $extension->setUrl($metaData['url']);
205
            $extension->setDescription($metaData['description']);
206
        }
207
208
        $form = $this->createForm(ExtensionModifyType::class, $extension);
209
        $form->handleRequest($request);
210
        if ($form->isSubmitted() && $form->isValid()) {
211
            if ($form->get('defaults')->isClicked()) {
0 ignored issues
show
Bug introduced by
The method isClicked() does not exist on Symfony\Component\Form\FormInterface. It seems like you code against a sub-type of Symfony\Component\Form\FormInterface such as Symfony\Component\Form\SubmitButton. ( Ignorable by Annotation )

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

211
            if ($form->get('defaults')->/** @scrutinizer ignore-call */ isClicked()) {
Loading history...
212
                $this->addFlash('info', 'Default values reloaded. Save to confirm.');
213
214
                return $this->redirectToRoute('zikulaextensionsmodule_extension_modify', ['id' => $extension->getId(), 'forceDefaults' => 1]);
215
            }
216
            if ($form->get('save')->isClicked()) {
217
                $em = $this->getDoctrine()->getManager();
218
                $em->persist($extension);
219
                $em->flush();
220
221
                $cacheClearer->clear('symfony');
222
                $this->addFlash('status', 'Done! Extension updated.');
223
            } elseif ($form->get('cancel')->isClicked()) {
224
                $this->addFlash('status', 'Operation cancelled.');
225
            }
226
227
            return $this->redirectToRoute('zikulaextensionsmodule_extension_list');
228
        }
229
230
        return [
231
            'form' => $form->createView()
232
        ];
233
    }
234
235
    /**
236
     * @Route("/compatibility/{id}", methods = {"GET"}, requirements={"id" = "^[1-9]\d*$"})
237
     * @Theme("admin")
238
     * @Template("@ZikulaExtensionsModule/Extension/compatibility.html.twig")
239
     *
240
     * Display information of a module compatibility with the version of the core
241
     *
242
     * @throws AccessDeniedException Thrown if the user doesn't have admin permission to the requested module
243
     */
244
    public function compatibilityAction(ExtensionEntity $extension): array
245
    {
246
        if (!$this->hasPermission('ZikulaExtensionsModule::', $extension->getName() . '::' . $extension->getId(), ACCESS_ADMIN)) {
247
            throw new AccessDeniedException();
248
        }
249
250
        return [
251
            'extension' => $extension
252
        ];
253
    }
254
255
    /**
256
     * @Route("/install/{id}/{token}", requirements={"id" = "^[1-9]\d*$"})
257
     * @PermissionCheck("admin")
258
     * @Theme("admin")
259
     * @Template("@ZikulaExtensionsModule/Extension/install.html.twig")
260
     *
261
     * Install and initialise an extension.
262
     *
263
     * @return array|RedirectResponse
264
     * @throws AccessDeniedException Thrown if the CSRF token is invalid
265
     */
266
    public function installAction(
267
        Request $request,
268
        ExtensionEntity $extension,
269
        string $token,
270
        ZikulaHttpKernelInterface $kernel,
271
        ExtensionRepositoryInterface $extensionRepository,
272
        ExtensionHelper $extensionHelper,
273
        ExtensionStateHelper $extensionStateHelper,
274
        ExtensionDependencyHelper $dependencyHelper,
275
        CacheClearer $cacheClearer
276
    ) {
277
        $id = $extension->getId();
278
        if (!$this->isCsrfTokenValid('install-extension', $token)) {
279
            throw new AccessDeniedException();
280
        }
281
282
        if (!$kernel->isBundle($extension->getName())) {
283
            $extensionStateHelper->updateState($id, Constant::STATE_TRANSITIONAL);
284
            $cacheClearer->clear('symfony');
285
286
            return $this->redirectToRoute('zikulaextensionsmodule_extension_install', ['id' => $id, 'token' => $token]);
287
        }
288
        $unsatisfiedDependencies = $dependencyHelper->getUnsatisfiedExtensionDependencies($extension);
289
        $form = $this->createForm(ExtensionInstallType::class, [
290
            'dependencies' => $this->formatDependencyCheckboxArray($extensionRepository, $unsatisfiedDependencies)
291
        ]);
292
        $hasNoUnsatisfiedDependencies = empty($unsatisfiedDependencies);
293
        $form->handleRequest($request);
294
        if ($hasNoUnsatisfiedDependencies || ($form->isSubmitted() && $form->isValid())) {
295
            if ($hasNoUnsatisfiedDependencies || $form->get('install')->isClicked()) {
296
                $extensionsInstalled = [];
297
                $data = $form->getData();
298
                foreach ($data['dependencies'] as $dependencyId => $installSelected) {
299
                    if (!$installSelected && MetaData::DEPENDENCY_REQUIRED !== $unsatisfiedDependencies[$dependencyId]->getStatus()) {
300
                        continue;
301
                    }
302
                    $dependencyExtensionEntity = $extensionRepository->get($unsatisfiedDependencies[$dependencyId]->getModname());
303
                    if (isset($dependencyExtensionEntity)) {
304
                        if (!$extensionHelper->install($dependencyExtensionEntity)) {
305
                            $this->addFlash('error', $this->trans('Failed to install dependency "%name%"!', ['%name%' => $dependencyExtensionEntity->getName()]));
306
307
                            return $this->redirectToRoute('zikulaextensionsmodule_extension_list');
308
                        }
309
                        $extensionsInstalled[] = $dependencyExtensionEntity->getId();
310
                        $this->addFlash('status', $this->trans('Installed dependency "%name%".', ['%name%' => $dependencyExtensionEntity->getName()]));
311
                    } else {
312
                        $this->addFlash('warning', $this->trans('Warning: could not install selected dependency "%name%".', ['%name%' => $unsatisfiedDependencies[$dependencyId]->getModname()]));
313
                    }
314
                }
315
                if ($extensionHelper->install($extension)) {
316
                    $this->addFlash('status', $this->trans('Done! Installed "%name%".', ['%name%' => $extension->getName()]));
317
                    $extensionsInstalled[] = $id;
318
                    $cacheClearer->clear('symfony');
319
320
                    return $this->redirectToRoute('zikulaextensionsmodule_extension_postinstall', ['extensions' => json_encode($extensionsInstalled)]);
321
                }
322
                $extensionStateHelper->updateState($id, Constant::STATE_UNINITIALISED);
323
                $this->addFlash('error', $this->trans('Initialization of "%name%" failed!', ['%name%' => $extension->getName()]));
324
            }
325
            if ($form->get('cancel')->isClicked()) {
326
                $extensionStateHelper->updateState($id, Constant::STATE_UNINITIALISED);
327
                $this->addFlash('status', 'Operation cancelled.');
328
            }
329
330
            return $this->redirectToRoute('zikulaextensionsmodule_extension_list');
331
        }
332
333
        return [
334
            'dependencies' => $unsatisfiedDependencies,
335
            'extension' => $extension,
336
            'form' => $form->createView()
337
        ];
338
    }
339
340
    /**
341
     * Post-installation action to trigger the MODULE_POSTINSTALL event.
342
     * The additional Action is required because this event must occur AFTER the rebuild of the cache which occurs on Request.
343
     *
344
     * @Route("/postinstall/{extensions}", methods = {"GET"})
345
     */
346
    public function postInstallAction(
347
        ExtensionRepositoryInterface $extensionRepository,
348
        ZikulaHttpKernelInterface $kernel,
349
        EventDispatcherInterface $eventDispatcher,
350
        string $extensions = null
351
    ): RedirectResponse {
352
        if (!empty($extensions)) {
353
            $extensions = json_decode($extensions);
354
            foreach ($extensions as $extensionId) {
355
                /** @var ExtensionEntity $extensionEntity */
356
                $extensionEntity = $extensionRepository->find($extensionId);
357
                if (null === $extensionRepository) {
358
                    continue;
359
                }
360
                /** @var AbstractExtension $extensionBundle */
361
                $extensionBundle = $kernel->getBundle($extensionEntity->getName());
362
                if (null === $extensionBundle) {
363
                    continue;
364
                }
365
                $event = new ExtensionStateEvent($extensionBundle, $extensionEntity->toArray());
366
                $eventDispatcher->dispatch($event, ExtensionEvents::EXTENSION_POSTINSTALL);
367
            }
368
        }
369
370
        return $this->redirectToRoute('zikulaextensionsmodule_extension_list', ['justinstalled' => json_encode($extensions)]);
371
    }
372
373
    /**
374
     * Create array suitable for checkbox FormType [[ID => bool][ID => bool]].
375
     */
376
    private function formatDependencyCheckboxArray(
377
        ExtensionRepositoryInterface $extensionRepository,
378
        array $dependencies
379
    ): array {
380
        $return = [];
381
        foreach ($dependencies as $dependency) {
382
            /** @var ExtensionEntity $dependencyExtension */
383
            $dependencyExtension = $extensionRepository->get($dependency->getModname());
384
            $return[$dependency->getId()] = null !== $dependencyExtension;
385
        }
386
387
        return $return;
388
    }
389
390
    /**
391
     * @Route("/upgrade/{id}/{token}", requirements={"id" = "^[1-9]\d*$"})
392
     * @PermissionCheck("admin")
393
     *
394
     * Upgrade an extension.
395
     *
396
     * @throws AccessDeniedException Thrown if the CSRF token is invalid
397
     */
398
    public function upgradeAction(
399
        ExtensionEntity $extension,
400
        $token,
401
        ExtensionHelper $extensionHelper
402
    ): RedirectResponse {
403
        if (!$this->isCsrfTokenValid('upgrade-extension', $token)) {
404
            throw new AccessDeniedException();
405
        }
406
407
        $result = $extensionHelper->upgrade($extension);
408
        if ($result) {
409
            $this->addFlash('status', $this->trans('%name% upgraded to new version and activated.', ['%name%' => $extension->getDisplayname()]));
410
        } else {
411
            $this->addFlash('error', 'Extension upgrade failed!');
412
        }
413
414
        return $this->redirectToRoute('zikulaextensionsmodule_extension_list');
415
    }
416
417
    /**
418
     * @Route("/uninstall/{id}/{token}", requirements={"id" = "^[1-9]\d*$"})
419
     * @PermissionCheck("admin")
420
     * @Theme("admin")
421
     * @Template("@ZikulaExtensionsModule/Extension/uninstall.html.twig")
422
     *
423
     * Uninstall an extension.
424
     *
425
     * @return array|Response|RedirectResponse
426
     * @throws AccessDeniedException Thrown if the CSRF token is invalid
427
     */
428
    public function uninstallAction(
429
        Request $request,
430
        ExtensionEntity $extension,
431
        string $token,
432
        ZikulaHttpKernelInterface $kernel,
433
        BlockRepositoryInterface $blockRepository,
434
        ExtensionHelper $extensionHelper,
435
        ExtensionStateHelper $extensionStateHelper,
436
        ExtensionDependencyHelper $dependencyHelper,
437
        CacheClearer $cacheClearer
438
    ) {
439
        if (!$this->isCsrfTokenValid('uninstall-extension', $token)) {
440
            throw new AccessDeniedException();
441
        }
442
443
        if (Constant::STATE_MISSING === $extension->getState()) {
444
            throw new RuntimeException($this->trans('Error! The requested extension cannot be uninstalled because its files are missing!'));
445
        }
446
        if (!$kernel->isBundle($extension->getName())) {
447
            $extensionStateHelper->updateState($extension->getId(), Constant::STATE_TRANSITIONAL);
448
            $cacheClearer->clear('symfony');
449
        }
450
        $requiredDependents = $dependencyHelper->getDependentExtensions($extension);
451
        $blocks = $blockRepository->findBy(['module' => $extension]);
452
453
        $form = $this->createForm(DeletionType::class, [], [
454
            'action' => $this->generateUrl('zikulaextensionsmodule_extension_uninstall', [
455
                'id' => $extension->getId(),
456
                'token' => $token
457
            ]),
458
        ]);
459
        $form->handleRequest($request);
460
        if ($form->isSubmitted() && $form->isValid()) {
461
            if ($form->get('delete')->isClicked()) {
462
                // remove dependent extensions
463
                if (!$extensionHelper->uninstallArray($requiredDependents)) {
464
                    $this->addFlash('error', 'Error: Could not uninstall dependent extensions.');
465
466
                    return $this->redirectToRoute('zikulaextensionsmodule_extension_list');
467
                }
468
                // remove blocks
469
                $blockRepository->remove($blocks);
470
471
                // remove the extension
472
                if ($extensionHelper->uninstall($extension)) {
473
                    $this->addFlash('status', 'Done! Uninstalled extension.');
474
                } else {
475
                    $this->addFlash('error', 'Extension removal failed! (note: blocks and dependents may have been removed)');
476
                }
477
            } elseif ($form->get('cancel')->isClicked()) {
478
                $this->addFlash('status', 'Operation cancelled.');
479
            }
480
481
            return $this->redirectToRoute('zikulaextensionsmodule_extension_list');
482
        }
483
484
        return [
485
            'form' => $form->createView(),
486
            'extension' => $extension,
487
            'blocks' => $blocks,
488
            'requiredDependents' => $requiredDependents
489
        ];
490
    }
491
492
    /**
493
     * @Route("/theme-preview/{themeName}")
494
     * @PermissionCheck("admin")
495
     */
496
    public function previewAction(Engine $engine, string $themeName): Response
497
    {
498
        $engine->setActiveTheme($themeName);
499
        $this->addFlash('warning', 'Please note that blocks may appear out of place or even missing in a theme preview because position names are not consistent from theme to theme.');
500
501
        return $this->forward('Zikula\Bundle\CoreBundle\Controller\MainController::homeAction');
502
    }
503
}
504