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 $errorMessage = reset($reply['httpHeaders']);
62 $updateServerEditor = new PackageUpdateServerEditor($updateServer);
63 $updateServerEditor->update(array(
64 'status' => 'offline',
65 'errorMessage' => $errorMessage
71 if ($refreshedPackageLists) {
72 PackageUpdateCacheBuilder
::getInstance()->reset();
77 * Gets the package_update.xml from an update server.
79 * @param \wcf\data\package\update\server\PackageUpdateServer $updateServer
81 protected function getPackageUpdateXML(PackageUpdateServer
$updateServer) {
82 $authData = $updateServer->getAuthData();
84 if ($authData) $settings['auth'] = $authData;
87 'lastUpdateTime' => $updateServer->lastUpdateTime
90 // append auth code if set and update server resolves to woltlab.com
91 if (PACKAGE_SERVER_AUTH_CODE
&& Regex
::compile('^https?://[a-z]+.woltlab.com/')->match($updateServer->serverURL
)) {
92 $postData['authCode'] = PACKAGE_SERVER_AUTH_CODE
;
95 $request = new HTTPRequest($updateServer->serverURL
, $settings, $postData);
99 $reply = $request->getReply();
101 catch (HTTPUnauthorizedException
$e) {
102 throw new PackageUpdateUnauthorizedException($request, $updateServer);
104 catch (SystemException
$e) {
105 $reply = $request->getReply();
107 $statusCode = (is_array($reply['statusCode'])) ?
reset($reply['statusCode']) : $reply['statusCode'];
108 throw new SystemException(WCF
::getLanguage()->get('wcf.acp.package.update.error.listNotFound') . ' ('.$statusCode.')');
111 // parse given package update xml
112 $allNewPackages = $this->parsePackageUpdateXML($reply['body']);
113 unset($request, $reply);
116 if (!empty($allNewPackages)) {
117 $this->savePackageUpdates($allNewPackages, $updateServer->packageUpdateServerID
);
119 unset($allNewPackages);
121 // update server status
122 $updateServerEditor = new PackageUpdateServerEditor($updateServer);
123 $updateServerEditor->update(array(
124 'lastUpdateTime' => TIME_NOW
,
125 'status' => 'online',
131 * Parses a stream containing info from a packages_update.xml.
133 * @param string $content
134 * @return array $allNewPackages
136 protected function parsePackageUpdateXML($content) {
139 $xml->loadXML('packageUpdateServer.xml', $content);
140 $xpath = $xml->xpath();
142 // loop through <package> tags inside the <section> tag.
143 $allNewPackages = array();
144 $packages = $xpath->query('/ns:section/ns:package');
145 foreach ($packages as $package) {
146 if (!Package
::isValidPackageName($package->getAttribute('name'))) {
147 throw new SystemException("'".$package->getAttribute('name')."' is not a valid package name.");
150 $allNewPackages[$package->getAttribute('name')] = $this->parsePackageUpdateXMLBlock($xpath, $package);
153 return $allNewPackages;
157 * Parses the xml stucture from a packages_update.xml.
159 * @param \DOMXPath $xpath
160 * @param \DOMNode $package
162 protected function parsePackageUpdateXMLBlock(\DOMXPath
$xpath, \DOMNode
$package) {
163 // define default values
164 $packageInfo = array(
167 'isApplication' => 0,
168 'packageDescription' => '',
169 'versions' => array()
172 // parse package information
173 $elements = $xpath->query('./ns:packageinformation/*', $package);
174 foreach ($elements as $element) {
175 switch ($element->tagName
) {
177 $packageInfo['packageName'] = $element->nodeValue
;
180 case 'packagedescription':
181 $packageInfo['packageDescription'] = $element->nodeValue
;
184 case 'isapplication':
185 $packageInfo['isApplication'] = intval($element->nodeValue
);
190 // parse author information
191 $elements = $xpath->query('./ns:authorinformation/*', $package);
192 foreach ($elements as $element) {
193 switch ($element->tagName
) {
195 $packageInfo['author'] = $element->nodeValue
;
199 $packageInfo['authorURL'] = $element->nodeValue
;
205 $elements = $xpath->query('./ns:versions/ns:version', $package);
206 foreach ($elements as $element) {
207 $versionNo = $element->getAttribute('name');
208 $packageInfo['versions'][$versionNo] = array(
209 'isAccessible' => ($element->getAttribute('accessible') == 'true' ?
true : false),
210 'isCritical' => ($element->getAttribute('critical') == 'true' ?
true : false)
213 $children = $xpath->query('child::*', $element);
214 foreach ($children as $child) {
215 switch ($child->tagName
) {
217 $fromversions = $xpath->query('child::*', $child);
218 foreach ($fromversions as $fromversion) {
219 $packageInfo['versions'][$versionNo]['fromversions'][] = $fromversion->nodeValue
;
224 $packageInfo['versions'][$versionNo]['packageDate'] = $child->nodeValue
;
228 $packageInfo['versions'][$versionNo]['file'] = $child->nodeValue
;
231 case 'requiredpackages':
232 $requiredPackages = $xpath->query('child::*', $child);
233 foreach ($requiredPackages as $requiredPackage) {
234 $minVersion = $requiredPackage->getAttribute('minversion');
235 $required = $requiredPackage->nodeValue
;
237 $packageInfo['versions'][$versionNo]['requiredPackages'][$required] = array();
238 if (!empty($minVersion)) {
239 $packageInfo['versions'][$versionNo]['requiredPackages'][$required]['minversion'] = $minVersion;
244 case 'optionalpackages':
245 $packageInfo['versions'][$versionNo]['optionalPackages'] = array();
247 $optionalPackages = $xpath->query('child::*', $child);
248 foreach ($optionalPackages as $optionalPackage) {
249 $packageInfo['versions'][$versionNo]['optionalPackages'][] = $optionalPackage->nodeValue
;
253 case 'excludedpackages':
254 $excludedpackages = $xpath->query('child::*', $child);
255 foreach ($excludedpackages as $excludedPackage) {
256 $exclusion = $excludedPackage->nodeValue
;
257 $version = $excludedPackage->getAttribute('version');
259 $packageInfo['versions'][$versionNo]['excludedPackages'][$exclusion] = array();
260 if (!empty($version)) {
261 $packageInfo['versions'][$versionNo]['excludedPackages'][$exclusion]['version'] = $version;
267 $packageInfo['versions'][$versionNo]['license'] = array(
268 'license' => $child->nodeValue
,
269 'licenseURL' => ($child->hasAttribute('url') ?
$child->getAttribute('url') : '')
280 * Updates information parsed from a packages_update.xml into the database.
282 * @param array $allNewPackages
283 * @param integer $packageUpdateServerID
285 protected function savePackageUpdates(array &$allNewPackages, $packageUpdateServerID) {
286 // find existing packages and delete them
287 // get existing packages
288 $existingPackages = array();
289 $packageUpdateList = new PackageUpdateList();
290 $packageUpdateList->getConditionBuilder()->add("package_update.packageUpdateServerID = ? AND package_update.package IN (?)", array($packageUpdateServerID, array_keys($allNewPackages)));
291 $packageUpdateList->readObjects();
292 $tmp = $packageUpdateList->getObjects();
294 foreach ($tmp as $packageUpdate) {
295 $existingPackages[$packageUpdate->package
] = $packageUpdate;
298 // get existing versions
299 $existingPackageVersions = array();
300 if (!empty($existingPackages)) {
301 // get package update ids
302 $packageUpdateIDs = array();
303 foreach ($existingPackages as $packageUpdate) {
304 $packageUpdateIDs[] = $packageUpdate->packageUpdateID
;
308 $versionList = new PackageUpdateVersionList();
309 $versionList->getConditionBuilder()->add("package_update_version.packageUpdateID IN (?)", array($packageUpdateIDs));
310 $versionList->readObjects();
311 $tmp = $versionList->getObjects();
313 foreach ($tmp as $version) {
314 if (!isset($existingPackageVersions[$version->packageUpdateID
])) $existingPackageVersions[$version->packageUpdateID
] = array();
315 $existingPackageVersions[$version->packageUpdateID
][$version->packageVersion
] = $version;
320 $excludedPackagesParameters = $fromversionParameters = $insertParameters = $optionalInserts = $requirementInserts = array();
321 foreach ($allNewPackages as $identifier => $packageData) {
322 if (isset($existingPackages[$identifier])) {
323 $packageUpdateID = $existingPackages[$identifier]->packageUpdateID
;
325 // update database entry
326 $packageUpdateEditor = new PackageUpdateEditor($existingPackages[$identifier]);
327 $packageUpdateEditor->update(array(
328 'packageName' => $packageData['packageName'],
329 'packageDescription' => $packageData['packageDescription'],
330 'author' => $packageData['author'],
331 'authorURL' => $packageData['authorURL'],
332 'isApplication' => $packageData['isApplication']
336 // create new database entry
337 $packageUpdate = PackageUpdateEditor
::create(array(
338 'packageUpdateServerID' => $packageUpdateServerID,
339 'package' => $identifier,
340 'packageName' => $packageData['packageName'],
341 'packageDescription' => $packageData['packageDescription'],
342 'author' => $packageData['author'],
343 'authorURL' => $packageData['authorURL'],
344 'isApplication' => $packageData['isApplication']
347 $packageUpdateID = $packageUpdate->packageUpdateID
;
350 // register version(s) of this update package.
351 if (isset($packageData['versions'])) {
352 foreach ($packageData['versions'] as $packageVersion => $versionData) {
353 if (isset($versionData['file'])) $packageFile = $versionData['file'];
354 else $packageFile = '';
356 if (isset($existingPackageVersions[$packageUpdateID]) && isset($existingPackageVersions[$packageUpdateID][$packageVersion])) {
357 $packageUpdateVersionID = $existingPackageVersions[$packageUpdateID][$packageVersion]->packageUpdateVersionID
;
359 // update database entry
360 $versionEditor = new PackageUpdateVersionEditor($existingPackageVersions[$packageUpdateID][$packageVersion]);
361 $versionEditor->update(array(
362 'filename' => $packageFile,
363 'isAccessible' => ($versionData['isAccessible'] ?
1 : 0),
364 'isCritical' => ($versionData['isCritical'] ?
1 : 0),
365 'license' => (isset($versionData['license']['license']) ?
$versionData['license']['license'] : ''),
366 'licenseURL' => (isset($versionData['license']['license']) ?
$versionData['license']['licenseURL'] : ''),
367 'packageDate' => $versionData['packageDate']
371 // create new database entry
372 $version = PackageUpdateVersionEditor
::create(array(
373 'filename' => $packageFile,
374 'license' => (isset($versionData['license']['license']) ?
$versionData['license']['license'] : ''),
375 'licenseURL' => (isset($versionData['license']['license']) ?
$versionData['license']['licenseURL'] : ''),
376 'isAccessible' => ($versionData['isAccessible'] ?
1 : 0),
377 'isCritical' => ($versionData['isCritical'] ?
1 : 0),
378 'packageDate' => $versionData['packageDate'],
379 'packageUpdateID' => $packageUpdateID,
380 'packageVersion' => $packageVersion
383 $packageUpdateVersionID = $version->packageUpdateVersionID
;
386 // register requirement(s) of this update package version.
387 if (isset($versionData['requiredPackages'])) {
388 foreach ($versionData['requiredPackages'] as $requiredIdentifier => $required) {
389 $requirementInserts[] = array(
390 'packageUpdateVersionID' => $packageUpdateVersionID,
391 'package' => $requiredIdentifier,
392 'minversion' => (isset($required['minversion']) ?
$required['minversion'] : '')
397 // register optional packages of this update package version
398 if (isset($versionData['optionalPackages'])) {
399 foreach ($versionData['optionalPackages'] as $optionalPackage) {
400 $optionalInserts[] = array(
401 'packageUpdateVersionID' => $packageUpdateVersionID,
402 'package' => $optionalPackage
407 // register excluded packages of this update package version.
408 if (isset($versionData['excludedPackages'])) {
409 foreach ($versionData['excludedPackages'] as $excludedIdentifier => $exclusion) {
410 $excludedPackagesParameters[] = array(
411 'packageUpdateVersionID' => $packageUpdateVersionID,
412 'excludedPackage' => $excludedIdentifier,
413 'excludedPackageVersion' => (isset($exclusion['version']) ?
$exclusion['version'] : '')
418 // register fromversions of this update package version.
419 if (isset($versionData['fromversions'])) {
420 foreach ($versionData['fromversions'] as $fromversion) {
421 $fromversionInserts[] = array(
422 'packageUpdateVersionID' => $packageUpdateVersionID,
423 'fromversion' => $fromversion
431 // save requirements, excluded packages and fromversions
432 // use multiple inserts to save some queries
433 if (!empty($requirementInserts)) {
435 $sql = "DELETE pur FROM wcf".WCF_N
."_package_update_requirement pur
436 LEFT JOIN wcf".WCF_N
."_package_update_version puv
437 ON (puv.packageUpdateVersionID = pur.packageUpdateVersionID)
438 LEFT JOIN wcf".WCF_N
."_package_update pu
439 ON (pu.packageUpdateID = puv.packageUpdateID)
440 WHERE pu.packageUpdateServerID = ?";
441 $statement = WCF
::getDB()->prepareStatement($sql);
442 $statement->execute(array($packageUpdateServerID));
444 // insert requirements
445 $sql = "INSERT INTO wcf".WCF_N
."_package_update_requirement
446 (packageUpdateVersionID, package, minversion)
448 $statement = WCF
::getDB()->prepareStatement($sql);
449 WCF
::getDB()->beginTransaction();
450 foreach ($requirementInserts as $requirement) {
451 $statement->execute(array(
452 $requirement['packageUpdateVersionID'],
453 $requirement['package'],
454 $requirement['minversion']
457 WCF
::getDB()->commitTransaction();
460 if (!empty($optionalInserts)) {
462 $sql = "DELETE puo FROM wcf".WCF_N
."_package_update_optional puo
463 LEFT JOIN wcf".WCF_N
."_package_update_version puv
464 ON (puv.packageUpdateVersionID = puo.packageUpdateVersionID)
465 LEFT JOIN wcf".WCF_N
."_package_update pu
466 ON (pu.packageUpdateID = puv.packageUpdateID)
467 WHERE pu.packageUpdateServerID = ?";
468 $statement = WCF
::getDB()->prepareStatement($sql);
469 $statement->execute(array($packageUpdateServerID));
471 // insert requirements
472 $sql = "INSERT INTO wcf".WCF_N
."_package_update_optional
473 (packageUpdateVersionID, package)
475 $statement = WCF
::getDB()->prepareStatement($sql);
476 WCF
::getDB()->beginTransaction();
477 foreach ($optionalInserts as $requirement) {
478 $statement->execute(array(
479 $requirement['packageUpdateVersionID'],
480 $requirement['package']
483 WCF
::getDB()->commitTransaction();
486 if (!empty($excludedPackagesParameters)) {
488 $sql = "DELETE pue FROM wcf".WCF_N
."_package_update_exclusion pue
489 LEFT JOIN wcf".WCF_N
."_package_update_version puv
490 ON (puv.packageUpdateVersionID = pue.packageUpdateVersionID)
491 LEFT JOIN wcf".WCF_N
."_package_update pu
492 ON (pu.packageUpdateID = puv.packageUpdateID)
493 WHERE pu.packageUpdateServerID = ?";
494 $statement = WCF
::getDB()->prepareStatement($sql);
495 $statement->execute(array($packageUpdateServerID));
498 $sql = "INSERT INTO wcf".WCF_N
."_package_update_exclusion
499 (packageUpdateVersionID, excludedPackage, excludedPackageVersion)
501 $statement = WCF
::getDB()->prepareStatement($sql);
502 WCF
::getDB()->beginTransaction();
503 foreach ($excludedPackagesParameters as $excludedPackage) {
504 $statement->execute(array(
505 $excludedPackage['packageUpdateVersionID'],
506 $excludedPackage['excludedPackage'],
507 $excludedPackage['excludedPackageVersion']
510 WCF
::getDB()->commitTransaction();
513 if (!empty($fromversionInserts)) {
515 $sql = "DELETE puf FROM wcf".WCF_N
."_package_update_fromversion puf
516 LEFT JOIN wcf".WCF_N
."_package_update_version puv
517 ON (puv.packageUpdateVersionID = puf.packageUpdateVersionID)
518 LEFT JOIN wcf".WCF_N
."_package_update pu
519 ON (pu.packageUpdateID = puv.packageUpdateID)
520 WHERE pu.packageUpdateServerID = ?";
521 $statement = WCF
::getDB()->prepareStatement($sql);
522 $statement->execute(array($packageUpdateServerID));
525 $sql = "INSERT INTO wcf".WCF_N
."_package_update_fromversion
526 (packageUpdateVersionID, fromversion)
528 $statement = WCF
::getDB()->prepareStatement($sql);
529 WCF
::getDB()->beginTransaction();
530 foreach ($fromversionInserts as $fromversion) {
531 $statement->execute(array(
532 $fromversion['packageUpdateVersionID'],
533 $fromversion['fromversion']
536 WCF
::getDB()->commitTransaction();
541 * Returns a list of available updates for installed packages.
543 * @param boolean $removeRequirements
544 * @param boolean $removeOlderMinorReleases
547 public function getAvailableUpdates($removeRequirements = true, $removeOlderMinorReleases = false) {
550 // get update server data
551 $updateServers = PackageUpdateServer
::getActiveUpdateServers();
552 $packageUpdateServerIDs = array_keys($updateServers);
553 if (empty($packageUpdateServerIDs)) return $updates;
555 // get existing packages and their versions
556 $existingPackages = array();
557 $sql = "SELECT packageID, package, packageDescription, packageName,
558 packageVersion, packageDate, author, authorURL, isApplication
559 FROM wcf".WCF_N
."_package";
560 $statement = WCF
::getDB()->prepareStatement($sql);
561 $statement->execute();
562 while ($row = $statement->fetchArray()) {
563 $existingPackages[$row['package']][] = $row;
565 if (empty($existingPackages)) return $updates;
567 // get all update versions
568 $conditions = new PreparedStatementConditionBuilder();
569 $conditions->add("pu.packageUpdateServerID IN (?)", array($packageUpdateServerIDs));
570 $conditions->add("package IN (SELECT DISTINCT package FROM wcf".WCF_N
."_package)");
572 $sql = "SELECT pu.packageUpdateID, pu.packageUpdateServerID, pu.package,
573 puv.packageUpdateVersionID, puv.isCritical, puv.packageDate, puv.filename, puv.packageVersion
574 FROM wcf".WCF_N
."_package_update pu
575 LEFT JOIN wcf".WCF_N
."_package_update_version puv
576 ON (puv.packageUpdateID = pu.packageUpdateID AND puv.isAccessible = 1)
578 $statement = WCF
::getDB()->prepareStatement($sql);
579 $statement->execute($conditions->getParameters());
580 while ($row = $statement->fetchArray()) {
582 foreach ($existingPackages[$row['package']] as $existingVersion) {
583 if (Package
::compareVersion($existingVersion['packageVersion'], $row['packageVersion'], '<')) {
585 if (!isset($updates[$existingVersion['packageID']])) {
586 $existingVersion['versions'] = array();
587 $updates[$existingVersion['packageID']] = $existingVersion;
591 if (!isset($updates[$existingVersion['packageID']]['versions'][$row['packageVersion']])) {
592 $updates[$existingVersion['packageID']]['versions'][$row['packageVersion']] = array(
593 'isCritical' => $row['isCritical'],
594 'packageDate' => $row['packageDate'],
595 'packageVersion' => $row['packageVersion'],
601 $updates[$existingVersion['packageID']]['versions'][$row['packageVersion']]['servers'][] = array(
602 'packageUpdateID' => $row['packageUpdateID'],
603 'packageUpdateServerID' => $row['packageUpdateServerID'],
604 'packageUpdateVersionID' => $row['packageUpdateVersionID'],
605 'filename' => $row['filename']
611 // sort package versions
612 // and remove old versions
613 foreach ($updates as $packageID => $data) {
614 uksort($updates[$packageID]['versions'], array('wcf\data\package\Package', 'compareVersion'));
615 $updates[$packageID]['version'] = end($updates[$packageID]['versions']);
618 // remove requirements of application packages
619 if ($removeRequirements) {
620 foreach ($existingPackages as $identifier => $instances) {
621 foreach ($instances as $instance) {
622 if ($instance['isApplication'] && isset($updates[$instance['packageID']])) {
623 $updates = $this->removeUpdateRequirements($updates, $updates[$instance['packageID']]['version']['servers'][0]['packageUpdateVersionID']);
629 // remove older minor releases from list, e.g. only display 1.0.2, even if 1.0.1 is available
630 if ($removeOlderMinorReleases) {
631 foreach ($updates as &$updateData) {
632 $highestVersions = array();
633 foreach ($updateData['versions'] as $versionNumber => $dummy) {
634 if (preg_match('~^(\d+\.\d+)\.~', $versionNumber, $matches)) {
635 $major = $matches[1];
636 if (isset($highestVersions[$major])) {
637 if (version_compare($highestVersions[$major], $versionNumber, '<')) {
638 // version is newer, discard current version
639 unset($updateData['versions'][$highestVersions[$major]]);
640 $highestVersions[$major] = $versionNumber;
643 // version is lower, discard
644 unset($updateData['versions'][$versionNumber]);
648 $highestVersions[$major] = $versionNumber;
660 * Removes unnecessary updates of requirements from the list of available updates.
662 * @param array $updates
663 * @param integer $packageUpdateVersionID
664 * @return array $updates
666 protected function removeUpdateRequirements(array $updates, $packageUpdateVersionID) {
667 $sql = "SELECT pur.package, pur.minversion, p.packageID
668 FROM wcf".WCF_N
."_package_update_requirement pur
669 LEFT JOIN wcf".WCF_N
."_package p
670 ON (p.package = pur.package)
671 WHERE pur.packageUpdateVersionID = ?";
672 $statement = WCF
::getDB()->prepareStatement($sql);
673 $statement->execute(array($packageUpdateVersionID));
674 while ($row = $statement->fetchArray()) {
675 if (isset($updates[$row['packageID']])) {
676 $updates = $this->removeUpdateRequirements($updates, $updates[$row['packageID']]['version']['servers'][0]['packageUpdateVersionID']);
677 if (Package
::compareVersion($row['minversion'], $updates[$row['packageID']]['version']['packageVersion'], '>=')) {
678 unset($updates[$row['packageID']]);
687 * Creates a new package installation scheduler.
689 * @param array $selectedPackages
690 * @return \wcf\system\package\PackageInstallationScheduler
692 public function prepareInstallation(array $selectedPackages) {
693 return new PackageInstallationScheduler($selectedPackages);
697 * Gets package update versions of a package.
699 * @param string $package package identifier
700 * @param string $version package version
701 * @return array package update versions
703 public function getPackageUpdateVersions($package, $version = '') {
704 // get newest package version
705 if (empty($version)) {
706 $version = $this->getNewestPackageVersion($package);
711 $sql = "SELECT puv.*, pu.*, pus.loginUsername, pus.loginPassword
712 FROM wcf".WCF_N
."_package_update_version puv
713 LEFT JOIN wcf".WCF_N
."_package_update pu
714 ON (pu.packageUpdateID = puv.packageUpdateID)
715 LEFT JOIN wcf".WCF_N
."_package_update_server pus
716 ON (pus.packageUpdateServerID = pu.packageUpdateServerID)
718 AND puv.packageVersion = ?";
719 $statement = WCF
::getDB()->prepareStatement($sql);
720 $statement->execute(array(
724 while ($row = $statement->fetchArray()) {
728 if (empty($versions)) {
729 throw new SystemException("Can not find package '".$package."' in version '".$version."'");
736 * Returns the newest available version of a package.
738 * @param string $package package identifier
739 * @return string newest package version
741 public function getNewestPackageVersion($package) {
744 $sql = "SELECT packageVersion
745 FROM wcf".WCF_N
."_package_update_version
746 WHERE packageUpdateID IN (
747 SELECT packageUpdateID
748 FROM wcf".WCF_N
."_package_update
751 $statement = WCF
::getDB()->prepareStatement($sql);
752 $statement->execute(array($package));
753 while ($row = $statement->fetchArray()) {
754 $versions[$row['packageVersion']] = $row['packageVersion'];
757 // sort by version number
758 usort($versions, array('wcf\data\package\Package', 'compareVersion'));
760 // take newest (last)
761 return array_pop($versions);
765 * Stores the filename of a download in session.
767 * @param string $package package identifier
768 * @param string $version package version
769 * @param string $filename
771 public function cacheDownload($package, $version, $filename) {
772 $cachedDownloads = WCF
::getSession()->getVar('cachedPackageUpdateDownloads');
773 if (!is_array($cachedDownloads)) {
774 $cachedDownloads = array();
778 $cachedDownloads[$package.'@'.$version] = $filename;
779 WCF
::getSession()->register('cachedPackageUpdateDownloads', $cachedDownloads);