/** * @see \wcf\system\exporter\IExporter::getSupportedData() */ public function getSupportedData() { $supportedData = array('com.woltlab.wcf.user' => array('com.woltlab.wcf.user.group', 'com.woltlab.wcf.user.avatar', 'com.woltlab.wcf.user.option', 'com.woltlab.wcf.user.comment', 'com.woltlab.wcf.user.follower', 'com.woltlab.wcf.user.rank'), 'com.woltlab.wbb.board' => array('com.woltlab.wbb.acl', 'com.woltlab.wbb.attachment', 'com.woltlab.wbb.poll', 'com.woltlab.wbb.watchedThread', 'com.woltlab.wbb.like', 'com.woltlab.wcf.label'), 'com.woltlab.wcf.conversation' => array('com.woltlab.wcf.conversation.attachment', 'com.woltlab.wcf.conversation.label'), 'com.woltlab.blog.entry' => array('com.woltlab.blog.category', 'com.woltlab.blog.entry.attachment', 'com.woltlab.blog.entry.comment', 'com.woltlab.blog.entry.like'), 'com.woltlab.calendar.event' => array('com.woltlab.calendar.category', 'com.woltlab.calendar.event.attachment', 'com.woltlab.calendar.event.date.comment', 'com.woltlab.calendar.event.date.participation', 'com.woltlab.calendar.event.like'), 'com.woltlab.wcf.smiley' => array()); $gallery = PackageCache::getInstance()->getPackageByIdentifier('com.woltlab.gallery'); if ($gallery && Package::compareVersion('2.1.0 Alpha 1', $gallery->packageVersion) != 1) { $supportedData['com.woltlab.gallery.image'] = array('com.woltlab.gallery.category', 'com.woltlab.gallery.album', 'com.woltlab.gallery.image.comment', 'com.woltlab.gallery.image.like', 'com.woltlab.gallery.image.marker'); } return $supportedData; }
<?php use wcf\data\package\Package; use wcf\system\exception\SystemException; use wcf\system\WCF; /** * @author Alexander Ebert * @copyright 2001-2015 WoltLab GmbH * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php> * @package com.woltlab.wcf * @category Community Framework */ if (Package::compareVersion(preg_replace('~ \\(Maelstrom\\)$~', '', WCF_VERSION), '2.0.10', '<')) { if (WCF::getLanguage()->getFixedLanguageCode() == 'de') { throw new SystemException("Die Aktualisierung erfordert WoltLab Community Framework (com.woltlab.wcf) in Version 2.0.10 oder hoeher"); } else { throw new SystemException("Update requires at least WoltLab Community Framework (com.woltlab.wcf) in version 2.0.10 or higher"); } }
/** * Installs current package. * * @param array $nodeData */ protected function installPackage(array $nodeData) { $installationStep = new PackageInstallationStep(); // check requirements if (!empty($nodeData['requirements'])) { foreach ($nodeData['requirements'] as $package => $requirementData) { // get existing package if ($requirementData['packageID']) { $sql = "SELECT packageName, packageVersion FROM wcf".WCF_N."_package WHERE packageID = ?"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(array($requirementData['packageID'])); } else { // try to find matching package $sql = "SELECT packageName, packageVersion FROM wcf".WCF_N."_package WHERE package = ?"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(array($package)); } $row = $statement->fetchArray(); // package is required but not available if ($row === false) { throw new SystemException("Package '".$package."' is required by '".$nodeData['packageName']."', but is neither installed nor shipped."); } // check version requirements if ($requirementData['minVersion']) { if (Package::compareVersion($row['packageVersion'], $requirementData['minVersion']) < 0) { throw new SystemException("Package '".$nodeData['packageName']."' requires the package '".$row['packageName']."' in version '".$requirementData['minVersion']."', but version '".$row['packageVersion']."'"); } } } } unset($nodeData['requirements']); if (!$this->queue->packageID) { // create package entry $package = PackageEditor::create($nodeData); // update package id for current queue $queueEditor = new PackageInstallationQueueEditor($this->queue); $queueEditor->update(array( 'packageID' => $package->packageID )); // save excluded packages if (count($this->getArchive()->getExcludedPackages()) > 0) { $sql = "INSERT INTO wcf".WCF_N."_package_exclusion (packageID, excludedPackage, excludedPackageVersion) VALUES (?, ?, ?)"; $statement = WCF::getDB()->prepareStatement($sql); foreach ($this->getArchive()->getExcludedPackages() as $excludedPackage) { $statement->execute(array($package->packageID, $excludedPackage['name'], (!empty($excludedPackage['version']) ? $excludedPackage['version'] : ''))); } } // if package is plugin to com.woltlab.wcf it must not have any other requirement $requirements = $this->getArchive()->getRequirements(); // insert requirements and dependencies $requirements = $this->getArchive()->getAllExistingRequirements(); if (!empty($requirements)) { $sql = "INSERT INTO wcf".WCF_N."_package_requirement (packageID, requirement) VALUES (?, ?)"; $statement = WCF::getDB()->prepareStatement($sql); foreach ($requirements as $identifier => $possibleRequirements) { if (count($possibleRequirements) == 1) { $requirement = array_shift($possibleRequirements); } else { $requirement = $possibleRequirements[$this->selectedRequirements[$identifier]]; } $statement->execute(array($package->packageID, $requirement['packageID'])); } } // reload queue $this->queue = new PackageInstallationQueue($this->queue->queueID); $this->package = null; if ($package->isApplication) { $host = StringUtil::replace(RouteHandler::getProtocol(), '', RouteHandler::getHost()); $path = RouteHandler::getPath(array('acp')); // insert as application ApplicationEditor::create(array( 'domainName' => $host, 'domainPath' => $path, 'cookieDomain' => $host, 'cookiePath' => $path, 'packageID' => $package->packageID )); } } if ($this->getPackage()->isApplication && $this->getPackage()->package != 'com.woltlab.wcf' && $this->getAction() == 'install') { if (empty($this->getPackage()->packageDir)) { $document = $this->promptPackageDir(); if ($document !== null && $document instanceof FormDocument) { $installationStep->setDocument($document); } $installationStep->setSplitNode(); } } return $installationStep; }
/** * Removes unnecessary updates of requirements from the list of available updates. * * @param array $updates * @param integer $packageUpdateVersionID * @return array $updates */ protected static function removeUpdateRequirements(array $updates, $packageUpdateVersionID) { $sql = "SELECT\t\tpur.package, pur.minversion, p.packageID\n\t\t\tFROM\t\twcf" . WCF_N . "_package_update_requirement pur\n\t\t\tLEFT JOIN\twcf" . WCF_N . "_package p\n\t\t\tON\t\t(p.package = pur.package)\n\t\t\tWHERE\t\tpur.packageUpdateVersionID = ?"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(array($packageUpdateVersionID)); while ($row = $statement->fetchArray()) { if (isset($updates[$row['packageID']])) { $updates = self::removeUpdateRequirements($updates, $updates[$row['packageID']]['version']['servers'][0]['packageUpdateVersionID']); if (Package::compareVersion($row['minversion'], $updates[$row['packageID']]['version']['packageVersion'], '>=')) { unset($updates[$row['packageID']]); } } } return $updates; }
/** * Builds nodes for required packages, whereas each has it own node. * * @return string */ protected function buildRequirementNodes() { $queue = $this->installation->queue; // handle requirements $requiredPackages = $this->installation->getArchive()->getOpenRequirements(); foreach ($requiredPackages as $packageName => $package) { if (!isset($package['file'])) { if (isset(self::$pendingPackages[$packageName]) && (!isset($package['minversion']) || Package::compareVersion(self::$pendingPackages[$packageName], $package['minversion']) >= 0)) { // the package will already be installed and no // minversion is given or the package which will be // installed satisfies the minversion, thus we can // ignore this requirement continue; } // requirements will be checked once package is about to be installed $this->requirements[$packageName] = array('minVersion' => isset($package['minversion']) ? $package['minversion'] : '', 'packageID' => $package['packageID']); continue; } if ($this->node == '' && !empty($this->parentNode)) { $this->node = $this->parentNode; } // extract package $index = $this->installation->getArchive()->getTar()->getIndexByFilename($package['file']); if ($index === false) { // workaround for WCFSetup if (!PACKAGE_ID && $packageName == 'com.woltlab.wcf') { continue; } throw new SystemException("Unable to find required package '" . $package['file'] . "' within archive of package '" . $this->installation->queue->package . "'."); } $fileName = FileUtil::getTemporaryFilename('package_', preg_replace('!^.*(?=\\.(?:tar\\.gz|tgz|tar)$)!i', '', basename($package['file']))); $this->installation->getArchive()->getTar()->extract($index, $fileName); // get archive data $archive = new PackageArchive($fileName); $archive->openArchive(); // check if delivered package has correct identifier if ($archive->getPackageInfo('name') != $packageName) { throw new SystemException("Invalid package file delivered for '" . $packageName . "' requirement of package '" . $this->installation->getArchive()->getPackageInfo('name') . "' (delivered package: '" . $archive->getPackageInfo('name') . "')."); } // check if delivered version satisfies minversion if (isset($package['minversion']) && Package::compareVersion($package['minversion'], $archive->getPackageInfo('version')) > 0) { throw new SystemException("Package '" . $this->installation->getArchive()->getPackageInfo('name') . "' requires package '" . $packageName . "' at least in version " . $package['minversion'] . ", but only delivers version " . $archive->getPackageInfo('version') . "."); } // get package id $sql = "SELECT\tpackageID\n\t\t\t\tFROM\twcf" . WCF_N . "_package\n\t\t\t\tWHERE\tpackage = ?"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(array($archive->getPackageInfo('name'))); $row = $statement->fetchArray(); $packageID = $row === false ? null : $row['packageID']; // check if package will already be installed if (isset(self::$pendingPackages[$packageName])) { if (Package::compareVersion(self::$pendingPackages[$packageName], $archive->getPackageInfo('version')) >= 0) { // the version to be installed satisfies the required version continue; } else { // the new delivered required version of the package has a // higher version number, thus update/replace the existing // package installation queue // todo } } // create new queue $queue = PackageInstallationQueueEditor::create(array('parentQueueID' => $queue->queueID, 'processNo' => $queue->processNo, 'userID' => WCF::getUser()->userID, 'package' => $archive->getPackageInfo('name'), 'packageID' => $packageID, 'packageName' => $archive->getLocalizedPackageInfo('packageName'), 'archive' => $fileName, 'action' => $packageID ? 'update' : 'install')); self::$pendingPackages[$archive->getPackageInfo('name')] = $archive->getPackageInfo('version'); // spawn nodes $installation = new PackageInstallationDispatcher($queue); $installation->nodeBuilder->setParentNode($this->node); $installation->nodeBuilder->buildNodes(); $this->node = $installation->nodeBuilder->getCurrentNode(); } }
/** * Installs the specified package. * * @param string $file */ private function install($file) { // PackageStartInstallForm::validateDownloadPackage() if (FileUtil::isURL($file)) { // download package $archive = new PackageArchive($file, null); try { if (VERBOSITY >= 1) { Log::info("Downloading '" . $file . "'"); } $file = $archive->downloadArchive(); } catch (SystemException $e) { $this->error('notFound', array('file' => $file)); } } else { // probably local path if (!file_exists($file)) { $this->error('notFound', array('file' => $file)); } $archive = new PackageArchive($file, null); } // PackageStartInstallForm::validateArchive() // try to open the archive try { // TODO: Exceptions thrown within openArchive() are discarded, resulting in // the meaningless message 'not a valid package' $archive->openArchive(); } catch (SystemException $e) { $this->error('noValidPackage'); } $errors = PackageInstallationDispatcher::validatePHPRequirements($archive->getPhpRequirements()); if (!empty($errors)) { // TODO: Nice output $this->error('phpRequirements', array('errors' => $errors)); } // try to find existing package $sql = "SELECT\t*\n\t\t\tFROM\twcf" . WCF_N . "_package\n\t\t\tWHERE\tpackage = ?"; $statement = CLIWCF::getDB()->prepareStatement($sql); $statement->execute(array($archive->getPackageInfo('name'))); $row = $statement->fetchArray(); $package = null; if ($row !== false) { $package = new Package(null, $row); } // check update or install support if ($package !== null) { CLIWCF::getSession()->checkPermissions(array('admin.system.package.canUpdatePackage')); $archive->setPackage($package); if (!$archive->isValidUpdate()) { $this->error('noValidUpdate'); } } else { CLIWCF::getSession()->checkPermissions(array('admin.system.package.canInstallPackage')); if (!$archive->isValidInstall()) { $this->error('noValidInstall'); } else { if ($archive->getPackageInfo('isApplication')) { // applications cannot be installed via CLI $this->error('cli.installIsApplication'); } else { if ($archive->isAlreadyInstalled()) { $this->error('uniqueAlreadyInstalled'); } else { if ($archive->getPackageInfo('isApplication') && $this->archive->hasUniqueAbbreviation()) { $this->error('noUniqueAbbrevation'); } } } } } // PackageStartInstallForm::save() $processNo = PackageInstallationQueue::getNewProcessNo(); // insert queue $queue = PackageInstallationQueueEditor::create(array('processNo' => $processNo, 'userID' => CLIWCF::getUser()->userID, 'package' => $archive->getPackageInfo('name'), 'packageName' => $archive->getLocalizedPackageInfo('packageName'), 'packageID' => $package !== null ? $package->packageID : null, 'archive' => $file, 'action' => $package !== null ? 'update' : 'install')); // PackageInstallationDispatcher::openQueue() $parentQueueID = 0; $conditions = new PreparedStatementConditionBuilder(); $conditions->add("userID = ?", array(CLIWCF::getUser()->userID)); $conditions->add("parentQueueID = ?", array($parentQueueID)); if ($processNo != 0) { $conditions->add("processNo = ?", array($processNo)); } $conditions->add("done = ?", array(0)); $sql = "SELECT\t\t*\n\t\t\tFROM\t\twcf" . WCF_N . "_package_installation_queue\n\t\t\t" . $conditions . "\n\t\t\tORDER BY\tqueueID ASC"; $statement = CLIWCF::getDB()->prepareStatement($sql); $statement->execute($conditions->getParameters()); $packageInstallation = $statement->fetchArray(); if (!isset($packageInstallation['queueID'])) { $this->error('internalOpenQueue'); return; } else { $queueID = $packageInstallation['queueID']; } // PackageInstallationConfirmPage::readParameters() $queue = new PackageInstallationQueue($queueID); if (!$queue->queueID || $queue->done) { $this->error('internalReadParameters'); return; } // PackageInstallationConfirmPage::readData() $missingPackages = 0; $packageInstallationDispatcher = new PackageInstallationDispatcher($queue); // get requirements $requirements = $packageInstallationDispatcher->getArchive()->getRequirements(); $openRequirements = $packageInstallationDispatcher->getArchive()->getOpenRequirements(); foreach ($requirements as &$requirement) { if (isset($openRequirements[$requirement['name']])) { $requirement['status'] = 'missing'; $requirement['action'] = $openRequirements[$requirement['name']]['action']; if (!isset($requirement['file'])) { if ($requirement['action'] === 'update') { $requirement['status'] = 'missingVersion'; $requirement['existingVersion'] = $openRequirements[$requirement['name']]['existingVersion']; } $missingPackages++; } else { $requirement['status'] = 'delivered'; $packageArchive = new PackageArchive($packageInstallationDispatcher->getArchive()->extractTar($requirement['file'])); $packageArchive->openArchive(); // make sure that the delivered package is correct if ($requirement['name'] != $packageArchive->getPackageInfo('name')) { $requirement['status'] = 'invalidDeliveredPackage'; $requirement['deliveredPackage'] = $packageArchive->getPackageInfo('name'); $missingPackages++; } else { if (isset($requirement['minversion'])) { // make sure that the delivered version is sufficient if (Package::compareVersion($requirement['minversion'], $packageArchive->getPackageInfo('version')) > 0) { $requirement['deliveredVersion'] = $packageArchive->getPackageInfo('version'); $requirement['status'] = 'missingVersion'; $missingPackages++; } } } } } else { $requirement['status'] = 'installed'; } } unset($requirement); // PackageInstallationConfirmPage::assignVariables/show() $excludingPackages = $packageInstallationDispatcher->getArchive()->getConflictedExcludingPackages(); $excludedPackages = $packageInstallationDispatcher->getArchive()->getConflictedExcludedPackages(); if (!($missingPackages == 0 && count($excludingPackages) == 0 && count($excludedPackages) == 0)) { $this->error('missingPackagesOrExclude', array('requirements' => $requirements, 'excludingPackages' => $excludingPackages, 'excludedPackages' => $excludedPackages)); return; } // AbstractDialogAction::readParameters() $step = 'prepare'; $queueID = $queue->queueID; $node = ''; // initialize progressbar $progressbar = new ProgressBar(new ConsoleProgressBar(array('width' => CLIWCF::getTerminal()->getWidth(), 'elements' => array(ConsoleProgressBar::ELEMENT_PERCENT, ConsoleProgressBar::ELEMENT_BAR, ConsoleProgressBar::ELEMENT_TEXT), 'textWidth' => min(floor(CLIWCF::getTerminal()->getWidth() / 2), 50)))); // InstallPackageAction::readParameters() $finished = false; while (!$finished) { $queue = new PackageInstallationQueue($queueID); if (!$queue->queueID) { // todo: what to output? echo "InstallPackageAction::readParameters()"; return; } $installation = new PackageInstallationDispatcher($queue); switch ($step) { case 'prepare': // InstallPackageAction::stepPrepare() // update package information $installation->updatePackage(); // clean-up previously created nodes $installation->nodeBuilder->purgeNodes(); // create node tree $installation->nodeBuilder->buildNodes(); $node = $installation->nodeBuilder->getNextNode(); $queueID = $installation->nodeBuilder->getQueueByNode($installation->queue->processNo, $node); $step = 'install'; $progress = 0; $currentAction = $installation->nodeBuilder->getPackageNameByQueue($queueID); break; case 'install': // InstallPackageAction::stepInstall() $step_ = $installation->install($node); $queueID = $installation->nodeBuilder->getQueueByNode($installation->queue->processNo, $step_->getNode()); if ($step_->hasDocument()) { $innerTemplate = $step_->getTemplate(); $progress = $installation->nodeBuilder->calculateProgress($node); $node = $step_->getNode(); $currentAction = $installation->nodeBuilder->getPackageNameByQueue($queueID); } else { if ($step_->getNode() == '') { // perform final actions $installation->completeSetup(); // InstallPackageAction::finalize() CacheHandler::getInstance()->flushAll(); // /InstallPackageAction::finalize() // show success $progress = 100; $currentAction = CLIWCF::getLanguage()->get('wcf.acp.package.installation.step.install.success'); $finished = true; continue; } else { // continue with next node $progress = $installation->nodeBuilder->calculateProgress($node); $node = $step_->getNode(); $currentAction = $installation->nodeBuilder->getPackageNameByQueue($queueID); } } break; } $progressbar->update($progress, $currentAction); } $progressbar->getAdapter()->finish(); }
/** * Removes unnecessary updates of requirements from the list of available updates. * * @param array $updates * @param integer $packageUpdateVersionID * @return array $updates */ protected function removeUpdateRequirements(array $updates, $packageUpdateVersionID) { $sql = "SELECT pur.package, pur.minversion, p.packageID FROM wcf".WCF_N."_package_update_requirement pur LEFT JOIN wcf".WCF_N."_package p ON (p.package = pur.package) WHERE pur.packageUpdateVersionID = ?"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(array($packageUpdateVersionID)); while ($row = $statement->fetchArray()) { if (isset($updates[$row['packageID']])) { $updates = $this->removeUpdateRequirements($updates, $updates[$row['packageID']]['version']['servers'][0]['packageUpdateVersionID']); if (Package::compareVersion($row['minversion'], $updates[$row['packageID']]['version']['packageVersion'], '>=')) { unset($updates[$row['packageID']]); } } } return $updates; }
/** * Validates if an installed package excludes the current package and vice versa. * * @param string $package */ protected function validateExclusion($package) { $packageVersion = $this->archive->getPackageInfo('version'); // excluding packages: installed -> current $sql = "SELECT\t\tpackage.*, package_exclusion.*\n\t\t\tFROM\t\twcf" . WCF_N . "_package_exclusion package_exclusion\n\t\t\tLEFT JOIN\twcf" . WCF_N . "_package package\n\t\t\tON\t\t(package.packageID = package_exclusion.packageID)\n\t\t\tWHERE\t\texcludedPackage = ?"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(array($this->getArchive()->getPackageInfo('name'))); $excludingPackages = array(); while ($row = $statement->fetchArray()) { $excludingPackage = $row['package']; // use exclusions of queued package if (isset(self::$excludedPackages[$excludingPackage])) { if (isset(self::$excludedPackages[$excludingPackage][$package])) { for ($i = 0, $count = count(self::$excludedPackages[$excludingPackage][$package]); $i < $count; $i++) { if (Package::compareVersion($packageVersion, self::$excludedPackages[$excludingPackage][$package][$i], '<')) { continue; } $excludingPackages[] = new Package(null, $row); } continue; } } else { if (Package::compareVersion($packageVersion, $row['excludedPackageVersion'], '<')) { continue; } $excludingPackages[] = new Package(null, $row); } } if (!empty($excludingPackages)) { throw new PackageValidationException(PackageValidationException::EXCLUDING_PACKAGES, array('packages' => $excludingPackages)); } // excluded packages: current -> installed if (!empty(self::$excludedPackages[$package])) { // get installed packages $conditions = new PreparedStatementConditionBuilder(); $conditions->add("package IN (?)", array(array_keys(self::$excludedPackages[$package]))); $sql = "SELECT\t*\n\t\t\t\tFROM\twcf" . WCF_N . "_package\n\t\t\t\t" . $conditions; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute($conditions->getParameters()); $packages = array(); while ($row = $statement->fetchArray()) { $packages[$row['package']] = new Package(null, $row); } $excludedPackages = array(); foreach ($packages as $excludedPackage => $packageObj) { $version = PackageValidationManager::getInstance()->getVirtualPackage($excludedPackage); if ($version === null) { $version = $packageObj->packageVersion; } for ($i = 0, $count = count(self::$excludedPackages[$package][$excludedPackage]); $i < $count; $i++) { if (Package::compareVersion($version, self::$excludedPackages[$package][$excludedPackage][$i], '<')) { continue; } $excludedPackages[] = $packageObj; } } if (!empty($excludedPackages)) { throw new PackageValidationException(PackageValidationException::EXCLUDED_PACKAGES, array('packages' => $excludedPackages)); } } }
/** * Returns a result list of a search for installable packages. * * @return array */ public function search() { PackageUpdateDispatcher::getInstance()->refreshPackageDatabase(); $availableUpdateServers = PackageUpdateServer::getActiveUpdateServers(); // there are no available package update servers if (empty($availableUpdateServers)) { WCF::getTPL()->assign(array('packageUpdates' => array())); return array('count' => 0, 'pageCount' => 0, 'searchID' => 0, 'template' => WCF::getTPL()->fetch('packageSearchResultList')); } $conditions = new PreparedStatementConditionBuilder(); $conditions->add("package_update.packageUpdateServerID IN (?)", array(array_keys($availableUpdateServers))); if (!empty($this->parameters['package'])) { $conditions->add("package_update.package LIKE ?", array('%' . $this->parameters['package'] . '%')); } if (!empty($this->parameters['packageDescription'])) { $conditions->add("package_update.packageDescription LIKE ?", array('%' . $this->parameters['packageDescription'] . '%')); } if (!empty($this->parameters['packageName'])) { $conditions->add("package_update.packageName LIKE ?", array('%' . $this->parameters['packageName'] . '%')); } $conditions->add("package.packageID IS NULL"); // find matching packages $sql = "SELECT\t\tpackage_update.packageUpdateID\n\t\t\tFROM\t\twcf" . WCF_N . "_package_update package_update\n\t\t\tLEFT JOIN\twcf" . WCF_N . "_package package\n\t\t\tON\t\t(package.package = package_update.package)\n\t\t\t" . $conditions . "\n\t\t\tORDER BY\tpackage_update.packageName ASC"; $statement = WCF::getDB()->prepareStatement($sql, 1000); $statement->execute($conditions->getParameters()); $packageUpdateIDs = array(); while ($row = $statement->fetchArray()) { $packageUpdateIDs[] = $row['packageUpdateID']; } // no matches found if (empty($packageUpdateIDs)) { WCF::getTPL()->assign(array('packageUpdates' => array())); return array('count' => 0, 'pageCount' => 0, 'searchID' => 0, 'template' => WCF::getTPL()->fetch('packageSearchResultList')); } // get excluded packages $sql = "SELECT\t*\n\t\t\tFROM\twcf" . WCF_N . "_package_update_exclusion"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(); $excludedPackages = array(); while ($row = $statement->fetchArray()) { $package = $row['excludedPackage']; $packageVersion = $row['excludedPackageVersion']; $packageUpdateVersionID = $row['packageUpdateVersionID']; if (!isset($excludedPackages[$packageUpdateVersionID][$package])) { $excludedPackages[$packageUpdateVersionID][$package] = $packageVersion; } else { if (Package::compareVersion($excludedPackages[$packageUpdateVersionID][$package], $packageVersion) == 1) { $excludedPackages[$packageUpdateVersionID][$package] = $packageVersion; } } } // get installed packages $sql = "SELECT\tpackage, packageVersion\n\t\t\tFROM\twcf" . WCF_N . "_package"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute(); $installedPackages = array(); while ($row = $statement->fetchArray()) { $installedPackages[$row['package']] = $row['packageVersion']; } // filter by version $conditions = new PreparedStatementConditionBuilder(); $conditions->add("puv.packageUpdateID IN (?)", array($packageUpdateIDs)); $sql = "SELECT\t\tpu.package, puv.packageUpdateVersionID, puv.packageUpdateID, puv.packageVersion, puv.isAccessible\n\t\t\tFROM\t\twcf" . WCF_N . "_package_update_version puv\n\t\t\tLEFT JOIN\twcf" . WCF_N . "_package_update pu\n\t\t\tON\t\t(pu.packageUpdateID = puv.packageUpdateID)\n\t\t\t" . $conditions; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute($conditions->getParameters()); $packageVersions = array(); while ($row = $statement->fetchArray()) { $package = $row['package']; $packageVersion = $row['packageVersion']; $packageUpdateVersionID = $row['packageUpdateVersionID']; // check excluded packages if (isset($excludedPackages[$packageUpdateVersionID])) { $isExcluded = false; foreach ($excludedPackages[$packageUpdateVersionID] as $excludedPackage => $excludedPackageVersion) { if (isset($installedPackages[$excludedPackage]) && Package::compareVersion($excludedPackageVersion, $installedPackages[$excludedPackage]) <= 0) { // excluded, ignore $isExcluded = true; break; } } if ($isExcluded) { continue; } } if (!isset($packageVersions[$package])) { $packageVersions[$package] = array(); } $packageUpdateID = $row['packageUpdateID']; if (!isset($packageVersions[$package][$packageUpdateID])) { $packageVersions[$package][$packageUpdateID] = array('accessible' => array(), 'existing' => array()); } if ($row['isAccessible']) { $packageVersions[$package][$packageUpdateID]['accessible'][$row['packageUpdateVersionID']] = $packageVersion; } $packageVersions[$package][$packageUpdateID]['existing'][$row['packageUpdateVersionID']] = $packageVersion; } // all found versions are excluded if (empty($packageVersions)) { WCF::getTPL()->assign(array('packageUpdates' => array())); return array('count' => 0, 'pageCount' => 0, 'searchID' => 0, 'template' => WCF::getTPL()->fetch('packageSearchResultList')); } // determine highest versions $packageUpdates = array(); foreach ($packageVersions as $package => $versionData) { $accessible = $existing = $versions = array(); foreach ($versionData as $packageUpdateID => $versionTypes) { // ignore unaccessible packages if (empty($versionTypes['accessible'])) { continue; } uasort($versionTypes['accessible'], array('wcf\\data\\package\\Package', 'compareVersion')); uasort($versionTypes['existing'], array('wcf\\data\\package\\Package', 'compareVersion')); $accessibleVersion = array_slice($versionTypes['accessible'], -1, 1, true); $existingVersion = array_slice($versionTypes['existing'], -1, 1, true); $ak = key($accessibleVersion); $av = current($accessibleVersion); $ek = key($existingVersion); $ev = current($existingVersion); $accessible[$av] = $ak; $existing[$ev] = $ek; $versions[$ak] = $packageUpdateID; $versions[$ek] = $packageUpdateID; } // ignore packages without accessible versions if (empty($accessible)) { continue; } uksort($accessible, array('wcf\\data\\package\\Package', 'compareVersion')); uksort($existing, array('wcf\\data\\package\\Package', 'compareVersion')); $accessible = array_pop($accessible); $existing = array_pop($existing); $packageUpdates[$versions[$accessible]] = array('accessible' => $accessible, 'existing' => $existing); } // no found packages is accessible if (empty($packageUpdates)) { WCF::getTPL()->assign(array('packageUpdates' => array())); return array('count' => 0, 'pageCount' => 0, 'searchID' => 0, 'template' => WCF::getTPL()->fetch('packageSearchResultList')); } $search = SearchEditor::create(array('userID' => WCF::getUser()->userID, 'searchData' => serialize($packageUpdates), 'searchTime' => TIME_NOW, 'searchType' => 'acpPackageSearch')); // forward call to build the actual result list $updateAction = new PackageUpdateAction(array(), 'getResultList', array('pageNo' => 1, 'search' => $search)); $returnValues = $updateAction->executeAction(); return $returnValues['returnValues']; }
/** * Determines intermediate update steps using a backtracking algorithm in case there is no direct upgrade possible. * * @param string $package package identifier * @param array $fromversions list of all fromversions * @param string $currentVersion current package version * @param string $newVersion new package version * @return array list of update steps (old version => new version, old version => new version, ...) */ protected function findShortestUpdateThread($package, $fromversions, $currentVersion, $newVersion) { if (!isset($fromversions[$newVersion])) { throw new SystemException("An update of package " . $package . " from version " . $currentVersion . " to " . $newVersion . " is not supported."); } // find direct update foreach ($fromversions[$newVersion] as $fromversion) { if (Package::checkFromversion($currentVersion, $fromversion)) { return array($currentVersion => $newVersion); } } // find intermediate update $packageVersions = array_keys($fromversions); $updateThreadList = array(); foreach ($fromversions[$newVersion] as $fromversion) { $innerUpdateThreadList = array(); // find matching package versions foreach ($packageVersions as $packageVersion) { if (Package::checkFromversion($packageVersion, $fromversion) && Package::compareVersion($packageVersion, $currentVersion, '>') && Package::compareVersion($packageVersion, $newVersion, '<')) { $innerUpdateThreadList[] = $this->findShortestUpdateThread($package, $fromversions, $currentVersion, $packageVersion) + array($packageVersion => $newVersion); } } if (!empty($innerUpdateThreadList)) { // sort by length usort($innerUpdateThreadList, array($this, 'compareUpdateThreadLists')); // add to thread list $updateThreadList[] = array_shift($innerUpdateThreadList); } } if (empty($updateThreadList)) { throw new SystemException("An update of package " . $package . " from version " . $currentVersion . " to " . $newVersion . " is not supported."); } // sort by length usort($updateThreadList, array($this, 'compareUpdateThreadLists')); // take shortest return array_shift($updateThreadList); }
/** * Adds a virtual package with the corresponding version, if the package is already known, * the higher version number will be stored. * * @param string $package * @param string $packageVersion * @return boolean */ public function addVirtualPackage($package, $packageVersion) { if (isset($this->virtualPackageList[$package])) { if (Package::compareVersion($packageVersion, $this->virtualPackageList[$package], '<')) { return false; } } $this->virtualPackageList[$package] = $packageVersion; return true; }
/** * @see \wcf\page\IPage::readData() */ public function readData() { parent::readData(); $serverList = new PackageUpdateServerList(); $serverList->readObjects(); foreach ($serverList as $server) { if (preg_match('~https?://store.woltlab.com/(?P<wcfMajorRelease>[a-z]+)/~', $server->serverURL, $matches)) { $this->updateServers[$matches['wcfMajorRelease']] = $server; } } foreach ($this->products as $packageUpdateID => $product) { $wcfMajorRelease = $product['wcfMajorRelease']; if (!isset($this->productData[$wcfMajorRelease])) { $this->productData[$wcfMajorRelease] = array(); } $languageCode = WCF::getLanguage()->languageCode; $packageName = isset($product['packageName'][$languageCode]) ? $product['packageName'][$languageCode] : $product['packageName']['en']; $this->productData[$wcfMajorRelease][$packageUpdateID] = array('author' => $product['author'], 'authorURL' => $product['authorURL'], 'package' => $product['package'], 'packageName' => $packageName, 'pluginStoreURL' => $product['pluginStoreURL'], 'version' => array('available' => $product['lastVersion'], 'installed' => ''), 'status' => isset($this->updateServers[$wcfMajorRelease]) ? 'install' : 'unavailable'); $package = PackageCache::getInstance()->getPackageByIdentifier($product['package']); if ($package !== null) { $this->productData[$wcfMajorRelease][$packageUpdateID]['version']['installed'] = $package->packageVersion; if (Package::compareVersion($product['lastVersion'], $package->packageVersion, '>')) { $this->productData[$wcfMajorRelease][$packageUpdateID]['status'] = 'update'; } else { if (Package::compareVersion($product['lastVersion'], $package->packageVersion, '=')) { $this->productData[$wcfMajorRelease][$packageUpdateID]['status'] = 'upToDate'; } } } if (isset($this->updateServers[$wcfMajorRelease]) && $this->updateServers[$wcfMajorRelease]->lastUpdateTime == 0) { $this->productData[$wcfMajorRelease][$packageUpdateID]['status'] = 'requireUpdate'; } } }
/** * Gets a list of packages. */ protected function readPackages() { if ($this->items) { $conditions = new PreparedStatementConditionBuilder(); $conditions->add("packageUpdateID IN (?)", array(explode(',', $this->search->searchData))); $sql = "SELECT\t\t*\n\t\t\t\tFROM\t\twcf" . WCF_N . "_package_update\n\t\t\t\t" . $conditions . "\n\t\t\t\tORDER BY\t" . $this->sortField . " " . $this->sortOrder; $statement = WCF::getDB()->prepareStatement($sql, $this->itemsPerPage, ($this->pageNo - 1) * $this->itemsPerPage); $statement->execute($conditions->getParameters()); while ($row = $statement->fetchArray()) { // default values $row['isUnique'] = 0; $row['updatableInstances'] = array(); $row['packageVersions'] = array(); $row['packageVersion'] = '1.0.0'; $row['instances'] = 0; // get package versions $sql = "SELECT\tpackageVersion\n\t\t\t\t\tFROM\twcf" . WCF_N . "_package_update_version\n\t\t\t\t\tWHERE\tpackageUpdateID IN (\n\t\t\t\t\t\t\tSELECT\tpackageUpdateID\n\t\t\t\t\t\t\tFROM\twcf" . WCF_N . "_package_update\n\t\t\t\t\t\t\tWHERE\tpackage = ?\n\t\t\t\t\t\t)"; $statement2 = WCF::getDB()->prepareStatement($sql); $statement2->execute(array($row['package'])); while ($row2 = $statement2->fetchArray()) { $row['packageVersions'][] = $row2['packageVersion']; } if (count($row['packageVersions'])) { // remove duplicates $row['packageVersions'] = array_unique($row['packageVersions']); // sort versions usort($row['packageVersions'], array('wcf\\data\\package\\Package', 'compareVersion')); // take lastest version $row['packageVersion'] = end($row['packageVersions']); } // get installed instances $sql = "SELECT\tpackage.*, CASE WHEN instanceName <> '' THEN instanceName ELSE packageName END AS packageName\n\t\t\t\t\tFROM\twcf" . WCF_N . "_package package\n\t\t\t\t\tWHERE \tpackage.package = ?"; $statement2 = WCF::getDB()->prepareStatement($sql); $statement2->execute(array($row['package'])); while ($row2 = $statement2->fetchArray()) { $row['instances']++; // is already installed unique? if ($row2['isUnique'] == 1) { $row['isUnique'] = 1; } // check update support if (Package::compareVersion($row2['packageVersion'], $row['packageVersion'], '<')) { $row['updatableInstances'][] = $row2; } } $this->packages[] = $row; } } }
/** * Returns a list of excluded packages. * * @return array */ public function getExcludedPackages() { $excludedPackages = array(); if (!empty($this->packageInstallationStack)) { $packageInstallations = array(); $packageIdentifier = array(); foreach ($this->packageInstallationStack as $packageInstallation) { $packageInstallation['newVersion'] = ($packageInstallation['action'] == 'update' ? $packageInstallation['toVersion'] : $packageInstallation['packageVersion']); $packageInstallations[] = $packageInstallation; $packageIdentifier[] = $packageInstallation['package']; } // check exclusions of the new packages // get package update ids $conditions = new PreparedStatementConditionBuilder(); $conditions->add("package IN (?)", array($packageIdentifier)); $sql = "SELECT packageUpdateID, package FROM wcf".WCF_N."_package_update ".$conditions; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute($conditions->getParameters()); while ($row = $statement->fetchArray()) { foreach ($packageInstallations as $key => $packageInstallation) { if ($packageInstallation['package'] == $row['package']) { $packageInstallations[$key]['packageUpdateID'] = $row['packageUpdateID']; } } } // get exclusions of the new packages // build conditions $conditions = ''; $statementParameters = array(); foreach ($packageInstallations as $packageInstallation) { if (!empty($conditions)) $conditions .= ' OR '; $conditions .= "(packageUpdateID = ? AND packageVersion = ?)"; $statementParameters[] = $packageInstallation['packageUpdateID']; $statementParameters[] = $packageInstallation['newVersion']; } $sql = "SELECT package.*, package_update_exclusion.*, package_update.packageUpdateID, package_update.package FROM wcf".WCF_N."_package_update_exclusion package_update_exclusion LEFT JOIN wcf".WCF_N."_package_update_version package_update_version ON (package_update_version.packageUpdateVersionID = package_update_exclusion.packageUpdateVersionID) LEFT JOIN wcf".WCF_N."_package_update package_update ON (package_update.packageUpdateID = package_update_version.packageUpdateID) LEFT JOIN wcf".WCF_N."_package package ON (package.package = package_update_exclusion.excludedPackage) WHERE package_update_exclusion.packageUpdateVersionID IN ( SELECT packageUpdateVersionID FROM wcf".WCF_N."_package_update_version WHERE ".$conditions." ) AND package.package IS NOT NULL"; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute($statementParameters); while ($row = $statement->fetchArray()) { foreach ($packageInstallations as $key => $packageInstallation) { if ($packageInstallation['package'] == $row['package']) { if (!isset($packageInstallations[$key]['excludedPackages'])) { $packageInstallations[$key]['excludedPackages'] = array(); } $packageInstallations[$key]['excludedPackages'][$row['excludedPackage']] = array('package' => $row['excludedPackage'], 'version' => $row['excludedPackageVersion']); // check version if (!empty($row['excludedPackageVersion'])) { if (Package::compareVersion($row['packageVersion'], $row['excludedPackageVersion'], '<')) { continue; } } $excludedPackages[] = array( 'package' => $row['package'], 'packageName' => $packageInstallations[$key]['packageName'], 'packageVersion' => $packageInstallations[$key]['newVersion'], 'action' => $packageInstallations[$key]['action'], 'conflict' => 'newPackageExcludesExistingPackage', 'existingPackage' => $row['excludedPackage'], 'existingPackageName' => $row['packageName'], 'existingPackageVersion' => $row['packageVersion'] ); } } } // check excluded packages of the existing packages $conditions = new PreparedStatementConditionBuilder(); $conditions->add("excludePackage IN (?)", array($packageIdentifier)); $sql = "SELECT package.*, package_exclusion.* FROM wcf".WCF_N."_package_exclusion package_exclusion LEFT JOIN wcf".WCF_N."_package package ON (package.packageID = package_exclusion.packageID) ".$conditions; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute($conditions->getParameters()); while ($row = $statement->fetchArray()) { foreach ($packageInstallations as $key => $packageInstallation) { if ($packageInstallation['package'] == $row['excludedPackage']) { if (!empty($row['excludedPackageVersion'])) { // check version if (Package::compareVersion($packageInstallation['newVersion'], $row['excludedPackageVersion'], '<')) { continue; } // search exclusing package in stack foreach ($packageInstallations as $packageUpdate) { if ($packageUpdate['packageID'] == $row['packageID']) { // check new exclusions if (!isset($packageUpdate['excludedPackages']) || !isset($packageUpdate['excludedPackages'][$row['excludedPackage']]) || (!empty($packageUpdate['excludedPackages'][$row['excludedPackage']]['version']) && Package::compareVersion($packageInstallation['newVersion'], $packageUpdate['excludedPackages'][$row['excludedPackage']]['version'], '<'))) { continue 2; } } } } $excludedPackages[] = array( 'package' => $row['excludedPackage'], 'packageName' => $packageInstallation['packageName'], 'packageVersion' => $packageInstallation['newVersion'], 'action' => $packageInstallation['action'], 'conflict' => 'existingPackageExcludesNewPackage', 'existingPackage' => $row['package'], 'existingPackageName' => $row['packageName'], 'existingPackageVersion' => $row['packageVersion'] ); } } } } return $excludedPackages; }
/** * Returns a list of packages, which are excluded by this package. * * @return array */ public function getConflictedExcludedPackages() { $conflictedPackages = array(); if (!empty($this->excludedPackages)) { $conditions = new PreparedStatementConditionBuilder(); $conditions->add("package IN (?)", array(array_keys($this->excludedPackages))); $sql = "SELECT * FROM wcf".WCF_N."_package ".$conditions; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute($conditions->getParameters()); while ($row = $statement->fetchArray()) { if (!empty($this->excludedPackages[$row['package']]['version'])) { if (Package::compareVersion($row['packageVersion'], $this->excludedPackages[$row['package']]['version'], '<')) { continue; } } $conflictedPackages[$row['packageID']] = $row; } } return $conflictedPackages; }
/** * Returns a list of packages which are excluded by this package. * * @return array<\wcf\data\package\Package> */ public function getConflictedExcludedPackages() { $conflictedPackages = array(); if (!empty($this->excludedPackages)) { $excludedPackages = array(); foreach ($this->excludedPackages as $excludedPackageData) { $excludedPackages[$excludedPackageData['name']] = $excludedPackageData['version']; } $conditions = new PreparedStatementConditionBuilder(); $conditions->add("package IN (?)", array(array_keys($excludedPackages))); $sql = "SELECT\t*\n\t\t\t\tFROM\twcf" . WCF_N . "_package\n\t\t\t\t" . $conditions; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute($conditions->getParameters()); while ($row = $statement->fetchArray()) { if (!empty($excludedPackages[$row['package']])) { if (Package::compareVersion($row['packageVersion'], $excludedPackages[$row['package']], '<')) { continue; } $row['excludedPackageVersion'] = $excludedPackages[$row['package']]; } $conflictedPackages[$row['packageID']] = new Package(null, $row); } } return $conflictedPackages; }