Completed
Push — master ( a15f89...abb079 )
by Craig
07:12 queued 01:36
created

ModuleController   F

Complexity

Total Complexity 70

Size/Duplication

Total Lines 471
Duplicated Lines 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
eloc 199
dl 0
loc 471
rs 2.8
c 2
b 0
f 0
wmc 70

10 Methods

Rating   Name   Duplication   Size   Complexity  
A compatibilityAction() 0 8 2
A deactivateAction() 0 29 5
A formatDependencyCheckboxArray() 0 12 2
A postInstallAction() 0 26 5
C installAction() 0 74 16
A activateAction() 0 27 4
A upgradeAction() 0 21 4
B modifyAction() 0 45 8
B uninstallAction() 0 60 11
C viewModuleListAction() 0 67 13

How to fix   Complexity   

Complex Class

Complex classes like ModuleController often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ModuleController, and based on these observations, apply Extract Interface, too.

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\EventDispatcher\EventDispatcherInterface;
19
use Symfony\Component\HttpFoundation\RedirectResponse;
20
use Symfony\Component\HttpFoundation\Request;
21
use Symfony\Component\HttpFoundation\Response;
22
use Symfony\Component\Routing\Annotation\Route;
23
use Symfony\Component\Routing\Exception\RouteNotFoundException;
24
use Symfony\Component\Routing\RouterInterface;
25
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
26
use Zikula\BlocksModule\Entity\RepositoryInterface\BlockRepositoryInterface;
27
use Zikula\Bundle\CoreBundle\Bundle\MetaData;
28
use Zikula\Bundle\CoreBundle\CacheClearer;
29
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaHttpKernelInterface;
30
use Zikula\Bundle\CoreBundle\HttpKernel\ZikulaKernel;
31
use Zikula\Bundle\FormExtensionBundle\Form\Type\DeletionType;
32
use Zikula\Component\SortableColumns\Column;
33
use Zikula\Component\SortableColumns\SortableColumns;
34
use Zikula\Core\AbstractBundle;
35
use Zikula\Core\Controller\AbstractController;
36
use Zikula\Core\CoreEvents;
37
use Zikula\Core\Event\GenericEvent;
38
use Zikula\Core\Event\ModuleStateEvent;
39
use Zikula\ExtensionsModule\Constant;
40
use Zikula\ExtensionsModule\Entity\ExtensionEntity;
41
use Zikula\ExtensionsModule\Entity\RepositoryInterface\ExtensionRepositoryInterface;
42
use Zikula\ExtensionsModule\ExtensionEvents;
43
use Zikula\ExtensionsModule\Form\Type\ExtensionInstallType;
44
use Zikula\ExtensionsModule\Form\Type\ExtensionModifyType;
45
use Zikula\ExtensionsModule\Helper\BundleSyncHelper;
46
use Zikula\ExtensionsModule\Helper\ExtensionDependencyHelper;
47
use Zikula\ExtensionsModule\Helper\ExtensionHelper;
48
use Zikula\ExtensionsModule\Helper\ExtensionStateHelper;
49
use Zikula\ThemeModule\Engine\Annotation\Theme;
50
51
/**
52
 * Class ModuleController
53
 * @Route("/module")
54
 */
55
class ModuleController extends AbstractController
56
{
57
    private const NEW_ROUTES_AVAIL = 'new.routes.avail';
58
59
    /**
60
     * @Route("/list/{pos}")
61
     * @Theme("admin")
62
     * @Template("@ZikulaExtensionsModule/Module/viewModuleList.html.twig")
63
     *
64
     * @throws AccessDeniedException Thrown if the user doesn't have admin permissions for the module
65
     */
66
    public function viewModuleListAction(
67
        Request $request,
68
        ExtensionRepositoryInterface $extensionRepository,
69
        BundleSyncHelper $bundleSyncHelper,
70
        RouterInterface $router,
71
        int $pos = 1
72
    ): array {
73
        if (!$this->hasPermission('ZikulaExtensionsModule::', '::', ACCESS_ADMIN)) {
74
            throw new AccessDeniedException();
75
        }
76
        $modulesJustInstalled = $request->query->get('justinstalled');
77
        if (!empty($modulesJustInstalled)) {
78
            // notify the event dispatcher that new routes are available (ids of modules just installed avail as args)
79
            $event = new GenericEvent(null, json_decode($modulesJustInstalled));
80
            $this->get('event_dispatcher')->dispatch($event, self::NEW_ROUTES_AVAIL);
81
        }
82
83
        $sortableColumns = new SortableColumns($router, 'zikulaextensionsmodule_module_viewmodulelist');
84
        $sortableColumns->addColumns([new Column('displayname'), new Column('state')]);
85
        $sortableColumns->setOrderByFromRequest($request);
86
87
        $upgradedExtensions = [];
88
        $vetoEvent = new GenericEvent();
89
        $this->get('event_dispatcher')->dispatch($vetoEvent, ExtensionEvents::REGENERATE_VETO);
90
        if (1 === $pos && !$vetoEvent->isPropagationStopped()) {
0 ignored issues
show
Deprecated Code introduced by
The function Symfony\Component\EventD...:isPropagationStopped() has been deprecated: since Symfony 4.3, use "Symfony\Contracts\EventDispatcher\Event" instead ( Ignorable by Annotation )

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

90
        if (1 === $pos && !/** @scrutinizer ignore-deprecated */ $vetoEvent->isPropagationStopped()) {

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
91
            // regenerate the extension list only when viewing the first page
92
            $extensionsInFileSystem = $bundleSyncHelper->scanForBundles();
93
            $upgradedExtensions = $bundleSyncHelper->syncExtensions($extensionsInFileSystem);
94
        }
95
96
        $pagedResult = $extensionRepository->getPagedCollectionBy([], [
97
            $sortableColumns->getSortColumn()->getName() => $sortableColumns->getSortDirection()
98
        ], $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

98
        ], /** @scrutinizer ignore-type */ $this->getVar('itemsperpage'), $pos);
Loading history...
99
100
        $adminRoutes = [];
101
102
        foreach ($pagedResult as $module) {
103
            if (Constant::STATE_ACTIVE !== $module['state'] || !isset($module['capabilities']['admin']) || empty($module['capabilities']['admin'])) {
104
                continue;
105
            }
106
107
            $adminCapabilityInfo = $module['capabilities']['admin'];
108
            $adminUrl = '';
109
            if (isset($adminCapabilityInfo['route'])) {
110
                try {
111
                    $adminUrl = $router->generate($adminCapabilityInfo['route']);
112
                } catch (RouteNotFoundException $routeNotFoundException) {
113
                    // do nothing, just skip this link
114
                }
115
            } elseif (isset($adminCapabilityInfo['url'])) {
116
                $adminUrl = $adminCapabilityInfo['url'];
117
            }
118
119
            if (!empty($adminUrl)) {
120
                $adminRoutes[$module['name']] = $adminUrl;
121
            }
122
        }
123
124
        return [
125
            'sort' => $sortableColumns->generateSortableColumns(),
126
            'pager' => [
127
                'limit' => $this->getVar('itemsperpage'),
128
                'count' => count($pagedResult)
129
            ],
130
            'modules' => $pagedResult,
131
            'adminRoutes' => $adminRoutes,
132
            'upgradedExtensions' => $upgradedExtensions
133
        ];
134
    }
135
136
    /**
137
     * @Route("/modules/activate/{id}/{token}", methods = {"GET"}, requirements={"id" = "^[1-9]\d*$"})
138
     *
139
     * Activate an extension.
140
     *
141
     * @throws AccessDeniedException Thrown if the user doesn't have admin permissions for the module
142
     */
143
    public function activateAction(
144
        int $id,
145
        string $token,
146
        ExtensionRepositoryInterface $extensionRepository,
147
        ExtensionStateHelper $extensionStateHelper,
148
        CacheClearer $cacheClearer
149
    ): RedirectResponse {
150
        if (!$this->hasPermission('ZikulaExtensionsModule::', '::', ACCESS_ADMIN)) {
151
            throw new AccessDeniedException();
152
        }
153
154
        if (!$this->isCsrfTokenValid('activate-extension', $token)) {
155
            throw new AccessDeniedException();
156
        }
157
158
        /** @var ExtensionEntity $extension */
159
        $extension = $extensionRepository->find($id);
160
        if (Constant::STATE_NOTALLOWED === $extension->getState()) {
161
            $this->addFlash('error', $this->__f('Error! Activation of module %s not allowed.', ['%s' => $extension->getName()]));
162
        } else {
163
            // Update state
164
            $extensionStateHelper->updateState($id, Constant::STATE_ACTIVE);
165
            $cacheClearer->clear('symfony.routing');
166
            $this->addFlash('status', $this->__f('Done! Activated %s module.', ['%s' => $extension->getName()]));
167
        }
168
169
        return $this->redirectToRoute('zikulaextensionsmodule_module_viewmodulelist');
170
    }
171
172
    /**
173
     * @Route("/modules/deactivate/{id}/{token}", methods = {"GET"}, requirements={"id" = "^[1-9]\d*$"})
174
     *
175
     * Deactivate an extension
176
     *
177
     * @throws AccessDeniedException Thrown if the user doesn't have admin permissions for the module
178
     */
179
    public function deactivateAction(
180
        int $id,
181
        string $token,
182
        ExtensionRepositoryInterface $extensionRepository,
183
        ExtensionStateHelper $extensionStateHelper,
184
        CacheClearer $cacheClearer
185
    ): RedirectResponse {
186
        if (!$this->hasPermission('ZikulaExtensionsModule::', '::', ACCESS_ADMIN)) {
187
            throw new AccessDeniedException();
188
        }
189
190
        if (!$this->isCsrfTokenValid('deactivate-extension', $token)) {
191
            throw new AccessDeniedException();
192
        }
193
194
        /** @var ExtensionEntity $extension */
195
        $extension = $extensionRepository->find($id);
196
        if (null !== $extension) {
197
            if (ZikulaKernel::isCoreModule($extension->getName())) {
198
                $this->addFlash('error', $this->__f('Error! You cannot deactivate this extension [%s]. It is a mandatory core extension, and is required by the system.', ['%s' => $extension->getName()]));
199
            } else {
200
                // Update state
201
                $extensionStateHelper->updateState($id, Constant::STATE_INACTIVE);
202
                $cacheClearer->clear('symfony.routing');
203
                $this->addFlash('status', $this->__('Done! Deactivated module.'));
204
            }
205
        }
206
207
        return $this->redirectToRoute('zikulaextensionsmodule_module_viewmodulelist');
208
    }
209
210
    /**
211
     * @Route("/modify/{id}/{forceDefaults}", requirements={"id" = "^[1-9]\d*$", "forceDefaults" = "0|1"})
212
     * @Theme("admin")
213
     * @Template("@ZikulaExtensionsModule/Module/modify.html.twig")
214
     *
215
     * Modify a module.
216
     *
217
     * @return array|RedirectResponse
218
     * @throws AccessDeniedException Thrown if the user doesn't have admin permissions for modifying the extension
219
     */
220
    public function modifyAction(
221
        Request $request,
222
        ExtensionEntity $extension,
223
        CacheClearer $cacheClearer,
224
        bool $forceDefaults = false
225
    ) {
226
        if (!$this->hasPermission('ZikulaExtensionsModule::modify', $extension->getName() . '::' . $extension->getId(), ACCESS_ADMIN)) {
227
            throw new AccessDeniedException();
228
        }
229
230
        /** @var AbstractBundle $bundle */
231
        $bundle = $this->get('kernel')->getModule($extension->getName());
232
        $metaData = $bundle->getMetaData()->getFilteredVersionInfoArray();
233
234
        if ($forceDefaults) {
235
            $extension->setName($metaData['name']);
236
            $extension->setUrl($metaData['url']);
237
            $extension->setDescription($metaData['description']);
238
        }
239
240
        $form = $this->createForm(ExtensionModifyType::class, $extension);
241
        $form->handleRequest($request);
242
        if ($form->isSubmitted() && $form->isValid()) {
243
            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

243
            if ($form->get('defaults')->/** @scrutinizer ignore-call */ isClicked()) {
Loading history...
244
                $this->addFlash('info', $this->__('Default values reloaded. Save to confirm.'));
245
246
                return $this->redirectToRoute('zikulaextensionsmodule_module_modify', ['id' => $extension->getId(), 'forceDefaults' => 1]);
247
            }
248
            if ($form->get('save')->isClicked()) {
249
                $em = $this->getDoctrine()->getManager();
250
                $em->persist($extension);
251
                $em->flush();
252
253
                $cacheClearer->clear('symfony.routing');
254
                $this->addFlash('status', $this->__('Done! Extension updated.'));
255
            }
256
            if ($form->get('cancel')->isClicked()) {
257
                $this->addFlash('status', $this->__('Operation cancelled.'));
258
            }
259
260
            return $this->redirectToRoute('zikulaextensionsmodule_module_viewmodulelist');
261
        }
262
263
        return [
264
            'form' => $form->createView()
265
        ];
266
    }
267
268
    /**
269
     * @Route("/compatibility/{id}", methods = {"GET"}, requirements={"id" = "^[1-9]\d*$"})
270
     * @Theme("admin")
271
     * @Template("@ZikulaExtensionsModule/Module/compatibility.html.twig")
272
     *
273
     * Display information of a module compatibility with the version of the core
274
     *
275
     * @throws AccessDeniedException Thrown if the user doesn't have admin permission to the requested module
276
     */
277
    public function compatibilityAction(ExtensionEntity $extension): array
278
    {
279
        if (!$this->hasPermission('ZikulaExtensionsModule::', $extension->getName() . '::' . $extension->getId(), ACCESS_ADMIN)) {
280
            throw new AccessDeniedException();
281
        }
282
283
        return [
284
            'extension' => $extension
285
        ];
286
    }
287
288
    /**
289
     * @Route("/install/{id}/{token}", requirements={"id" = "^[1-9]\d*$"})
290
     * @Theme("admin")
291
     * @Template("@ZikulaExtensionsModule/Module/install.html.twig")
292
     *
293
     * Install and initialise an extension.
294
     *
295
     * @return array|RedirectResponse
296
     * @throws AccessDeniedException Thrown if the user doesn't have admin permission for the module
297
     */
298
    public function installAction(
299
        Request $request,
300
        ExtensionEntity $extension,
301
        string $token,
302
        ExtensionRepositoryInterface $extensionRepository,
303
        ExtensionHelper $extensionHelper,
304
        ExtensionStateHelper $extensionStateHelper,
305
        ExtensionDependencyHelper $dependencyHelper,
306
        CacheClearer $cacheClearer
307
    ) {
308
        if (!$this->hasPermission('ZikulaExtensionsModule::', '::', ACCESS_ADMIN)) {
309
            throw new AccessDeniedException();
310
        }
311
312
        $id = $extension->getId();
313
        if (!$this->isCsrfTokenValid('install-extension', $token)) {
314
            throw new AccessDeniedException();
315
        }
316
317
        if (!$this->get('kernel')->isBundle($extension->getName())) {
318
            $extensionStateHelper->updateState($id, Constant::STATE_TRANSITIONAL);
319
            $cacheClearer->clear('symfony');
320
321
            return $this->redirectToRoute('zikulaextensionsmodule_module_install', ['id' => $id]);
322
        }
323
        $unsatisfiedDependencies = $dependencyHelper->getUnsatisfiedExtensionDependencies($extension);
324
        $form = $this->createForm(ExtensionInstallType::class, [
325
            'dependencies' => $this->formatDependencyCheckboxArray($extensionRepository, $unsatisfiedDependencies)
326
        ]);
327
        $hasNoUnsatisfiedDependencies = empty($unsatisfiedDependencies);
328
        $form->handleRequest($request);
329
        if ($hasNoUnsatisfiedDependencies || ($form->isSubmitted() && $form->isValid())) {
330
            if ($hasNoUnsatisfiedDependencies || $form->get('install')->isClicked()) {
331
                $extensionsInstalled = [];
332
                $data = $form->getData();
333
                foreach ($data['dependencies'] as $dependencyId => $installSelected) {
334
                    if (!$installSelected && MetaData::DEPENDENCY_REQUIRED !== $unsatisfiedDependencies[$dependencyId]->getStatus()) {
335
                        continue;
336
                    }
337
                    $dependencyExtensionEntity = $extensionRepository->get($unsatisfiedDependencies[$dependencyId]->getModname());
338
                    if (isset($dependencyExtensionEntity)) {
339
                        if (!$extensionHelper->install($dependencyExtensionEntity)) {
340
                            $this->addFlash('error', $this->__f('Failed to install dependency %s!', ['%s' => $dependencyExtensionEntity->getName()]));
341
342
                            return $this->redirectToRoute('zikulaextensionsmodule_module_viewmodulelist');
343
                        }
344
                        $extensionsInstalled[] = $dependencyExtensionEntity->getId();
345
                        $this->addFlash('status', $this->__f('Installed dependency %s.', ['%s' => $dependencyExtensionEntity->getName()]));
346
                    } else {
347
                        $this->addFlash('warning', $this->__f('Warning: could not install selected dependency %s', ['%s' => $unsatisfiedDependencies[$dependencyId]->getModname()]));
348
                    }
349
                }
350
                if ($extensionHelper->install($extension)) {
351
                    $this->addFlash('status', $this->__f('Done! Installed %s.', ['%s' => $extension->getName()]));
352
                    $extensionsInstalled[] = $id;
353
                    $cacheClearer->clear('symfony');
354
355
                    return $this->redirectToRoute('zikulaextensionsmodule_module_postinstall', ['extensions' => json_encode($extensionsInstalled)]);
356
                }
357
                $extensionStateHelper->updateState($id, Constant::STATE_UNINITIALISED);
358
                $this->addFlash('error', $this->__f('Initialization of %s failed!', ['%s' => $extension->getName()]));
359
            }
360
            if ($form->get('cancel')->isClicked()) {
361
                $extensionStateHelper->updateState($id, Constant::STATE_UNINITIALISED);
362
                $this->addFlash('status', $this->__('Operation cancelled.'));
363
            }
364
365
            return $this->redirectToRoute('zikulaextensionsmodule_module_viewmodulelist');
366
        }
367
368
        return [
369
            'dependencies' => $unsatisfiedDependencies,
370
            'extension' => $extension,
371
            'form' => $form->createView()
372
        ];
373
    }
374
375
    /**
376
     * Post-installation action to trigger the MODULE_POSTINSTALL event.
377
     * The additional Action is required because this event must occur AFTER the rebuild of the cache which occurs on Request.
378
     *
379
     * @Route("/postinstall/{extensions}", methods = {"GET"})
380
     */
381
    public function postInstallAction(
382
        ExtensionRepositoryInterface $extensionRepository,
383
        ZikulaHttpKernelInterface $kernel,
384
        EventDispatcherInterface $eventDispatcher,
385
        string $extensions = null
386
    ): RedirectResponse {
387
        if (!empty($extensions)) {
388
            $extensions = json_decode($extensions);
389
            foreach ($extensions as $extensionId) {
390
                /** @var ExtensionEntity $extensionEntity */
391
                $extensionEntity = $extensionRepository->find($extensionId);
392
                if (null === $extensionRepository) {
393
                    continue;
394
                }
395
                $bundle = $kernel->getModule($extensionEntity->getName());
396
                if (null === $bundle) {
397
                    continue;
398
                }
399
                $event = new ModuleStateEvent($bundle, $extensionEntity->toArray());
400
                $eventDispatcher->dispatch($event, CoreEvents::MODULE_POSTINSTALL);
0 ignored issues
show
Unused Code introduced by
The call to Symfony\Contracts\EventD...erInterface::dispatch() has too many arguments starting with Zikula\Core\CoreEvents::MODULE_POSTINSTALL. ( Ignorable by Annotation )

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

400
                $eventDispatcher->/** @scrutinizer ignore-call */ 
401
                                  dispatch($event, CoreEvents::MODULE_POSTINSTALL);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
401
            }
402
            // currently commented out because it takes a long time.
403
            //$extensionHelper->installAssets();
404
        }
405
406
        return $this->redirectToRoute('zikulaextensionsmodule_module_viewmodulelist', ['justinstalled' => json_encode($extensions)]);
407
    }
408
409
    /**
410
     * Create array suitable for checkbox FormType [[ID => bool][ID => bool]].
411
     */
412
    private function formatDependencyCheckboxArray(
413
        ExtensionRepositoryInterface $extensionRepository,
414
        array $dependencies
415
    ): array {
416
        $return = [];
417
        foreach ($dependencies as $dependency) {
418
            /** @var ExtensionEntity $dependencyExtension */
419
            $dependencyExtension = $extensionRepository->get($dependency->getModname());
420
            $return[$dependency->getId()] = null !== $dependencyExtension;
421
        }
422
423
        return $return;
424
    }
425
426
    /**
427
     * @Route("/upgrade/{id}/{token}", requirements={"id" = "^[1-9]\d*$"})
428
     *
429
     * Upgrade an extension.
430
     *
431
     * @throws AccessDeniedException Thrown if the user doesn't have admin permission for the module
432
     */
433
    public function upgradeAction(
434
        ExtensionEntity $extension,
435
        $token,
436
        ExtensionHelper $extensionHelper
437
    ): RedirectResponse {
438
        if (!$this->hasPermission('ZikulaExtensionsModule::', '::', ACCESS_ADMIN)) {
439
            throw new AccessDeniedException();
440
        }
441
442
        if (!$this->isCsrfTokenValid('upgrade-extension', $token)) {
443
            throw new AccessDeniedException();
444
        }
445
446
        $result = $extensionHelper->upgrade($extension);
447
        if ($result) {
448
            $this->addFlash('status', $this->__f('%name% upgraded to new version and activated.', ['%name%' => $extension->getDisplayname()]));
449
        } else {
450
            $this->addFlash('error', $this->__('Extension upgrade failed!'));
451
        }
452
453
        return $this->redirectToRoute('zikulaextensionsmodule_module_viewmodulelist');
454
    }
455
456
    /**
457
     * @Route("/uninstall/{id}/{token}", requirements={"id" = "^[1-9]\d*$"})
458
     * @Theme("admin")
459
     * @Template("@ZikulaExtensionsModule/Module/uninstall.html.twig")
460
     *
461
     * Uninstall an extension.
462
     *
463
     * @return array|Response|RedirectResponse
464
     * @throws AccessDeniedException Thrown if the user doesn't have admin permission for the module
465
     */
466
    public function uninstallAction(
467
        Request $request,
468
        ExtensionEntity $extension,
469
        string $token,
470
        BlockRepositoryInterface $blockRepository,
471
        ExtensionHelper $extensionHelper,
472
        ExtensionStateHelper $extensionStateHelper,
473
        ExtensionDependencyHelper $dependencyHelper,
474
        CacheClearer $cacheClearer
475
    ) {
476
        if (!$this->hasPermission('ZikulaExtensionsModule::', '::', ACCESS_ADMIN)) {
477
            throw new AccessDeniedException();
478
        }
479
480
        if (!$this->isCsrfTokenValid('uninstall-extension', $token)) {
481
            throw new AccessDeniedException();
482
        }
483
484
        if (Constant::STATE_MISSING === $extension->getState()) {
485
            throw new RuntimeException($this->__('Error! The requested extension cannot be uninstalled because its files are missing!'));
486
        }
487
        if (!$this->get('kernel')->isBundle($extension->getName())) {
488
            $extensionStateHelper->updateState($extension->getId(), Constant::STATE_TRANSITIONAL);
489
            $cacheClearer->clear('symfony');
490
        }
491
        $requiredDependents = $dependencyHelper->getDependentExtensions($extension);
492
        $blocks = $blockRepository->findBy(['module' => $extension]);
493
494
        $form = $this->createForm(DeletionType::class);
495
        $form->handleRequest($request);
496
        if ($form->isSubmitted() && $form->isValid()) {
497
            if ($form->get('delete')->isClicked()) {
498
                // remove dependent extensions
499
                if (!$extensionHelper->uninstallArray($requiredDependents)) {
500
                    $this->addFlash('error', $this->__('Error: Could not uninstall dependent extensions.'));
501
502
                    return $this->redirectToRoute('zikulaextensionsmodule_module_viewmodulelist');
503
                }
504
                // remove blocks
505
                $blockRepository->remove($blocks);
506
507
                // remove the extension
508
                if ($extensionHelper->uninstall($extension)) {
509
                    $this->addFlash('status', $this->__('Done! Uninstalled extension.'));
510
                } else {
511
                    $this->addFlash('error', $this->__('Extension removal failed! (note: blocks and dependents may have been removed)'));
512
                }
513
            }
514
            if ($form->get('cancel')->isClicked()) {
515
                $this->addFlash('status', $this->__('Operation cancelled.'));
516
            }
517
518
            return $this->redirectToRoute('zikulaextensionsmodule_module_viewmodulelist');
519
        }
520
521
        return [
522
            'form' => $form->createView(),
523
            'extension' => $extension,
524
            'blocks' => $blocks,
525
            'requiredDependents' => $requiredDependents
526
        ];
527
    }
528
}
529