1<?php
2
3/*
4 * This file is part of the TYPO3 CMS project.
5 *
6 * It is free software; you can redistribute it and/or modify it under
7 * the terms of the GNU General Public License, either version 2
8 * of the License, or any later version.
9 *
10 * For the full copyright and license information, please read the
11 * LICENSE.txt file that was distributed with this source code.
12 *
13 * The TYPO3 project - inspiring people to share!
14 */
15
16namespace TYPO3\CMS\Extensionmanager\Controller;
17
18use TYPO3\CMS\Core\Core\Environment;
19use TYPO3\CMS\Core\Messaging\FlashMessage;
20use TYPO3\CMS\Core\Package\Exception;
21use TYPO3\CMS\Core\Package\Exception\PackageStatesFileNotWritableException;
22use TYPO3\CMS\Core\Registry;
23use TYPO3\CMS\Core\Utility\ExtensionManagementUtility;
24use TYPO3\CMS\Core\Utility\GeneralUtility;
25use TYPO3\CMS\Core\Utility\PathUtility;
26use TYPO3\CMS\Extbase\Utility\LocalizationUtility;
27use TYPO3\CMS\Extensionmanager\Exception\ExtensionManagerException;
28use TYPO3\CMS\Extensionmanager\Service\ExtensionManagementService;
29use TYPO3\CMS\Extensionmanager\Utility\ExtensionModelUtility;
30use TYPO3\CMS\Extensionmanager\Utility\FileHandlingUtility;
31use TYPO3\CMS\Extensionmanager\Utility\InstallUtility;
32
33/**
34 * Controller for handling extension related actions like
35 * installing, removing, downloading of data or files
36 * @internal This class is a specific controller implementation and is not considered part of the Public TYPO3 API.
37 */
38class ActionController extends AbstractController
39{
40    /**
41     * @var InstallUtility
42     */
43    protected $installUtility;
44
45    /**
46     * @var FileHandlingUtility
47     */
48    protected $fileHandlingUtility;
49
50    /**
51     * @var ExtensionModelUtility
52     */
53    protected $extensionModelUtility;
54
55    /**
56     * @var ExtensionManagementService
57     */
58    protected $managementService;
59
60    /**
61     * @param InstallUtility $installUtility
62     */
63    public function injectInstallUtility(InstallUtility $installUtility)
64    {
65        $this->installUtility = $installUtility;
66    }
67
68    /**
69     * @param FileHandlingUtility $fileHandlingUtility
70     */
71    public function injectFileHandlingUtility(FileHandlingUtility $fileHandlingUtility)
72    {
73        $this->fileHandlingUtility = $fileHandlingUtility;
74    }
75
76    /**
77     * @param ExtensionModelUtility $extensionModelUtility
78     */
79    public function injectExtensionModelUtility(ExtensionModelUtility $extensionModelUtility)
80    {
81        $this->extensionModelUtility = $extensionModelUtility;
82    }
83
84    /**
85     * @param ExtensionManagementService $managementService
86     */
87    public function injectManagementService(ExtensionManagementService $managementService)
88    {
89        $this->managementService = $managementService;
90    }
91
92    /**
93     * Toggle extension installation state action
94     *
95     * @param string $extensionKey
96     */
97    protected function toggleExtensionInstallationStateAction($extensionKey)
98    {
99        $installedExtensions = ExtensionManagementUtility::getLoadedExtensionListArray();
100        try {
101            if (in_array($extensionKey, $installedExtensions)) {
102                // uninstall
103                $this->installUtility->uninstall($extensionKey);
104            } else {
105                // install
106                $extension = $this->extensionModelUtility->mapExtensionArrayToModel(
107                    $this->installUtility->enrichExtensionWithDetails($extensionKey, false)
108                );
109                if ($this->managementService->installExtension($extension) === false) {
110                    $this->redirect('unresolvedDependencies', 'List', null, ['extensionKey' => $extensionKey]);
111                }
112            }
113        } catch (ExtensionManagerException|PackageStatesFileNotWritableException $e) {
114            $this->addFlashMessage($e->getMessage(), '', FlashMessage::ERROR);
115        }
116        $this->redirect('index', 'List', null, [
117            self::TRIGGER_RefreshModuleMenu => true,
118            self::TRIGGER_RefreshTopbar => true
119        ]);
120    }
121
122    /**
123     * Install an extension and omit dependency checking
124     *
125     * @param string $extensionKey
126     */
127    public function installExtensionWithoutSystemDependencyCheckAction($extensionKey)
128    {
129        $this->managementService->setSkipDependencyCheck(true);
130        $this->forward('toggleExtensionInstallationState', null, null, ['extensionKey' => $extensionKey]);
131    }
132
133    /**
134     * Remove an extension (if it is still installed, uninstall it first)
135     *
136     * @param string $extension
137     * @return string
138     */
139    protected function removeExtensionAction($extension)
140    {
141        try {
142            if (Environment::isComposerMode()) {
143                throw new ExtensionManagerException(
144                    'The system is set to composer mode. You are not allowed to remove any extension.',
145                    1590314046
146                );
147            }
148
149            $this->installUtility->removeExtension($extension);
150            $this->addFlashMessage(
151                LocalizationUtility::translate(
152                    'extensionList.remove.message',
153                    'extensionmanager',
154                    [
155                        'extension' => $extension,
156                    ]
157                ) ?? ''
158            );
159        } catch (ExtensionManagerException|Exception $e) {
160            $this->addFlashMessage($e->getMessage(), '', FlashMessage::ERROR);
161        }
162
163        return '';
164    }
165
166    /**
167     * Download an extension as a zip file
168     *
169     * @param string $extension
170     */
171    protected function downloadExtensionZipAction($extension)
172    {
173        $fileName = $this->fileHandlingUtility->createZipFileFromExtension($extension);
174        $this->sendZipFileToBrowserAndDelete($fileName);
175    }
176
177    /**
178     * Sends a zip file to the browser and deletes it afterwards
179     *
180     * @param string $fileName
181     */
182    protected function sendZipFileToBrowserAndDelete(string $fileName): void
183    {
184        header('Content-Type: application/zip');
185        header('Content-Length: ' . filesize($fileName));
186        header('Content-Disposition: attachment; filename="' . PathUtility::basename($fileName) . '"');
187        readfile($fileName);
188        unlink($fileName);
189        die;
190    }
191
192    /**
193     * Reloads the static SQL data of an extension
194     *
195     * @param string $extension
196     */
197    protected function reloadExtensionDataAction($extension)
198    {
199        $extension = $this->installUtility->enrichExtensionWithDetails($extension, false);
200        $registryKey = $extension['siteRelPath'] . 'ext_tables_static+adt.sql';
201
202        $registry = GeneralUtility::makeInstance(Registry::class);
203        $registry->remove('extensionDataImport', $registryKey);
204
205        $this->installUtility->processExtensionSetup($extension['key']);
206
207        $this->redirect('index', 'List');
208    }
209}
210