Fix XSD filename in newly created ACL option PIP files
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / system / package / PackageUninstallationDispatcher.class.php
1 <?php
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;
17 use wcf\system\WCF;
18
19 /**
20 * Handles the whole uninstallation process.
21 *
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
26 */
27 class PackageUninstallationDispatcher extends PackageInstallationDispatcher {
28 /**
29 * is true if the package's uninstall script has been executed or if no
30 * such script exists
31 * @var boolean
32 */
33 protected $didExecuteUninstallScript = false;
34
35 /** @noinspection PhpMissingParentConstructorInspection */
36 /**
37 * Creates a new instance of PackageUninstallationDispatcher.
38 *
39 * @param PackageInstallationQueue $queue
40 */
41 public function __construct(PackageInstallationQueue $queue) {
42 $this->queue = $queue;
43 $this->nodeBuilder = new PackageUninstallationNodeBuilder($this);
44
45 $this->action = $this->queue->action;
46 }
47
48 /**
49 * Uninstalls node components and returns next node.
50 *
51 * @param string $node
52 * @return string
53 */
54 public function uninstall($node) {
55 $nodes = $this->nodeBuilder->getNodeData($node);
56
57 // invoke node-specific actions
58 foreach ($nodes as $data) {
59 $nodeData = unserialize($data['nodeData']);
60
61 switch ($data['nodeType']) {
62 case 'package':
63 $this->uninstallPackage($nodeData);
64 break;
65
66 case 'pip':
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();
71
72 $this->didExecuteUninstallScript = true;
73 }
74
75 $this->executePIP($nodeData);
76 break;
77 }
78 }
79
80 // mark node as completed
81 $this->nodeBuilder->completeNode($node);
82 $node = $this->nodeBuilder->getNextNode($node);
83
84 // perform post-uninstall actions
85 if ($node == '') {
86 // update options.inc.php if uninstallation is completed
87 OptionEditor::resetCache();
88
89 // clear cache
90 CacheHandler::getInstance()->flushAll();
91
92 // reset language cache
93 LanguageFactory::getInstance()->clearCache();
94 LanguageFactory::getInstance()->deleteLanguageCache();
95
96 // reset stylesheets
97 StyleHandler::resetStylesheets();
98
99 // rebuild application paths
100 ApplicationHandler::rebuild();
101
102 // clear user storage
103 UserStorageHandler::getInstance()->clear();
104
105 EventHandler::getInstance()->fireAction($this, 'postUninstall');
106 }
107
108 // return next node
109 return $node;
110 }
111
112 /**
113 * @inheritDoc
114 */
115 protected function executePIP(array $nodeData) {
116 /** @var IPackageInstallationPlugin $pip */
117 $pip = new $nodeData['className']($this);
118
119 $pip->uninstall();
120 }
121
122 /**
123 * Executes the package's uninstall script (if existing).
124 *
125 * @since 3.0
126 */
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);
132 }
133 }
134
135 /**
136 * Uninstalls current package.
137 *
138 * @param array $nodeData
139 */
140 protected function uninstallPackage(array $nodeData) {
141 PackageEditor::deleteAll([$this->queue->packageID]);
142
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
150 ]);
151
152 // reset package cache
153 PackageCacheBuilder::getInstance()->reset();
154 }
155
156 /**
157 * Deletes the given list of files from the target dir.
158 *
159 * @param string $targetDir
160 * @param string[] $files
161 * @param boolean $deleteEmptyDirectories
162 * @param boolean $deleteEmptyTargetDir
163 */
164 public function deleteFiles($targetDir, $files, $deleteEmptyTargetDir = false, $deleteEmptyDirectories = true) {
165 new Uninstaller($targetDir, $files, $deleteEmptyTargetDir, $deleteEmptyDirectories);
166 }
167
168 /**
169 * Adds an uninstall entry to the package installation queue.
170 *
171 * @param Package $package
172 * @param array $packages
173 */
174 public static function addQueueEntries(Package $package, $packages = []) {
175 // get new process no
176 $processNo = PackageInstallationQueue::getNewProcessNo();
177
178 // add dependent packages to queue
179 $statementParameters = [];
180 foreach ($packages as $dependentPackage) {
181 $statementParameters[] = [
182 'packageName' => $dependentPackage['packageName'],
183 'packageID' => $dependentPackage['packageID']
184 ];
185 }
186
187 // add uninstalling package to queue
188 $statementParameters[] = [
189 'packageName' => $package->getName(),
190 'packageID' => $package->packageID
191 ];
192
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([
200 $processNo,
201 WCF::getUser()->userID,
202 $parameter['packageName'],
203 $parameter['packageID'],
204 'uninstall'
205 ]);
206 }
207
208 self::openQueue(0, $processNo);
209 }
210 }