Completed
Pull Request — master (#4264)
by Craig
05:18
created

ExtensionController::postInstallAction()   A

Complexity

Conditions 5
Paths 2

Size

Total Lines 24
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 11
nc 2
nop 4
dl 0
loc 24
rs 9.6111
c 0
b 0
f 0

1 Method

Rating   Name   Duplication   Size   Complexity  
A ExtensionController::postUninstallAction() 0 5 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Zikula package.
7
 *
8
 * Copyright Zikula - 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 Psr\Log\LoggerInterface;
17
use RuntimeException;
18
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
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\RouterInterface;
24
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
25
use Symfony\Contracts\EventDispatcher\EventDispatcherInterface;
26
use Zikula\BlocksModule\Entity\RepositoryInterface\BlockRepositoryInterface;
27
use Zikula\Bundle\CoreBundle\CacheClearer;
28
use Zikula\Bundle\CoreBundle\Controller\AbstractController;
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\ExtensionsModule\AbstractExtension;
35
use Zikula\ExtensionsModule\Api\VariableApi;
36
use Zikula\ExtensionsModule\Constant;
37
use Zikula\ExtensionsModule\Entity\ExtensionEntity;
38
use Zikula\ExtensionsModule\Entity\RepositoryInterface\ExtensionRepositoryInterface;
39
use Zikula\ExtensionsModule\Event\ExtensionListPreReSyncEvent;
40
use Zikula\ExtensionsModule\Form\Type\ExtensionModifyType;
41
use Zikula\ExtensionsModule\Helper\BundleSyncHelper;
42
use Zikula\ExtensionsModule\Helper\ExtensionDependencyHelper;
43
use Zikula\ExtensionsModule\Helper\ExtensionHelper;
44
use Zikula\ExtensionsModule\Helper\ExtensionStateHelper;
45
use Zikula\PermissionsModule\Annotation\PermissionCheck;
46
use Zikula\RoutesModule\Event\RoutesNewlyAvailableEvent;
47
use Zikula\ThemeModule\Engine\Annotation\Theme;
48
use Zikula\ThemeModule\Engine\Engine;
49
50
/**
51
 * @Route("")
52
 */
53
class ExtensionController extends AbstractController
54
{
55
    /**
56
     * @Route("/list/{page}", methods = {"GET"}, requirements={"page" = "\d+"})
57
     * @PermissionCheck("admin")
58
     * @Theme("admin")
59
     * @Template("@ZikulaExtensionsModule/Extension/list.html.twig")
60
     */
61
    public function listAction(
62
        Request $request,
63
        EventDispatcherInterface $eventDispatcher,
64
        ExtensionRepositoryInterface $extensionRepository,
65
        BundleSyncHelper $bundleSyncHelper,
66
        RouterInterface $router,
67
        int $page = 1
68
    ): array {
69
        $modulesJustInstalled = $request->query->get('justinstalled');
70
        if (!empty($modulesJustInstalled)) {
71
            // notify the event dispatcher that new routes are available (ids of modules just installed avail as args)
72
            $eventDispatcher->dispatch(new RoutesNewlyAvailableEvent(json_decode($modulesJustInstalled)));
73
        }
74
75
        $sortableColumns = new SortableColumns($router, 'zikulaextensionsmodule_extension_list');
76
        $sortableColumns->addColumns([new Column('displayname'), new Column('state')]);
77
        $sortableColumns->setOrderByFromRequest($request);
78
79
        $upgradedExtensions = [];
80
        $extensionListPreReSyncEvent = new ExtensionListPreReSyncEvent();
81
        $eventDispatcher->dispatch($extensionListPreReSyncEvent);
82
        if (1 === $page && !$extensionListPreReSyncEvent->isPropagationStopped()) {
83
            // regenerate the extension list only when viewing the first page
84
            $extensionsInFileSystem = $bundleSyncHelper->scanForBundles();
85
            $upgradedExtensions = $bundleSyncHelper->syncExtensions($extensionsInFileSystem);
86
        }
87
88
        $pageSize = $this->getVar('itemsperpage');
89
90
        $paginator = $extensionRepository->getPagedCollectionBy([], [
91
            $sortableColumns->getSortColumn()->getName() => $sortableColumns->getSortDirection()
92
        ], $page, $pageSize);
0 ignored issues
show
Bug introduced by
It seems like $pageSize can also be of type false; however, parameter $pageSize 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

92
        ], $page, /** @scrutinizer ignore-type */ $pageSize);
Loading history...
93
        $paginator->setRoute('zikulaextensionsmodule_extension_list');
94
95
        return [
96
            'sort' => $sortableColumns->generateSortableColumns(),
97
            'paginator' => $paginator,
98
            'upgradedExtensions' => $upgradedExtensions
99
        ];
100
    }
101
102
    /**
103
     * @Route("/activate/{id}/{token}", methods = {"GET"}, requirements={"id" = "^[1-9]\d*$"})
104
     * @PermissionCheck("admin")
105
     *
106
     * Activate an extension.
107
     *
108
     * @throws AccessDeniedException Thrown if the CSRF token is invalid
109
     */
110
    public function activateAction(
111
        int $id,
112
        string $token,
113
        ExtensionRepositoryInterface $extensionRepository,
114
        ExtensionStateHelper $extensionStateHelper,
115
        LoggerInterface $zikulaLogger
116
    ): RedirectResponse {
117
        if (!$this->isCsrfTokenValid('activate-extension', $token)) {
118
            throw new AccessDeniedException();
119
        }
120
121
        /** @var ExtensionEntity $extension */
122
        $extension = $extensionRepository->find($id);
123
        $zikulaLogger->notice(sprintf('Extension activating'), ['name' => $extension->getName()]);
124
        if (Constant::STATE_NOTALLOWED === $extension->getState()) {
125
            $this->addFlash('error', $this->trans('Error! Activation of %name% not allowed.', ['%name%' => $extension->getName()]));
126
        } else {
127
            $extensionStateHelper->updateState($id, Constant::STATE_ACTIVE);
128
            $this->addFlash('status', $this->trans('Done! Activated %name%.', ['%name%' => $extension->getName()]));
129
        }
130
131
        return $this->redirectToRoute('zikulaextensionsmodule_extension_list');
132
    }
133
134
    /**
135
     * @Route("/deactivate/{id}/{token}", methods = {"GET"}, requirements={"id" = "^[1-9]\d*$"})
136
     * @PermissionCheck("admin")
137
     *
138
     * Deactivate an extension
139
     *
140
     * @throws AccessDeniedException Thrown if the CSRF token is invalid
141
     */
142
    public function deactivateAction(
143
        int $id,
144
        string $token,
145
        ExtensionRepositoryInterface $extensionRepository,
146
        ExtensionStateHelper $extensionStateHelper,
147
        LoggerInterface $zikulaLogger
148
    ): RedirectResponse {
149
        if (!$this->isCsrfTokenValid('deactivate-extension', $token)) {
150
            throw new AccessDeniedException();
151
        }
152
153
        /** @var ExtensionEntity $extension */
154
        $extension = $extensionRepository->find($id);
155
        $zikulaLogger->notice(sprintf('Extension deactivating'), ['name' => $extension->getName()]);
156
        $defaultTheme = $this->getVariableApi()->get(VariableApi::CONFIG, 'defaulttheme');
157
        $adminTheme = $this->getVariableApi()->get('ZikulaAdminModule', 'admintheme');
158
159
        if (null !== $extension) {
160
            if (ZikulaKernel::isCoreExtension($extension->getName())) {
161
                $this->addFlash('error', $this->trans('Error! You cannot deactivate the %name%. It is required by the system.', ['%name%' => $extension->getName()]));
162
            } elseif (in_array($extension->getName(), [$defaultTheme, $adminTheme])) {
163
                $this->addFlash('error', $this->trans('Error! You cannot deactivate the %name%. The theme is in use.', ['%name%' => $extension->getName()]));
164
            } else {
165
                $extensionStateHelper->updateState($id, Constant::STATE_INACTIVE);
166
                $this->addFlash('status', $this->trans('Done! Deactivated %name%.', ['%name%' => $extension->getName()]));
167
            }
168
        }
169
170
        return $this->redirectToRoute('zikulaextensionsmodule_extension_list');
171
    }
172
173
    /**
174
     * @Route("/modify/{id}/{forceDefaults}", requirements={"id" = "^[1-9]\d*$", "forceDefaults" = "0|1"})
175
     * @Theme("admin")
176
     * @Template("@ZikulaExtensionsModule/Extension/modify.html.twig")
177
     *
178
     * Modify a module.
179
     *
180
     * @return array|RedirectResponse
181
     * @throws AccessDeniedException Thrown if the user doesn't have admin permissions for modifying the extension
182
     */
183
    public function modifyAction(
184
        Request $request,
185
        ZikulaHttpKernelInterface $kernel,
186
        ExtensionEntity $extension,
187
        CacheClearer $cacheClearer,
188
        bool $forceDefaults = false
189
    ) {
190
        if (!$this->hasPermission('ZikulaExtensionsModule::modify', $extension->getName() . '::' . $extension->getId(), ACCESS_ADMIN)) {
191
            throw new AccessDeniedException();
192
        }
193
194
        /** @var AbstractExtension $extensionBundle */
195
        $extensionBundle = $kernel->getBundle($extension->getName());
196
        $metaData = $extensionBundle->getMetaData()->getFilteredVersionInfoArray();
197
198
        if ($forceDefaults) {
199
            $extension->setName($metaData['name']);
200
            $extension->setUrl($metaData['url']);
201
            $extension->setDescription($metaData['description']);
202
        }
203
204
        $form = $this->createForm(ExtensionModifyType::class, $extension);
205
        $form->handleRequest($request);
206
        if ($form->isSubmitted() && $form->isValid()) {
207
            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

207
            if ($form->get('defaults')->/** @scrutinizer ignore-call */ isClicked()) {
Loading history...
208
                $this->addFlash('info', 'Default values reloaded. Save to confirm.');
209
210
                return $this->redirectToRoute('zikulaextensionsmodule_extension_modify', ['id' => $extension->getId(), 'forceDefaults' => 1]);
211
            }
212
            if ($form->get('save')->isClicked()) {
213
                $em = $this->getDoctrine()->getManager();
214
                $em->persist($extension);
215
                $em->flush();
216
217
                $cacheClearer->clear('symfony.routing');
218
                $this->addFlash('status', 'Done! Extension updated.');
219
            } elseif ($form->get('cancel')->isClicked()) {
220
                $this->addFlash('status', 'Operation cancelled.');
221
            }
222
223
            return $this->redirectToRoute('zikulaextensionsmodule_extension_list');
224
        }
225
226
        return [
227
            'form' => $form->createView()
228
        ];
229
    }
230
231
    /**
232
     * @Route("/compatibility/{id}", methods = {"GET"}, requirements={"id" = "^[1-9]\d*$"})
233
     * @Theme("admin")
234
     * @Template("@ZikulaExtensionsModule/Extension/compatibility.html.twig")
235
     *
236
     * Display information of a module compatibility with the version of the core
237
     *
238
     * @throws AccessDeniedException Thrown if the user doesn't have admin permission to the requested module
239
     */
240
    public function compatibilityAction(ExtensionEntity $extension): array
241
    {
242
        if (!$this->hasPermission('ZikulaExtensionsModule::', $extension->getName() . '::' . $extension->getId(), ACCESS_ADMIN)) {
243
            throw new AccessDeniedException();
244
        }
245
246
        return [
247
            'extension' => $extension
248
        ];
249
    }
250
251
    /**
252
     * @Route("/upgrade/{id}/{token}", requirements={"id" = "^[1-9]\d*$"})
253
     * @PermissionCheck("admin")
254
     *
255
     * Upgrade an extension.
256
     *
257
     * @throws AccessDeniedException Thrown if the CSRF token is invalid
258
     */
259
    public function upgradeAction(
260
        ExtensionEntity $extension,
261
        $token,
262
        ExtensionHelper $extensionHelper
263
    ): RedirectResponse {
264
        if (!$this->isCsrfTokenValid('upgrade-extension', $token)) {
265
            throw new AccessDeniedException();
266
        }
267
268
        $result = $extensionHelper->upgrade($extension);
269
        if ($result) {
270
            $this->addFlash('status', $this->trans('%name% upgraded to new version and activated.', ['%name%' => $extension->getDisplayname()]));
271
        } else {
272
            $this->addFlash('error', 'Extension upgrade failed!');
273
        }
274
275
        return $this->redirectToRoute('zikulaextensionsmodule_extension_list');
276
    }
277
278
    /**
279
     * @Route("/uninstall/{id}/{token}", requirements={"id" = "^[1-9]\d*$"})
280
     * @PermissionCheck("admin")
281
     * @Theme("admin")
282
     * @Template("@ZikulaExtensionsModule/Extension/uninstall.html.twig")
283
     *
284
     * Uninstall an extension.
285
     *
286
     * @return array|Response|RedirectResponse
287
     * @throws AccessDeniedException Thrown if the CSRF token is invalid
288
     */
289
    public function uninstallAction(
290
        Request $request,
291
        ExtensionEntity $extension,
292
        string $token,
293
        ZikulaHttpKernelInterface $kernel,
294
        BlockRepositoryInterface $blockRepository,
295
        ExtensionHelper $extensionHelper,
296
        ExtensionStateHelper $extensionStateHelper,
297
        ExtensionDependencyHelper $dependencyHelper
298
    ) {
299
        if (!$this->isCsrfTokenValid('uninstall-extension', $token)) {
300
            throw new AccessDeniedException();
301
        }
302
303
        if (Constant::STATE_MISSING === $extension->getState()) {
304
            throw new RuntimeException($this->trans('Error! The requested extension cannot be uninstalled because its files are missing!'));
305
        }
306
        if (!$kernel->isBundle($extension->getName())) {
307
            $extensionStateHelper->updateState($extension->getId(), Constant::STATE_TRANSITIONAL);
308
        }
309
        $requiredDependents = $dependencyHelper->getDependentExtensions($extension);
310
        $blocks = $blockRepository->findBy(['module' => $extension]);
311
312
        $form = $this->createForm(DeletionType::class, [], [
313
            'action' => $this->generateUrl('zikulaextensionsmodule_extension_uninstall', [
314
                'id' => $extension->getId(),
315
                'token' => $token
316
            ]),
317
        ]);
318
        $form->handleRequest($request);
319
        if ($form->isSubmitted() && $form->isValid()) {
320
            if ($form->get('delete')->isClicked()) {
321
                // remove dependent extensions
322
                if (!$extensionHelper->uninstallArray($requiredDependents)) {
323
                    $this->addFlash('error', 'Error: Could not uninstall dependent extensions.');
324
325
                    return $this->redirectToRoute('zikulaextensionsmodule_extension_list');
326
                }
327
                // remove blocks
328
                $blockRepository->remove($blocks);
329
330
                // remove the extension
331
                if ($extensionHelper->uninstall($extension)) {
332
                    $this->addFlash('status', 'Done! Uninstalled extension.');
333
                } else {
334
                    $this->addFlash('error', 'Extension removal failed! (note: blocks and dependents may have been removed)');
335
                }
336
            } elseif ($form->get('cancel')->isClicked()) {
337
                $this->addFlash('status', 'Operation cancelled.');
338
            }
339
340
            return $this->redirectToRoute('zikulaextensionsmodule_extension_postuninstall');
341
        }
342
343
        return [
344
            'form' => $form->createView(),
345
            'extension' => $extension,
346
            'blocks' => $blocks,
347
            'requiredDependents' => $requiredDependents
348
        ];
349
    }
350
351
    /**
352
     * @Route("/post-uninstall")
353
     * @PermissionCheck("admin")
354
     *
355
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
356
     */
357
    public function postUninstallAction(CacheClearer $cacheClearer)
358
    {
359
        $cacheClearer->clear('symfony');
360
361
        return $this->redirectToRoute('zikulaextensionsmodule_extension_list');
362
    }
363
364
    /**
365
     * @Route("/theme-preview/{themeName}")
366
     * @PermissionCheck("admin")
367
     */
368
    public function previewAction(Engine $engine, string $themeName): Response
369
    {
370
        $engine->setActiveTheme($themeName);
371
        $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.');
372
373
        return $this->forward('Zikula\Bundle\CoreBundle\Controller\MainController::homeAction');
374
    }
375
}
376