From: Alexander Ebert Date: Tue, 20 Nov 2012 08:37:41 +0000 (+0100) Subject: AJAX error handling overhaul X-Git-Tag: 2.0.0_Beta_1~762^2~4 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=3631f7bd0af6cc0019c75ae7f2f283e4aae15c24;p=GitHub%2FWoltLab%2FWCF.git AJAX error handling overhaul --- diff --git a/wcfsetup/install/files/lib/action/AJAXInvokeAction.class.php b/wcfsetup/install/files/lib/action/AJAXInvokeAction.class.php index 8dfafb0c45..c23a9769a8 100644 --- a/wcfsetup/install/files/lib/action/AJAXInvokeAction.class.php +++ b/wcfsetup/install/files/lib/action/AJAXInvokeAction.class.php @@ -5,6 +5,7 @@ use wcf\system\exception\IllegalLinkException; use wcf\system\exception\PermissionDeniedException; use wcf\system\exception\SystemException; use wcf\system\exception\UserInputException; +use wcf\system\exception\ValidateActionException; use wcf\system\WCF; use wcf\util\ClassUtil; use wcf\util\JSON; @@ -147,7 +148,18 @@ class AJAXInvokeAction extends AbstractSecureAction { throw new AJAXException($e->getMessage(), AJAXException::INTERNAL_ERROR, $e->__getTraceAsString()); } else if ($e instanceof UserInputException) { - throw new AJAXException($e->getMessage(), AJAXException::BAD_PARAMETERS, $e->getTraceAsString()); + // repackage as ValidationActionException + $exception = new ValidateActionException($e->getField(), $e->getType(), $e->getVariables()); + throw new AJAXException($exception->getMessage(), AJAXException::BAD_PARAMETERS, $e->getTraceAsString(), array( + 'errorMessage' => $exception->getMessage(), + 'fieldName' => $exception->getFieldName() + )); + } + else if ($e instanceof ValidateActionException) { + throw new AJAXException($exception->getMessage(), AJAXException::BAD_PARAMETERS, $e->getTraceAsString(), array( + 'errorMessage' => $exception->getMessage(), + 'fieldName' => $exception->getFieldName() + )); } else { throw new AJAXException($e->getMessage(), AJAXException::INTERNAL_ERROR, $e->getTraceAsString()); diff --git a/wcfsetup/install/files/lib/data/AbstractDatabaseObjectAction.class.php b/wcfsetup/install/files/lib/data/AbstractDatabaseObjectAction.class.php index 9b17792eb1..41b1f2dba4 100644 --- a/wcfsetup/install/files/lib/data/AbstractDatabaseObjectAction.class.php +++ b/wcfsetup/install/files/lib/data/AbstractDatabaseObjectAction.class.php @@ -1,10 +1,10 @@ userID && !in_array($this->getActionName(), $this->allowGuestAccess)) { - throw new ValidateActionException("Please login before executing this action"); + throw new IllegalLinkException(); } // validate action name if (!method_exists($this, $this->getActionName())) { - throw new ValidateActionException("unknown action '".$this->getActionName()."'"); + throw new SystemException("unknown action '".$this->getActionName()."'"); } $actionName = 'validate'.StringUtil::firstCharToUpperCase($this->getActionName()); if (!method_exists($this, $actionName)) { - throw new ValidateActionException("validation of action '".$this->getActionName()."' failed"); + throw new PermissionDeniedException(); } // execute action @@ -219,15 +219,10 @@ abstract class AbstractDatabaseObjectAction implements IDatabaseObjectAction, ID public function validateCreate() { // validate permissions if (is_array($this->permissionsCreate) && !empty($this->permissionsCreate)) { - try { - WCF::getSession()->checkPermissions($this->permissionsCreate); - } - catch (PermissionDeniedException $e) { - throw new ValidateActionException('Insufficient permissions'); - } + WCF::getSession()->checkPermissions($this->permissionsCreate); } else { - throw new ValidateActionException('Insufficient permissions'); + throw new PermissionDeniedException(); } } @@ -237,15 +232,10 @@ abstract class AbstractDatabaseObjectAction implements IDatabaseObjectAction, ID public function validateDelete() { // validate permissions if (is_array($this->permissionsDelete) && !empty($this->permissionsDelete)) { - try { - WCF::getSession()->checkPermissions($this->permissionsDelete); - } - catch (PermissionDeniedException $e) { - throw new ValidateActionException('Insufficient permissions'); - } + WCF::getSession()->checkPermissions($this->permissionsDelete); } else { - throw new ValidateActionException('Insufficient permissions'); + throw new PermissionDeniedException(); } // read objects @@ -253,7 +243,7 @@ abstract class AbstractDatabaseObjectAction implements IDatabaseObjectAction, ID $this->readObjects(); if (empty($this->objects)) { - throw new ValidateActionException('Invalid object id'); + throw new UserInputException('objectIDs'); } } } @@ -264,15 +254,10 @@ abstract class AbstractDatabaseObjectAction implements IDatabaseObjectAction, ID public function validateUpdate() { // validate permissions if (is_array($this->permissionsUpdate) && !empty($this->permissionsUpdate)) { - try { - WCF::getSession()->checkPermissions($this->permissionsUpdate); - } - catch (PermissionDeniedException $e) { - throw new ValidateActionException('Insufficient permissions'); - } + WCF::getSession()->checkPermissions($this->permissionsUpdate); } else { - throw new ValidateActionException('Insufficient permissions'); + throw new PermissionDeniedException(); } // read objects @@ -280,7 +265,7 @@ abstract class AbstractDatabaseObjectAction implements IDatabaseObjectAction, ID $this->readObjects(); if (empty($this->objects)) { - throw new ValidateActionException('Invalid object id'); + throw new UserInputException('objectIDs'); } } } diff --git a/wcfsetup/install/files/lib/data/category/CategoryAction.class.php b/wcfsetup/install/files/lib/data/category/CategoryAction.class.php index f415d68302..2356e08011 100644 --- a/wcfsetup/install/files/lib/data/category/CategoryAction.class.php +++ b/wcfsetup/install/files/lib/data/category/CategoryAction.class.php @@ -7,7 +7,7 @@ use wcf\data\IToggleAction; use wcf\system\category\CategoryHandler; use wcf\system\exception\PermissionDeniedException; use wcf\system\exception\SystemException; -use wcf\system\exception\ValidateActionException; +use wcf\system\exception\UserInputException; use wcf\system\user\collapsible\content\UserCollapsibleContentHandler; use wcf\system\WCF; @@ -102,24 +102,19 @@ class CategoryAction extends AbstractDatabaseObjectAction implements ICollapsibl public function validateCreate() { // validate permissions if (!empty($this->permissionsCreate)) { - try { - WCF::getSession()->checkPermissions($this->permissionsCreate); - } - catch (PermissionDeniedException $e) { - throw new ValidateActionException('Insufficient permissions'); - } + WCF::getSession()->checkPermissions($this->permissionsCreate); } if (!isset($this->parameters['data']['objectTypeID'])) { - throw new ValidateActionException("Missing 'objectTypeID' data parameter"); + throw new UserInputException('objectTypeID'); } $objectType = CategoryHandler::getInstance()->getObjectType($this->parameters['data']['objectTypeID']); if ($objectType === null) { - throw new ValidateActionException("Unknown category object type with id '".$this->parameters['data']['objectTypeID']."'"); + throw new UserInputException('objectTypeID', 'notValid'); } if (!$objectType->getProcessor()->canAddCategory()) { - throw new ValidateActionException('Insufficient permissions'); + throw new PermissionDeniedException(); } } @@ -133,7 +128,7 @@ class CategoryAction extends AbstractDatabaseObjectAction implements ICollapsibl WCF::getSession()->checkPermissions($this->permissionsDelete); } catch (PermissionDeniedException $e) { - throw new ValidateActionException('Insufficient permissions'); + throw new PermissionDeniedException(); } } @@ -142,13 +137,13 @@ class CategoryAction extends AbstractDatabaseObjectAction implements ICollapsibl $this->readObjects(); if (empty($this->objects)) { - throw new ValidateActionException('Invalid object id'); + throw new UserInputException('objectIDs'); } } foreach ($this->objects as $categoryEditor) { if (!$categoryEditor->getCategoryType()->canDeleteCategory()) { - throw new ValidateActionException('Insufficient permissions'); + throw new PermissionDeniedException(); } } } @@ -173,26 +168,21 @@ class CategoryAction extends AbstractDatabaseObjectAction implements ICollapsibl public function validateUpdate() { // validate permissions if (!empty($this->permissionsUpdate)) { - try { - WCF::getSession()->checkPermissions($this->permissionsUpdate); - } - catch (PermissionDeniedException $e) { - throw new ValidateActionException('Insufficient permissions'); - } + WCF::getSession()->checkPermissions($this->permissionsUpdate); } // read objects if (empty($this->objects)) { $this->readObjects(); - } - - if (empty($this->objects)) { - throw new ValidateActionException('Invalid object id'); + + if (empty($this->objects)) { + throw new UserInputException('objectIDs'); + } } foreach ($this->objects as $categoryEditor) { if (!$categoryEditor->getCategoryType()->canEditCategory()) { - throw new ValidateActionException('Insufficient permissions'); + throw new PermissionDeniedException(); } } } @@ -207,16 +197,13 @@ class CategoryAction extends AbstractDatabaseObjectAction implements ICollapsibl WCF::getSession()->checkPermissions($this->permissionsUpdate); } catch (PermissionDeniedException $e) { - throw new ValidateActionException('Insufficient permissions'); + throw new PermissionDeniedException(); } } // validate 'structure' parameter - if (!isset($this->parameters['data']['structure'])) { - throw new ValidateActionException("Missing 'structure' parameter"); - } - if (!is_array($this->parameters['data']['structure'])) { - throw new ValidateActionException("'structure' parameter is no array"); + if (!isset($this->parameters['data']['structure']) || !is_array($this->parameters['data']['structure'])) { + throw new UserInputException('structure'); } // validate given category ids @@ -225,14 +212,14 @@ class CategoryAction extends AbstractDatabaseObjectAction implements ICollapsibl // validate category $category = CategoryHandler::getInstance()->getCategory($parentCategoryID); if ($category === null) { - throw new ValidateActionException("Unknown category with id '".$parentCategoryID."'"); + throw new UserInputException('structure'); } $this->objects[$category->categoryID] = new $this->className($category); // validate permissions if (!$category->getCategoryType()->canEditCategory()) { - throw new ValidateActionException('Insufficient permissions'); + throw new PermissionDeniedException(); } } @@ -240,14 +227,14 @@ class CategoryAction extends AbstractDatabaseObjectAction implements ICollapsibl // validate category $category = CategoryHandler::getInstance()->getCategory($categoryID); if ($category === null) { - throw new ValidateActionException("Unknown category with id '".$categoryID."'"); + throw new UserInputException('structure'); } $this->objects[$category->categoryID] = new $this->className($category); // validate permissions if (!$category->getCategoryType()->canEditCategory()) { - throw new ValidateActionException('Insufficient permissions'); + throw new PermissionDeniedException(); } } } diff --git a/wcfsetup/install/files/lib/data/cronjob/CronjobAction.class.php b/wcfsetup/install/files/lib/data/cronjob/CronjobAction.class.php index 2f4ae937a1..2dde106414 100644 --- a/wcfsetup/install/files/lib/data/cronjob/CronjobAction.class.php +++ b/wcfsetup/install/files/lib/data/cronjob/CronjobAction.class.php @@ -4,7 +4,7 @@ use wcf\data\cronjob\log\CronjobLogEditor; use wcf\data\AbstractDatabaseObjectAction; use wcf\data\IToggleAction; use wcf\system\cronjob\CronjobScheduler; -use wcf\system\exception\ValidateActionException; +use wcf\system\exception\PermissionDeniedException; use wcf\system\WCF; use wcf\util\DateUtil; @@ -52,7 +52,7 @@ class CronjobAction extends AbstractDatabaseObjectAction implements IToggleActio foreach ($this->objects as $cronjob) { if (!$cronjob->isDeletable()) { - throw new ValidateActionException('Insufficient permissions'); + throw new PermissionDeniedException(); } } } @@ -65,7 +65,7 @@ class CronjobAction extends AbstractDatabaseObjectAction implements IToggleActio foreach ($this->objects as $cronjob) { if (!$cronjob->isEditable()) { - throw new ValidateActionException('Insufficient permissions'); + throw new PermissionDeniedException(); } } } @@ -78,7 +78,7 @@ class CronjobAction extends AbstractDatabaseObjectAction implements IToggleActio foreach ($this->objects as $cronjob) { if (!$cronjob->canBeDisabled()) { - throw new ValidateActionException('Insufficient permissions'); + throw new PermissionDeniedException(); } } } diff --git a/wcfsetup/install/files/lib/data/language/LanguageAction.class.php b/wcfsetup/install/files/lib/data/language/LanguageAction.class.php index 16cea56843..fc7c8ac030 100644 --- a/wcfsetup/install/files/lib/data/language/LanguageAction.class.php +++ b/wcfsetup/install/files/lib/data/language/LanguageAction.class.php @@ -2,7 +2,7 @@ namespace wcf\data\language; use wcf\data\AbstractDatabaseObjectAction; use wcf\system\exception\PermissionDeniedException; -use wcf\system\exception\ValidateActionException; +use wcf\system\exception\UserInputException; use wcf\system\WCF; /** @@ -40,19 +40,14 @@ class LanguageAction extends AbstractDatabaseObjectAction { * Validates permission to set a language as default. */ public function validateSetAsDefault() { - try { - WCF::getSession()->checkPermissions($this->permissionsUpdate); - } - catch (PermissionDeniedException $e) { - throw new ValidateActionException('Insufficient permissions'); - } + WCF::getSession()->checkPermissions($this->permissionsUpdate); // read objects if (empty($this->objects)) { $this->readObjects(); if (empty($this->objects)) { - throw new ValidateActionException('Invalid object id'); + throw new UserInputException('objectIDs'); } } } diff --git a/wcfsetup/install/files/lib/data/package/PackageAction.class.php b/wcfsetup/install/files/lib/data/package/PackageAction.class.php index cf8aa691c3..e98c2a3d38 100644 --- a/wcfsetup/install/files/lib/data/package/PackageAction.class.php +++ b/wcfsetup/install/files/lib/data/package/PackageAction.class.php @@ -1,7 +1,7 @@ parameters['activePage']) || !intval($this->parameters['activePage'])) { - throw new ValidateActionException("Missing or invalid parameter 'activePage'"); + throw new UserInputException('activePage'); } } diff --git a/wcfsetup/install/files/lib/data/package/installation/queue/PackageInstallationQueueAction.class.php b/wcfsetup/install/files/lib/data/package/installation/queue/PackageInstallationQueueAction.class.php index 15d91fa59f..4676a49147 100644 --- a/wcfsetup/install/files/lib/data/package/installation/queue/PackageInstallationQueueAction.class.php +++ b/wcfsetup/install/files/lib/data/package/installation/queue/PackageInstallationQueueAction.class.php @@ -2,7 +2,7 @@ namespace wcf\data\package\installation\queue; use wcf\data\package\Package; use wcf\data\AbstractDatabaseObjectAction; -use wcf\system\exception\ValidateActionException; +use wcf\system\exception\UserInputException; use wcf\system\WCF; /** @@ -41,11 +41,11 @@ class PackageInstallationQueueAction extends AbstractDatabaseObjectAction { $this->package = new Package($this->packageID); if (!$this->package->packageID) { - throw new ValidateActionException('Invalid package id'); + throw new UserInputException('packageID'); } if (!isset($this->parameters['action']) || !in_array($this->parameters['action'], array('install', 'update', 'uninstall', 'rollback'))) { - throw new ValidateActionException('Invalid or missing action'); + throw new UserInputException('action'); } } diff --git a/wcfsetup/install/files/lib/data/user/UserAction.class.php b/wcfsetup/install/files/lib/data/user/UserAction.class.php index ffcb44d5ec..a4bc4118c1 100644 --- a/wcfsetup/install/files/lib/data/user/UserAction.class.php +++ b/wcfsetup/install/files/lib/data/user/UserAction.class.php @@ -7,7 +7,7 @@ use wcf\data\ISearchAction; use wcf\system\clipboard\ClipboardHandler; use wcf\system\database\util\PreparedStatementConditionBuilder; use wcf\system\exception\PermissionDeniedException; -use wcf\system\exception\ValidateActionException; +use wcf\system\exception\UserInputException; use wcf\system\WCF; use wcf\util\StringUtil; @@ -47,7 +47,7 @@ class UserAction extends AbstractDatabaseObjectAction implements IClipboardActio */ public function validateCreate() { if (!isset($this->parameters['data']['password'])) { - throw new ValidateActionException("Missing parameter 'password'"); + throw new UserInputException('password'); } } @@ -70,7 +70,7 @@ class UserAction extends AbstractDatabaseObjectAction implements IClipboardActio // list might be empty because only our own user id was given if (empty($userIDs)) { - throw new ValidateActionException("Invalid object id"); + throw new UserInputException('objectIDs'); } // validate groups @@ -89,7 +89,7 @@ class UserAction extends AbstractDatabaseObjectAction implements IClipboardActio } if (!UserGroup::isAccessibleGroup($groupIDs)) { - throw new ValidateActionException('Insufficient permissions'); + throw new PermissionDeniedException(); } } @@ -102,7 +102,7 @@ class UserAction extends AbstractDatabaseObjectAction implements IClipboardActio $this->readObjects(); if (empty($this->objects)) { - throw new ValidateActionException('Invalid object id'); + throw new UserInputException('objectIDs'); } } @@ -114,11 +114,11 @@ class UserAction extends AbstractDatabaseObjectAction implements IClipboardActio if (count($this->objects) == 1 && ($this->objects[0]->userID == WCF::getUser()->userID)) { $count = count($this->parameters); if ($count > 1 || ($count == 1 && !isset($this->parameters['options']))) { - throw new ValidateActionException('Insufficient permissions'); + throw new PermissionDeniedException(); } } - throw new ValidateActionException('Insufficient permissions'); + throw new PermissionDeniedException(); } } @@ -202,15 +202,15 @@ class UserAction extends AbstractDatabaseObjectAction implements IClipboardActio */ public function validateGetSearchResultList() { if (!isset($this->parameters['data']['searchString'])) { - throw new ValidateActionException("Missing parameter 'searchString'"); + throw new UserInputException('searchString'); } if (!isset($this->parameters['data']['includeUserGroups'])) { - throw new ValidateActionException("Missing parameter 'includeUserGroups'"); + throw new UserInputException('includeUserGroups'); } if (isset($this->parameters['data']['excludedSearchValues']) && !is_array($this->parameters['data']['excludedSearchValues'])) { - throw new ValidateActionException("Invalid parameter 'excludedSearchValues' given"); + throw new UserInputException('excludedSearchValues'); } } diff --git a/wcfsetup/install/files/lib/system/exception/AJAXException.class.php b/wcfsetup/install/files/lib/system/exception/AJAXException.class.php index 75a7f987b5..ba366dbbfc 100644 --- a/wcfsetup/install/files/lib/system/exception/AJAXException.class.php +++ b/wcfsetup/install/files/lib/system/exception/AJAXException.class.php @@ -50,8 +50,9 @@ class AJAXException extends LoggedException { * @param string $message * @param boolean $isDoomsday * @param string $stacktrace + * @param array $returnValues */ - public function __construct($message, $errorType = self::INTERNAL_ERROR, $stacktrace = null) { + public function __construct($message, $errorType = self::INTERNAL_ERROR, $stacktrace = null, $returnValues = array()) { if ($stacktrace === null) $stacktrace = $this->getTraceAsString(); if (WCF::debugModeIsEnabled()) { @@ -67,6 +68,8 @@ class AJAXException extends LoggedException { } $responseData['code'] = $errorType; + $responseData['returnValues'] = $returnValues; + $statusHeader = ''; switch ($errorType) { case self::MISSING_PARAMETERS: diff --git a/wcfsetup/install/files/lib/system/exception/UserInputException.class.php b/wcfsetup/install/files/lib/system/exception/UserInputException.class.php index d82bfb2c5a..afa31614f3 100644 --- a/wcfsetup/install/files/lib/system/exception/UserInputException.class.php +++ b/wcfsetup/install/files/lib/system/exception/UserInputException.class.php @@ -24,15 +24,23 @@ class UserInputException extends UserException { */ protected $type = null; + /** + * variables for AJAX error handling + * @var array + */ + protected $variables = array(); + /** * Creates a new UserInputException. * * @param string $field affected formular field * @param string $type kind of this error + * @param array $variables additional variables for AJAX error handling */ - public function __construct($field = '', $type = 'empty') { + public function __construct($field = '', $type = 'empty', array $variables = array()) { $this->field = $field; $this->type = $type; + $this->variables = $variables; $this->message = 'Parameter '.$field.' is missing or invalid'; parent::__construct(); @@ -55,4 +63,13 @@ class UserInputException extends UserException { public function getType() { return $this->type; } + + /** + * Returns additional variables for AJAX error handling. + * + * @return array + */ + public function getVariables() { + return $this->variables; + } } diff --git a/wcfsetup/install/files/lib/system/exception/ValidateActionException.class.php b/wcfsetup/install/files/lib/system/exception/ValidateActionException.class.php index 2e0e29520c..1d4c10d249 100644 --- a/wcfsetup/install/files/lib/system/exception/ValidateActionException.class.php +++ b/wcfsetup/install/files/lib/system/exception/ValidateActionException.class.php @@ -1,5 +1,7 @@ message = $message; + public function __construct($fieldName, $errorMessage = 'empty', array $variables = array()) { + $this->errorMessage = $errorMessage; + if (StringUtil::indexOf($this->errorMessage, '.') === false) { + $this->errorMessage = WCF::getLanguage()->get('wcf.global.form.error.'.$this->errorMessage); + } + else { + $this->errorMessage = WCF::getLanguage()->getDynamicVariable($this->errorMessage, $variables); + } + + $this->fieldName = $fieldName; + $this->message = WCF::getLanguage()->getDynamicVariable('wcf.global.error.invalidParameter', array('fieldName' => $this->fieldName)); + } + + /** + * Returns error message. + * + * @return string + */ + public function getErrorMessage() { + return $this->errorMessage; + } + + /** + * Returns erroneous field name. + * + * @return string + */ + public function getFieldName() { + return $this->fieldName; } /**