2 declare(strict_types
=1);
3 namespace wcf\system\package
;
4 use wcf\data\option\OptionEditor
;
5 use wcf\data\package\installation\queue\PackageInstallationQueue
;
6 use wcf\data\package\Package
;
7 use wcf\data\package\PackageEditor
;
8 use wcf\system\application\ApplicationHandler
;
9 use wcf\system\cache\builder\PackageCacheBuilder
;
10 use wcf\system\cache\CacheHandler
;
11 use wcf\system\event\EventHandler
;
12 use wcf\system\language\LanguageFactory
;
13 use wcf\system\package\plugin\IPackageInstallationPlugin
;
14 use wcf\system\setup\Uninstaller
;
15 use wcf\system\style\StyleHandler
;
16 use wcf\system\user\storage\UserStorageHandler
;
20 * Handles the whole uninstallation process.
22 * @author Alexander Ebert
23 * @copyright 2001-2018 WoltLab GmbH
24 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
25 * @package WoltLabSuite\Core\System\Package
27 class PackageUninstallationDispatcher
extends PackageInstallationDispatcher
{
29 * is true if the package's uninstall script has been executed or if no
33 protected $didExecuteUninstallScript = false;
35 /** @noinspection PhpMissingParentConstructorInspection */
37 * Creates a new instance of PackageUninstallationDispatcher.
39 * @param PackageInstallationQueue $queue
41 public function __construct(PackageInstallationQueue
$queue) {
42 $this->queue
= $queue;
43 $this->nodeBuilder
= new PackageUninstallationNodeBuilder($this);
45 $this->action
= $this->queue
->action
;
49 * Uninstalls node components and returns next node.
54 public function uninstall($node) {
55 $nodes = $this->nodeBuilder
->getNodeData($node);
57 // invoke node-specific actions
58 foreach ($nodes as $data) {
59 $nodeData = unserialize($data['nodeData']);
61 switch ($data['nodeType']) {
63 $this->uninstallPackage($nodeData);
67 // the file pip is always executed last, thus, just before it,
68 // execute the uninstall script
69 if ($nodeData['pluginName'] == 'file' && !$this->didExecuteUninstallScript
) {
70 $this->executeUninstallScript();
72 $this->didExecuteUninstallScript
= true;
75 $this->executePIP($nodeData);
80 // mark node as completed
81 $this->nodeBuilder
->completeNode($node);
82 $node = $this->nodeBuilder
->getNextNode($node);
84 // perform post-uninstall actions
86 // update options.inc.php if uninstallation is completed
87 OptionEditor
::resetCache();
90 CacheHandler
::getInstance()->flushAll();
92 // reset language cache
93 LanguageFactory
::getInstance()->clearCache();
94 LanguageFactory
::getInstance()->deleteLanguageCache();
97 StyleHandler
::resetStylesheets();
99 // rebuild application paths
100 ApplicationHandler
::rebuild();
102 // clear user storage
103 UserStorageHandler
::getInstance()->clear();
105 EventHandler
::getInstance()->fireAction($this, 'postUninstall');
115 protected function executePIP(array $nodeData) {
116 /** @var IPackageInstallationPlugin $pip */
117 $pip = new $nodeData['className']($this);
123 * Executes the package's uninstall script (if existing).
127 protected function executeUninstallScript() {
128 // check if uninstall script file for the uninstalled package exists
129 $uninstallScript = WCF_DIR
.'acp/uninstall/'.$this->getPackage()->package
.'.php';
130 if (file_exists($uninstallScript)) {
131 include($uninstallScript);
136 * Uninstalls current package.
138 * @param array $nodeData
140 protected function uninstallPackage(array $nodeData) {
141 PackageEditor
::deleteAll([$this->queue
->packageID
]);
143 // remove localized package info
144 $sql = "DELETE FROM wcf".WCF_N
."_language_item
145 WHERE languageItem IN (?, ?)";
146 $statement = WCF
::getDB()->prepareStatement($sql);
147 $statement->execute([
148 'wcf.acp.package.packageName.package'.$this->queue
->packageID
,
149 'wcf.acp.package.packageDescription.package'.$this->queue
->packageID
152 // reset package cache
153 PackageCacheBuilder
::getInstance()->reset();
157 * Deletes the given list of files from the target dir.
159 * @param string $targetDir
160 * @param string[] $files
161 * @param boolean $deleteEmptyDirectories
162 * @param boolean $deleteEmptyTargetDir
164 public function deleteFiles($targetDir, $files, $deleteEmptyTargetDir = false, $deleteEmptyDirectories = true) {
165 new Uninstaller($targetDir, $files, $deleteEmptyTargetDir, $deleteEmptyDirectories);
169 * Adds an uninstall entry to the package installation queue.
171 * @param Package $package
172 * @param array $packages
174 public static function addQueueEntries(Package
$package, $packages = []) {
175 // get new process no
176 $processNo = PackageInstallationQueue
::getNewProcessNo();
178 // add dependent packages to queue
179 $statementParameters = [];
180 foreach ($packages as $dependentPackage) {
181 $statementParameters[] = [
182 'packageName' => $dependentPackage['packageName'],
183 'packageID' => $dependentPackage['packageID']
187 // add uninstalling package to queue
188 $statementParameters[] = [
189 'packageName' => $package->getName(),
190 'packageID' => $package->packageID
193 // insert queue entry (entries)
194 $sql = "INSERT INTO wcf".WCF_N
."_package_installation_queue
195 (processNo, userID, package, packageID, action)
196 VALUES (?, ?, ?, ?, ?)";
197 $statement = WCF
::getDB()->prepareStatement($sql);
198 foreach ($statementParameters as $parameter) {
199 $statement->execute([
201 WCF
::getUser()->userID
,
202 $parameter['packageName'],
203 $parameter['packageID'],
208 self
::openQueue(0, $processNo);