Login
* @copyright Tamaranga
*/
class Login extends AuthForm
{
/** @var string */
public $title = '';
/** @var string */
public $back = '';
public function init()
{
parent::init();
$this->setTemplate('auth/login', 'users');
}
public function data()
{
$data = parent::data();
if ($this->request->user()) {
return Users::redirectToAccountSettings();
}
$data['title'] = _t('users', 'Sign In');
if (empty($this->back)) {
$data['back'] = $this->request->get('ref', TYPE_STR);
}
$data['emailOnly'] = $this->emailOnly();
$data['forgot_url'] = $this->forgotUrl();
$data['register_url'] = $this->registrationUrl('start');
$data['providers'] = Users::social()->getProvidersEnabled();
$data['login_social_url'] = Users::url('login.social');
return $data;
}
public function validate($data = [])
{
$data = $this->validateUsingRules($data, [
'email' => [TYPE_NOTAGS, 'len' => 100],
/** Phone number may come from "Social" registration form */
'phone_number' => [TYPE_NOTAGS, 'len' => 30],
'pass' => [TYPE_NOTRIM, 'required' => _t('users', 'Enter password')],
'social' => [TYPE_BOOL], # Using Social Account
'remember' => [TYPE_BOOL], # Remember Me
'back' => [TYPE_NOTAGS], # Return Url
]);
do {
if ($this->emailOnly() && ! $this->input->isEmail($this->email)) {
$this->errors->set(_t('users', 'Incorrect email'), 'email');
break;
}
} while (false);
# Blocks
if ($this->errors->no()) {
$data = $this->validateBlocks($data);
}
return $data;
}
public function submit()
{
# Resending the "activation" letter
if ($this->request->get('step', TYPE_STR) === 'resend-activation') {
do {
if (! $this->security->validateReferer()) {
$this->errors->reloadPage();
break;
}
if ($this->tooManyRequests('users-login-resend-activation', ['timeout' => 5, 'silent' => true])) {
$this->errors->set(_t('', 'Please, try again in a couple of seconds'));
break;
}
if ($this->request->user()) {
$this->errors->reloadPage();
break;
}
$email = $this->request->post('email', [TYPE_NOTAGS, 'len' => 100]);
$pass = $this->request->post('pass', TYPE_NOTRIM); # Open password
$response = $this->resendActivation($email, $pass);
if (is_array($response)) {
$this->respond($response);
}
} while (false);
return parent::submit();
}
$this->respond([
'success' => false,
'status' => 0,
]);
do {
# Already authenticated
if ($this->request->user()) {
$this->respond('success', true);
break;
}
# Validate
$this->validate();
if ($this->errors->any()) {
break;
}
# Is IP in blacklist
if ($blockedReason = Users::checkBan($this->request->remoteAddress())) {
$this->errors->set(_t('users', 'Access denied due to: [reason]', [
'reason' => $blockedReason,
]));
break;
}
if ($this->tooManyRequests('users-login', ['timeout' => rand(3, 6), 'silent' => true])) {
$this->errors->set(_t('', 'Please, try again in a couple of seconds'));
break;
}
# Authenticate by: email or phone_number
$credentials = [];
if ($this->input->isEmail($this->email)) {
$credentials = ['email' => $this->email];
} elseif ($this->input->isPhoneNumber($this->email)) {
$credentials = ['phone_number' => $this->email];
} elseif ($this->input->isPhoneNumber($this->phone_number)) {
$credentials = ['phone_number' => $this->phone_number];
}
$result = Users::authByCredentials($credentials, $this->pass, [
'remember' => $this->remember,
'silent' => false,
]);
# Correct password: confirm phone number & activate account
if (
$result === 1 && /* not active */
array_key_exists('phone_number', $credentials) &&
$this->phoneOnly()
) {
$userData = $this->config('__users_preactivate_data');
Users::userActivate($userData['id'], ['verifyPhone' => true]);
# Try again
$result = Users::authByCredentials($credentials, $this->pass, [
'remember' => $this->remember,
'silent' => false,
]);
}
# Account needs activation
if ($result === 1) {
$this->respond('status', 1);
$userData = $this->config('__users_preactivate_data');
$userData = Users::model()->userData($userData['id'], [
'user_id as id', 'email', 'name', 'activate_key', 'phone_number',
'phone_number_verified', 'email_verified', 'password',
]);
# Phone activation:
if (
($this->phoneOnly() || $this->phoneAndEmail()) &&
! empty($userData['phone_number']) &&
empty($userData['phone_number_verified'])
) {
if (empty($userData['activate_key'])) {
# Update activation key
$activationData = Users::updateActivationKey($userData['id']);
if (! $activationData) {
$this->errors->set(_t('users', 'Registration error, contact the administrator'));
break;
} else {
$userData['activate_key'] = $activationData['key'];
}
}
# Send activation code to confirm phone number
Users::sms(false)->sendActivationCode($userData['phone_number'], $userData['activate_key']);
$this->startData([
'id' => $userData['id'],
'password' => $this->pass,
]);
$this->errors->set(_t('users', 'This account is not activated. Activate', [
'link_activate' => 'href="' . $this->registrationUrl('phone') . '"'
]));
}
# Email activation:
if ($this->emailOnly() && ! empty($userData['email']) && empty($userData['email_verified'])) {
$this->errors->set(_t('users', 'This account is not activated, please follow the link sent to your email.
Retrieve email', [
'link_resend' => 'href="javascript:void(0);" class="ajax j-resend-activation"',
]));
}
break;
}
# Success
if ($result === true) {
$userID = $this->request->user()->id();
# Link social account
if ($this->social) {
Users::social()->authFinish($userID);
}
# Save favorite listings
Listings::favorites()->onUserLogin($userID); # todo: listen @see \modules\users\events\Login
$this->respond([
'success' => true,
'status' => 2, # success
]);
break;
}
} while (false);
if ($this->respond('success')) {
if (
empty($this->back) ||
mb_strpos($this->back, '/user/register') !== false ||
mb_strpos($this->back, '/user/login') !== false ||
! $this->security->validateReferer($this->back)
) {
$this->back = $this->router->url('index');
}
$this->respond('redirect', $this->back);
}
return parent::submit();
}
/**
* Resend confirmation email notification with "activation link"
* @param string $email
* @param string $password
* @return array|bool
*/
public function resendActivation(string $email, string $password)
{
if (! $this->input->isEmail($email)) {
$this->errors->set(_t('users', 'Incorrect email'), 'email');
return false;
}
$user = Users::model()->userDataByFilter(['email' => $email], [
'user_id','name','password','password_salt','activate_key',
'phone_number','email_verified',
]);
if (empty($user) || $user['email_verified']) {
$this->errors->reloadPage();
return false;
}
# Compare password
if (! $this->security->passwordCheck($password, $user['password'], $user['password_salt'])) {
$this->errors->set(_t('users', 'Incorrect email or password'), 'email');
return false;
}
# Regenerate activation data
$activation = Users::updateActivationKey($user['user_id'], $user['activate_key']);
if (! $activation) {
$this->errors->reloadPage();
return false;
}
# Send confirmation notification
Users::sendEmailRegistrationConfirmation($user['user_id'], $email, $password, $activation['link']);
# Save data for next step
$this->startData([
'id' => $user['user_id'],
'password' => $password,
'activate_link' => $activation['link'],
]);
return [
'redirect' => $this->registrationUrl('emailed', ['resend' => 1])
];
}
}