Completed
Push — develop ( 752c7f...4a7063 )
by Tino
05:54
created

System/Controller/Admin/Extensions/Modules.php (2 issues)

Labels
Severity
1
<?php
2
3
/**
4
 * Copyright (c) by the ACP3 Developers.
5
 * See the LICENSE file at the top-level module directory for licensing details.
6
 */
7
8
namespace ACP3\Modules\ACP3\System\Controller\Admin\Extensions;
9
10
use ACP3\Core;
11
use ACP3\Modules\ACP3\Permissions;
12
use ACP3\Modules\ACP3\System;
13
14
class Modules extends Core\Controller\AbstractFormAction
15
{
16
    /**
17
     * @var \ACP3\Core\Modules\ModuleInfoCacheInterface
18
     */
19
    protected $moduleInfoCache;
20
    /**
21
     * @var \ACP3\Modules\ACP3\System\Model\Repository\ModulesRepository
22
     */
23
    protected $systemModuleRepository;
24
    /**
25
     * @var \ACP3\Modules\ACP3\System\Helper\Installer
26
     */
27
    protected $installerHelper;
28
    /**
29
     * @var \ACP3\Modules\ACP3\Permissions\Cache
30
     */
31
    protected $permissionsCache;
32
    /**
33
     * @var Core\I18n\DictionaryCache
34
     */
35
    private $dictionaryCache;
36
    /**
37
     * @var Core\Installer\SchemaRegistrar
38
     */
39
    private $schemaRegistrar;
40
    /**
41
     * @var Core\Modules\SchemaInstaller
42
     */
43
    private $schemaInstaller;
44
    /**
45
     * @var Core\Modules\AclInstaller
46
     */
47
    private $aclInstaller;
48
49
    public function __construct(
50
        Core\Controller\Context\FormContext $context,
51
        Core\I18n\DictionaryCache $dictionaryCache,
52
        Core\Modules\ModuleInfoCacheInterface $moduleInfoCache,
53
        System\Model\Repository\ModulesRepository $systemModuleRepository,
54
        System\Helper\Installer $installerHelper,
55
        Permissions\Cache $permissionsCache,
56
        Core\Installer\SchemaRegistrar $schemaRegistrar,
57
        Core\Modules\SchemaInstaller $schemaInstaller,
58
        Core\Modules\AclInstaller $aclInstaller
59
    ) {
60
        parent::__construct($context);
61
62
        $this->moduleInfoCache = $moduleInfoCache;
63
        $this->systemModuleRepository = $systemModuleRepository;
64
        $this->installerHelper = $installerHelper;
65
        $this->permissionsCache = $permissionsCache;
66
        $this->dictionaryCache = $dictionaryCache;
67
        $this->schemaRegistrar = $schemaRegistrar;
68
        $this->schemaInstaller = $schemaInstaller;
69
        $this->aclInstaller = $aclInstaller;
70
    }
71
72
    /**
73
     * @param string $action
74
     * @param string $dir
75
     *
76
     * @return array|\Symfony\Component\HttpFoundation\RedirectResponse
77
     *
78
     * @throws \Doctrine\DBAL\ConnectionException
79
     * @throws \Doctrine\DBAL\DBALException
80
     * @throws \MJS\TopSort\CircularDependencyException
81
     * @throws \MJS\TopSort\ElementNotFoundException
82
     */
83
    public function execute(?string $action = null, string $dir = '')
84
    {
85
        switch ($action) {
86
            case 'activate':
87
                return $this->enableModule($dir);
88
            case 'deactivate':
89
                return $this->disableModule($dir);
90
            case 'install':
91
                return $this->installModule($dir);
92
            case 'uninstall':
93
                return $this->uninstallModule($dir);
94
            default:
95
                return $this->outputPage();
96
        }
97
    }
98
99
    /**
100
     * @param string $moduleDirectory
101
     *
102
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
103
     *
104
     * @throws \Doctrine\DBAL\DBALException
105
     */
106
    protected function enableModule(string $moduleDirectory)
107
    {
108
        $result = false;
109
110
        try {
111
            $this->checkPreconditions($moduleDirectory);
112
113
            $this->moduleInstallerExists($moduleDirectory);
114
115
            $moduleSchema = $this->schemaRegistrar->get($moduleDirectory);
116
117
            $dependencies = $this->installerHelper->checkInstallDependencies($moduleSchema);
118
            $this->checkForFailedModuleDependencies($dependencies, 'enable_following_modules_first');
119
120
            $result = $this->saveModuleState($moduleDirectory, 1);
121
122
            $this->purgeCaches();
123
124
            $text = $this->translator->t(
125
                'system',
126
                'mod_activate_' . ($result !== false ? 'success' : 'error')
127
            );
128
        } catch (System\Exception\ModuleInstallerException $e) {
129
            $text = $e->getMessage();
130
        }
131
132
        return $this->redirectMessages()->setMessage($result, $text, $this->request->getFullPath());
0 ignored issues
show
It seems like $result can also be of type integer; however, parameter $isSuccess of ACP3\Core\Helpers\RedirectMessages::setMessage() does only seem to accept boolean, 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

132
        return $this->redirectMessages()->setMessage(/** @scrutinizer ignore-type */ $result, $text, $this->request->getFullPath());
Loading history...
133
    }
134
135
    /**
136
     * @param string $moduleDirectory
137
     *
138
     * @throws System\Exception\ModuleInstallerException
139
     */
140
    private function checkPreconditions(string $moduleDirectory)
141
    {
142
        $info = $this->modules->getModuleInfo($moduleDirectory);
143
        if (empty($info) || $info['protected'] === true || $info['installable'] === false) {
144
            throw new System\Exception\ModuleInstallerException(
145
                $this->translator->t('system', 'could_not_complete_request')
146
            );
147
        }
148
    }
149
150
    /**
151
     * @param string $serviceId
152
     *
153
     * @throws System\Exception\ModuleInstallerException
154
     */
155
    protected function moduleInstallerExists(string $serviceId)
156
    {
157
        if ($this->schemaRegistrar->has($serviceId) === false) {
158
            throw new System\Exception\ModuleInstallerException(
159
                $this->translator->t('system', 'module_installer_not_found')
160
            );
161
        }
162
    }
163
164
    /**
165
     * @param array  $dependencies
166
     * @param string $phrase
167
     *
168
     * @throws \ACP3\Modules\ACP3\System\Exception\ModuleInstallerException
169
     */
170
    protected function checkForFailedModuleDependencies(array $dependencies, string $phrase)
171
    {
172
        if (!empty($dependencies)) {
173
            throw new System\Exception\ModuleInstallerException(
174
                $this->translator->t(
175
                    'system',
176
                    $phrase,
177
                    ['%modules%' => \implode(', ', $dependencies)]
178
                )
179
            );
180
        }
181
    }
182
183
    /**
184
     * @param string $moduleDirectory
185
     * @param int    $active
186
     *
187
     * @return bool|int
188
     *
189
     * @throws \Doctrine\DBAL\DBALException
190
     */
191
    protected function saveModuleState(string $moduleDirectory, int $active)
192
    {
193
        return $this->systemModuleRepository->update(['active' => $active], ['name' => $moduleDirectory]);
194
    }
195
196
    protected function purgeCaches()
197
    {
198
        Core\Cache\Purge::doPurge([
199
            $this->appPath->getCacheDir() . 'http',
200
            $this->appPath->getCacheDir() . 'sql',
201
            $this->appPath->getCacheDir() . 'tpl_compiled',
202
            $this->appPath->getCacheDir() . 'tpl_cached',
203
            $this->appPath->getCacheDir() . 'container.php',
204
            $this->appPath->getCacheDir() . 'container.php.meta',
205
        ]);
206
    }
207
208
    /**
209
     * @throws \MJS\TopSort\CircularDependencyException
210
     * @throws \MJS\TopSort\ElementNotFoundException
211
     */
212
    protected function renewCaches()
213
    {
214
        $this->dictionaryCache->saveLanguageCache($this->translator->getLocale());
215
        $this->moduleInfoCache->saveModulesInfoCache();
216
        $this->permissionsCache->saveResourcesCache();
217
    }
218
219
    /**
220
     * @param string $moduleDirectory
221
     *
222
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
223
     *
224
     * @throws \Doctrine\DBAL\DBALException
225
     */
226
    protected function disableModule(string $moduleDirectory)
227
    {
228
        $result = false;
229
230
        try {
231
            $this->checkPreconditions($moduleDirectory);
232
233
            $this->moduleInstallerExists($moduleDirectory);
234
235
            $moduleSchema = $this->schemaRegistrar->get($moduleDirectory);
236
237
            $dependencies = $this->installerHelper->checkUninstallDependencies($moduleSchema);
238
            $this->checkForFailedModuleDependencies($dependencies, 'module_disable_not_possible');
239
240
            $result = $this->saveModuleState($moduleDirectory, 0);
241
242
            $this->purgeCaches();
243
244
            $text = $this->translator->t(
245
                'system',
246
                'mod_deactivate_' . ($result !== false ? 'success' : 'error')
247
            );
248
        } catch (System\Exception\ModuleInstallerException $e) {
249
            $text = $e->getMessage();
250
        }
251
252
        return $this->redirectMessages()->setMessage($result, $text, $this->request->getFullPath());
0 ignored issues
show
It seems like $result can also be of type integer; however, parameter $isSuccess of ACP3\Core\Helpers\RedirectMessages::setMessage() does only seem to accept boolean, 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

252
        return $this->redirectMessages()->setMessage(/** @scrutinizer ignore-type */ $result, $text, $this->request->getFullPath());
Loading history...
253
    }
254
255
    /**
256
     * @param string $moduleDirectory
257
     *
258
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
259
     *
260
     * @throws \Doctrine\DBAL\DBALException
261
     */
262
    protected function installModule(string $moduleDirectory)
263
    {
264
        $result = false;
265
266
        try {
267
            if ($this->modules->isInstalled($moduleDirectory) === true) {
268
                throw new System\Exception\ModuleInstallerException(
269
                    $this->translator->t('system', 'module_already_installed')
270
                );
271
            }
272
273
            $this->moduleInstallerExists($moduleDirectory);
274
275
            $moduleSchema = $this->schemaRegistrar->get($moduleDirectory);
276
277
            $dependencies = $this->installerHelper->checkInstallDependencies($moduleSchema);
278
            $this->checkForFailedModuleDependencies($dependencies, 'enable_following_modules_first');
279
280
            $result = $this->schemaInstaller->install($moduleSchema);
281
            $resultAcl = $this->aclInstaller->install($moduleSchema);
282
283
            $this->purgeCaches();
284
285
            $text = $this->translator->t(
286
                'system',
287
                'mod_installation_' . ($result !== false && $resultAcl !== false ? 'success' : 'error')
288
            );
289
        } catch (System\Exception\ModuleInstallerException $e) {
290
            $text = $e->getMessage();
291
        }
292
293
        return $this->redirectMessages()->setMessage($result, $text, $this->request->getFullPath());
294
    }
295
296
    /**
297
     * @param string $moduleDirectory
298
     *
299
     * @return \Symfony\Component\HttpFoundation\RedirectResponse
300
     *
301
     * @throws \Doctrine\DBAL\ConnectionException
302
     */
303
    protected function uninstallModule(string $moduleDirectory)
304
    {
305
        $result = false;
306
307
        try {
308
            $info = $this->modules->getModuleInfo($moduleDirectory);
309
            if ($this->modules->isInstalled($moduleDirectory) === false || $info['protected'] === true) {
310
                throw new System\Exception\ModuleInstallerException(
311
                    $this->translator->t('system', 'protected_module_description')
312
                );
313
            }
314
315
            $this->moduleInstallerExists($moduleDirectory);
316
317
            $moduleSchema = $this->schemaRegistrar->get($moduleDirectory);
318
319
            $dependencies = $this->installerHelper->checkUninstallDependencies($moduleSchema);
320
            $this->checkForFailedModuleDependencies($dependencies, 'uninstall_following_modules_first');
321
322
            $result = $this->schemaInstaller->uninstall($moduleSchema);
323
            $resultAcl = $this->aclInstaller->uninstall($moduleSchema);
324
325
            $this->purgeCaches();
326
327
            $text = $this->translator->t(
328
                'system',
329
                'mod_uninstallation_' . ($result !== false && $resultAcl !== false ? 'success' : 'error')
330
            );
331
        } catch (System\Exception\ModuleInstallerException $e) {
332
            $text = $e->getMessage();
333
        }
334
335
        return $this->redirectMessages()->setMessage($result, $text, $this->request->getFullPath());
336
    }
337
338
    /**
339
     * @return array
340
     *
341
     * @throws \MJS\TopSort\CircularDependencyException
342
     * @throws \MJS\TopSort\ElementNotFoundException
343
     */
344
    protected function outputPage()
345
    {
346
        $this->renewCaches();
347
348
        $installedModules = $newModules = [];
349
350
        foreach ($this->modules->getAllModulesAlphabeticallySorted() as $key => $values) {
351
            $values['dir'] = \strtolower($values['dir']);
352
            if ($this->modules->isInstalled($values['dir']) === true || $values['installable'] === false) {
353
                $installedModules[$key] = $values;
354
            } else {
355
                $newModules[$key] = $values;
356
            }
357
        }
358
359
        return [
360
            'installed_modules' => $installedModules,
361
            'new_modules' => $newModules,
362
        ];
363
    }
364
}
365