</nav>
</header>
-{include file='shared_formNotice'}
-
-<form id="adForm" method="post" action="{if $action == 'add'}{link controller='CaptchaQuestionAdd'}{/link}{else}{link controller='CaptchaQuestionEdit' id=$captchaQuestion->questionID}{/link}{/if}">
- <div class="section">
- <dl{if $errorField == 'question'} class="formError"{/if}>
- <dt><label for="question">{lang}wcf.acp.captcha.question.question{/lang}</label></dt>
- <dd>
- <input type="text" id="question" name="question" value="{$i18nPlainValues[question]}" required autofocus class="long">
- {if $errorField == 'question'}
- <small class="innerError">
- {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}
- </small>
- {/if}
- </dd>
- </dl>
- {include file='shared_multipleLanguageInputJavascript' elementIdentifier='question' forceSelection=false}
-
- <dl{if $errorField == 'answers'} class="formError"{/if}>
- <dt><label for="answers">{lang}wcf.acp.captcha.question.answers{/lang}</label></dt>
- <dd>
- <textarea id="answers" name="answers" cols="40" rows="10">{$i18nPlainValues[answers]}</textarea>
- <small>{lang}wcf.acp.captcha.question.answers.description{/lang}</small>
- {if $errorField == 'answers'}
- <small class="innerError">
- {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}
- </small>
- {/if}
- </dd>
- </dl>
- {include file='shared_multipleLanguageInputJavascript' elementIdentifier='answers' forceSelection=false}
-
- <dl>
- <dt></dt>
- <dd>
- <label><input type="checkbox" name="isDisabled" value="1"{if $isDisabled} checked{/if}> {lang}wcf.acp.captcha.question.isDisabled{/lang}</label>
- </dd>
- </dl>
-
- {event name='dataFields'}
- </div>
-
- {event name='sections'}
-
- <div class="formSubmit">
- <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
- {csrfToken}
- </div>
-</form>
+{unsafe:$form->getHtml()}
{include file='footer'}
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 <http://opensource.org/licenses/lgpl-license.php>
+ * @author Olaf Braun, Matthias Schmidt
+ * @copyright 2001-2024 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ *
+ * @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
*/
/**
* @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
+ ]
+ )
+ );
}
}
}
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.
*/
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);
}
}
*/
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;
}
$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]);
use wcf\data\AbstractDatabaseObjectAction;
use wcf\data\IToggleAction;
use wcf\data\TDatabaseObjectToggle;
+use wcf\data\TI18nDatabaseObjectAction;
/**
* Executes captcha question-related actions.
* @copyright 2001-2019 WoltLab GmbH
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
*
- * @method CaptchaQuestion create()
* @method CaptchaQuestionEditor[] getObjects()
* @method CaptchaQuestionEditor getSingleObject()
+ * @property CaptchaQuestionEditor[] $objects
*/
class CaptchaQuestionAction extends AbstractDatabaseObjectAction implements IToggleAction
{
use TDatabaseObjectToggle;
+ use TI18nDatabaseObjectAction;
/**
* @inheritDoc
* @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;
+ }
}
<item name="wcf.acp.captcha.question.add"><![CDATA[Captcha-Frage hinzufügen]]></item>
<item name="wcf.acp.captcha.question.answers"><![CDATA[Antworten]]></item>
<item name="wcf.acp.captcha.question.answers.description"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Gib{else}Geben Sie{/if} pro Zeile eine mögliche Antwort ein. Antworten, die mit „~“ beginnen und enden, werden als reguläre Ausdrücke interpretiert.]]></item>
- <item name="wcf.acp.captcha.question.answers.error.invalidRegex"><![CDATA[Der reguläre Ausdruck „{$invalidRegex}“ ist ungültig.]]></item>
+ <item name="wcf.acp.captcha.question.answers.error.invalidRegex"><![CDATA[Der reguläre Ausdruck „{$invalidRegex}“ ist ungültig{if $language} für die Sprache „{$language}“{/if}.]]></item>
<item name="wcf.acp.captcha.question.edit"><![CDATA[Captcha-Frage bearbeiten]]></item>
<item name="wcf.acp.captcha.question.isDisabled"><![CDATA[Frage deaktivieren]]></item>
<item name="wcf.acp.captcha.question.list"><![CDATA[Captcha-Fragen]]></item>
<item name="wcf.acp.captcha.question.add"><![CDATA[Add Captcha Question]]></item>
<item name="wcf.acp.captcha.question.answers"><![CDATA[Answers]]></item>
<item name="wcf.acp.captcha.question.answers.description"><![CDATA[Answers beginning and ending with “~” will be interpreted as regular expressions. Enter one answer per line.]]></item>
- <item name="wcf.acp.captcha.question.answers.error.invalidRegex"><![CDATA[The regular expression “{$invalidRegex}” is invalid.]]></item>
+ <item name="wcf.acp.captcha.question.answers.error.invalidRegex"><![CDATA[The regular expression “{$invalidRegex}” is invalid{if $language} for language “{$language}”{/if}.]]></item>
<item name="wcf.acp.captcha.question.edit"><![CDATA[Edit Captcha Question]]></item>
<item name="wcf.acp.captcha.question.isDisabled"><![CDATA[Disable captcha question]]></item>
<item name="wcf.acp.captcha.question.list"><![CDATA[Captcha Questions]]></item>