From 823aa7469c3af440709059cba472bc072180086d Mon Sep 17 00:00:00 2001 From: Matthias Schmidt Date: Sat, 16 Mar 2019 15:44:54 +0100 Subject: [PATCH] Handle global form error message directly in form builder API This way, there is no need to include the `formError` template manually anymore. See #2509 --- com.woltlab.wcf/templates/__form.tpl | 8 +- .../install/files/acp/templates/__form.tpl | 8 +- .../acp/templates/devtoolsProjectAdd.tpl | 2 - .../templates/devtoolsProjectPipEntryAdd.tpl | 2 - .../files/acp/templates/languageItemAdd.tpl | 2 - .../files/acp/templates/reactionTypeAdd.tpl | 2 - .../files/lib/form/AbstractForm.class.php | 10 ++ .../form/AbstractFormBuilderForm.class.php | 7 ++ .../form/builder/FormDocument.class.php | 112 +++++++++++++++++- .../form/builder/IFormDocument.class.php | 68 ++++++++++- 10 files changed, 205 insertions(+), 16 deletions(-) diff --git a/com.woltlab.wcf/templates/__form.tpl b/com.woltlab.wcf/templates/__form.tpl index 0d9f06e45a..bacb6c0db4 100644 --- a/com.woltlab.wcf/templates/__form.tpl +++ b/com.woltlab.wcf/templates/__form.tpl @@ -5,6 +5,10 @@ }); +{if $form->hasValidationErrors() && $form->showsErrorMessage()} + +{/if} + {if $form->isAjax()}
getClasses()|empty} class="{implode from=$form->getClasses() item='class' glue=' '}{$class}{/implode}"{/if}{* @@ -31,11 +35,11 @@ {/foreach} {/if} - - {@SECURITY_TOKEN_INPUT_TAG} + {if $form->isAjax()}
{else} + {@SECURITY_TOKEN_INPUT_TAG} {/if} diff --git a/wcfsetup/install/files/acp/templates/__form.tpl b/wcfsetup/install/files/acp/templates/__form.tpl index 0d9f06e45a..bacb6c0db4 100644 --- a/wcfsetup/install/files/acp/templates/__form.tpl +++ b/wcfsetup/install/files/acp/templates/__form.tpl @@ -5,6 +5,10 @@ }); +{if $form->hasValidationErrors() && $form->showsErrorMessage()} + +{/if} + {if $form->isAjax()}
getClasses()|empty} class="{implode from=$form->getClasses() item='class' glue=' '}{$class}{/implode}"{/if}{* @@ -31,11 +35,11 @@ {/foreach} {/if} - - {@SECURITY_TOKEN_INPUT_TAG} + {if $form->isAjax()}
{else} + {@SECURITY_TOKEN_INPUT_TAG} {/if} diff --git a/wcfsetup/install/files/acp/templates/devtoolsProjectAdd.tpl b/wcfsetup/install/files/acp/templates/devtoolsProjectAdd.tpl index c0bde7f525..8150f51d30 100644 --- a/wcfsetup/install/files/acp/templates/devtoolsProjectAdd.tpl +++ b/wcfsetup/install/files/acp/templates/devtoolsProjectAdd.tpl @@ -20,8 +20,6 @@

{lang}wcf.acp.devtools.project.edit.warning{/lang}

{/if} -{include file='formError'} - {if $success|isset}

{lang}wcf.global.success.{$action}{/lang}

{/if} diff --git a/wcfsetup/install/files/acp/templates/devtoolsProjectPipEntryAdd.tpl b/wcfsetup/install/files/acp/templates/devtoolsProjectPipEntryAdd.tpl index ecf5b5eda9..b5b59f9ddc 100644 --- a/wcfsetup/install/files/acp/templates/devtoolsProjectPipEntryAdd.tpl +++ b/wcfsetup/install/files/acp/templates/devtoolsProjectPipEntryAdd.tpl @@ -16,8 +16,6 @@ -{include file='formError'} - {if $success|isset}

{lang}wcf.global.success.{$action}{/lang}

{/if} diff --git a/wcfsetup/install/files/acp/templates/languageItemAdd.tpl b/wcfsetup/install/files/acp/templates/languageItemAdd.tpl index eb1487a0c4..f8e5602761 100644 --- a/wcfsetup/install/files/acp/templates/languageItemAdd.tpl +++ b/wcfsetup/install/files/acp/templates/languageItemAdd.tpl @@ -14,8 +14,6 @@ -{include file='formError'} - {if $success|isset}

{lang}wcf.global.success.{$action}{/lang}

{/if} diff --git a/wcfsetup/install/files/acp/templates/reactionTypeAdd.tpl b/wcfsetup/install/files/acp/templates/reactionTypeAdd.tpl index 62a6a91829..5b13f86a77 100644 --- a/wcfsetup/install/files/acp/templates/reactionTypeAdd.tpl +++ b/wcfsetup/install/files/acp/templates/reactionTypeAdd.tpl @@ -14,8 +14,6 @@ -{include file='formError'} - {if $success|isset}

{lang}wcf.global.success.{$action}{/lang}

{/if} diff --git a/wcfsetup/install/files/lib/form/AbstractForm.class.php b/wcfsetup/install/files/lib/form/AbstractForm.class.php index 0d6ad3ec3e..cc1e898dc8 100644 --- a/wcfsetup/install/files/lib/form/AbstractForm.class.php +++ b/wcfsetup/install/files/lib/form/AbstractForm.class.php @@ -84,6 +84,16 @@ abstract class AbstractForm extends AbstractPage implements IForm { // call validate event EventHandler::getInstance()->fireAction($this, 'validate'); + $this->validateSecurityToken(); + } + + /** + * Validates the form security token. + * + * @throws UserInputException if the security token is invalid + * @since 5.2 + */ + protected function validateSecurityToken() { if (!isset($_POST['t']) || !WCF::getSession()->checkSecurityToken($_POST['t'])) { throw new UserInputException('__securityToken'); } diff --git a/wcfsetup/install/files/lib/form/AbstractFormBuilderForm.class.php b/wcfsetup/install/files/lib/form/AbstractFormBuilderForm.class.php index bc51930f04..179c563a6b 100644 --- a/wcfsetup/install/files/lib/form/AbstractFormBuilderForm.class.php +++ b/wcfsetup/install/files/lib/form/AbstractFormBuilderForm.class.php @@ -224,4 +224,11 @@ abstract class AbstractFormBuilderForm extends AbstractForm { throw new UserInputException($this->form->getPrefixedId()); } } + + /** + * @inheritDoc + */ + protected function validateSecurityToken() { + // does nothing, is handled by `IFormDocument` object + } } diff --git a/wcfsetup/install/files/lib/system/form/builder/FormDocument.class.php b/wcfsetup/install/files/lib/system/form/builder/FormDocument.class.php index c137720ab7..1c9b724c70 100644 --- a/wcfsetup/install/files/lib/system/form/builder/FormDocument.class.php +++ b/wcfsetup/install/files/lib/system/form/builder/FormDocument.class.php @@ -27,7 +27,9 @@ class FormDocument implements IFormDocument { use TFormNode; use TFormParentNode { TFormParentNode::cleanup insteadof TFormNode; - readValues as protected defaultReadValues; + hasValidationErrors as protected traitHasValidationErrors; + readValues as protected traitReadValues; + validate as protected traitValidate; } /** @@ -67,12 +69,24 @@ class FormDocument implements IFormDocument { */ protected $enctype = ''; + /** + * global form error message + * @var null|string + */ + protected $errorMessage; + /** * is `true` if form document has already been built and is `false` otherwise * @var bool */ protected $isBuilt = false; + /** + * is `true` if form document is in invalid due to external factors and is `false` otherwise + * @var boolean + */ + protected $invalid = false; + /** * form mode (see `self::FORM_MODE_*` constants) * @var null|string @@ -98,6 +112,12 @@ class FormDocument implements IFormDocument { */ protected $requestData; + /** + * TODO + * @var boolean + */ + protected $showErrorMessage = true; + /** * Cleans up the form document before the form document object is destroyed. */ @@ -207,10 +227,33 @@ class FormDocument implements IFormDocument { FormButton::create('submitButton') ->label('wcf.global.button.submit') ->accessKey('s') + ->submit(!$this->isAjax()) ->addClass('buttonPrimary') ); } + /** + * @inheritDoc + */ + public function errorMessage($languageItem = null, array $variables = []) { + if ($languageItem === null) { + if (!empty($variables)) { + throw new \InvalidArgumentException("Cannot use variables when unsetting error element of form '{$this->getId()}'"); + } + + $this->errorMessage = null; + } + else { + if (!is_string($languageItem)) { + throw new \InvalidArgumentException("Given error message language item is no string, " . gettype($languageItem) . " given."); + } + + $this->errorMessage = WCF::getLanguage()->getDynamicVariable($languageItem, $variables); + } + + return $this; + } + /** * @inheritDoc */ @@ -292,6 +335,17 @@ class FormDocument implements IFormDocument { return $this->enctype; } + /** + * @inheritDoc + */ + public function getErrorMessage() { + if ($this->errorMessage === null) { + $this->errorMessage = WCF::getLanguage()->getDynamicVariable('wcf.global.form.error'); + } + + return $this->errorMessage; + } + /** * @inheritDoc */ @@ -358,6 +412,13 @@ class FormDocument implements IFormDocument { return $this->addDefaultButton; } + /** + * @inheritDoc + */ + public function hasValidationErrors() { + return $this->isInvalid() || $this->traitHasValidationErrors(); + } + /** * @inheritDoc */ @@ -371,6 +432,15 @@ class FormDocument implements IFormDocument { return !empty($requestData); } + /** + * @inheritDoc + */ + public function invalid($invalid = true) { + $this->invalid = $invalid; + + return $this; + } + /** * @inheritDoc */ @@ -378,6 +448,13 @@ class FormDocument implements IFormDocument { return $this->ajax; } + /** + * @inheritDoc + */ + public function isInvalid() { + return $this->invalid; + } + /** * @inheritDoc */ @@ -445,7 +522,7 @@ class FormDocument implements IFormDocument { $this->requestData = $_POST; } - return $this->defaultReadValues(); + return $this->traitReadValues(); } /** @@ -460,4 +537,35 @@ class FormDocument implements IFormDocument { return $this; } + + /** + * @inheritDoc + */ + public function showErrorMessage($showErrorMessage = true) { + $this->showErrorMessage = $showErrorMessage; + + return $this; + } + + /** + * @inheritDoc + */ + public function showsErrorMessage() { + return $this->showErrorMessage; + } + + /** + * @inheritDoc + */ + public function validate() { + // check security token + if (!isset($_POST['t']) || !WCF::getSession()->checkSecurityToken($_POST['t'])) { + $this->invalid(); + + $this->errorMessage('wcf.global.form.error.securityToken'); + } + else { + $this->traitValidate(); + } + } } diff --git a/wcfsetup/install/files/lib/system/form/builder/IFormDocument.class.php b/wcfsetup/install/files/lib/system/form/builder/IFormDocument.class.php index ba9314b8ce..8491729104 100644 --- a/wcfsetup/install/files/lib/system/form/builder/IFormDocument.class.php +++ b/wcfsetup/install/files/lib/system/form/builder/IFormDocument.class.php @@ -75,6 +75,22 @@ interface IFormDocument extends IFormParentNode { * @throws \BadMethodCallException if this document has already been built */ public function build(); + + /** + * Sets the error message of this form using the given language item and returns this + * document. If `null` is passed, the error message is unset. + * + * Unsetting the current error message causes `IFormDocument::getErrorMessage()` to + * return the default error message. + * + * @param null|string $languageItem language item containing the error message or `null` to unset error message + * @param array $variables additional variables used when resolving the language item + * + * @return static this document + * + * @throws \InvalidArgumentException if the given form mode is invalid + */ + public function errorMessage($languageItem = null, array $variables = []); /** * Sets the form mode (see `self::FORM_MODE_*` constants). @@ -132,6 +148,18 @@ interface IFormDocument extends IFormParentNode { */ public function getEnctype(); + /** + * Returns the error message for the whole form. + * + * By default, `wcf.global.form.error` in the active user's language is returned. + * This method always returns the error message! To check, if the error message should + * be displayed, use `IParentFormNode::hasValidationErrors()` and + * `IFormDocument::showsErrorMessage()`. + * + * @return string + */ + public function getErrorMessage(); + /** * Returns the form mode (see `self::FORM_MODE_*` constants). * @@ -191,7 +219,7 @@ interface IFormDocument extends IFormParentNode { * If no request data is set, `$_POST` will be set as the request data. * * @param null|string $index array index of the returned data - * @return bool `tu + * @return bool */ public function hasRequestData($index = null); @@ -205,6 +233,24 @@ interface IFormDocument extends IFormParentNode { */ public function isAjax(); + /** + * Returns `true` if the form document is in invalid due to external factors and is `false` + * otherwise. + * + * By default, the form document is not invalid. + * + * @return boolean + */ + public function isInvalid(); + + /** + * Sets if the form document is in invalid due to external factors. + * + * @param boolean $invalid + * @return static this document + */ + public function invalid($invalid = true); + /** * Loads the field values from the given object and returns this document. * @@ -244,9 +290,27 @@ interface IFormDocument extends IFormParentNode { * Sets the request data of the form's fields. * * @param array $requestData request data of the form's fields - * @return static this field + * @return static this document * * @throws \BadMethodCallException if request data has already been set */ public function requestData(array $requestData); + + /** + * Sets if the global form error message should be shown if the form has validation errors. + * + * @param boolean $showErrorMessage + * @return static this document + */ + public function showErrorMessage($showErrorMessage = true); + + /** + * Returns `true` if the global form error message should be shown if the form has validation + * errors. + * + * By default, the global form error message is shown. + * + * @return boolean + */ + public function showsErrorMessage(); } -- 2.20.1