birzha/plugins/rainlab/user/components/Account.php

735 lines
22 KiB
PHP

<?php namespace RainLab\User\Components;
use Lang;
use Auth;
use Mail;
use Event;
use Flash;
use Input;
use Request;
use Redirect;
use Validator;
use ValidationException;
use ApplicationException;
use October\Rain\Auth\AuthException;
use Cms\Classes\Page;
use Cms\Classes\ComponentBase;
use RainLab\User\Models\User as UserModel;
use RainLab\User\Models\Settings as UserSettings;
use Exception;
use Throwable;
use TPS\Birzha\Classes\SMS;
/**
* Account component
*
* Allows users to register, sign in and update their account. They can also
* deactivate their account and resend the account verification email.
*/
class Account extends ComponentBase
{
/**
* Validation messages
*/
public $accountValidationMsgs;
public function componentDetails()
{
return [
'name' => /*Account*/'rainlab.user::lang.account.account',
'description' => /*User management form.*/'rainlab.user::lang.account.account_desc'
];
}
public function defineProperties()
{
return [
'redirect' => [
'title' => /*Redirect to*/'rainlab.user::lang.account.redirect_to',
'description' => /*Page name to redirect to after update, sign in or registration.*/'rainlab.user::lang.account.redirect_to_desc',
'type' => 'dropdown',
'default' => ''
],
'paramCode' => [
'title' => /*Activation Code Param*/'rainlab.user::lang.account.code_param',
'description' => /*The page URL parameter used for the registration activation code*/ 'rainlab.user::lang.account.code_param_desc',
'type' => 'string',
'default' => 'code'
],
'forceSecure' => [
'title' => /*Force secure protocol*/'rainlab.user::lang.account.force_secure',
'description' => /*Always redirect the URL with the HTTPS schema.*/'rainlab.user::lang.account.force_secure_desc',
'type' => 'checkbox',
'default' => 0
],
'requirePassword' => [
'title' => /*Confirm password on update*/'rainlab.user::lang.account.update_requires_password',
'description' => /*Require the current password of the user when changing their profile.*/'rainlab.user::lang.account.update_requires_password_comment',
'type' => 'checkbox',
'default' => 0
],
'view' => [
'title' => 'View',
'description' => 'Choose view type',
'type' => 'dropdown',
'options' => ['signin','signup','profile'],
'default' => 'signin'
]
];
}
public function getRedirectOptions()
{
return [''=>'- refresh page -', '0' => '- no redirect -'] + Page::sortBy('baseFileName')->lists('baseFileName', 'baseFileName');
}
/**
* Executed when this component is initialized
*/
public function prepareVars()
{
$this->page['user'] = $this->user();
$this->page['canRegister'] = $this->canRegister();
$this->page['loginAttribute'] = $this->loginAttribute();
$this->page['loginAttributeLabel'] = $this->loginAttributeLabel();
$this->page['updateRequiresPassword'] = $this->updateRequiresPassword();
$this->page['rememberLoginMode'] = $this->rememberLoginMode();
}
/**
* Executed when this component is bound to a page or layout.
*/
public function onRun()
{
/*
* Redirect to HTTPS checker
*/
if ($redirect = $this->redirectForceSecure()) {
return $redirect;
}
/*
* Activation code supplied
*/
if ($code = $this->activationCode()) {
$this->onActivate($code);
}
$this->prepareVars();
}
//
// Properties
//
/**
* Returns the logged in user, if available
*/
public function user()
{
if (!Auth::check()) {
return null;
}
return Auth::getUser();
}
/**
* Flag for allowing registration, pulled from UserSettings
*/
public function canRegister()
{
return UserSettings::get('allow_registration', true);
}
/**
* Returns the login model attribute.
*/
public function loginAttribute()
{
return UserSettings::get('login_attribute', UserSettings::LOGIN_EMAIL);
}
/**
* Returns the login label as a word.
*/
public function loginAttributeLabel()
{
return Lang::get($this->loginAttribute() == UserSettings::LOGIN_EMAIL
? /*Email*/'rainlab.user::lang.login.attribute_email'
: /*Username*/'rainlab.user::lang.login.attribute_username'
);
}
/**
* Returns the update requires password setting
*/
public function updateRequiresPassword()
{
return $this->property('requirePassword', false);
}
/**
* Returns the login remember mode.
*/
public function rememberLoginMode()
{
return UserSettings::get('remember_login', UserSettings::REMEMBER_ALWAYS);
}
/**
* Looks for the activation code from the URL parameter. If nothing
* is found, the GET parameter 'activate' is used instead.
* @return string
*/
public function activationCode()
{
$routeParameter = $this->property('paramCode');
if ($code = $this->param($routeParameter)) {
return $code;
}
return get('activate');
}
//
// AJAX
//
/**
* Sign in the user
*/
public function onSignin()
{
try {
/*
* Validate input
*/
$data = post();
$rules = [];
$rules['login'] = $this->loginAttribute() == UserSettings::LOGIN_USERNAME
? 'required|between:2,255'
: 'required|email|between:6,255';
$rules['password'] = 'required|between:4,255';
if (!array_key_exists('login', $data)) {
$data['login'] = post('username', post('email'));
}
$data['login'] = trim($data['login']);
$validation = Validator::make($data, $rules);
if ($validation->fails()) {
throw new ValidationException($validation);
}
/*
* Authenticate user
*/
$credentials = [
'login' => array_get($data, 'dial_code') . array_get($data, 'login'),
'password' => array_get($data, 'password')
];
/*
* Login remember mode
*/
switch ($this->rememberLoginMode()) {
case UserSettings::REMEMBER_ALWAYS:
$remember = true;
break;
case UserSettings::REMEMBER_NEVER:
$remember = false;
break;
case UserSettings::REMEMBER_ASK:
$remember = (bool) array_get($data, 'remember', false);
break;
}
Event::fire('rainlab.user.beforeAuthenticate', [$this, $credentials]);
$user = Auth::authenticate($credentials, $remember);
if ($user->isBanned()) {
Auth::logout();
throw new AuthException(/*Sorry, this user is currently not activated. Please contact us for further assistance.*/'rainlab.user::lang.account.banned');
}
/*
* Record IP address
*/
if ($ipAddress = Request::ip()) {
$user->touchIpAddress($ipAddress);
}
/*
* Redirect
*/
if ($redirect = $this->makeRedirection(true)) {
return $redirect;
}
}
catch (Exception $ex) {
if ($ex instanceof AuthException) {
throw new ValidationException(['no_user' => trans('validation.no_user')]);
}
// if (Request::ajax()) throw $ex;
if (Request::ajax()) info($ex);
else Flash::error($ex->getMessage());
}
}
/**
* Register the user
*/
public function onRegister()
{
try {
if (!$this->canRegister()) {
throw new ApplicationException(Lang::get(/*Registrations are currently disabled.*/'rainlab.user::lang.account.registration_disabled'));
}
if ($this->isRegisterThrottled()) {
throw new ApplicationException(Lang::get(/*Registration is throttled. Please try again later.*/'rainlab.user::lang.account.registration_throttled'));
}
/*
* Validate input
*/
$data = post();
if (!array_key_exists('password_confirmation', $data)) {
$data['password_confirmation'] = post('password');
}
$rules = array_merge((new UserModel)->rules, [
'username' => [
'required',
'unique' => function($attribute, $value, $fail) use($data) {
$u = UserModel::find($data['dial_code'] . $value);
if(!is_null($u)) $fail();
}
],
]);
if ($this->loginAttribute() !== UserSettings::LOGIN_USERNAME) {
unset($rules['username']);
}
$validation = Validator::make($data, $rules, [
'username.required' => trans('validation.auth_profile.phone_number_required'),
'username.numeric' => trans('validation.auth_profile.phone_number_numeric'),
'username.digits_between' => trans('validation.auth_profile.phone_number_digits_between'),
'username.unique' => trans('validation.auth_profile.phone_number_unique'),
// 'iu_about.digits' => trans('validation.auth_profile.iu_about_digits'),
// 'iu_company.max' => trans('validation.auth_profile.iu_company_max'),
]);
if ($validation->fails()) {
throw new ValidationException($validation);
}
/*
* Record IP address
*/
if ($ipAddress = Request::ip()) {
$data['created_ip_address'] = $data['last_ip_address'] = $ipAddress;
}
/*
* Register user
*/
Event::fire('rainlab.user.beforeRegister', [&$data]);
$requireActivation = UserSettings::get('require_activation', true);
$automaticActivation = UserSettings::get('activate_mode') == UserSettings::ACTIVATE_AUTO;
$userActivation = UserSettings::get('activate_mode') == UserSettings::ACTIVATE_USER;
$adminActivation = UserSettings::get('activate_mode') == UserSettings::ACTIVATE_ADMIN;
$user = Auth::register(array_merge($data, [
'username' => $data['dial_code'] . $data['username']
]), $automaticActivation);
Event::fire('rainlab.user.register', [$user, $data]);
/*
* Activation is by the user, send the email
*/
if ($userActivation) {
// $this->sendActivationEmail($user);
//
// Flash::success(Lang::get(/*An activation email has been sent to your email address.*/'rainlab.user::lang.account.activation_email_sent'));
//todo open activation view
}
/*
* Activation is by the admin, show message
* For automatic email on account activation RainLab.Notify plugin is needed
*/
if ($adminActivation) {
Flash::success(Lang::get(/*You have successfully registered. Your account is not yet active and must be approved by an administrator.*/'rainlab.user::lang.account.activation_by_admin'));
}
/*
* Automatically activated or not required, log the user in
*/
if ($automaticActivation || !$requireActivation) {
Auth::login($user);
}
/*
* Redirect to the intended page after successful sign in
*/
// if ($redirect = $this->makeRedirection(true)) {
// return $redirect;
// }
return \Redirect::to('/profile');
}
catch (Exception $ex) {
if (Request::ajax()) throw $ex;
else Flash::error($ex->getMessage());
}
}
/**
* Send sms with 6 digits code to user
*/
public function onSendSmsCode()
{
try {
if($this->user()->dial_code == '+993' && !$this->user()->phone_verified) {
$code = random_int(100000, 999999);
$result = SMS::send(str_replace(array('+', ' ', '(' , ')', '-'), '', $this->user()->username), $code);
// $result = 0;
switch ($result) {
case 0:
$this->user()->activation_code = $code;
$this->user()->save();
break;
case 1:
return \Redirect::to('/error');
break;
default:
return \Redirect::to('/error');
break;
}
}
} catch(Throwable $th) {
\Log::info($th);
return \Redirect::to('/error');
}
}
/**
* Check SMS code sent by user
*/
public function onCheckSmsCode()
{
$data = post();
$validator = \Validator::make($data, [
'sms_code' => 'required|digits:6',
]);
if($validator->fails()) {
throw new ValidationException($validator);
}
if($this->user()->activation_code == $data['sms_code']) {
$this->user()->phone_verified = true;
$this->user()->save();
Flash::success('Your phone number has been succesfully verified');
return \Redirect::to('profile');
} else {
Flash::error('Invalid sms code');
return \Redirect::to('profile');
}
}
/**
* Activate the user
* @param string $code Activation code
*/
public function onActivate($code = null)
{
try {
$code = post('code', $code);
$errorFields = ['code' => Lang::get(/*Invalid activation code supplied.*/'rainlab.user::lang.account.invalid_activation_code')];
/*
* Break up the code parts
*/
$parts = explode('!', $code);
if (count($parts) != 2) {
throw new ValidationException($errorFields);
}
list($userId, $code) = $parts;
if (!strlen(trim($userId)) || !strlen(trim($code))) {
throw new ValidationException($errorFields);
}
if (!$user = Auth::findUserById($userId)) {
throw new ValidationException($errorFields);
}
if (!$user->attemptActivation($code)) {
throw new ValidationException($errorFields);
}
Flash::success(Lang::get(/*Successfully activated your account.*/'rainlab.user::lang.account.success_activation'));
/*
* Sign in the user
*/
Auth::login($user);
}
catch (Exception $ex) {
if (Request::ajax()) throw $ex;
else Flash::error($ex->getMessage());
}
}
/**
* Update the user
*/
public function onUpdate()
{
if (!$user = $this->user()) {
return;
}
$data = post();
$rules = [
'email' => 'required|between:6,191|email',
'username' => 'required|digits_between:8,20|numeric',
'name' => 'required',
'surname' => 'required',
'username' => 'required|digits_between:8,20|numeric',
'iu_company' => 'max:191',
'iu_about' => 'digits:6|numeric',
];
$validation = Validator::make($data, $rules, [
'username.required' => trans('validation.auth_profile.phone_number_required'),
'username.numeric' => trans('validation.auth_profile.phone_number_numeric'),
'username.digits_between' => trans('validation.auth_profile.phone_number_digits_between'),
'username.unique' => trans('validation.auth_profile.phone_number_unique'),
'zip.digits' => trans('validation.auth_profile.iu_about_digits'),
'company.max' => trans('validation.auth_profile.iu_company_max'),
]);
if ($validation->fails()) {
throw new ValidationException($validation);
}
if ($this->updateRequiresPassword()) {
if (!$user->checkHashValue('password', $data['password_current'])) {
throw new ValidationException(['password_current' => Lang::get('rainlab.user::lang.account.invalid_current_pass')]);
}
}
if (Input::hasFile('avatar')) {
$user->avatar = Input::file('avatar');
}
$user->fill($data);
$user->save();
/*
* Password has changed, reauthenticate the user
*/
if (array_key_exists('password', $data) && strlen($data['password'])) {
Auth::login($user->reload(), true);
}
Flash::success(post('flash', Lang::get(/*Settings successfully saved!*/'rainlab.user::lang.account.success_saved')));
/*
* Redirect
*/
if ($redirect = $this->makeRedirection()) {
return $redirect;
}
$this->prepareVars();
}
/**
* Deactivate user
*/
public function onDeactivate()
{
if (!$user = $this->user()) {
return;
}
if (!$user->checkHashValue('password', post('password'))) {
throw new ValidationException(['password' => Lang::get('rainlab.user::lang.account.invalid_deactivation_pass')]);
}
Auth::logout();
$user->delete();
Flash::success(post('flash', Lang::get(/*Successfully deactivated your account. Sorry to see you go!*/'rainlab.user::lang.account.success_deactivation')));
/*
* Redirect
*/
if ($redirect = $this->makeRedirection()) {
return $redirect;
}
}
/**
* Trigger a subsequent activation email
*/
public function onSendActivationEmail()
{
try {
if (!$user = $this->user()) {
throw new ApplicationException(Lang::get(/*You must be logged in first!*/'rainlab.user::lang.account.login_first'));
}
if ($user->is_activated) {
throw new ApplicationException(Lang::get(/*Your account is already activated!*/'rainlab.user::lang.account.already_active'));
}
Flash::success(Lang::get(/*An activation email has been sent to your email address.*/'rainlab.user::lang.account.activation_email_sent'));
$this->sendActivationEmail($user);
}
catch (Exception $ex) {
if (Request::ajax()) throw $ex;
else Flash::error($ex->getMessage());
}
/*
* Redirect
*/
if ($redirect = $this->makeRedirection()) {
return $redirect;
}
}
//
// Helpers
//
/**
* Returns a link used to activate the user account.
* @return string
*/
protected function makeActivationUrl($code)
{
$params = [
$this->property('paramCode') => $code
];
if ($pageName = $this->property('activationPage')) {
$url = $this->pageUrl($pageName, $params);
}
else {
$url = $this->currentPageUrl($params);
}
if (strpos($url, $code) === false) {
$url .= '?activate=' . $code;
}
return $url;
}
/**
* Sends the activation email to a user
* @param User $user
* @return void
*/
protected function sendActivationEmail($user)
{
$code = implode('!', [$user->id, $user->getActivationCode()]);
$link = $this->makeActivationUrl($code);
$data = [
'name' => $user->name,
'link' => $link,
'code' => $code
];
Mail::send('rainlab.user::mail.activate', $data, function($message) use ($user) {
$message->to($user->email, $user->name);
});
}
/**
* Redirect to the intended page after successful update, sign in or registration.
* The URL can come from the "redirect" property or the "redirect" postback value.
* @return mixed
*/
protected function makeRedirection($intended = false)
{
$method = $intended ? 'intended' : 'to';
$property = trim((string) $this->property('redirect'));
// No redirect
if ($property === '0') {
return;
}
// Refresh page
if ($property === '') {
return Redirect::refresh();
}
$redirectUrl = $this->pageUrl($property) ?: $property;
if ($redirectUrl = post('redirect', $redirectUrl)) {
return Redirect::$method($redirectUrl);
}
}
/**
* Checks if the force secure property is enabled and if so
* returns a redirect object.
* @return mixed
*/
protected function redirectForceSecure()
{
if (
Request::secure() ||
Request::ajax() ||
!$this->property('forceSecure')
) {
return;
}
return Redirect::secure(Request::path());
}
/**
* Returns true if user is throttled.
* @return bool
*/
protected function isRegisterThrottled()
{
if (!UserSettings::get('use_register_throttle', false)) {
return false;
}
return UserModel::isRegisterThrottled(Request::ip());
}
}