From 3536d2fe3861c5da467bbff4ff42efb24c9ad9c3 Mon Sep 17 00:00:00 2001 From: Alexander Ebert Date: Wed, 24 Apr 2013 17:05:37 +0200 Subject: [PATCH] Added package update management --- com.woltlab.wcf/acpMenu.xml | 12 - wcfsetup/install/files/acp/js/WCF.ACP.js | 226 +++++++++++- .../acp/templates/packageAutoUpdateList.tpl | 97 ----- .../files/acp/templates/packageList.tpl | 14 +- .../files/acp/templates/packageUpdate.tpl | 97 +++-- .../templates/packageUpdateUnauthorized.tpl | 46 +++ .../lib/acp/form/PackageUpdateForm.class.php | 140 -------- ....class.php => PackageUpdatePage.class.php} | 35 +- .../update/PackageUpdateAction.class.php | 126 +++++++ .../server/PackageUpdateServer.class.php | 57 ++- .../clipboard/ClipboardEditorItem.class.php | 2 +- .../exception/HTTPNotFoundException.class.php | 14 + .../HTTPServerErrorException.class.php | 14 + .../HTTPUnauthorizedException.class.php | 14 + .../exception/LoggedException.class.php | 1 + .../PackageInstallationScheduler.class.php | 333 +++++++++++++----- ...teAuthorizationRequiredException.class.php | 92 ----- .../package/PackageUpdateDispatcher.class.php | 10 +- ...ckageUpdateUnauthorizedException.class.php | 92 +++++ .../files/lib/util/HTTPRequest.class.php | 22 +- wcfsetup/install/lang/de.xml | 14 + wcfsetup/install/lang/en.xml | 14 + 22 files changed, 961 insertions(+), 511 deletions(-) delete mode 100644 wcfsetup/install/files/acp/templates/packageAutoUpdateList.tpl create mode 100644 wcfsetup/install/files/acp/templates/packageUpdateUnauthorized.tpl delete mode 100755 wcfsetup/install/files/lib/acp/form/PackageUpdateForm.class.php rename wcfsetup/install/files/lib/acp/page/{PackageAutoUpdateListPage.class.php => PackageUpdatePage.class.php} (69%) mode change 100755 => 100644 create mode 100644 wcfsetup/install/files/lib/system/exception/HTTPNotFoundException.class.php create mode 100644 wcfsetup/install/files/lib/system/exception/HTTPServerErrorException.class.php create mode 100644 wcfsetup/install/files/lib/system/exception/HTTPUnauthorizedException.class.php delete mode 100644 wcfsetup/install/files/lib/system/package/PackageUpdateAuthorizationRequiredException.class.php create mode 100644 wcfsetup/install/files/lib/system/package/PackageUpdateUnauthorizedException.class.php diff --git a/com.woltlab.wcf/acpMenu.xml b/com.woltlab.wcf/acpMenu.xml index db13987056..fb045ee251 100644 --- a/com.woltlab.wcf/acpMenu.xml +++ b/com.woltlab.wcf/acpMenu.xml @@ -56,18 +56,6 @@ 2 - - wcf.acp.menu.link.package - 2 - - - - - wcf.acp.menu.link.package.update - admin.system.package.canUpdatePackage - 1 - - wcf.acp.menu.link.package 2 diff --git a/wcfsetup/install/files/acp/js/WCF.ACP.js b/wcfsetup/install/files/acp/js/WCF.ACP.js index a181546983..7a6494d14f 100644 --- a/wcfsetup/install/files/acp/js/WCF.ACP.js +++ b/wcfsetup/install/files/acp/js/WCF.ACP.js @@ -189,7 +189,7 @@ WCF.ACP.Menu = Class.extend({ /** * Namespace for ACP package management. */ -WCF.ACP.Package = {}; +WCF.ACP.Package = { }; /** * Provides the package installation. @@ -868,6 +868,230 @@ WCF.ACP.Package.Search = Class.extend({ } }); +/** + * Namespace for package update related classes. + */ +WCF.ACP.Package.Update = { }; + +/** + * Handles the package update process. + */ +WCF.ACP.Package.Update.Manager = Class.extend({ + /** + * dialog overlay + * @var jQuery + */ + _dialog: null, + + /** + * action proxy + * @var WCF.Action.Proxy + */ + _proxy: null, + + /** + * submit button + * @var jQuery + */ + _submitButton: null, + + /** + * Initializes the WCF.ACP.Package.Update.Manager class. + */ + init: function() { + this._dialog = null; + this._submitButton = $('.formSubmit > button').click($.proxy(this._click, this)); + + this._proxy = new WCF.Action.Proxy({ + success: $.proxy(this._success, this) + }); + + $('.jsPackageUpdate').each($.proxy(function(index, packageUpdate) { + var $packageUpdate = $(packageUpdate); + $packageUpdate.find('input[type=checkbox]').data('packageUpdate', $packageUpdate).change($.proxy(this._change, this)); + }, this)); + }, + + /** + * Handles toggles for a specific update. + */ + _change: function(event) { + var $checkbox = $(event.currentTarget); + + if ($checkbox.is(':checked')) { + $checkbox.data('packageUpdate').find('select').enable(); + $checkbox.data('packageUpdate').find('dl').removeClass('disabled'); + + this._submitButton.enable(); + } + else { + $checkbox.data('packageUpdate').find('select').disable(); + $checkbox.data('packageUpdate').find('dl').addClass('disabled'); + + // disable submit button + if (!$('input[type=checkbox]:checked').length) { + this._submitButton.disable(); + } + } + }, + + /** + * Handles clicks on the submit button. + * + * @param object event + * @param integer packageUpdateServerID + */ + _click: function(event, packageUpdateServerID) { + var $packages = { }; + $('.jsPackageUpdate').each($.proxy(function(index, packageUpdate) { + var $packageUpdate = $(packageUpdate); + if ($packageUpdate.find('input[type=checkbox]:checked').length) { + $packages[$packageUpdate.data('package')] = $packageUpdate.find('select').val(); + } + }, this)); + + if ($.getLength($packages)) { + var $parameters = { + packages: $packages + }; + if (packageUpdateServerID) { + $parameters.authData = { + packageUpdateServerID: packageUpdateServerID, + password: $.trim($('#packageUpdateServerPassword').val()), + saveCredentials: ($('#packageUpdateServerSaveCredentials:checked').length ? true : false), + username: $.trim($('#packageUpdateServerUsername').val()) + } + } + + this._proxy.setOption('data', { + actionName: 'prepareUpdate', + className: 'wcf\\data\\package\\update\\PackageUpdateAction', + parameters: $parameters, + }); + this._proxy.sendRequest(); + } + }, + + /** + * Handles successful AJAX requests. + * + * @param object data + * @param string textStatus + * @param jQuery jqXHR + */ + _success: function(data, textStatus, jqXHR) { + if (data.returnValues.queueID) { + if (this._dialog !== null) { + this._dialog.wcfDialog('close'); + } + + var $installation = new WCF.ACP.Package.Installation(data.returnValues.queueID, undefined, false); + $installation.prepareInstallation(); + } + else if (data.returnValues.template) { + if (this._dialog === null) { + this._dialog = $('
' + data.returnValues.template + '
').hide().appendTo(document.body); + this._dialog.wcfDialog({ + title: WCF.Language.get('wcf.acp.package.update.unauthorized') + }); + } + else { + this._dialog.html(data.returnValues.template).wcfDialog('open'); + } + + this._dialog.find('.formSubmit > button').click($.proxy(this._submitAuthentication, this)); + } + }, + + /** + * Submits authentication data for current update server. + * + * @param object event + */ + _submitAuthentication: function(event) { + var $usernameField = $('#packageUpdateServerUsername'); + var $passwordField = $('#packageUpdateServerPassword'); + + // remove error messages if any + $usernameField.next('small.innerError').remove(); + $passwordField.next('small.innerError').remove(); + + var $continue = true; + if ($.trim($usernameField.val()) === '') { + $('' + WCF.Language.get('wcf.global.form.error.empty') + '').insertAfter($usernameField); + $continue = false; + } + + if ($.trim($passwordField.val()) === '') { + $('' + WCF.Language.get('wcf.global.form.error.empty') + '').insertAfter($passwordField); + $continue = false; + } + + if ($continue) { + this._click(undefined, $(event.currentTarget).data('packageUpdateServerID')) + } + } +}); + +/** + * Searches for available updates. + */ +WCF.ACP.Package.Update.Search = Class.extend({ + /** + * dialog overlay + * @var jQuery + */ + _dialog: null, + + /** + * initializes the WCF.ACP.Package.SearchForUpdates class. + */ + init: function() { + this._dialog = null; + + var $button = $('
  • ' + WCF.Language.get('wcf.acp.package.searchForUpdates') + '
  • '); + $button.click($.proxy(this._click, this)).prependTo($('.contentNavigation:eq(0) > nav > ul')); + }, + + /** + * Handles clicks on the search button. + */ + _click: function() { + if (this._dialog === null) { + new WCF.Action.Proxy({ + autoSend: true, + data: { + actionName: 'searchForUpdates', + className: 'wcf\\data\\package\\update\\PackageUpdateAction' + }, + success: $.proxy(this._success, this) + }); + } + else { + this._dialog.wcfDialog('open'); + } + }, + + /** + * Handles successful AJAX requests. + * + * @param object data + * @param string textStatus + * @param jQuery jqXHR + */ + _success: function(data, textStatus, jqXHR) { + if (data.returnValues.url) { + window.location = data.returnValues.url; + } + else { + this._dialog = $('
    ' + WCF.Language.get('wcf.acp.package.searchForUpdates.noResults') + '
    ').hide().appendTo(document.body); + this._dialog.wcfDialog({ + title: WCF.Language.get('wcf.acp.package.searchForUpdates') + }); + } + } +}); + /** * Handles option selection. */ diff --git a/wcfsetup/install/files/acp/templates/packageAutoUpdateList.tpl b/wcfsetup/install/files/acp/templates/packageAutoUpdateList.tpl deleted file mode 100644 index ca688c5ff4..0000000000 --- a/wcfsetup/install/files/acp/templates/packageAutoUpdateList.tpl +++ /dev/null @@ -1,97 +0,0 @@ -{include file='header' pageTitle='wcf.acp.packageUpdate'} - -{* - todo: - * update CSS classes - * add events -*} - - - -
    -
    -
    -

    {lang}wcf.acp.packageUpdate{/lang}

    - {if $availableUpdates|count} -

    - {/if} -
    -
    - - {if !$availableUpdates|count} -

    {lang}wcf.acp.packageUpdate.noneAvailable{/lang}

    - {else} - {foreach from=$availableUpdates item=availableUpdate} -
    -
    -
    -

    - -

    -
    - -
    -
    -
    -
    {$availableUpdate.packageVersion}
    -
    - -
    -
    -
    - -
    -
    - - {if $availableUpdate.author} -
    -
    -
    {if $availableUpdate.authorURL}{$availableUpdate.author}{else}{$availableUpdate.author}{/if}
    -
    - {/if} - - {if $availableUpdate.packageDescription} -
    -
    {lang}wcf.acp.package.description{/lang}
    -
    {$availableUpdate.packageDescription}
    -
    - {/if} -
    -
    -
    -
    - {/foreach} - -
    - -
    - {/if} -
    - -{include file='footer'} diff --git a/wcfsetup/install/files/acp/templates/packageList.tpl b/wcfsetup/install/files/acp/templates/packageList.tpl index 926410568c..7a5c8284d9 100644 --- a/wcfsetup/install/files/acp/templates/packageList.tpl +++ b/wcfsetup/install/files/acp/templates/packageList.tpl @@ -3,9 +3,19 @@ diff --git a/wcfsetup/install/files/acp/templates/packageUpdate.tpl b/wcfsetup/install/files/acp/templates/packageUpdate.tpl index 390b23ff34..fe88eb3356 100644 --- a/wcfsetup/install/files/acp/templates/packageUpdate.tpl +++ b/wcfsetup/install/files/acp/templates/packageUpdate.tpl @@ -1,61 +1,58 @@ {include file='header' pageTitle='wcf.acp.packageUpdate'} + +

    {lang}wcf.acp.packageUpdate{/lang}

    -{if $errorField == 'updates'} - {if $errorType === 'empty'} -

    {lang}wcf.acp.packageUpdate.noneSelected{/lang}

    - {else} -

    {lang}wcf.acp.packageUpdate.error{/lang} {$errorType->getMessage()} ({@$errorType->getCode()})

    - - {/if} -{/if} - -{if $errorField == 'excludedPackages'} -
    {lang}wcf.acp.packageUpdate.excludedPackages{/lang} -
      - {foreach from=$excludedPackages item=excludedPackage} -
    • {if $excludedPackage.conflict == 'existingPackageExcludesNewPackage'}{lang}wcf.acp.packageUpdate.excludedPackages.existingPackageExcludesNewPackage{/lang}{else}{lang}wcf.acp.packageUpdate.excludedPackages.newPackageExcludesExistingPackage{/lang}{/if}
    • - {/foreach} -
    -
    -{/if} - -{if $packageInstallationStack|count} -
    -
    -
    - {lang}wcf.acp.packageUpdate.updates{/lang} - -
      - {foreach from=$packageInstallationStack item=package} -
    • - {if $package.action == 'install'} - {lang}wcf.acp.packageUpdate.install{/lang} - {else} - {lang}wcf.acp.packageUpdate.update{/lang} - {/if} -
    • - {/foreach} -
    -
    +
    + {foreach from=$availableUpdates item=update} +
    + + {if $update[packageDescription]}{$update[packageDescription]|language}{/if} - {event name='fieldsets'} -
    - -
    - {if !$errorField}{/if} - {@SID_INPUT_TAG} - - {foreach from=$updates key=package item=version} - - {/foreach} -
    - -{/if} + +
    + {lang}wcf.acp.package.installedVersion{/lang} +
    +
    + {$update[packageVersion]} ({$update[packageDate]|date}) +
    + +
    +
    + {lang}wcf.acp.package.availableVersions{/lang} +
    +
    + +
    +
    + + {/foreach} +
    + +
    + +
    {include file='footer'} diff --git a/wcfsetup/install/files/acp/templates/packageUpdateUnauthorized.tpl b/wcfsetup/install/files/acp/templates/packageUpdateUnauthorized.tpl new file mode 100644 index 0000000000..cf6848f1c9 --- /dev/null +++ b/wcfsetup/install/files/acp/templates/packageUpdateUnauthorized.tpl @@ -0,0 +1,46 @@ +{assign var='serverAuthData' value=$updateServer->getAuthData()} +{assign var='serverReply' value=$request->getReply()} + +{if !$serverAuthData|empty} +

    {lang}wcf.acp.package.update.errorCode.{@$serverReply[statusCode]}{/lang}

    +{/if} + + + {lang}wcf.acp.package.update.server{/lang} + +
    +
    {lang}wcf.acp.package.name{/lang}
    +
    {$packageUpdateVersion[packageName]} ({$packageUpdateVersion[packageVersion]})
    +
    +
    +
    {lang}wcf.acp.package.update.server.url{/lang}
    +
    {@$updateServer->getHighlightedURL()}
    +
    + +
    +
    {lang}wcf.acp.package.update.server.message{/lang}
    +
    {$serverReply[body]}
    +
    + + +
    + {lang}wcf.acp.package.update.credentials{/lang} + +
    +
    +
    +
    + +
    +
    +
    +
    + +
    +
    +
    +
    + +
    + +
    \ No newline at end of file diff --git a/wcfsetup/install/files/lib/acp/form/PackageUpdateForm.class.php b/wcfsetup/install/files/lib/acp/form/PackageUpdateForm.class.php deleted file mode 100755 index e114c4e860..0000000000 --- a/wcfsetup/install/files/lib/acp/form/PackageUpdateForm.class.php +++ /dev/null @@ -1,140 +0,0 @@ - - * @package com.woltlab.wcf - * @subpackage acp.form - * @category Community Framework - */ -class PackageUpdateForm extends AbstractForm { - /** - * @see wcf\page\AbstractPage::$activeMenuItem - */ - public $activeMenuItem = 'wcf.acp.menu.link.package'; - - /** - * @see wcf\page\AbstractPage::$neededPermissions - */ - public $neededPermissions = array('admin.system.package.canUpdatePackage', 'admin.system.package.canInstallPackage'); - - /** - * list of packages to update - * @var array - */ - public $updates = array(); - - /** - * list with data of excluded packages - * @var array - */ - public $excludedPackages = array(); - - /** - * list with data of packages which will be installed - * @var array - */ - public $packageInstallationStack = array(); - - /** - * scheduler for package update - * @var wcf\system\package\PackageInstallationScheduler - */ - public $packageUpdate = null; - - /** - * @see wcf\page\IPage::readParameters() - */ - public function readParameters() { - parent::readParameters(); - - if (isset($_POST['updates']) && is_array($_POST['updates'])) $this->updates = $_POST['updates']; - } - - /** - * @see wcf\form\IForm::validate() - */ - public function validate() { - parent::validate(); - - if (empty($this->updates)) { - throw new UserInputException('updates'); - } - - // build update stack - $this->packageUpdate = PackageUpdateDispatcher::getInstance()->prepareInstallation($this->updates, array(), isset($_POST['send'])); - try { - $this->packageUpdate->buildPackageInstallationStack(); - $this->excludedPackages = $this->packageUpdate->getExcludedPackages(); - if (!empty($this->excludedPackages)) { - throw new UserInputException('excludedPackages'); - } - } - catch (SystemException $e) { - // show detailed error message - throw new UserInputException('updates', $e); - } - } - - /** - * @see wcf\form\IForm::save() - */ - public function save() { - if (isset($_POST['send'])) { - parent::save(); - - // save stack - $processNo = $this->packageUpdate->savePackageInstallationStack(); - $this->saved(); - - // open queue - PackageInstallationDispatcher::openQueue(0, $processNo); - } - } - - /** - * @see wcf\page\IPage::readData() - */ - public function readData() { - parent::readData(); - - // get installation stack - if ($this->packageInstallationStack !== null && $this->packageUpdate !== null) { - $this->packageInstallationStack = $this->packageUpdate->getPackageInstallationStack(); - } - } - - /** - * @see wcf\page\IPage::assignVariables() - */ - public function assignVariables() { - parent::assignVariables(); - - WCF::getTPL()->assign(array( - 'updates' => $this->updates, - 'packageInstallationStack' => $this->packageInstallationStack, - 'excludedPackages' => $this->excludedPackages - )); - } - - /** - * @see wcf\page\IPage::assignVariables() - */ - public function show() { - // check master password - WCFACP::checkMasterPassword(); - - parent::show(); - } -} diff --git a/wcfsetup/install/files/lib/acp/page/PackageAutoUpdateListPage.class.php b/wcfsetup/install/files/lib/acp/page/PackageUpdatePage.class.php old mode 100755 new mode 100644 similarity index 69% rename from wcfsetup/install/files/lib/acp/page/PackageAutoUpdateListPage.class.php rename to wcfsetup/install/files/lib/acp/page/PackageUpdatePage.class.php index 154385594e..23cd7c79dd --- a/wcfsetup/install/files/lib/acp/page/PackageAutoUpdateListPage.class.php +++ b/wcfsetup/install/files/lib/acp/page/PackageUpdatePage.class.php @@ -1,49 +1,46 @@ * @package com.woltlab.wcf * @subpackage acp.page * @category Community Framework */ -class PackageAutoUpdateListPage extends AbstractPage { +class PackageUpdatePage extends AbstractPage { /** * @see wcf\page\AbstractPage::$activeMenuItem */ - public $activeMenuItem = 'wcf.acp.menu.link.package.autoupdate'; + public $activeMenuItem = 'wcf.acp.menu.link.package'; /** - * @see wcf\page\AbstractPage::$neededPermissions + * list of available updates + * @var array */ - public $neededPermissions = array('admin.system.package.canUpdatePackage'); + public $availableUpdates = array(); /** - * list with data of available updates - * @var array + * @see wcf\page\AbstractPage::$neededPermissions */ - public $availableUpdates = array(); + public $neededPermissions = array('admin.system.package.canUpdatePackage'); /** - * @see wcf\page\IPage::assignVariables() + * @see wcf\page\IPage::readData() */ - public function readParameters() { - parent::readParameters(); + public function readData() { + parent::readData(); - if (empty($_POST)) { - // refresh package database - PackageUpdateDispatcher::getInstance()->refreshPackageDatabase(); - } - - // get updatable packages $this->availableUpdates = PackageUpdateDispatcher::getInstance()->getAvailableUpdates(); } @@ -59,7 +56,7 @@ class PackageAutoUpdateListPage extends AbstractPage { } /** - * @see wcf\page\IPage::show() + * @see wcf\page\IPage::assignVariables() */ public function show() { // check master password diff --git a/wcfsetup/install/files/lib/data/package/update/PackageUpdateAction.class.php b/wcfsetup/install/files/lib/data/package/update/PackageUpdateAction.class.php index 5aeb51e315..31a5b76bb9 100644 --- a/wcfsetup/install/files/lib/data/package/update/PackageUpdateAction.class.php +++ b/wcfsetup/install/files/lib/data/package/update/PackageUpdateAction.class.php @@ -1,11 +1,19 @@ WCF::getTPL()->fetch('packageSearchResultList') ); } + + /** + * Validates permissions to search for updates. + */ + public function validateSearchForUpdates() { + WCF::getSession()->checkPermissions(array('admin.system.package.canUpdatePackage')); + } + + /** + * Searches for updates. + * + * @return array + */ + public function searchForUpdates() { + PackageUpdateDispatcher::getInstance()->refreshPackageDatabase(); + + $updates = PackageUpdateDispatcher::getInstance()->getAvailableUpdates(); + $url = ''; + if (!empty($updates)) { + $url = LinkHandler::getInstance()->getLink('PackageUpdate'); + } + + return array( + 'url' => $url + ); + } + + /** + * Validates parameters to perform a system update. + */ + public function validatePrepareUpdate() { + WCF::getSession()->checkPermissions(array('admin.system.package.canUpdatePackage')); + + if (!isset($this->parameters['packages']) || !is_array($this->parameters['packages'])) { + throw new UserInputException('packages'); + } + + // validate packages for their existance + $availableUpdates = PackageUpdateDispatcher::getInstance()->getAvailableUpdates(); + foreach ($this->parameters['packages'] as $packageName => $versionNumber) { + $isValid = false; + + foreach ($availableUpdates as $package) { + if ($package['package'] == $packageName) { + // validate version + if (isset($package['versions'][$versionNumber])) { + $isValid = true; + break; + } + } + } + + if (!$isValid) { + throw new UserInputException('packages'); + } + } + + if (isset($this->parameters['authData'])) { + if (!is_array($this->parameters['authData'])) { + throw new UserInputException('authData'); + } + + $this->readInteger('packageUpdateServerID', false, 'authData'); + $this->readString('password', false, 'authData'); + $this->readString('username', false, 'authData'); + $this->readBoolean('saveCredentials', true, 'authData'); + } + } + + /** + * Prepares a system update. + * + * @return array + */ + public function prepareUpdate() { + if (isset($this->parameters['authData'])) { + PackageUpdateServer::storeAuthData($this->parameters['authData']['packageUpdateServerID'], $this->parameters['authData']['username'], $this->parameters['authData']['password'], $this->parameters['authData']['saveCredentials']); + } + + $scheduler = new PackageInstallationScheduler($this->parameters['packages']); + + try { + $scheduler->buildPackageInstallationStack(); + } + catch (PackageUpdateUnauthorizedException $e) { + return array( + 'template' => $e->getRenderedTemplate() + ); + } + + $stack = $scheduler->getPackageInstallationStack(); + $queueID = null; + if (!empty($stack)) { + $parentQueueID = 0; + $processNo = PackageInstallationQueue::getNewProcessNo(); + foreach ($stack as $package) { + $queue = PackageInstallationQueueEditor::create(array( + 'parentQueueID' => $parentQueueID, + 'processNo' => $processNo, + 'userID' => WCF::getUser()->userID, + 'package' => $package['package'], + 'packageName' => $package['packageName'], + 'packageID' => ($package['packageID'] ?: null), + 'archive' => $package['archive'], + 'action' => 'update' + )); + $parentQueueID = $queue->queueID; + + if ($queueID === null) { + $queueID = $queue->queueID; + } + } + } + + return array( + 'queueID' => $queueID + ); + } } diff --git a/wcfsetup/install/files/lib/data/package/update/server/PackageUpdateServer.class.php b/wcfsetup/install/files/lib/data/package/update/server/PackageUpdateServer.class.php index 8ef1fc1ecd..519487a318 100644 --- a/wcfsetup/install/files/lib/data/package/update/server/PackageUpdateServer.class.php +++ b/wcfsetup/install/files/lib/data/package/update/server/PackageUpdateServer.class.php @@ -1,13 +1,15 @@ * @package com.woltlab.wcf * @subpackage data.package.update.server @@ -78,11 +80,60 @@ class PackageUpdateServer extends DatabaseObject { } // session data - $packageUpdateAuthData = WCF::getSession()->getVar('packageUpdateAuthData'); + $packageUpdateAuthData = @unserialize(WCF::getSession()->getVar('packageUpdateAuthData')); if ($packageUpdateAuthData !== null && isset($packageUpdateAuthData[$this->packageUpdateServerID])) { $authData = $packageUpdateAuthData[$this->packageUpdateServerID]; } return $authData; } + + /** + * Stores auth data for a package update server. + * + * @param integer $packageUpdateServerID + * @param string $username + * @param string $password + * @param boolean $saveCredentials + */ + public static function storeAuthData($packageUpdateServerID, $username, $password, $saveCredentials = false) { + $packageUpdateAuthData = @unserialize(WCF::getSession()->getVar('packageUpdateAuthData')); + if ($packageUpdateAuthData === null || !is_array($packageUpdateAuthData)) { + $packageUpdateAuthData = array(); + } + + $packageUpdateAuthData[$packageUpdateServerID] = array( + 'username' => $username, + 'password' => $password + ); + + WCF::getSession()->register('packageUpdateAuthData', serialize($packageUpdateAuthData)); + + if ($saveCredentials) { + $serverAction = new PackageUpdateServerAction(array($packageUpdateServerID), 'update', array('data' => array( + 'loginUsername' => $username, + 'loginPassword' => $password + ))); + $serverAction->executeAction(); + } + } + + /** + * Returns true, if update server requires license data instead of username/password. + * + * @return integer + */ + public final function requiresLicense() { + return Regex::compile('^https?://update.woltlab.com/')->match($this->serverURL); + } + + /** + * Returns the highlighed server URL. + * + * @return string + */ + public function getHighlightedURL() { + $host = parse_url($this->serverURL, PHP_URL_HOST); + return StringUtil::replace($host, ''.$host.'', $this->serverURL); + } } diff --git a/wcfsetup/install/files/lib/system/clipboard/ClipboardEditorItem.class.php b/wcfsetup/install/files/lib/system/clipboard/ClipboardEditorItem.class.php index 261ee9943b..b37d5ed6e3 100644 --- a/wcfsetup/install/files/lib/system/clipboard/ClipboardEditorItem.class.php +++ b/wcfsetup/install/files/lib/system/clipboard/ClipboardEditorItem.class.php @@ -115,7 +115,7 @@ final class ClipboardEditorItem { * @param string $name */ public function setName($name) { - if (!preg_match('~^[a-zA-Z0-9\.]+$~', $name)) { + if (!preg_match('~^[a-zA-Z0-9\.-]+$~', $name)) { throw new SystemException("item name '".$name."' is invalid"); } diff --git a/wcfsetup/install/files/lib/system/exception/HTTPNotFoundException.class.php b/wcfsetup/install/files/lib/system/exception/HTTPNotFoundException.class.php new file mode 100644 index 0000000000..9b803bad9d --- /dev/null +++ b/wcfsetup/install/files/lib/system/exception/HTTPNotFoundException.class.php @@ -0,0 +1,14 @@ + + * @package com.woltlab.wcf + * @subpackage system.exception + * @category Community Framework + */ +class HTTPNotFoundException extends SystemException { } diff --git a/wcfsetup/install/files/lib/system/exception/HTTPServerErrorException.class.php b/wcfsetup/install/files/lib/system/exception/HTTPServerErrorException.class.php new file mode 100644 index 0000000000..5b8ad91a92 --- /dev/null +++ b/wcfsetup/install/files/lib/system/exception/HTTPServerErrorException.class.php @@ -0,0 +1,14 @@ + + * @package com.woltlab.wcf + * @subpackage system.exception + * @category Community Framework + */ +class HTTPServerErrorException extends SystemException { } diff --git a/wcfsetup/install/files/lib/system/exception/HTTPUnauthorizedException.class.php b/wcfsetup/install/files/lib/system/exception/HTTPUnauthorizedException.class.php new file mode 100644 index 0000000000..3b7c19fdc9 --- /dev/null +++ b/wcfsetup/install/files/lib/system/exception/HTTPUnauthorizedException.class.php @@ -0,0 +1,14 @@ + + * @package com.woltlab.wcf + * @subpackage system.exception + * @category Community Framework + */ +class HTTPUnauthorizedException extends SystemException { } diff --git a/wcfsetup/install/files/lib/system/exception/LoggedException.class.php b/wcfsetup/install/files/lib/system/exception/LoggedException.class.php index 732f65f271..21d1682de1 100644 --- a/wcfsetup/install/files/lib/system/exception/LoggedException.class.php +++ b/wcfsetup/install/files/lib/system/exception/LoggedException.class.php @@ -86,6 +86,7 @@ class LoggedException extends \Exception { 'WCF version: '.WCF_VERSION."\n". 'Request URI: '.(isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '')."\n". 'Referrer: '.(isset($_SERVER['HTTP_REFERER']) ? $_SERVER['HTTP_REFERER'] : '')."\n". + 'User-Agent: '.(isset($_SERVER['HTTP_USER_AGENT']) ? $_SERVER['HTTP_USER_AGENT'] : '')."\n". "Stacktrace: \n ".implode("\n ", explode("\n", $e->getTraceAsString()))."\n"; // calculate Exception-ID diff --git a/wcfsetup/install/files/lib/system/package/PackageInstallationScheduler.class.php b/wcfsetup/install/files/lib/system/package/PackageInstallationScheduler.class.php index c41add9e16..8c0dbc70e8 100644 --- a/wcfsetup/install/files/lib/system/package/PackageInstallationScheduler.class.php +++ b/wcfsetup/install/files/lib/system/package/PackageInstallationScheduler.class.php @@ -2,11 +2,14 @@ namespace wcf\system\package; use wcf\data\package\update\server\PackageUpdateServer; use wcf\data\package\Package; +use wcf\data\package\PackageCache; use wcf\system\database\util\PreparedStatementConditionBuilder; +use wcf\system\exception\HTTPUnauthorizedException; use wcf\system\exception\SystemException; use wcf\system\io\File; use wcf\system\WCF; use wcf\util\FileUtil; +use wcf\util\HTTPRequest; /** * Contains business logic related to preparation of package installations. @@ -20,22 +23,22 @@ use wcf\util\FileUtil; */ class PackageInstallationScheduler { /** - * list of packages to update or install + * stack of package installations / updates * @var array */ - protected $selectedPackages = array(); + protected $packageInstallationStack = array(); /** - * list of package update server ids - * @var array + * list of package update servers + * @var array */ - protected $packageUpdateServerIDs; + protected $packageUpdateServers = array(); /** - * enables downloading of updates - * @var boolean + * list of packages to update or install + * @var array */ - protected $download; + protected $selectedPackages = array(); /** * virtual package versions @@ -43,23 +46,14 @@ class PackageInstallationScheduler { */ protected $virtualPackageVersions = array(); - /** - * stack of package installations / updates - * @var array - */ - protected $packageInstallationStack = array(); - /** * Creates a new instance of PackageInstallationScheduler * - * @param array $selectedPackages - * @param array $packageUpdateServerIDs - * @param boolean $download + * @param array $selectedPackages */ - public function __construct(array $selectedPackages, array $packageUpdateServerIDs = array(), $download = true) { + public function __construct(array $selectedPackages) { $this->selectedPackages = $selectedPackages; - $this->packageUpdateServerIDs = $packageUpdateServerIDs; - $this->download = $download; + $this->packageUpdateServers = PackageUpdateServer::getActiveUpdateServers(); } /** @@ -67,12 +61,7 @@ class PackageInstallationScheduler { */ public function buildPackageInstallationStack() { foreach ($this->selectedPackages as $package => $version) { - if (is_numeric($package)) { - $this->updatePackage($package, $version); - } - else { - $this->tryToInstallPackage($package, $version, true); - } + $this->tryToInstallPackage($package, $version, true); } } @@ -101,8 +90,18 @@ class PackageInstallationScheduler { } } else { - // package is missing -> install - $this->installPackage($package, ($installOldVersion ? $minversion : '')); + // check if package is already installed + $packageID = PackageCache::getInstance()->getPackageID($package); + if ($packageID === null) { + // package is missing -> install + $this->installPackage($package, ($installOldVersion ? $minversion : '')); + } + else { + $package = PackageCache::getInstance()->getPackage($packageID); + if (!empty($minversion) && Package::compareVersion($package->packageVersion, $minversion, '<')) { + $this->updatePackage($packageID, ($installOldVersion ? $minversion : '')); + } + } } } @@ -121,10 +120,7 @@ class PackageInstallationScheduler { $this->resolveRequirements($packageUpdateVersions[0]['packageUpdateVersionID']); // download package - $download = ''; - if ($this->download) { - $download = $this->downloadPackage($package, $packageUpdateVersions); - } + $download = $this->downloadPackage($package, $packageUpdateVersions); // add to stack $data = array( @@ -183,7 +179,7 @@ class PackageInstallationScheduler { if (isset($installedPackages[$row['package']])) { // package already installed -> check version // sort multiple instances by version number - uasort($installedPackages[$row['package']], array('Package', 'compareVersion')); + uasort($installedPackages[$row['package']], array('wcf\data\package\Package', 'compareVersion')); foreach ($installedPackages[$row['package']] as $packageID => $packageVersion) { if (empty($row['minversion']) || Package::compareVersion($row['minversion'], $packageVersion, '<=')) { @@ -215,63 +211,55 @@ class PackageInstallationScheduler { } // download file - $authorizationRequiredException = array(); - $systemExceptions = array(); foreach ($packageUpdateVersions as $packageUpdateVersion) { + // get auth data + $authData = $this->getAuthData($packageUpdateVersion); + + // create request + $request = new HTTPRequest( + $this->packageUpdateServers[$packageUpdateVersion['packageUpdateServerID']]->serverURL, + (!empty($authData) ? array('auth' => $authData) : array()), + array( + 'packageName' => $packageUpdateVersion['package'], + 'packageVersion' => $packageUpdateVersion['packageVersion'] + ) + ); + try { - // get auth data - $authData = $this->getAuthData($packageUpdateVersion); - - // send request - // TODO: Use HTTPRequest - if (!empty($packageUpdateVersion['file'])) { - $response = PackageUpdateDispatcher::getInstance()->sendRequest($packageUpdateVersion['file'], array(), $authData); - } - else { - $response = PackageUpdateDispatcher::getInstance()->sendRequest($packageUpdateVersion['server'], array('packageName' => $packageUpdateVersion['package'], 'packageVersion' => $packageUpdateVersion['packageVersion']), $authData); - } - - // check response - // check http code - if ($response['httpStatusCode'] == 401) { - throw new PackageUpdateAuthorizationRequiredException($packageUpdateVersion['packageUpdateServerID'], (!empty($packageUpdateVersion['file']) ? $packageUpdateVersion['file'] : $packageUpdateVersion['server']), $response); - } - - if ($response['httpStatusCode'] != 200) { - throw new SystemException(WCF::getLanguage()->get('wcf.acp.packageUpdate.error.downloadFailed', array('$package' => $package)) . ' ('.$response['httpStatusLine'].')'); - } - - // write content to tmp file - $filename = FileUtil::getTemporaryFilename('package_'); - $file = new File($filename); - $file->write($response['content']); - $file->close(); - unset($response['content']); - - // test package - $archive = new PackageArchive($filename); - $archive->openArchive(); - $archive->getTar()->close(); - - // cache download in session - PackageUpdateDispatcher::getInstance()->cacheDownload($package, $packageUpdateVersion['packageVersion'], $filename); - - return $filename; + $request->execute(); } - catch (PackageUpdateAuthorizationRequiredException $e) { - $authorizationRequiredException[] = $e; + catch (HTTPUnauthorizedException $e) { + throw new PackageUpdateUnauthorizedException($request, $this->packageUpdateServers[$packageUpdateVersion['packageUpdateServerID']], $packageUpdateVersion); } - catch (SystemException $e) { - $systemExceptions[] = $e; + + $response = $request->getReply(); + + // check response + // 401 = missing/invalid auth data, 403 = valid auth data, but unaccessible + if ($response['statusCode'] == 401 || $response['statusCode'] == 403) { + throw new PackageUpdateAuthorizationRequiredException($packageUpdateVersion['packageUpdateServerID'], (!empty($packageUpdateVersion['file']) ? $packageUpdateVersion['file'] : $packageUpdateVersion['server']), $response); } - } - - if (!empty($authorizationRequiredException)) { - throw array_shift($authorizationRequiredException); - } - - if (!empty($systemExceptions)) { - throw array_shift($systemExceptions); + + if ($response['statusCode'] != 200) { + throw new SystemException(WCF::getLanguage()->get('wcf.acp.packageUpdate.error.downloadFailed', array('$package' => $package)) . ' ('.$response['body'].')'); + } + + // write content to tmp file + $filename = FileUtil::getTemporaryFilename('package_'); + $file = new File($filename); + $file->write($response['body']); + $file->close(); + unset($response['body']); + + // test package + $archive = new PackageArchive($filename); + $archive->openArchive(); + $archive->getTar()->close(); + + // cache download in session + PackageUpdateDispatcher::getInstance()->cacheDownload($package, $packageUpdateVersion['packageVersion'], $filename); + + return $filename; } return false; @@ -428,6 +416,181 @@ class PackageInstallationScheduler { return $this->packageInstallationStack; } + /** + * Updates an existing package. + * + * @param integer $packageID + * @param string $version + */ + protected function updatePackage($packageID, $version) { + // get package info + $package = PackageCache::getInstance()->getPackage($packageID); + + // get current package version + $packageVersion = $package->packageVersion; + if (isset($this->virtualPackageVersions[$packageID])) { + $packageVersion = $this->virtualPackageVersions[$packageID]; + // check virtual package version + if (Package::compareVersion($packageVersion, $version, '>=')) { + // virtual package version is greater than requested version + // skip package update + return; + } + } + + // get highest version of the required major release + if (preg_match('/(\d+\.\d+\.)/', $version, $match)) { + $packageVersions = array(); + $sql = "SELECT DISTINCT packageVersion + FROM wcf".WCF_N."_package_update_version + WHERE packageUpdateID IN ( + SELECT packageUpdateID + FROM wcf".WCF_N."_package_update + WHERE package = ? + ) + AND packageVersion LIKE ?"; + $statement = WCF::getDB()->prepareStatement($sql); + $statement->execute(array( + $package->package, + $match[1].'%' + )); + while ($row = $statement->fetchArray()) { + $packageVersions[] = $row['packageVersion']; + } + + if (count($packageVersions) > 1) { + // sort by version number + usort($packageVersions, array('wcf\data\package\Package', 'compareVersion')); + + // get highest version + $version = array_pop($packageVersions); + } + } + + // get all fromversion + $fromversions = array(); + $sql = "SELECT puv.packageVersion, puf.fromversion + FROM wcf".WCF_N."_package_update_fromversion puf + LEFT JOIN wcf".WCF_N."_package_update_version puv + ON (puv.packageUpdateVersionID = puf.packageUpdateVersionID) + WHERE puf.packageUpdateVersionID IN ( + SELECT packageUpdateVersionID + FROM wcf".WCF_N."_package_update_version + WHERE packageUpdateID IN ( + SELECT packageUpdateID + FROM wcf".WCF_N."_package_update + WHERE package = ? + ) + )"; + $statement = WCF::getDB()->prepareStatement($sql); + $statement->execute(array($package->package)); + while ($row = $statement->fetchArray()) { + if (!isset($fromversions[$row['packageVersion']])) $fromversions[$row['packageVersion']] = array(); + $fromversions[$row['packageVersion']][$row['fromversion']] = $row['fromversion']; + } + + // sort by version number + uksort($fromversions, array('wcf\data\package\Package', 'compareVersion')); + + // find shortest update thread + $updateThread = $this->findShortestUpdateThread($package->package, $fromversions, $packageVersion, $version); + + // process update thread + foreach ($updateThread as $fromversion => $toVersion) { + $packageUpdateVersions = PackageUpdateDispatcher::getInstance()->getPackageUpdateVersions($package->package, $toVersion); + + // resolve requirements + $this->resolveRequirements($packageUpdateVersions[0]['packageUpdateVersionID']); + + // download package + $download = $this->downloadPackage($package->package, $packageUpdateVersions); + + // add to stack + $this->packageInstallationStack[] = array( + 'packageName' => $package->getName(), + 'fromversion' => $fromversion, + 'toVersion' => $toVersion, + 'package' => $package->package, + 'packageID' => $packageID, + 'archive' => $download, + 'action' => 'update' + ); + + // update virtual versions + $this->virtualPackageVersions[$packageID] = $toVersion; + } + } + + /** + * 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); + } + + /** + * Compares the length of two updates threads. + * + * @param array $updateThreadListA + * @param array $updateThreadListB + * @return integer + */ + protected function compareUpdateThreadLists($updateThreadListA, $updateThreadListB) { + $countA = count($updateThreadListA); + $countB = count($updateThreadListB); + + if ($countA < $countB) return -1; + if ($countA > $countB) return 1; + + return 0; + } + /** * Gets the filename of in session stored donwloads. * diff --git a/wcfsetup/install/files/lib/system/package/PackageUpdateAuthorizationRequiredException.class.php b/wcfsetup/install/files/lib/system/package/PackageUpdateAuthorizationRequiredException.class.php deleted file mode 100644 index 8e2771eda2..0000000000 --- a/wcfsetup/install/files/lib/system/package/PackageUpdateAuthorizationRequiredException.class.php +++ /dev/null @@ -1,92 +0,0 @@ - - * @package com.woltlab.wcf - * @subpackage system.package - * @category Community Framework - */ -class PackageUpdateAuthorizationRequiredException extends UserException { - /** - * id of the package update server that requires authorization - * @var integer - */ - protected $packageUpdateServerID = 0; - - /** - * url of the requested package update - * @var string - */ - protected $url = ''; - - /** - * package update sever response data - * @var array - */ - protected $response = array(); - - /** - * Creates a new PackageUpdateAuthorizationRequiredException object. - * - * @param integer $packageUpdateServerID - * @param string $url - * @param array $response - */ - public function __construct($packageUpdateServerID, $url, array $response) { - $this->packageUpdateServerID = $packageUpdateServerID; - $this->url = $url; - $this->response = $response; - } - - /** - * Shows the package update authentification form. - */ - public function show() { - new PackageUpdateAuthForm($this); - exit; - } - - /** - * Returns the package update server id. - * - * @return integer - */ - public function getPackageUpdateServerID() { - return $this->packageUpdateServerID; - } - - /** - * Returns the server url. - * - * @return string - */ - public function getURL() { - return $this->url; - } - - /** - * Returns the response header. - * - * @return string - */ - public function getResponseHeader() { - return $this->response['headers']; - } - - /** - * Returns the response content. - * - * @return string - */ - public function getResponseContent() { - return $this->response['body']; - } -} diff --git a/wcfsetup/install/files/lib/system/package/PackageUpdateDispatcher.class.php b/wcfsetup/install/files/lib/system/package/PackageUpdateDispatcher.class.php index ed661c08c8..ac522467a5 100644 --- a/wcfsetup/install/files/lib/system/package/PackageUpdateDispatcher.class.php +++ b/wcfsetup/install/files/lib/system/package/PackageUpdateDispatcher.class.php @@ -525,7 +525,7 @@ class PackageUpdateDispatcher extends SingletonFactory { // get existing packages and their versions $existingPackages = array(); - $sql = "SELECT packageID, package, packageDescription, + $sql = "SELECT packageID, package, packageDescription, packageName, packageVersion, packageDate, author, authorURL, isApplication FROM wcf".WCF_N."_package"; $statement = WCF::getDB()->prepareStatement($sql); @@ -544,7 +544,7 @@ class PackageUpdateDispatcher extends SingletonFactory { puv.packageUpdateVersionID, puv.isCritical, puv.packageDate, puv.filename, puv.packageVersion FROM wcf".WCF_N."_package_update pu LEFT JOIN wcf".WCF_N."_package_update_version puv - ON (puv.packageUpdateID = pu.packageUpdateID) + ON (puv.packageUpdateID = pu.packageUpdateID AND puv.isAccessible = 1) ".$conditions; $statement = WCF::getDB()->prepareStatement($sql); $statement->execute($conditions->getParameters()); @@ -631,12 +631,10 @@ class PackageUpdateDispatcher extends SingletonFactory { * Creates a new package installation scheduler. * * @param array $selectedPackages - * @param array $packageUpdateServerIDs - * @param boolean $download * @return wcf\system\package\PackageInstallationScheduler */ - public function prepareInstallation(array $selectedPackages, array $packageUpdateServerIDs = array(), $download = true) { - return new PackageInstallationScheduler($selectedPackages, $packageUpdateServerIDs, $download); + public function prepareInstallation(array $selectedPackages) { + return new PackageInstallationScheduler($selectedPackages); } /** diff --git a/wcfsetup/install/files/lib/system/package/PackageUpdateUnauthorizedException.class.php b/wcfsetup/install/files/lib/system/package/PackageUpdateUnauthorizedException.class.php new file mode 100644 index 0000000000..d6fbbeddb1 --- /dev/null +++ b/wcfsetup/install/files/lib/system/package/PackageUpdateUnauthorizedException.class.php @@ -0,0 +1,92 @@ + + * @package com.woltlab.wcf + * @subpackage system.package + * @category Community Framework + */ +class PackageUpdateUnauthorizedException extends UserException { + /** + * package update version + * @var array + */ + protected $packageUpdateVersion = array(); + + /** + * HTTP request object + * @var wcf\util\HTTPRequest + */ + protected $request = null; + + /** + * package update server object + * @var wcf\data\package\update\server\PackageUpdateServer + */ + protected $updateServer = null; + + /** + * Creates a new PackageUpdateUnauthorizedException object. + * + * @param wcf\util\HTTPRequest $request + * @param wcf\data\package\update\server\PackageUpdateServer $updateServer + * @param array $packageUpdateVersion + */ + public function __construct(HTTPRequest $request, PackageUpdateServer $updateServer, array $packageUpdateVersion) { + $this->request = $request; + $this->updateServer = $updateServer; + $this->packageUpdateVersion = $packageUpdateVersion; + } + + /** + * Returns the rendered template. + * + * @return string + */ + public function getRenderedTemplate() { + WCF::getTPL()->assign(array( + 'packageUpdateVersion' => $this->packageUpdateVersion, + 'request' => $this->request, + 'updateServer' => $this->updateServer + )); + + return WCF::getTPL()->fetch('packageUpdateUnauthorized'); + } + + /** + * Returns package update version. + * + * @return array + */ + public function getPackageUpdateVersion() { + return $this->packageUpdateVersion; + } + + /** + * Returns the HTTP request object. + * + * @return wcf\util\HTTPRequest + */ + public function getRequest() { + return $this->request; + } + + /** + * Returns package update server object. + * + * @return wcf\data\package\update\server\PackageUpdateServer + */ + public function getUpdateServer() { + return $this->updateServer; + } +} diff --git a/wcfsetup/install/files/lib/util/HTTPRequest.class.php b/wcfsetup/install/files/lib/util/HTTPRequest.class.php index f7c527ce7c..aa39de56c6 100644 --- a/wcfsetup/install/files/lib/util/HTTPRequest.class.php +++ b/wcfsetup/install/files/lib/util/HTTPRequest.class.php @@ -1,5 +1,8 @@ * @package com.woltlab.wcf * @subpackage util @@ -240,8 +243,21 @@ final class HTTPRequest { // we are fine break; + case '401': + case '403': + throw new HTTPUnauthorizedException("Received status code '".$this->statusCode."' from server"); + break; + + case '404': + throw new HTTPNotFoundException("Received status code '404' from server"); + break; + + case '500': + throw new HTTPServerErrorException("Received status code '500' from server"); + break; + default: - throw new SystemException("Got status '".$this->statusCode."' and I don't know how to handle it"); + throw new SystemException("Received unhandled status code '".$this->statusCode."' from server"); break; } } diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index 6459ee9baf..b65240ffec 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -452,6 +452,8 @@ + + @@ -463,6 +465,18 @@ getName()}“ wirklich deinstallieren?]]> + + requiresLicense()}Lizenz- und Seriennummer{else}Benutzername und Passwort{/if}.]]> + + + + + + + + + + diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index 12976ee25b..f9cc7a16c3 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -452,6 +452,8 @@ + + @@ -463,6 +465,18 @@ getName()}”?]]> + + requiresLicense()}license and serial number{else}username and password{/if}.]]> + + + + + + + + + + -- 2.20.1