Completed
Push — master ( 89c3e8...c161b3 )
by Craig
05:56 queued 40s
created

ExtensionController::uninstallAction()   B

Complexity

Conditions 11
Paths 15

Size

Total Lines 65
Code Lines 34

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 34
c 0
b 0
f 0
nc 15
nop 9
dl 0
loc 65
rs 7.3166

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\AbstractBundle;
27
use Zikula\Bundle\CoreBundle\CacheClearer;
28
use Zikula\Bundle\CoreBundle\Composer\MetaData;
29
use Zikula\Bundle\CoreBundle\Controller\AbstractController;
30
use Zikula\Bundle\CoreBundle\Event\GenericEvent;
31
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaHttpKernelInterface;
32
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaKernel;
33
use Zikula\Bundle\FormExtensionBundle\Form\Type\DeletionType;
34
use Zikula\Component\SortableColumns\Column;
35
use Zikula\Component\SortableColumns\SortableColumns;
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\ThemeModule\Engine\Annotation\Theme;
48
49
/**
50
 * Class ExtensionController
51
 * @Route("")
52
 */
53
class ExtensionController extends AbstractController
54
{
55
    private const NEW_ROUTES_AVAIL = 'new.routes.avail';
56
57
    /**
58
     * @Route("/list/{pos}")
59
     * @Theme("admin")
60
     * @Template("@ZikulaExtensionsModule/Extension/list.html.twig")
61
     *
62
     * @throws AccessDeniedException Thrown if the user doesn't have admin permissions for the module
63
     */
64
    public function listAction(
65
        Request $request,
66
        EventDispatcherInterface $eventDispatcher,
67
        ExtensionRepositoryInterface $extensionRepository,
68
        BundleSyncHelper $bundleSyncHelper,
69
        RouterInterface $router,
70
        int $pos = 1
71
    ): array {
72
        if (!$this->hasPermission('ZikulaExtensionsModule::', '::', ACCESS_ADMIN)) {
73
            throw new AccessDeniedException();
74
        }
75
        $modulesJustInstalled = $request->query->get('justinstalled');
76
        if (!empty($modulesJustInstalled)) {
77
            // notify the event dispatcher that new routes are available (ids of modules just installed avail as args)
78
            $event = new GenericEvent(null, json_decode($modulesJustInstalled));
79
            $eventDispatcher->dispatch($event, self::NEW_ROUTES_AVAIL);
80
        }
81
82
        $sortableColumns = new SortableColumns($router, 'zikulaextensionsmodule_extension_list');
83
        $sortableColumns->addColumns([new Column('displayname'), new Column('state')]);
84
        $sortableColumns->setOrderByFromRequest($request);
85
86
        $upgradedExtensions = [];
87
        $vetoEvent = new GenericEvent();
88
        $eventDispatcher->dispatch($vetoEvent, ExtensionEvents::REGENERATE_VETO);
89
        if (1 === $pos && !$vetoEvent->isPropagationStopped()) {
90
            // regenerate the extension list only when viewing the first page
91
            $extensionsInFileSystem = $bundleSyncHelper->scanForBundles();
92
            $upgradedExtensions = $bundleSyncHelper->syncExtensions($extensionsInFileSystem);
93
        }
94
95
        $pagedResult = $extensionRepository->getPagedCollectionBy([], [
96
            $sortableColumns->getSortColumn()->getName() => $sortableColumns->getSortDirection()
97
        ], $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

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

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