Migrate new password form to form builder form
authorMarcel Werk <burntime@woltlab.com>
Wed, 29 Nov 2023 15:23:31 +0000 (16:23 +0100)
committerMarcel Werk <burntime@woltlab.com>
Wed, 29 Nov 2023 15:23:31 +0000 (16:23 +0100)
com.woltlab.wcf/templates/newPassword.tpl
wcfsetup/install/files/lib/form/NewPasswordForm.class.php

index 963b977ba198f0b51ee3f03716014329efa9db5f..c2269e9e1955e7ee0620bcc667a34bafc57f335e 100644 (file)
@@ -1,67 +1,20 @@
-{include file='header' __disableAds=true}
-
-{include file='formError'}
+{include file='authFlowHeader'}
 
 <woltlab-core-notice type="info">{lang}wcf.user.newPassword.info{/lang}</woltlab-core-notice>
 
-<form method="post" action="{link controller='NewPassword'}{/link}">
-       <div class="section">
-               <dl{if $errorField == 'newPassword'} class="formError"{/if}>
-                       <dt><label for="newPassword">{lang}wcf.user.newPassword{/lang}</label></dt>
-                       <dd>
-                               <input type="password" id="newPassword" name="newPassword" value="{$newPassword}" class="medium" autocomplete="new-password" passwordrules="{$passwordRulesAttributeValue}">
-                                       
-                               {if $errorField == 'newPassword'}
-                                       <small class="innerError">
-                                               {if $errorType == 'empty'}
-                                                       {lang}wcf.global.form.error.empty{/lang}
-                                               {else}
-                                                       {lang}wcf.user.password.error.{@$errorType}{/lang}
-                                               {/if}
-                                       </small>
-                               {/if}
-                       </dd>
-               </dl>
-               
-               <dl{if $errorField == 'confirmNewPassword'} class="formError"{/if}>
-                       <dt><label for="confirmNewPassword">{lang}wcf.user.confirmPassword{/lang}</label></dt>
-                       <dd>
-                               <input type="password" id="confirmNewPassword" name="confirmNewPassword" value="{$confirmNewPassword}" class="medium" autocomplete="new-password" passwordrules="{$passwordRulesAttributeValue}">
-                                       
-                               {if $errorField == 'confirmNewPassword'}
-                                       <small class="innerError">
-                                               {if $errorType == 'empty'}
-                                                       {lang}wcf.global.form.error.empty{/lang}
-                                               {else}
-                                                       {lang}wcf.user.confirmPassword.error.{@$errorType}{/lang}
-                                               {/if}
-                                       </small>
-                               {/if}
-                       </dd>
-               </dl>
-               
-               {event name='fields'}
-               
-               <script data-relocate="true">
-                       require(['WoltLabSuite/Core/Ui/User/PasswordStrength', 'Language'], function (PasswordStrength, Language) {
-                               {include file='passwordStrengthLanguage'}
-                               
-                               new PasswordStrength(elById('newPassword'), {
-                                       staticDictionary: [
-                                               '{$user->username|encodeJS}',
-                                               '{$user->email|encodeJS}',
-                                       ]
-                               });
-                       })
-               </script>
-       </div>
-       
-       {event name='sections'}
+{@$form->getHtml()}
+
+<script data-relocate="true">
+       require(['WoltLabSuite/Core/Ui/User/PasswordStrength', 'Language'], (PasswordStrength, Language) => {
+               {include file='passwordStrengthLanguage'}
                
-       <div class="formSubmit">
-               <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s">
-               {csrfToken}
-       </div>
-</form>
+               new PasswordStrength(document.getElementById('newPassword'), {
+                       staticDictionary: [
+                               '{$user->username|encodeJS}',
+                               '{$user->email|encodeJS}',
+                       ]
+               });
+       })
+</script>
 
-{include file='footer' __disableAds=true}
+{include file='authFlowFooter'}
index 5b3a50568dca2dbcff080c1bf4a6ec57b3d3ec7d..0ad7941a9ded5111074f84b4fc8dc7f108edf386 100644 (file)
@@ -8,7 +8,11 @@ use wcf\system\exception\IllegalLinkException;
 use wcf\system\exception\NamedUserException;
 use wcf\system\exception\PermissionDeniedException;
 use wcf\system\exception\SystemException;
-use wcf\system\exception\UserInputException;
+use wcf\system\form\builder\container\FormContainer;
+use wcf\system\form\builder\field\PasswordFormField;
+use wcf\system\form\builder\field\validation\FormFieldValidationError;
+use wcf\system\form\builder\field\validation\FormFieldValidator;
+use wcf\system\form\builder\FormDocument;
 use wcf\system\request\LinkHandler;
 use wcf\system\WCF;
 use wcf\util\HeaderUtil;
@@ -19,48 +23,17 @@ use wcf\util\UserRegistrationUtil;
 /**
  * Shows the new password form.
  *
- * @author  Marcel Werk
- * @copyright   2001-2019 WoltLab GmbH
- * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @author      Marcel Werk
+ * @copyright   2001-2023 WoltLab GmbH
+ * @license     GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
  */
-class NewPasswordForm extends AbstractForm
+final class NewPasswordForm extends AbstractFormBuilderForm
 {
     const AVAILABLE_DURING_OFFLINE_MODE = true;
 
-    /**
-     * user id
-     * @var int
-     */
-    public $userID = 0;
-
-    /**
-     * lost password key
-     * @var string
-     */
-    public $lostPasswordKey = '';
-
-    /**
-     * User object
-     * @var User
-     */
-    public $user;
-
-    /**
-     * new password
-     * @var string
-     */
-    public $newPassword = '';
-
-    /**
-     * @var mixed[]
-     */
-    public $newPasswordStrengthVerdict = [];
-
-    /**
-     * confirmed new password
-     * @var string
-     */
-    public $confirmNewPassword = '';
+    public int $userID;
+    public string $lostPasswordKey;
+    public User $user;
 
     /**
      * @inheritDoc
@@ -115,47 +88,60 @@ class NewPasswordForm extends AbstractForm
     /**
      * @inheritDoc
      */
-    public function readFormParameters()
+    protected function createForm()
     {
-        parent::readFormParameters();
+        // We have to create the form manually here to avoid the form getting the ID 'newPassword'.
+        $this->form = FormDocument::create('newPasswordForm');
+
+        $this->form->appendChild(
+            FormContainer::create('data')
+                ->appendChildren([
+                    PasswordFormField::create('newPassword')
+                        ->label('wcf.user.newPassword')
+                        ->required()
+                        ->autoFocus()
+                        ->removeFieldClass('medium')
+                        ->addFieldClass('long')
+                        ->autocomplete('new-password')
+                        ->fieldAttribute('passwordrules', UserRegistrationUtil::getPasswordRulesAttributeValue())
+                        ->addValidator(new FormFieldValidator(
+                            'passwordValidator',
+                            $this->validatePassword(...)
+                        )),
+                ])
+        );
+    }
 
-        if (isset($_POST['newPassword'])) {
-            $this->newPassword = $_POST['newPassword'];
-        }
+    private function validatePassword(PasswordFormField $formField): void
+    {
         if (isset($_POST['newPassword_passwordStrengthVerdict'])) {
             try {
-                $this->newPasswordStrengthVerdict = JSON::decode($_POST['newPassword_passwordStrengthVerdict']);
+                $newPasswordStrengthVerdict = JSON::decode($_POST['newPassword_passwordStrengthVerdict']);
             } catch (SystemException $e) {
                 // ignore
             }
         }
-        if (isset($_POST['confirmNewPassword'])) {
-            $this->confirmNewPassword = $_POST['confirmNewPassword'];
+
+        if (($newPasswordStrengthVerdict['score'] ?? 4) < PASSWORD_MIN_SCORE) {
+            $formField->addValidationError(
+                new FormFieldValidationError(
+                    'notSecure',
+                    'wcf.user.newPassword.error.notSecure'
+                )
+            );
         }
     }
 
     /**
      * @inheritDoc
      */
-    public function validate()
+    public function assignVariables()
     {
-        parent::validate();
-
-        if (empty($this->newPassword)) {
-            throw new UserInputException('newPassword');
-        }
-
-        if (empty($this->confirmNewPassword)) {
-            throw new UserInputException('confirmNewPassword');
-        }
-
-        if (($this->newPasswordStrengthVerdict['score'] ?? 4) < PASSWORD_MIN_SCORE) {
-            throw new UserInputException('newPassword', 'notSecure');
-        }
+        parent::assignVariables();
 
-        if ($this->newPassword != $this->confirmNewPassword) {
-            throw new UserInputException('confirmNewPassword', 'notEqual');
-        }
+        WCF::getTPL()->assign([
+            'user' => $this->user,
+        ]);
     }
 
     /**
@@ -163,45 +149,43 @@ class NewPasswordForm extends AbstractForm
      */
     public function save()
     {
-        parent::save();
+        AbstractForm::save();
 
         WCF::getSession()->unregister('lostPasswordRequest');
+        $this->updateUser();
+        $this->saved();
+        $this->forwardToIndexPage();
+
+        exit;
+    }
 
-        // update user
+    private function updateUser(): void
+    {
+        $formData = $this->form->getData()['data'];
         $this->objectAction = new UserAction([$this->user], 'update', [
             'data' => \array_merge($this->additionalFields, [
-                'password' => $this->newPassword,
+                'password' => $formData['newPassword'],
                 'lastLostPasswordRequestTime' => 0,
                 'lostPasswordKey' => '',
             ]),
         ]);
         $this->objectAction->executeAction();
+    }
 
-        // forward to index page
+    private function forwardToIndexPage(): void
+    {
         HeaderUtil::delayedRedirect(
             LinkHandler::getInstance()->getLink(),
-            WCF::getLanguage()->getDynamicVariable('wcf.user.newPassword.success', ['user' => $this->user])
+            WCF::getLanguage()->getDynamicVariable('wcf.user.newPassword.success', ['user' => $this->user]),
+            10,
+            'success',
+            true
         );
 
         exit;
     }
 
-    /**
-     * @inheritDoc
-     */
-    public function assignVariables()
-    {
-        parent::assignVariables();
-
-        WCF::getTPL()->assign([
-            'user' => $this->user,
-            'newPassword' => $this->newPassword,
-            'confirmNewPassword' => $this->confirmNewPassword,
-            'passwordRulesAttributeValue' => UserRegistrationUtil::getPasswordRulesAttributeValue(),
-        ]);
-    }
-
-    private function throwInvalidLinkException()
+    private function throwInvalidLinkException(): void
     {
         throw new NamedUserException(WCF::getLanguage()->getDynamicVariable('wcf.user.newPassword.error.invalidLink'));
     }