From 27532b5e5c9213129538cf77bef1f509435f275d Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Thu, 14 Nov 2024 13:40:51 +0100 Subject: [PATCH 1/2] Use the FormBuilder to create/edit captcha questions --- .../acp/templates/captchaQuestionAdd.tpl | 61 +---- .../acp/form/CaptchaQuestionAddForm.class.php | 236 ++++++------------ .../form/CaptchaQuestionEditForm.class.php | 123 ++------- .../data/TI18nDatabaseObjectAction.class.php | 12 +- .../question/CaptchaQuestionAction.class.php | 48 +++- wcfsetup/install/lang/de.xml | 2 +- wcfsetup/install/lang/en.xml | 2 +- 7 files changed, 148 insertions(+), 336 deletions(-) diff --git a/wcfsetup/install/files/acp/templates/captchaQuestionAdd.tpl b/wcfsetup/install/files/acp/templates/captchaQuestionAdd.tpl index 64d2d423e1e..636ce79406a 100644 --- a/wcfsetup/install/files/acp/templates/captchaQuestionAdd.tpl +++ b/wcfsetup/install/files/acp/templates/captchaQuestionAdd.tpl @@ -14,65 +14,6 @@ -{include file='shared_formNotice'} - -
-
- -
-
- - {if $errorField == 'question'} - - {if $errorType == 'empty'} - {lang}wcf.global.form.error.empty{/lang} - {elseif $errorType == 'multilingual'} - {lang}wcf.global.form.error.multilingual{/lang} - {else} - {lang}wcf.acp.captcha.question.question.error.{$errorType}{/lang} - {/if} - - {/if} -
- - {include file='shared_multipleLanguageInputJavascript' elementIdentifier='question' forceSelection=false} - - -
-
- - {lang}wcf.acp.captcha.question.answers.description{/lang} - {if $errorField == 'answers'} - - {if $errorType == 'empty'} - {lang}wcf.global.form.error.empty{/lang} - {elseif $errorType == 'multilingual'} - {lang}wcf.global.form.error.multilingual{/lang} - {else} - {lang}wcf.acp.captcha.question.answers.error.{$errorType}{/lang} - {/if} - - {/if} -
- - {include file='shared_multipleLanguageInputJavascript' elementIdentifier='answers' forceSelection=false} - -
-
-
- -
-
- - {event name='dataFields'} -
- - {event name='sections'} - -
- - {csrfToken} -
-
+{unsafe:$form->getHtml()} {include file='footer'} diff --git a/wcfsetup/install/files/lib/acp/form/CaptchaQuestionAddForm.class.php b/wcfsetup/install/files/lib/acp/form/CaptchaQuestionAddForm.class.php index 502054afb5f..95df4f71481 100644 --- a/wcfsetup/install/files/lib/acp/form/CaptchaQuestionAddForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/CaptchaQuestionAddForm.class.php @@ -2,42 +2,35 @@ namespace wcf\acp\form; +use wcf\data\captcha\question\CaptchaQuestion; use wcf\data\captcha\question\CaptchaQuestionAction; -use wcf\data\captcha\question\CaptchaQuestionEditor; -use wcf\form\AbstractForm; -use wcf\system\exception\UserInputException; -use wcf\system\language\I18nHandler; +use wcf\data\language\Language; +use wcf\form\AbstractFormBuilderForm; +use wcf\system\form\builder\container\FormContainer; +use wcf\system\form\builder\field\BooleanFormField; +use wcf\system\form\builder\field\MultilineTextFormField; +use wcf\system\form\builder\field\TextFormField; +use wcf\system\form\builder\field\validation\FormFieldValidationError; +use wcf\system\form\builder\field\validation\FormFieldValidator; +use wcf\system\language\LanguageFactory; use wcf\system\Regex; -use wcf\system\request\LinkHandler; -use wcf\system\WCF; -use wcf\util\StringUtil; /** * Shows the form to create a new captcha question. * - * @author Matthias Schmidt - * @copyright 2001-2019 WoltLab GmbH - * @license GNU Lesser General Public License + * @author Olaf Braun, Matthias Schmidt + * @copyright 2001-2024 WoltLab GmbH + * @license GNU Lesser General Public License + * + * @property CaptchaQuestion $formObject */ -class CaptchaQuestionAddForm extends AbstractForm +class CaptchaQuestionAddForm extends AbstractFormBuilderForm { /** * @inheritDoc */ public $activeMenuItem = 'wcf.acp.menu.link.captcha.question.add'; - /** - * invalid regex in answers - * @var string - */ - public $invalidRegex = ''; - - /** - * 1 if the question is disabled - * @var int - */ - public $isDisabled = 0; - /** * @inheritDoc */ @@ -46,155 +39,76 @@ class CaptchaQuestionAddForm extends AbstractForm /** * @inheritDoc */ - public function assignVariables() - { - parent::assignVariables(); - - I18nHandler::getInstance()->assignVariables(); - - WCF::getTPL()->assign([ - 'action' => 'add', - 'isDisabled' => $this->isDisabled, - 'invalidRegex' => $this->invalidRegex, - ]); - } - - /** - * @inheritDoc - */ - public function readFormParameters() - { - parent::readFormParameters(); - - I18nHandler::getInstance()->readValues(); - - if (isset($_POST['isDisabled'])) { - $this->isDisabled = 1; - } - } + public $objectActionClass = CaptchaQuestionAction::class; /** * @inheritDoc */ - public function readParameters() - { - parent::readParameters(); - - I18nHandler::getInstance()->register('question'); - I18nHandler::getInstance()->register('answers'); - } + public $objectEditLinkController = CaptchaQuestionEditForm::class; - /** - * @inheritDoc - */ - public function save() + #[\Override] + protected function createForm() { - parent::save(); - - $this->objectAction = new CaptchaQuestionAction([], 'create', [ - 'data' => \array_merge($this->additionalFields, [ - 'answers' => I18nHandler::getInstance()->isPlainValue('answers') ? I18nHandler::getInstance()->getValue('answers') : '', - 'isDisabled' => $this->isDisabled, - 'question' => I18nHandler::getInstance()->isPlainValue('question') ? I18nHandler::getInstance()->getValue('question') : '', - ]), - ]); - $returnValues = $this->objectAction->executeAction(); - $questionID = $returnValues['returnValues']->questionID; - - // set i18n values - $questionUpdates = []; - if (!I18nHandler::getInstance()->isPlainValue('question')) { - I18nHandler::getInstance()->save( - 'question', - 'wcf.captcha.question.question.question' . $questionID, - 'wcf.captcha.question', - 1 - ); - - $questionUpdates['question'] = 'wcf.captcha.question.question.question' . $questionID; - } - if (!I18nHandler::getInstance()->isPlainValue('answers')) { - I18nHandler::getInstance()->save( - 'answers', - 'wcf.captcha.question.answers.question' . $questionID, - 'wcf.captcha.question', - 1 - ); - - $questionUpdates['answers'] = 'wcf.captcha.question.answers.question' . $questionID; - } - - if (!empty($questionUpdates)) { - $questionEditor = new CaptchaQuestionEditor($returnValues['returnValues']); - $questionEditor->update($questionUpdates); - } - - $this->saved(); - - // reset values - I18nHandler::getInstance()->reset(); - $this->isDisabled = 0; - - // show success message - WCF::getTPL()->assign([ - 'success' => true, - 'objectEditLink' => LinkHandler::getInstance()->getControllerLink( - CaptchaQuestionEditForm::class, - ['id' => $questionID] - ), + parent::createForm(); + + $this->form->appendChildren([ + FormContainer::create('general') + ->appendChildren([ + TextFormField::create('question') + ->label('wcf.acp.captcha.question.question') + ->i18n() + ->languageItemPattern('wcf.captcha.question.question.question\d+') + ->required(), + MultilineTextFormField::create('answers') + ->label('wcf.acp.captcha.question.answers') + ->i18n() + ->languageItemPattern('wcf.captcha.question.answers.question\d+') + ->required() + ->addValidator( + new FormFieldValidator('regexValidator', function (MultilineTextFormField $formField) { + $value = $formField->getValue(); + + if ($formField->hasPlainValue()) { + $this->validateAnswer($value, $formField); + } else { + foreach ($value as $languageID => $languageValue) { + $this->validateAnswer( + $languageValue, + $formField, + LanguageFactory::getInstance()->getLanguage($languageID) + ); + } + } + }) + ), + BooleanFormField::create('isDisabled') + ->label('wcf.acp.captcha.question.isDisabled') + ->value(false) + ]) ]); } - /** - * @inheritDoc - */ - public function validate() - { - parent::validate(); - - // validate question - if (!I18nHandler::getInstance()->validateValue('question')) { - if (I18nHandler::getInstance()->isPlainValue('question')) { - throw new UserInputException('question'); - } else { - throw new UserInputException('question', 'multilingual'); - } + protected function validateAnswer( + string $answer, + MultilineTextFormField $formField, + ?Language $language = null + ): void { + if (!\str_starts_with('~', $answer) || !\str_ends_with('~', $answer)) { + return; } - // validate answers - if (!I18nHandler::getInstance()->validateValue('answers')) { - if (I18nHandler::getInstance()->isPlainValue('answers')) { - throw new UserInputException('answers'); - } else { - throw new UserInputException('answers', 'multilingual'); - } - } - - if (I18nHandler::getInstance()->isPlainValue('answers')) { - $answers = \explode("\n", StringUtil::unifyNewlines(I18nHandler::getInstance()->getValue('answers'))); - foreach ($answers as $answer) { - if (\mb_substr($answer, 0, 1) == '~' && \mb_substr($answer, -1, 1) == '~') { - $regexLength = \mb_strlen($answer) - 2; - if (!$regexLength || !Regex::compile(\mb_substr($answer, 1, $regexLength))->isValid()) { - $this->invalidRegex = $answer; - - throw new UserInputException('answers', 'invalidRegex'); - } - } - } - } - foreach (I18nHandler::getInstance()->getValues('answers') as $languageAnswers) { - $answers = \explode("\n", StringUtil::unifyNewlines($languageAnswers)); - foreach ($answers as $answer) { - if (\mb_substr($answer, 0, 1) == '~' && \mb_substr($answer, -1, 1) == '~') { - $regexLength = \mb_strlen($answer) - 2; - if (!$regexLength || !Regex::compile(\mb_substr($answer, 1, $regexLength))->isValid()) { - $this->invalidRegex = $answer; - - throw new UserInputException('answers', 'invalidRegex'); - } - } - } + $regexLength = \mb_strlen($answer) - 2; + if (!$regexLength || !Regex::compile(\mb_substr($answer, 1, $regexLength))->isValid()) { + $formField->addValidationError( + new FormFieldValidationError( + 'invalidRegex', + 'wcf.acp.captcha.question.answers.error.invalidRegex', + [ + 'invalidRegex' => $answer, + 'language' => $language + ] + ) + ); } } } diff --git a/wcfsetup/install/files/lib/acp/form/CaptchaQuestionEditForm.class.php b/wcfsetup/install/files/lib/acp/form/CaptchaQuestionEditForm.class.php index 5532e18c5f5..100053ad805 100644 --- a/wcfsetup/install/files/lib/acp/form/CaptchaQuestionEditForm.class.php +++ b/wcfsetup/install/files/lib/acp/form/CaptchaQuestionEditForm.class.php @@ -2,12 +2,10 @@ namespace wcf\acp\form; +use CuyZ\Valinor\Mapper\MappingError; use wcf\data\captcha\question\CaptchaQuestion; -use wcf\data\captcha\question\CaptchaQuestionAction; -use wcf\form\AbstractForm; +use wcf\http\Helper; use wcf\system\exception\IllegalLinkException; -use wcf\system\language\I18nHandler; -use wcf\system\WCF; /** * Shows the form to edit an existing captcha question. @@ -23,119 +21,32 @@ class CaptchaQuestionEditForm extends CaptchaQuestionAddForm */ public $activeMenuItem = 'wcf.acp.menu.link.captcha.question.list'; - /** - * edited captcha question - * @var CaptchaQuestion - */ - public $captchaQuestion; - - /** - * id of the edited captcha question - * @var int - */ - public $captchaQuestionID = 0; - /** * @inheritDoc */ - public function assignVariables() - { - parent::assignVariables(); - - I18nHandler::getInstance()->assignVariables(!empty($_POST)); - - WCF::getTPL()->assign([ - 'action' => 'edit', - 'captchaQuestion' => $this->captchaQuestion, - ]); - } - - /** - * @inheritDoc - */ - public function readData() - { - parent::readData(); - - if (empty($_POST)) { - I18nHandler::getInstance()->setOptions( - 'question', - 1, - $this->captchaQuestion->question, - 'wcf.captcha.question.question.question\d+' - ); - I18nHandler::getInstance()->setOptions( - 'answers', - 1, - $this->captchaQuestion->answers, - 'wcf.captcha.question.question.answers\d+' - ); - - $this->isDisabled = $this->captchaQuestion->isDisabled; - } - } + public $formAction = 'edit'; - /** - * @inheritDoc - */ + #[\Override] public function readParameters() { parent::readParameters(); - if (isset($_REQUEST['id'])) { - $this->captchaQuestionID = \intval($_REQUEST['id']); - } - $this->captchaQuestion = new CaptchaQuestion($this->captchaQuestionID); - if (!$this->captchaQuestion->questionID) { - throw new IllegalLinkException(); - } - } - - /** - * @inheritDoc - */ - public function save() - { - AbstractForm::save(); - - if (I18nHandler::getInstance()->isPlainValue('question')) { - if ($this->captchaQuestion->question == 'wcf.captcha.question.question.question' . $this->captchaQuestion->questionID) { - I18nHandler::getInstance()->remove($this->captchaQuestion->question); - } - } else { - I18nHandler::getInstance()->save( - 'question', - 'wcf.captcha.question.question.question' . $this->captchaQuestion->questionID, - 'wcf.captcha.question', - 1 + try { + $queryParameters = Helper::mapQueryParameters( + $_GET, + <<<'EOT' + array { + id: positive-int + } + EOT ); - } + $this->formObject = new CaptchaQuestion($queryParameters['id']); - if (I18nHandler::getInstance()->isPlainValue('answers')) { - if ($this->captchaQuestion->answers == 'wcf.captcha.question.question.answers' . $this->captchaQuestion->questionID) { - I18nHandler::getInstance()->remove($this->captchaQuestion->answers); + if (!$this->formObject->getObjectID()) { + throw new IllegalLinkException(); } - } else { - I18nHandler::getInstance()->save( - 'answers', - 'wcf.captcha.question.question.answers' . $this->captchaQuestion->questionID, - 'wcf.captcha.question', - 1 - ); + } catch (MappingError) { + throw new IllegalLinkException(); } - - $this->objectAction = new CaptchaQuestionAction([$this->captchaQuestion], 'update', [ - 'data' => \array_merge($this->additionalFields, [ - 'answers' => I18nHandler::getInstance()->isPlainValue('answers') ? I18nHandler::getInstance()->getValue('answers') : 'wcf.captcha.question.question.answers' . $this->captchaQuestion->questionID, - 'isDisabled' => $this->isDisabled, - 'question' => I18nHandler::getInstance()->isPlainValue('question') ? I18nHandler::getInstance()->getValue('question') : 'wcf.captcha.question.question.question' . $this->captchaQuestion->questionID, - ]), - ]); - $this->objectAction->executeAction(); - - $this->saved(); - - // show success message - WCF::getTPL()->assign('success', true); } } diff --git a/wcfsetup/install/files/lib/data/TI18nDatabaseObjectAction.class.php b/wcfsetup/install/files/lib/data/TI18nDatabaseObjectAction.class.php index 68fa37ed796..36166f3facc 100644 --- a/wcfsetup/install/files/lib/data/TI18nDatabaseObjectAction.class.php +++ b/wcfsetup/install/files/lib/data/TI18nDatabaseObjectAction.class.php @@ -25,23 +25,23 @@ trait TI18nDatabaseObjectAction */ protected function deleteI18nValues(): void { - $langaugeItems = []; + $languageItems = []; foreach ($this->getObjects() as $object) { foreach ($this->getI18nSaveTypes() as $name => $regex) { if ($object->$name === \str_replace('\d+', $object->getObjectID(), $regex)) { - $langaugeItems[] = $object->$name; + $languageItems[] = $object->$name; } } } - $this->deleteI18nItems($langaugeItems); + $this->deleteI18nItems($languageItems); } /** * Deletes language items and clears the language cache. */ - private function deleteI18nItems(array $langaugeItems): void + private function deleteI18nItems(array $languageItems): void { - if ($langaugeItems !== []) { + if ($languageItems === []) { return; } @@ -53,7 +53,7 @@ private function deleteI18nItems(array $langaugeItems): void $languageCategoryID = $statement->fetchSingleColumn(); $conditions = new PreparedStatementConditionBuilder(); - $conditions->add('languageItem IN (?)', [$langaugeItems]); + $conditions->add('languageItem IN (?)', [$languageItems]); $conditions->add('packageID = ?', [$this->getPackageID()]); $conditions->add('languageCategoryID = ?', [$languageCategoryID]); diff --git a/wcfsetup/install/files/lib/data/captcha/question/CaptchaQuestionAction.class.php b/wcfsetup/install/files/lib/data/captcha/question/CaptchaQuestionAction.class.php index b3939440ec1..669abae2876 100644 --- a/wcfsetup/install/files/lib/data/captcha/question/CaptchaQuestionAction.class.php +++ b/wcfsetup/install/files/lib/data/captcha/question/CaptchaQuestionAction.class.php @@ -5,6 +5,7 @@ use wcf\data\AbstractDatabaseObjectAction; use wcf\data\IToggleAction; use wcf\data\TDatabaseObjectToggle; +use wcf\data\TI18nDatabaseObjectAction; /** * Executes captcha question-related actions. @@ -13,13 +14,14 @@ * @copyright 2001-2019 WoltLab GmbH * @license GNU Lesser General Public License * - * @method CaptchaQuestion create() * @method CaptchaQuestionEditor[] getObjects() * @method CaptchaQuestionEditor getSingleObject() + * @property CaptchaQuestionEditor[] $objects */ class CaptchaQuestionAction extends AbstractDatabaseObjectAction implements IToggleAction { use TDatabaseObjectToggle; + use TI18nDatabaseObjectAction; /** * @inheritDoc @@ -30,4 +32,48 @@ class CaptchaQuestionAction extends AbstractDatabaseObjectAction implements ITog * @inheritDoc */ protected $permissionsUpdate = ['admin.captcha.canManageCaptchaQuestion']; + + #[\Override] + public function getI18nSaveTypes(): array + { + return [ + 'question' => 'wcf.captcha.question.question.question\d+', + 'answers' => 'wcf.captcha.question.answers.question\d+', + ]; + } + + #[\Override] + public function getLanguageCategory(): string + { + return 'wcf.captcha.question'; + } + + #[\Override] + public function getPackageID(): int + { + return PACKAGE_ID; + } + + #[\Override] + public function update() + { + parent::update(); + + foreach ($this->objects as $object) { + $this->saveI18nValue($object->getDecoratedObject()); + } + } + + #[\Override] + public function create() + { + // Question column doesn't have a default value + $this->parameters['data']['question'] = $this->parameters['data']['question'] ?? ''; + + $captchaQuestion = parent::create(); + + $this->saveI18nValue($captchaQuestion); + + return $captchaQuestion; + } } diff --git a/wcfsetup/install/lang/de.xml b/wcfsetup/install/lang/de.xml index 62793c611b2..1ba6c21f594 100644 --- a/wcfsetup/install/lang/de.xml +++ b/wcfsetup/install/lang/de.xml @@ -268,7 +268,7 @@ - + diff --git a/wcfsetup/install/lang/en.xml b/wcfsetup/install/lang/en.xml index e213f89ae24..9bf835587e3 100644 --- a/wcfsetup/install/lang/en.xml +++ b/wcfsetup/install/lang/en.xml @@ -267,7 +267,7 @@ - + From 983bacca48840d6265ed2a5240933d9acfcde317 Mon Sep 17 00:00:00 2001 From: Cyperghost Date: Fri, 15 Nov 2024 09:25:11 +0100 Subject: [PATCH 2/2] Deleting language items after deleting a captcha question --- .../captcha/question/CaptchaQuestionAction.class.php | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/wcfsetup/install/files/lib/data/captcha/question/CaptchaQuestionAction.class.php b/wcfsetup/install/files/lib/data/captcha/question/CaptchaQuestionAction.class.php index 669abae2876..abb4fcd1c40 100644 --- a/wcfsetup/install/files/lib/data/captcha/question/CaptchaQuestionAction.class.php +++ b/wcfsetup/install/files/lib/data/captcha/question/CaptchaQuestionAction.class.php @@ -76,4 +76,14 @@ public function create() return $captchaQuestion; } + + #[\Override] + public function delete() + { + $returnValue = parent::delete(); + + $this->deleteI18nValues(); + + return $returnValue; + } }