2 namespace wcf\system\package
;
3 use wcf\data\package\update\server\PackageUpdateServer
;
4 use wcf\data\package\update\server\PackageUpdateServerEditor
;
5 use wcf\data\package\update\version\PackageUpdateVersionEditor
;
6 use wcf\data\package\update\version\PackageUpdateVersionList
;
7 use wcf\data\package\update\PackageUpdateEditor
;
8 use wcf\data\package\update\PackageUpdateList
;
9 use wcf\data\package\Package
;
10 use wcf\system\cache\builder\PackageUpdateCacheBuilder
;
11 use wcf\system\database\util\PreparedStatementConditionBuilder
;
12 use wcf\system\exception\HTTPUnauthorizedException
;
13 use wcf\system\exception\SystemException
;
14 use wcf\system\package\PackageUpdateUnauthorizedException
;
16 use wcf\system\SingletonFactory
;
18 use wcf\util\HTTPRequest
;
22 * Provides functions to manage package updates.
24 * @author Alexander Ebert
25 * @copyright 2001-2014 WoltLab GmbH
26 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
27 * @package com.woltlab.wcf
28 * @subpackage system.package
29 * @category Community Framework
31 class PackageUpdateDispatcher
extends SingletonFactory
{
33 * Refreshes the package database.
35 * @param array<integer> $packageUpdateServerIDs
36 * @param boolean $ignoreCache
38 public function refreshPackageDatabase(array $packageUpdateServerIDs = array(), $ignoreCache = false) {
39 // get update server data
40 $updateServers = PackageUpdateServer
::getActiveUpdateServers($packageUpdateServerIDs);
43 $refreshedPackageLists = false;
44 foreach ($updateServers as $updateServer) {
45 if ($ignoreCache ||
$updateServer->lastUpdateTime
< TIME_NOW
- 600) {
49 $this->getPackageUpdateXML($updateServer);
50 $refreshedPackageLists = true;
52 catch (SystemException
$e) {
53 $errorMessage = $e->getMessage();
55 catch (PackageUpdateUnauthorizedException
$e) {
56 $reply = $e->getRequest()->getReply();
57 foreach ($reply['headers'] as $header) {
58 if (preg_match('~^HTTP~', $header)) {
59 $errorMessage = $header;
65 $errorMessage = 'Unknown (HTTP status ' . (is_array($reply['statusCode']) ?
reset($reply['statusCode']) : $reply['statusCode']) . ')';
71 $updateServerEditor = new PackageUpdateServerEditor($updateServer);
72 $updateServerEditor->update(array(
73 'status' => 'offline',
74 'errorMessage' => $errorMessage
80 if ($refreshedPackageLists) {
81 PackageUpdateCacheBuilder
::getInstance()->reset();
86 * Gets the package_update.xml from an update server.
88 * @param \wcf\data\package\update\server\PackageUpdateServer $updateServer
90 protected function getPackageUpdateXML(PackageUpdateServer
$updateServer) {
91 $authData = $updateServer->getAuthData();
93 if ($authData) $settings['auth'] = $authData;
96 'lastUpdateTime' => $updateServer->lastUpdateTime
99 // append auth code if set and update server resolves to woltlab.com
100 if (PACKAGE_SERVER_AUTH_CODE
&& Regex
::compile('^https?://[a-z]+.woltlab.com/')->match($updateServer->serverURL
)) {
101 $postData['authCode'] = PACKAGE_SERVER_AUTH_CODE
;
104 $request = new HTTPRequest($updateServer->serverURL
, $settings, $postData);
108 $reply = $request->getReply();
110 catch (HTTPUnauthorizedException
$e) {
111 throw new PackageUpdateUnauthorizedException($request, $updateServer);
113 catch (SystemException
$e) {
114 $reply = $request->getReply();
116 $statusCode = (is_array($reply['statusCode'])) ?
reset($reply['statusCode']) : $reply['statusCode'];
117 throw new SystemException(WCF
::getLanguage()->get('wcf.acp.package.update.error.listNotFound') . ' ('.$statusCode.')');
120 // parse given package update xml
121 $allNewPackages = $this->parsePackageUpdateXML($reply['body']);
122 unset($request, $reply);
125 if (!empty($allNewPackages)) {
126 $this->savePackageUpdates($allNewPackages, $updateServer->packageUpdateServerID
);
128 unset($allNewPackages);
130 // update server status
131 $updateServerEditor = new PackageUpdateServerEditor($updateServer);
132 $updateServerEditor->update(array(
133 'lastUpdateTime' => TIME_NOW
,
134 'status' => 'online',
140 * Parses a stream containing info from a packages_update.xml.
142 * @param string $content
143 * @return array $allNewPackages
145 protected function parsePackageUpdateXML($content) {
148 $xml->loadXML('packageUpdateServer.xml', $content);
149 $xpath = $xml->xpath();
151 // loop through <package> tags inside the <section> tag.
152 $allNewPackages = array();
153 $packages = $xpath->query('/ns:section/ns:package');
154 foreach ($packages as $package) {
155 if (!Package
::isValidPackageName($package->getAttribute('name'))) {
156 throw new SystemException("'".$package->getAttribute('name')."' is not a valid package name.");
159 $allNewPackages[$package->getAttribute('name')] = $this->parsePackageUpdateXMLBlock($xpath, $package);
162 return $allNewPackages;
166 * Parses the xml stucture from a packages_update.xml.
168 * @param \DOMXPath $xpath
169 * @param \DOMNode $package
171 protected function parsePackageUpdateXMLBlock(\DOMXPath
$xpath, \DOMNode
$package) {
172 // define default values
173 $packageInfo = array(
176 'isApplication' => 0,
177 'packageDescription' => '',
178 'versions' => array()
181 // parse package information
182 $elements = $xpath->query('./ns:packageinformation/*', $package);
183 foreach ($elements as $element) {
184 switch ($element->tagName
) {
186 $packageInfo['packageName'] = $element->nodeValue
;
189 case 'packagedescription':
190 $packageInfo['packageDescription'] = $element->nodeValue
;
193 case 'isapplication':
194 $packageInfo['isApplication'] = intval($element->nodeValue
);
199 // parse author information
200 $elements = $xpath->query('./ns:authorinformation/*', $package);
201 foreach ($elements as $element) {
202 switch ($element->tagName
) {
204 $packageInfo['author'] = $element->nodeValue
;
208 $packageInfo['authorURL'] = $element->nodeValue
;
214 $elements = $xpath->query('./ns:versions/ns:version', $package);
215 foreach ($elements as $element) {
216 $versionNo = $element->getAttribute('name');
217 $packageInfo['versions'][$versionNo] = array(
218 'isAccessible' => ($element->getAttribute('accessible') == 'true' ?
true : false),
219 'isCritical' => ($element->getAttribute('critical') == 'true' ?
true : false)
222 $children = $xpath->query('child::*', $element);
223 foreach ($children as $child) {
224 switch ($child->tagName
) {
226 $fromversions = $xpath->query('child::*', $child);
227 foreach ($fromversions as $fromversion) {
228 $packageInfo['versions'][$versionNo]['fromversions'][] = $fromversion->nodeValue
;
233 $packageInfo['versions'][$versionNo]['packageDate'] = $child->nodeValue
;
237 $packageInfo['versions'][$versionNo]['file'] = $child->nodeValue
;
240 case 'requiredpackages':
241 $requiredPackages = $xpath->query('child::*', $child);
242 foreach ($requiredPackages as $requiredPackage) {
243 $minVersion = $requiredPackage->getAttribute('minversion');
244 $required = $requiredPackage->nodeValue
;
246 $packageInfo['versions'][$versionNo]['requiredPackages'][$required] = array();
247 if (!empty($minVersion)) {
248 $packageInfo['versions'][$versionNo]['requiredPackages'][$required]['minversion'] = $minVersion;
253 case 'optionalpackages':
254 $packageInfo['versions'][$versionNo]['optionalPackages'] = array();
256 $optionalPackages = $xpath->query('child::*', $child);
257 foreach ($optionalPackages as $optionalPackage) {
258 $packageInfo['versions'][$versionNo]['optionalPackages'][] = $optionalPackage->nodeValue
;
262 case 'excludedpackages':
263 $excludedpackages = $xpath->query('child::*', $child);
264 foreach ($excludedpackages as $excludedPackage) {
265 $exclusion = $excludedPackage->nodeValue
;
266 $version = $excludedPackage->getAttribute('version');
268 $packageInfo['versions'][$versionNo]['excludedPackages'][$exclusion] = array();
269 if (!empty($version)) {
270 $packageInfo['versions'][$versionNo]['excludedPackages'][$exclusion]['version'] = $version;
276 $packageInfo['versions'][$versionNo]['license'] = array(
277 'license' => $child->nodeValue
,
278 'licenseURL' => ($child->hasAttribute('url') ?
$child->getAttribute('url') : '')
289 * Updates information parsed from a packages_update.xml into the database.
291 * @param array $allNewPackages
292 * @param integer $packageUpdateServerID
294 protected function savePackageUpdates(array &$allNewPackages, $packageUpdateServerID) {
295 // find existing packages and delete them
296 // get existing packages
297 $existingPackages = array();
298 $packageUpdateList = new PackageUpdateList();
299 $packageUpdateList->getConditionBuilder()->add("package_update.packageUpdateServerID = ? AND package_update.package IN (?)", array($packageUpdateServerID, array_keys($allNewPackages)));
300 $packageUpdateList->readObjects();
301 $tmp = $packageUpdateList->getObjects();
303 foreach ($tmp as $packageUpdate) {
304 $existingPackages[$packageUpdate->package
] = $packageUpdate;
307 // get existing versions
308 $existingPackageVersions = array();
309 if (!empty($existingPackages)) {
310 // get package update ids
311 $packageUpdateIDs = array();
312 foreach ($existingPackages as $packageUpdate) {
313 $packageUpdateIDs[] = $packageUpdate->packageUpdateID
;
317 $versionList = new PackageUpdateVersionList();
318 $versionList->getConditionBuilder()->add("package_update_version.packageUpdateID IN (?)", array($packageUpdateIDs));
319 $versionList->readObjects();
320 $tmp = $versionList->getObjects();
322 foreach ($tmp as $version) {
323 if (!isset($existingPackageVersions[$version->packageUpdateID
])) $existingPackageVersions[$version->packageUpdateID
] = array();
324 $existingPackageVersions[$version->packageUpdateID
][$version->packageVersion
] = $version;
329 $excludedPackagesParameters = $fromversionParameters = $insertParameters = $optionalInserts = $requirementInserts = array();
330 foreach ($allNewPackages as $identifier => $packageData) {
331 if (isset($existingPackages[$identifier])) {
332 $packageUpdateID = $existingPackages[$identifier]->packageUpdateID
;
334 // update database entry
335 $packageUpdateEditor = new PackageUpdateEditor($existingPackages[$identifier]);
336 $packageUpdateEditor->update(array(
337 'packageName' => $packageData['packageName'],
338 'packageDescription' => $packageData['packageDescription'],
339 'author' => $packageData['author'],
340 'authorURL' => $packageData['authorURL'],
341 'isApplication' => $packageData['isApplication']
345 // create new database entry
346 $packageUpdate = PackageUpdateEditor
::create(array(
347 'packageUpdateServerID' => $packageUpdateServerID,
348 'package' => $identifier,
349 'packageName' => $packageData['packageName'],
350 'packageDescription' => $packageData['packageDescription'],
351 'author' => $packageData['author'],
352 'authorURL' => $packageData['authorURL'],
353 'isApplication' => $packageData['isApplication']
356 $packageUpdateID = $packageUpdate->packageUpdateID
;
359 // register version(s) of this update package.
360 if (isset($packageData['versions'])) {
361 foreach ($packageData['versions'] as $packageVersion => $versionData) {
362 if (isset($versionData['file'])) $packageFile = $versionData['file'];
363 else $packageFile = '';
365 if (isset($existingPackageVersions[$packageUpdateID]) && isset($existingPackageVersions[$packageUpdateID][$packageVersion])) {
366 $packageUpdateVersionID = $existingPackageVersions[$packageUpdateID][$packageVersion]->packageUpdateVersionID
;
368 // update database entry
369 $versionEditor = new PackageUpdateVersionEditor($existingPackageVersions[$packageUpdateID][$packageVersion]);
370 $versionEditor->update(array(
371 'filename' => $packageFile,
372 'isAccessible' => ($versionData['isAccessible'] ?
1 : 0),
373 'isCritical' => ($versionData['isCritical'] ?
1 : 0),
374 'license' => (isset($versionData['license']['license']) ?
$versionData['license']['license'] : ''),
375 'licenseURL' => (isset($versionData['license']['license']) ?
$versionData['license']['licenseURL'] : ''),
376 'packageDate' => $versionData['packageDate']
380 // create new database entry
381 $version = PackageUpdateVersionEditor
::create(array(
382 'filename' => $packageFile,
383 'license' => (isset($versionData['license']['license']) ?
$versionData['license']['license'] : ''),
384 'licenseURL' => (isset($versionData['license']['license']) ?
$versionData['license']['licenseURL'] : ''),
385 'isAccessible' => ($versionData['isAccessible'] ?
1 : 0),
386 'isCritical' => ($versionData['isCritical'] ?
1 : 0),
387 'packageDate' => $versionData['packageDate'],
388 'packageUpdateID' => $packageUpdateID,
389 'packageVersion' => $packageVersion
392 $packageUpdateVersionID = $version->packageUpdateVersionID
;
395 // register requirement(s) of this update package version.
396 if (isset($versionData['requiredPackages'])) {
397 foreach ($versionData['requiredPackages'] as $requiredIdentifier => $required) {
398 $requirementInserts[] = array(
399 'packageUpdateVersionID' => $packageUpdateVersionID,
400 'package' => $requiredIdentifier,
401 'minversion' => (isset($required['minversion']) ?
$required['minversion'] : '')
406 // register optional packages of this update package version
407 if (isset($versionData['optionalPackages'])) {
408 foreach ($versionData['optionalPackages'] as $optionalPackage) {
409 $optionalInserts[] = array(
410 'packageUpdateVersionID' => $packageUpdateVersionID,
411 'package' => $optionalPackage
416 // register excluded packages of this update package version.
417 if (isset($versionData['excludedPackages'])) {
418 foreach ($versionData['excludedPackages'] as $excludedIdentifier => $exclusion) {
419 $excludedPackagesParameters[] = array(
420 'packageUpdateVersionID' => $packageUpdateVersionID,
421 'excludedPackage' => $excludedIdentifier,
422 'excludedPackageVersion' => (isset($exclusion['version']) ?
$exclusion['version'] : '')
427 // register fromversions of this update package version.
428 if (isset($versionData['fromversions'])) {
429 foreach ($versionData['fromversions'] as $fromversion) {
430 $fromversionInserts[] = array(
431 'packageUpdateVersionID' => $packageUpdateVersionID,
432 'fromversion' => $fromversion
440 // save requirements, excluded packages and fromversions
441 // use multiple inserts to save some queries
442 if (!empty($requirementInserts)) {
444 $sql = "DELETE pur FROM wcf".WCF_N
."_package_update_requirement pur
445 LEFT JOIN wcf".WCF_N
."_package_update_version puv
446 ON (puv.packageUpdateVersionID = pur.packageUpdateVersionID)
447 LEFT JOIN wcf".WCF_N
."_package_update pu
448 ON (pu.packageUpdateID = puv.packageUpdateID)
449 WHERE pu.packageUpdateServerID = ?";
450 $statement = WCF
::getDB()->prepareStatement($sql);
451 $statement->execute(array($packageUpdateServerID));
453 // insert requirements
454 $sql = "INSERT INTO wcf".WCF_N
."_package_update_requirement
455 (packageUpdateVersionID, package, minversion)
457 $statement = WCF
::getDB()->prepareStatement($sql);
458 WCF
::getDB()->beginTransaction();
459 foreach ($requirementInserts as $requirement) {
460 $statement->execute(array(
461 $requirement['packageUpdateVersionID'],
462 $requirement['package'],
463 $requirement['minversion']
466 WCF
::getDB()->commitTransaction();
469 if (!empty($optionalInserts)) {
471 $sql = "DELETE puo FROM wcf".WCF_N
."_package_update_optional puo
472 LEFT JOIN wcf".WCF_N
."_package_update_version puv
473 ON (puv.packageUpdateVersionID = puo.packageUpdateVersionID)
474 LEFT JOIN wcf".WCF_N
."_package_update pu
475 ON (pu.packageUpdateID = puv.packageUpdateID)
476 WHERE pu.packageUpdateServerID = ?";
477 $statement = WCF
::getDB()->prepareStatement($sql);
478 $statement->execute(array($packageUpdateServerID));
480 // insert requirements
481 $sql = "INSERT INTO wcf".WCF_N
."_package_update_optional
482 (packageUpdateVersionID, package)
484 $statement = WCF
::getDB()->prepareStatement($sql);
485 WCF
::getDB()->beginTransaction();
486 foreach ($optionalInserts as $requirement) {
487 $statement->execute(array(
488 $requirement['packageUpdateVersionID'],
489 $requirement['package']
492 WCF
::getDB()->commitTransaction();
495 if (!empty($excludedPackagesParameters)) {
497 $sql = "DELETE pue FROM wcf".WCF_N
."_package_update_exclusion pue
498 LEFT JOIN wcf".WCF_N
."_package_update_version puv
499 ON (puv.packageUpdateVersionID = pue.packageUpdateVersionID)
500 LEFT JOIN wcf".WCF_N
."_package_update pu
501 ON (pu.packageUpdateID = puv.packageUpdateID)
502 WHERE pu.packageUpdateServerID = ?";
503 $statement = WCF
::getDB()->prepareStatement($sql);
504 $statement->execute(array($packageUpdateServerID));
507 $sql = "INSERT INTO wcf".WCF_N
."_package_update_exclusion
508 (packageUpdateVersionID, excludedPackage, excludedPackageVersion)
510 $statement = WCF
::getDB()->prepareStatement($sql);
511 WCF
::getDB()->beginTransaction();
512 foreach ($excludedPackagesParameters as $excludedPackage) {
513 $statement->execute(array(
514 $excludedPackage['packageUpdateVersionID'],
515 $excludedPackage['excludedPackage'],
516 $excludedPackage['excludedPackageVersion']
519 WCF
::getDB()->commitTransaction();
522 if (!empty($fromversionInserts)) {
524 $sql = "DELETE puf FROM wcf".WCF_N
."_package_update_fromversion puf
525 LEFT JOIN wcf".WCF_N
."_package_update_version puv
526 ON (puv.packageUpdateVersionID = puf.packageUpdateVersionID)
527 LEFT JOIN wcf".WCF_N
."_package_update pu
528 ON (pu.packageUpdateID = puv.packageUpdateID)
529 WHERE pu.packageUpdateServerID = ?";
530 $statement = WCF
::getDB()->prepareStatement($sql);
531 $statement->execute(array($packageUpdateServerID));
534 $sql = "INSERT INTO wcf".WCF_N
."_package_update_fromversion
535 (packageUpdateVersionID, fromversion)
537 $statement = WCF
::getDB()->prepareStatement($sql);
538 WCF
::getDB()->beginTransaction();
539 foreach ($fromversionInserts as $fromversion) {
540 $statement->execute(array(
541 $fromversion['packageUpdateVersionID'],
542 $fromversion['fromversion']
545 WCF
::getDB()->commitTransaction();
550 * Returns a list of available updates for installed packages.
552 * @param boolean $removeRequirements
553 * @param boolean $removeOlderMinorReleases
556 public function getAvailableUpdates($removeRequirements = true, $removeOlderMinorReleases = false) {
559 // get update server data
560 $updateServers = PackageUpdateServer
::getActiveUpdateServers();
561 $packageUpdateServerIDs = array_keys($updateServers);
562 if (empty($packageUpdateServerIDs)) return $updates;
564 // get existing packages and their versions
565 $existingPackages = array();
566 $sql = "SELECT packageID, package, packageDescription, packageName,
567 packageVersion, packageDate, author, authorURL, isApplication
568 FROM wcf".WCF_N
."_package";
569 $statement = WCF
::getDB()->prepareStatement($sql);
570 $statement->execute();
571 while ($row = $statement->fetchArray()) {
572 $existingPackages[$row['package']][] = $row;
574 if (empty($existingPackages)) return $updates;
576 // get all update versions
577 $conditions = new PreparedStatementConditionBuilder();
578 $conditions->add("pu.packageUpdateServerID IN (?)", array($packageUpdateServerIDs));
579 $conditions->add("package IN (SELECT DISTINCT package FROM wcf".WCF_N
."_package)");
581 $sql = "SELECT pu.packageUpdateID, pu.packageUpdateServerID, pu.package,
582 puv.packageUpdateVersionID, puv.isCritical, puv.packageDate, puv.filename, puv.packageVersion
583 FROM wcf".WCF_N
."_package_update pu
584 LEFT JOIN wcf".WCF_N
."_package_update_version puv
585 ON (puv.packageUpdateID = pu.packageUpdateID AND puv.isAccessible = 1)
587 $statement = WCF
::getDB()->prepareStatement($sql);
588 $statement->execute($conditions->getParameters());
589 while ($row = $statement->fetchArray()) {
591 foreach ($existingPackages[$row['package']] as $existingVersion) {
592 if (Package
::compareVersion($existingVersion['packageVersion'], $row['packageVersion'], '<')) {
594 if (!isset($updates[$existingVersion['packageID']])) {
595 $existingVersion['versions'] = array();
596 $updates[$existingVersion['packageID']] = $existingVersion;
600 if (!isset($updates[$existingVersion['packageID']]['versions'][$row['packageVersion']])) {
601 $updates[$existingVersion['packageID']]['versions'][$row['packageVersion']] = array(
602 'isCritical' => $row['isCritical'],
603 'packageDate' => $row['packageDate'],
604 'packageVersion' => $row['packageVersion'],
610 $updates[$existingVersion['packageID']]['versions'][$row['packageVersion']]['servers'][] = array(
611 'packageUpdateID' => $row['packageUpdateID'],
612 'packageUpdateServerID' => $row['packageUpdateServerID'],
613 'packageUpdateVersionID' => $row['packageUpdateVersionID'],
614 'filename' => $row['filename']
620 // sort package versions
621 // and remove old versions
622 foreach ($updates as $packageID => $data) {
623 uksort($updates[$packageID]['versions'], array('wcf\data\package\Package', 'compareVersion'));
624 $updates[$packageID]['version'] = end($updates[$packageID]['versions']);
627 // remove requirements of application packages
628 if ($removeRequirements) {
629 foreach ($existingPackages as $identifier => $instances) {
630 foreach ($instances as $instance) {
631 if ($instance['isApplication'] && isset($updates[$instance['packageID']])) {
632 $updates = $this->removeUpdateRequirements($updates, $updates[$instance['packageID']]['version']['servers'][0]['packageUpdateVersionID']);
638 // remove older minor releases from list, e.g. only display 1.0.2, even if 1.0.1 is available
639 if ($removeOlderMinorReleases) {
640 foreach ($updates as &$updateData) {
641 $highestVersions = array();
642 foreach ($updateData['versions'] as $versionNumber => $dummy) {
643 if (preg_match('~^(\d+\.\d+)\.~', $versionNumber, $matches)) {
644 $major = $matches[1];
645 if (isset($highestVersions[$major])) {
646 if (version_compare($highestVersions[$major], $versionNumber, '<')) {
647 // version is newer, discard current version
648 unset($updateData['versions'][$highestVersions[$major]]);
649 $highestVersions[$major] = $versionNumber;
652 // version is lower, discard
653 unset($updateData['versions'][$versionNumber]);
657 $highestVersions[$major] = $versionNumber;
669 * Removes unnecessary updates of requirements from the list of available updates.
671 * @param array $updates
672 * @param integer $packageUpdateVersionID
673 * @return array $updates
675 protected function removeUpdateRequirements(array $updates, $packageUpdateVersionID) {
676 $sql = "SELECT pur.package, pur.minversion, p.packageID
677 FROM wcf".WCF_N
."_package_update_requirement pur
678 LEFT JOIN wcf".WCF_N
."_package p
679 ON (p.package = pur.package)
680 WHERE pur.packageUpdateVersionID = ?";
681 $statement = WCF
::getDB()->prepareStatement($sql);
682 $statement->execute(array($packageUpdateVersionID));
683 while ($row = $statement->fetchArray()) {
684 if (isset($updates[$row['packageID']])) {
685 $updates = $this->removeUpdateRequirements($updates, $updates[$row['packageID']]['version']['servers'][0]['packageUpdateVersionID']);
686 if (Package
::compareVersion($row['minversion'], $updates[$row['packageID']]['version']['packageVersion'], '>=')) {
687 unset($updates[$row['packageID']]);
696 * Creates a new package installation scheduler.
698 * @param array $selectedPackages
699 * @return \wcf\system\package\PackageInstallationScheduler
701 public function prepareInstallation(array $selectedPackages) {
702 return new PackageInstallationScheduler($selectedPackages);
706 * Gets package update versions of a package.
708 * @param string $package package identifier
709 * @param string $version package version
710 * @return array package update versions
712 public function getPackageUpdateVersions($package, $version = '') {
713 // get newest package version
714 if (empty($version)) {
715 $version = $this->getNewestPackageVersion($package);
720 $sql = "SELECT puv.*, pu.*, pus.loginUsername, pus.loginPassword
721 FROM wcf".WCF_N
."_package_update_version puv
722 LEFT JOIN wcf".WCF_N
."_package_update pu
723 ON (pu.packageUpdateID = puv.packageUpdateID)
724 LEFT JOIN wcf".WCF_N
."_package_update_server pus
725 ON (pus.packageUpdateServerID = pu.packageUpdateServerID)
727 AND puv.packageVersion = ?";
728 $statement = WCF
::getDB()->prepareStatement($sql);
729 $statement->execute(array(
733 while ($row = $statement->fetchArray()) {
737 if (empty($versions)) {
738 throw new SystemException("Can not find package '".$package."' in version '".$version."'");
745 * Returns the newest available version of a package.
747 * @param string $package package identifier
748 * @return string newest package version
750 public function getNewestPackageVersion($package) {
753 $sql = "SELECT packageVersion
754 FROM wcf".WCF_N
."_package_update_version
755 WHERE packageUpdateID IN (
756 SELECT packageUpdateID
757 FROM wcf".WCF_N
."_package_update
760 $statement = WCF
::getDB()->prepareStatement($sql);
761 $statement->execute(array($package));
762 while ($row = $statement->fetchArray()) {
763 $versions[$row['packageVersion']] = $row['packageVersion'];
766 // sort by version number
767 usort($versions, array('wcf\data\package\Package', 'compareVersion'));
769 // take newest (last)
770 return array_pop($versions);
774 * Stores the filename of a download in session.
776 * @param string $package package identifier
777 * @param string $version package version
778 * @param string $filename
780 public function cacheDownload($package, $version, $filename) {
781 $cachedDownloads = WCF
::getSession()->getVar('cachedPackageUpdateDownloads');
782 if (!is_array($cachedDownloads)) {
783 $cachedDownloads = array();
787 $cachedDownloads[$package.'@'.$version] = $filename;
788 WCF
::getSession()->register('cachedPackageUpdateDownloads', $cachedDownloads);