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
+ );
}
/**
// remove node data
$this->installation->nodeBuilder->purgeNodes();
- // TODO: Show 'success' template at this point
+ // show success
$this->data = array(
'progress' => 100,
'step' => 'success'
}
// 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
);
}
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;
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
*
<?php
namespace wcf\system\package;
+use wcf\data\package\installation\queue\PackageInstallationQueueEditor;
+use wcf\system\package\PackageUninstallationDispatcher;
use wcf\system\WCF;
/**
* Builds node for current uninstallation queue.
*/
public function buildNodes() {
+ if (!empty($this->parentNode)) {
+ $this->node = $this->getToken();
+ }
+
+ // build nodes for dependent packages
+ $this->buildDependentPackageNodes();
+
// build pip nodes
$this->buildPluginNodes();
$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();
// 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;
$this->installation->queue->processNo,
$sequenceNo,
$this->node,
+ $this->parentNode,
'pip',
serialize(array(
'pluginName' => $pip['pluginName'],