From e25413a03b032c8d609f56d78750eae879e90d61 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Wed, 5 Oct 2011 20:41:12 +0200 Subject: [PATCH] Uninstallation of packages with dependencies now working --- .../action/UninstallPackageAction.class.php | 66 +++++++++---------- .../PackageUninstallationDispatcher.class.php | 49 ++++++++++++++ ...PackageUninstallationNodeBuilder.class.php | 49 +++++++++++++- 3 files changed, 127 insertions(+), 37 deletions(-) diff --git a/wcfsetup/install/files/lib/acp/action/UninstallPackageAction.class.php b/wcfsetup/install/files/lib/acp/action/UninstallPackageAction.class.php index ae054a77a9..6f69cdd4ac 100644 --- a/wcfsetup/install/files/lib/acp/action/UninstallPackageAction.class.php +++ b/wcfsetup/install/files/lib/acp/action/UninstallPackageAction.class.php @@ -64,42 +64,38 @@ class UninstallPackageAction extends InstallPackageAction { throw new IllegalLinkException(); } - if (PackageUninstallationDispatcher::hasDependencies($package->packageID)) { - throw new SystemException('hasDependencies'); - } - else { - // get new process no - $processNo = PackageInstallationQueue::getNewProcessNo(); - - // create queue - $queue = PackageInstallationQueueEditor::create(array( - 'processNo' => $processNo, - 'userID' => WCF::getUser()->userID, - 'packageName' => $package->getName(), - 'packageID' => $package->packageID, - 'action' => 'uninstall', - 'cancelable' => 0 - )); + // get new process no + $processNo = PackageInstallationQueue::getNewProcessNo(); - // initialize uninstallation - $this->installation = new PackageUninstallationDispatcher($queue); + // create queue + $queue = PackageInstallationQueueEditor::create(array( + 'processNo' => $processNo, + 'userID' => WCF::getUser()->userID, + 'packageName' => $package->getName(), + 'packageID' => $package->packageID, + 'action' => 'uninstall', + 'cancelable' => 0 + )); - $this->installation->nodeBuilder->purgeNodes(); - $this->installation->nodeBuilder->buildNodes(); + // initialize uninstallation + $this->installation = new PackageUninstallationDispatcher($queue); - WCF::getTPL()->assign(array( - 'queue' => $queue - )); + $this->installation->nodeBuilder->purgeNodes(); + $this->installation->nodeBuilder->buildNodes(); - $this->data = array( - 'template' => WCF::getTPL()->fetch($this->templateName), - 'step' => 'uninstall', - 'node' => $this->installation->nodeBuilder->getNextNode(), - 'currentAction' => WCF::getLanguage()->get('wcf.package.installation.step.uninstalling'), - 'progress' => 0, - 'queueID' => $queue->queueID - ); - } + WCF::getTPL()->assign(array( + 'queue' => $queue + )); + + $queueID = $this->installation->nodeBuilder->getQueueByNode($queue->processNo, $this->installation->nodeBuilder->getNextNode()); + $this->data = array( + 'template' => WCF::getTPL()->fetch($this->templateName), + 'step' => 'uninstall', + 'node' => $this->installation->nodeBuilder->getNextNode(), + 'currentAction' => WCF::getLanguage()->get('wcf.package.installation.step.uninstalling'), + 'progress' => 0, + 'queueID' => $queueID + ); } /** @@ -115,7 +111,7 @@ class UninstallPackageAction extends InstallPackageAction { // remove node data $this->installation->nodeBuilder->purgeNodes(); - // TODO: Show 'success' template at this point + // show success $this->data = array( 'progress' => 100, 'step' => 'success' @@ -124,10 +120,12 @@ class UninstallPackageAction extends InstallPackageAction { } // continue with next node + $queueID = $this->installation->nodeBuilder->getQueueByNode($this->installation->queue->processNo, $this->node); $this->data = array( 'step' => 'uninstall', 'node' => $node, - 'progress' => $this->installation->nodeBuilder->calculateProgress($this->node) + 'progress' => $this->installation->nodeBuilder->calculateProgress($this->node), + 'queueID' => $queueID ); } diff --git a/wcfsetup/install/files/lib/system/package/PackageUninstallationDispatcher.class.php b/wcfsetup/install/files/lib/system/package/PackageUninstallationDispatcher.class.php index adfcb5e061..7361f45d34 100644 --- a/wcfsetup/install/files/lib/system/package/PackageUninstallationDispatcher.class.php +++ b/wcfsetup/install/files/lib/system/package/PackageUninstallationDispatcher.class.php @@ -4,6 +4,7 @@ use wcf\system\menu\acp\ACPMenu; use wcf\data\option\OptionEditor; use wcf\data\package\Package; use wcf\data\package\PackageEditor; +use wcf\data\package\PackageList; use wcf\data\package\installation\queue\PackageInstallationQueue; use wcf\data\package\installation\queue\PackageInstallationQueueEditor; use wcf\system\cache\CacheHandler; @@ -174,6 +175,54 @@ class PackageUninstallationDispatcher extends PackageInstallationDispatcher { return $packages; } + /** + * Returns an ordered list of depenencies for given package id. The order is + * curcial, whereas the first package has to be uninstalled first. + * + * @package integer + * @return wcf\data\package\PackageList + */ + public static function getOrderedPackageDependencies($packageID) { + $sql = "SELECT packageID, MAX(level) AS level + FROM wcf".WCF_N."_package_requirement_map + WHERE requirement = ? + GROUP BY packageID"; + $statement = WCF::getDB()->prepareStatement($sql); + $statement->execute(array($packageID)); + + $dependencies = array(); + while ($row = $statement->fetchArray()) { + $dependencies[$row['packageID']] = $row['level']; + } + + $packageIDs = array(); + $maxLevel = max(array_values($dependencies)); + if ($maxLevel == 0) { + // order does not matter + $packageIDs = array_keys($dependencies); + } + else { + // order by level while ignoring individual connections as they don't + // matter if uninstall begins with the lowest dependency in tree + for ($i = $maxLevel; $i >= 0; $i--) { + foreach ($dependencies as $packageID => $level) { + if ($level == $i) { + $packageIDs[] = $packageID; + unset($dependencies[$packageID]); + } + } + } + } + + // get packages + $packageList = new PackageList(); + $packageList->sqlLimit = 0; + $packageList->getConditionBuilder()->add("packageID IN (?)", array($packageIDs)); + $packageList->readObjects(); + + return $packageList; + } + /** * Returns true if package has dependencies * diff --git a/wcfsetup/install/files/lib/system/package/PackageUninstallationNodeBuilder.class.php b/wcfsetup/install/files/lib/system/package/PackageUninstallationNodeBuilder.class.php index 2068697562..ef912ee07b 100644 --- a/wcfsetup/install/files/lib/system/package/PackageUninstallationNodeBuilder.class.php +++ b/wcfsetup/install/files/lib/system/package/PackageUninstallationNodeBuilder.class.php @@ -1,5 +1,7 @@ parentNode)) { + $this->node = $this->getToken(); + } + + // build nodes for dependent packages + $this->buildDependentPackageNodes(); + // build pip nodes $this->buildPluginNodes(); @@ -26,12 +35,45 @@ class PackageUninstallationNodeBuilder extends PackageInstallationNodeBuilder { $this->buildPackageNode(); } + /** + * Builds nodes for all dependent packages. + */ + protected function buildDependentPackageNodes() { + if (!PackageUninstallationDispatcher::hasDependencies($this->installation->queue->packageID)) { + return; + } + + $packageList = PackageUninstallationDispatcher::getOrderedPackageDependencies($this->installation->queue->packageID); + $queue = $this->installation->queue; + + foreach ($packageList as $package) { + $queue = PackageInstallationQueueEditor::create(array( + 'processNo' => $queue->processNo, + 'parentQueueID' => $queue->queueID, + 'userID' => WCF::getUser()->userID, + 'package' => $package->package, + 'packageName' => $package->getName(), + 'packageID' => $package->packageID, + 'action' => 'uninstall' + )); + + // spawn nodes + $uninstallation = new PackageUninstallationDispatcher($queue); + $uninstallation->nodeBuilder->setParentNode($this->node); + $uninstallation->nodeBuilder->buildNodes(); + $this->parentNode = $uninstallation->nodeBuilder->getCurrentNode(); + $this->node = $this->getToken(); + } + } + /** * Creates a node-tree for package installation plugins, whereas the PIP- and files-plugin * will be executed last. */ protected function buildPluginNodes() { - $this->node = $this->getToken(); + if (empty($this->node)) { + $this->node = $this->getToken(); + } // fetch ordered pips $pips = array(); @@ -47,8 +89,8 @@ class PackageUninstallationNodeBuilder extends PackageInstallationNodeBuilder { // insert pips $sql = "INSERT INTO wcf".WCF_N."_package_installation_node - (queueID, processNo, sequenceNo, node, nodeType, nodeData) - VALUES (?, ?, ?, ?, ?, ?)"; + (queueID, processNo, sequenceNo, node, parentNode, nodeType, nodeData) + VALUES (?, ?, ?, ?, ?, ?, ?)"; $statement = WCF::getDB()->prepareStatement($sql); $sequenceNo = 0; @@ -58,6 +100,7 @@ class PackageUninstallationNodeBuilder extends PackageInstallationNodeBuilder { $this->installation->queue->processNo, $sequenceNo, $this->node, + $this->parentNode, 'pip', serialize(array( 'pluginName' => $pip['pluginName'], -- 2.20.1