Completed
Pull Request — master (#4264)
by Craig
10:39
created

ExtensionController::postUninstallAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 1
dl 0
loc 5
rs 10
c 0
b 0
f 0
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
        CacheClearer $cacheClearer
117
    ): RedirectResponse {
118
        if (!$this->isCsrfTokenValid('activate-extension', $token)) {
119
            throw new AccessDeniedException();
120
        }
121
122
        /** @var ExtensionEntity $extension */
123
        $extension = $extensionRepository->find($id);
124
        $zikulaLogger->notice(sprintf('Extension activating'), ['name' => $extension->getName()]);
125
        if (Constant::STATE_NOTALLOWED === $extension->getState()) {
126
            $this->addFlash('error', $this->trans('Error! Activation of %name% not allowed.', ['%name%' => $extension->getName()]));
127
        } else {
128
            // Update state
129
            $extensionStateHelper->updateState($id, Constant::STATE_ACTIVE);
130
//            $cacheClearer->clear('symfony');
131
            $this->addFlash('status', $this->trans('Done! Activated %name%.', ['%name%' => $extension->getName()]));
132
        }
133
134
        return $this->redirectToRoute('zikulaextensionsmodule_extension_list');
135
    }
136
137
    /**
138
     * @Route("/deactivate/{id}/{token}", methods = {"GET"}, requirements={"id" = "^[1-9]\d*$"})
139
     * @PermissionCheck("admin")
140
     *
141
     * Deactivate an extension
142
     *
143
     * @throws AccessDeniedException Thrown if the CSRF token is invalid
144
     */
145
    public function deactivateAction(
146
        int $id,
147
        string $token,
148
        ExtensionRepositoryInterface $extensionRepository,
149
        ExtensionStateHelper $extensionStateHelper,
150
        LoggerInterface $zikulaLogger,
151
        CacheClearer $cacheClearer
152
    ): RedirectResponse {
153
        if (!$this->isCsrfTokenValid('deactivate-extension', $token)) {
154
            throw new AccessDeniedException();
155
        }
156
157
        /** @var ExtensionEntity $extension */
158
        $extension = $extensionRepository->find($id);
159
        $zikulaLogger->notice(sprintf('Extension deactivating'), ['name' => $extension->getName()]);
160
        $defaultTheme = $this->getVariableApi()->get(VariableApi::CONFIG, 'defaulttheme');
161
        $adminTheme = $this->getVariableApi()->get('ZikulaAdminModule', 'admintheme');
162
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
            } elseif (in_array($extension->getName(), [$defaultTheme, $adminTheme])) {
167
                $this->addFlash('error', $this->trans('Error! You cannot deactivate the %name%. The theme is in use.', ['%name%' => $extension->getName()]));
168
            } else {
169
                // Update state
170
                $extensionStateHelper->updateState($id, Constant::STATE_INACTIVE);
171
//                $cacheClearer->clear('symfony');
172
                $this->addFlash('status', $this->trans('Done! Deactivated %name%.', ['%name%' => $extension->getName()]));
173
            }
174
        }
175
176
        return $this->redirectToRoute('zikulaextensionsmodule_extension_list');
177
    }
178
179
    /**
180
     * @Route("/modify/{id}/{forceDefaults}", requirements={"id" = "^[1-9]\d*$", "forceDefaults" = "0|1"})
181
     * @Theme("admin")
182
     * @Template("@ZikulaExtensionsModule/Extension/modify.html.twig")
183
     *
184
     * Modify a module.
185
     *
186
     * @return array|RedirectResponse
187
     * @throws AccessDeniedException Thrown if the user doesn't have admin permissions for modifying the extension
188
     */
189
    public function modifyAction(
190
        Request $request,
191
        ZikulaHttpKernelInterface $kernel,
192
        ExtensionEntity $extension,
193
        CacheClearer $cacheClearer,
194
        bool $forceDefaults = false
195
    ) {
196
        if (!$this->hasPermission('ZikulaExtensionsModule::modify', $extension->getName() . '::' . $extension->getId(), ACCESS_ADMIN)) {
197
            throw new AccessDeniedException();
198
        }
199
200
        /** @var AbstractExtension $extensionBundle */
201
        $extensionBundle = $kernel->getBundle($extension->getName());
202
        $metaData = $extensionBundle->getMetaData()->getFilteredVersionInfoArray();
203
204
        if ($forceDefaults) {
205
            $extension->setName($metaData['name']);
206
            $extension->setUrl($metaData['url']);
207
            $extension->setDescription($metaData['description']);
208
        }
209
210
        $form = $this->createForm(ExtensionModifyType::class, $extension);
211
        $form->handleRequest($request);
212
        if ($form->isSubmitted() && $form->isValid()) {
213
            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

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