Register > Phone Confirmation * @copyright Tamaranga */ class PhoneConfirmation extends AuthForm { /** @var string */ public $title = ''; public function init() { parent::init(); $this->setTemplate('register/phone.confirmation', 'users'); $this->setTitle(_t('@users', 'Phone Confirmation')); } public function data() { $data = parent::data(); if ($this->request->user()) { return Users::redirectToAccountSettings(); } # Wrong registration data (from prev step) if (! $this->userId() || ! $this->user('phone_number')) { return $this->errors->error404(); } # User account was blocked: stop registration if (! $this->user() || $this->user('blocked')) { return $this->errors->error404(); } # User account is already activated: login & redirect to cabinet if ($this->user('activated')) { Users::authById($this->userId()); return Users::redirectToAccountSettings(); } $data['phone'] = $this->user('phone_number'); $data['codeAsPassword'] = $this->phoneOnly(); return $data; } public function submit() { if ($this->request->user()) { return parent::submit(); } if (! $this->security->validateReferer()) { $this->errors->reloadPage(); return parent::submit(); } if (! ($this->phoneOnly() || $this->phoneAndEmail())) { $this->errors->reloadPage(); return parent::submit(); } $data = $this->data(); if ($data instanceof Response) { return $data; } $userId = $this->userId(); $userPhone = $this->user('phone_number'); switch ($this->request->post('act')) { case 'code-validate': { $code = $this->request->post('code', TYPE_NOTAGS); $this->codeValidate($userId, $code); } break; case 'code-resend': { $this->codeResend($userId, $userPhone); } break; case 'phone-change': { $newPhone = $this->request->post('phone', [TYPE_NOTAGS, 'len' => 30]); if ($this->phoneChange($userId, $newPhone, $userPhone)) { $this->respond('phone', '+' . $newPhone); } } break; } return parent::submit(); } /** * Validate code sended to phone number * @param int $userId * @param string $code * @param bool $refreshPassword * @return bool */ public function codeValidate($userId, string $code, $refreshPassword = false) { # User data $user = ($userId == $this->userId() ? $this->user() : $this->userDataById($userId) ); # Validate if ( $code != $user['activate_key'] && mb_strtolower($code) != $user['activate_key'] ) { $this->errors->set(_t('users', 'Incorrect verification code')); return false; } # Current password $password = $this->startData('password'); if (is_null($password) || $refreshPassword) { $refreshPassword = true; $password = $this->security->generatePassword(); } # Activate if (! $user['activated']) { $activated = Users::userActivate($userId, [ 'verifyPhone' => true, 'password' => ($refreshPassword ? $password : null), ]); if (! $activated) { $this->errors->set(_t('users', 'Registration error, contact the administrator')); return false; } } # Authenticate Users::authById($userId); # No name => redirect to Optional step if ($this->phoneOnly() && empty($user['name'])) { $this->respond('redirect', $this->registrationUrl('optional')); return true; } # Email confirmation if (! empty($user['email'])) { if ($user['email_verified']) { # Registration finished notification (verified emails only): Users::sendPhoneRegistrationNotification($userId, $user['email'], $user['phone_number'], $password); } else { # Send email change confirmation notification: # Flow: phoneAndEmail if (Users::sendEmailChangeConfirmation($userId, $user['email'])) { $this->respond('msg', _t('users', 'An email has been sent to [email]. Follow the instructions in the email to confirm your email address.', [ 'email' => $user['email'], ])); } } } # Finish registration $this->finishRegistration(); $this->respond('redirect', $this->registrationUrl('finished')); return true; } /** * Resend confirmation code * @param int $userId * @param string $phone * @return bool */ public function codeResend($userId, $phone) { # Generate new activation code $activation = Users::getActivationInfo(); $activationKey = $activation['key']; $sended = false; if ($this->phoneOnly()) { # Set user password => activationKey and send it via sms $password = $this->updateUserPassword($userId, $activationKey); $sended = Users::sms(false)->sendPassword($phone, $password); } elseif ($this->phoneAndEmail()) { # Send new code via sms $sended = Users::sms()->sendActivationCode($phone, $activationKey); } if ($sended) { $activation = Users::updateActivationKey($userId, $activationKey); if (! $activation) { $this->errors->reloadPage(); return false; } } return true; } /** * Change phone number * @param int $userId * @param string $newPhone * @param string $currentPhone * @param array $opts [fieldName] * @return bool */ public function phoneChange($userId, $newPhone, $currentPhone, array $opts = []) { # Opts $fieldName = $opts['fieldName'] ?? 'phone'; # Validate number if (! $this->input->isPhoneNumber($newPhone)) { $this->errors->set(_t('users', 'Incorrect phone number'), $fieldName); return false; } # Same number as on "Start" step if ($newPhone === $currentPhone) { return true; } if (Users::model()->userPhoneExists($newPhone, $userId)) { if ($this->request->user()) { $this->errors->set(_t('users', 'A user with this phone number is already registered'), $fieldName); } else { $this->errors->set(_t('users', 'A user with this phone number is already registered. Forgot password?', [ 'link_forgot' => 'href="' . $this->forgotUrl() . '"', ]), $fieldName); } return false; } $changed = Users::model()->userSave($userId, [ 'phone_number' => $newPhone, 'phone_number_verified' => 0, ]); if (! $changed) { $this->errors->reloadPage(); return false; } # Send new activation key via sms $activateKey = Users::updateActivationKey($userId); if ($this->phoneOnly()) { Users::sms(false)->sendPassword($newPhone, $activateKey); } elseif ($this->phoneAndEmail()) { Users::sms()->sendActivationCode($newPhone, $activateKey); } return true; } /** * @inheritdoc */ public function settingsForm($form) { $form->text('title', _t('@site', 'Form Title'), _t('users', 'Phone number confirmation', true)); } }