Commit 3e347d0e by Alexander Makarov

Merge pull request #2244 from yiisoft/advanced-application-forms

Moved most of the user-related logic into form models
parents 0cde160c a1497ca0
<?php <?php
namespace backend\controllers; namespace backend\controllers;
use Yii; use Yii;
use yii\web\AccessControl;
use yii\web\Controller; use yii\web\Controller;
use common\models\LoginForm; use common\models\LoginForm;
/**
* Site controller
*/
class SiteController extends Controller class SiteController extends Controller
{ {
/**
* @inheritdoc
*/
public function behaviors() public function behaviors()
{ {
return [ return [
'access' => [ 'access' => [
'class' => \yii\web\AccessControl::className(), 'class' => AccessControl::className(),
'rules' => [ 'rules' => [
[ [
'actions' => ['login', 'error'], 'actions' => ['login', 'error'],
...@@ -28,6 +34,9 @@ class SiteController extends Controller ...@@ -28,6 +34,9 @@ class SiteController extends Controller
]; ];
} }
/**
* @inheritdoc
*/
public function actions() public function actions()
{ {
return [ return [
......
...@@ -5,7 +5,7 @@ use yii\widgets\ActiveForm; ...@@ -5,7 +5,7 @@ use yii\widgets\ActiveForm;
/** /**
* @var yii\web\View $this * @var yii\web\View $this
* @var yii\widgets\ActiveForm $form * @var yii\widgets\ActiveForm $form
* @var common\models\LoginForm $model * @var \common\models\LoginForm $model
*/ */
$this->title = 'Login'; $this->title = 'Login';
$this->params['breadcrumbs'][] = $this->title; $this->params['breadcrumbs'][] = $this->title;
......
<?php <?php
namespace common\models; namespace common\models;
use Yii; use common\models\User;
use yii\base\Model; use yii\base\Model;
use Yii;
/** /**
* LoginForm is the model behind the login form. * Login form
*/ */
class LoginForm extends Model class LoginForm extends Model
{ {
...@@ -17,7 +17,7 @@ class LoginForm extends Model ...@@ -17,7 +17,7 @@ class LoginForm extends Model
private $_user = false; private $_user = false;
/** /**
* @return array the validation rules. * @inheritdoc
*/ */
public function rules() public function rules()
{ {
......
...@@ -6,8 +6,7 @@ use yii\helpers\Security; ...@@ -6,8 +6,7 @@ use yii\helpers\Security;
use yii\web\IdentityInterface; use yii\web\IdentityInterface;
/** /**
* Class User * User model
* @package common\models
* *
* @property integer $id * @property integer $id
* @property string $username * @property string $username
...@@ -19,19 +18,30 @@ use yii\web\IdentityInterface; ...@@ -19,19 +18,30 @@ use yii\web\IdentityInterface;
* @property integer $status * @property integer $status
* @property integer $created_at * @property integer $created_at
* @property integer $updated_at * @property integer $updated_at
* @property string $password write-only password
*/ */
class User extends ActiveRecord implements IdentityInterface class User extends ActiveRecord implements IdentityInterface
{ {
/**
* @var string the raw password. Used to collect password input and isn't saved in database
*/
public $password;
const STATUS_DELETED = 0; const STATUS_DELETED = 0;
const STATUS_ACTIVE = 10; const STATUS_ACTIVE = 10;
const ROLE_USER = 10; const ROLE_USER = 10;
public static function create($attributes)
{
/** @var User $user */
$user = new static();
$user->setAttributes($attributes);
if ($user->save()) {
return $user;
} else {
return null;
}
}
/**
* @inheritdoc
*/
public function behaviors() public function behaviors()
{ {
return [ return [
...@@ -46,10 +56,7 @@ class User extends ActiveRecord implements IdentityInterface ...@@ -46,10 +56,7 @@ class User extends ActiveRecord implements IdentityInterface
} }
/** /**
* Finds an identity by the given ID. * @inheritdoc
*
* @param string|integer $id the ID to be looked for
* @return IdentityInterface|null the identity object that matches the given ID.
*/ */
public static function findIdentity($id) public static function findIdentity($id)
{ {
...@@ -68,7 +75,7 @@ class User extends ActiveRecord implements IdentityInterface ...@@ -68,7 +75,7 @@ class User extends ActiveRecord implements IdentityInterface
} }
/** /**
* @return int|string|array current user ID * @inheritdoc
*/ */
public function getId() public function getId()
{ {
...@@ -76,7 +83,7 @@ class User extends ActiveRecord implements IdentityInterface ...@@ -76,7 +83,7 @@ class User extends ActiveRecord implements IdentityInterface
} }
/** /**
* @return string current user auth key * @inheritdoc
*/ */
public function getAuthKey() public function getAuthKey()
{ {
...@@ -84,8 +91,7 @@ class User extends ActiveRecord implements IdentityInterface ...@@ -84,8 +91,7 @@ class User extends ActiveRecord implements IdentityInterface
} }
/** /**
* @param string $authKey * @inheritdoc
* @return boolean if auth key is valid for current user
*/ */
public function validateAuthKey($authKey) public function validateAuthKey($authKey)
{ {
...@@ -93,6 +99,8 @@ class User extends ActiveRecord implements IdentityInterface ...@@ -93,6 +99,8 @@ class User extends ActiveRecord implements IdentityInterface
} }
/** /**
* Validates password
*
* @param string $password password to validate * @param string $password password to validate
* @return bool if password provided is valid for current user * @return bool if password provided is valid for current user
*/ */
...@@ -101,6 +109,35 @@ class User extends ActiveRecord implements IdentityInterface ...@@ -101,6 +109,35 @@ class User extends ActiveRecord implements IdentityInterface
return Security::validatePassword($password, $this->password_hash); return Security::validatePassword($password, $this->password_hash);
} }
/**
* Generates password hash from password and sets it to the model
*
* @param string $password
*/
public function setPassword($password)
{
$this->password_hash = Security::generatePasswordHash($password);
}
/**
* Generates new password reset token
*/
public function generatePasswordResetToken()
{
$this->password_reset_token = Security::generateRandomKey();
}
/**
* Removes password reset token
*/
public function removePasswordResetToken()
{
$this->password_reset_token = '';
}
/**
* @inheritdoc
*/
public function rules() public function rules()
{ {
return [ return [
...@@ -117,34 +154,7 @@ class User extends ActiveRecord implements IdentityInterface ...@@ -117,34 +154,7 @@ class User extends ActiveRecord implements IdentityInterface
['email', 'filter', 'filter' => 'trim'], ['email', 'filter', 'filter' => 'trim'],
['email', 'required'], ['email', 'required'],
['email', 'email'], ['email', 'email'],
['email', 'unique', 'message' => 'This email address has already been taken.', 'on' => 'signup'], ['email', 'unique'],
['email', 'exist', 'message' => 'There is no user with such email.', 'on' => 'requestPasswordResetToken'],
['password', 'required'],
['password', 'string', 'min' => 6],
]; ];
} }
public function scenarios()
{
return [
'signup' => ['username', 'email', 'password', '!status', '!role'],
'resetPassword' => ['password'],
'requestPasswordResetToken' => ['email'],
];
}
public function beforeSave($insert)
{
if (parent::beforeSave($insert)) {
if (($this->isNewRecord || $this->getScenario() === 'resetPassword') && !empty($this->password)) {
$this->password_hash = Security::generatePasswordHash($this->password);
}
if ($this->isNewRecord) {
$this->auth_key = Security::generateRandomKey();
}
return true;
}
return false;
}
} }
<?php <?php
namespace frontend\controllers; namespace frontend\controllers;
use Yii;
use yii\web\Controller;
use common\models\LoginForm; use common\models\LoginForm;
use frontend\models\PasswordResetRequestForm;
use frontend\models\ResetPasswordForm;
use frontend\models\SignupForm;
use frontend\models\ContactForm; use frontend\models\ContactForm;
use common\models\User; use yii\base\InvalidParamException;
use yii\web\BadRequestHttpException; use yii\web\BadRequestHttpException;
use yii\helpers\Security; use yii\web\Controller;
use Yii;
/**
* Site controller
*/
class SiteController extends Controller class SiteController extends Controller
{ {
/**
* @inheritdoc
*/
public function behaviors() public function behaviors()
{ {
return [ return [
...@@ -34,6 +41,9 @@ class SiteController extends Controller ...@@ -34,6 +41,9 @@ class SiteController extends Controller
]; ];
} }
/**
* @inheritdoc
*/
public function actions() public function actions()
{ {
return [ return [
...@@ -59,7 +69,7 @@ class SiteController extends Controller ...@@ -59,7 +69,7 @@ class SiteController extends Controller
} }
$model = new LoginForm(); $model = new LoginForm();
if ($model->load($_POST) && $model->login()) { if ($model->load(Yii::$app->request->post()) && $model->login()) {
return $this->goBack(); return $this->goBack();
} else { } else {
return $this->render('login', [ return $this->render('login', [
...@@ -94,11 +104,13 @@ class SiteController extends Controller ...@@ -94,11 +104,13 @@ class SiteController extends Controller
public function actionSignup() public function actionSignup()
{ {
$model = new User(); $model = new SignupForm();
$model->setScenario('signup'); if ($model->load(Yii::$app->request->post())) {
if ($model->load($_POST) && $model->save()) { $user = $model->signup();
if (Yii::$app->getUser()->login($model)) { if ($user) {
return $this->goHome(); if (Yii::$app->getUser()->login($user)) {
return $this->goHome();
}
} }
} }
...@@ -109,16 +121,14 @@ class SiteController extends Controller ...@@ -109,16 +121,14 @@ class SiteController extends Controller
public function actionRequestPasswordReset() public function actionRequestPasswordReset()
{ {
$model = new User(); $model = new PasswordResetRequestForm();
$model->scenario = 'requestPasswordResetToken'; if ($model->load(Yii::$app->request->post()) && $model->sendEmail()) {
if ($model->load($_POST) && $model->validate()) { Yii::$app->getSession()->setFlash('success', 'Check your email for further instructions.');
if ($this->sendPasswordResetEmail($model->email)) { return $this->goHome();
Yii::$app->getSession()->setFlash('success', 'Check your email for further instructions.'); } else {
return $this->goHome(); Yii::$app->getSession()->setFlash('error', 'There was an error sending email.');
} else {
Yii::$app->getSession()->setFlash('error', 'There was an error sending email.');
}
} }
return $this->render('requestPasswordResetToken', [ return $this->render('requestPasswordResetToken', [
'model' => $model, 'model' => $model,
]); ]);
...@@ -126,21 +136,13 @@ class SiteController extends Controller ...@@ -126,21 +136,13 @@ class SiteController extends Controller
public function actionResetPassword($token) public function actionResetPassword($token)
{ {
if (empty($token) || is_array($token)) { try {
throw new BadRequestHttpException('Invalid password reset token.'); $model = new ResetPasswordForm($token);
} } catch (InvalidParamException $e) {
throw new BadRequestHttpException($e->getMessage());
$model = User::find([
'password_reset_token' => $token,
'status' => User::STATUS_ACTIVE,
]);
if ($model === null) {
throw new BadRequestHttpException('Wrong password reset token.');
} }
$model->scenario = 'resetPassword'; if ($model->load($_POST) && $model->resetPassword()) {
if ($model->load($_POST) && $model->save()) {
Yii::$app->getSession()->setFlash('success', 'New password was saved.'); Yii::$app->getSession()->setFlash('success', 'New password was saved.');
return $this->goHome(); return $this->goHome();
} }
...@@ -149,27 +151,4 @@ class SiteController extends Controller ...@@ -149,27 +151,4 @@ class SiteController extends Controller
'model' => $model, 'model' => $model,
]); ]);
} }
private function sendPasswordResetEmail($email)
{
$user = User::find([
'status' => User::STATUS_ACTIVE,
'email' => $email,
]);
if (!$user) {
return false;
}
$user->password_reset_token = Security::generateRandomKey();
if ($user->save(false)) {
return \Yii::$app->mail->compose('passwordResetToken', ['user' => $user])
->setFrom([\Yii::$app->params['supportEmail'] => \Yii::$app->name . ' robot'])
->setTo($email)
->setSubject('Password reset for ' . \Yii::$app->name)
->send();
}
return false;
}
} }
<?php
namespace frontend\models;
use common\models\User;
use yii\base\Model;
/**
* Password reset request form
*/
class PasswordResetRequestForm extends Model
{
public $email;
/**
* @inheritdoc
*/
public function rules()
{
return [
['email', 'filter', 'filter' => 'trim'],
['email', 'required'],
['email', 'email'],
['email', 'exist', 'targetClass' => 'User', 'message' => 'There is no user with such email.'],
];
}
/**
*
* @return boolean sends an email
*/
public function sendEmail()
{
/** @var User $user */
$user = User::find([
'status' => User::STATUS_ACTIVE,
'email' => $this->email,
]);
if (!$user) {
return false;
}
$user->generatePasswordResetToken();
if ($user->save()) {
return \Yii::$app->mail->compose('passwordResetToken', ['user' => $user])
->setFrom([\Yii::$app->params['supportEmail'] => \Yii::$app->name . ' robot'])
->setTo($this->email)
->setSubject('Password reset for ' . \Yii::$app->name)
->send();
}
return false;
}
}
\ No newline at end of file
<?php
namespace frontend\models;
use common\models\User;
use yii\base\InvalidParamException;
use yii\base\Model;
use Yii;
/**
* Password reset form
*/
class ResetPasswordForm extends Model
{
public $password;
/**
* @var \common\models\User
*/
private $_user;
/**
* Creates a form model given a token
*
* @param string $token
* @param array $config name-value pairs that will be used to initialize the object properties
* @throws \yii\base\InvalidParamException if token is empty or not valid
*/
public function __construct($token, $config = [])
{
if (empty($token) || !is_string($token)) {
throw new InvalidParamException('Password reset token cannot be blank.');
}
$this->_user = User::find([
'password_reset_token' => $token,
'status' => User::STATUS_ACTIVE,
]);
if (!$this->_user) {
throw new InvalidParamException('Wrong password reset token.');
}
parent::__construct($config);
}
/**
* @return array the validation rules.
*/
public function rules()
{
return [
['password', 'required'],
['password', 'string', 'min' => 6],
];
}
/**
* Resets password.
* @return boolean if password was reset.
*/
public function resetPassword()
{
$user = $this->_user;
$user->password = $this->password;
$user->removePasswordResetToken();
return $user->save();
}
}
\ No newline at end of file
<?php
namespace frontend\models;
use common\models\User;
use yii\base\Model;
use Yii;
/**
* Signup form
*/
class SignupForm extends Model
{
public $username;
public $email;
public $password;
/**
* @inheritdoc
*/
public function rules()
{
return [
['username', 'filter', 'filter' => 'trim'],
['username', 'required'],
['username', 'string', 'min' => 2, 'max' => 255],
['email', 'filter', 'filter' => 'trim'],
['email', 'required'],
['email', 'email'],
['email', 'unique', 'targetClass' => 'User', 'message' => 'This email address has already been taken.'],
['password', 'required'],
['password', 'string', 'min' => 6],
];
}
/**
* Signs user up.
* @return User saved model
*/
public function signup()
{
if ($this->validate()) {
return User::create($this->attributes);
}
return null;
}
}
\ No newline at end of file
...@@ -6,7 +6,7 @@ use yii\captcha\Captcha; ...@@ -6,7 +6,7 @@ use yii\captcha\Captcha;
/** /**
* @var yii\web\View $this * @var yii\web\View $this
* @var yii\widgets\ActiveForm $form * @var yii\widgets\ActiveForm $form
* @var frontend\models\ContactForm $model * @var \frontend\models\ContactForm $model
*/ */
$this->title = 'Contact'; $this->title = 'Contact';
$this->params['breadcrumbs'][] = $this->title; $this->params['breadcrumbs'][] = $this->title;
......
...@@ -5,7 +5,7 @@ use yii\widgets\ActiveForm; ...@@ -5,7 +5,7 @@ use yii\widgets\ActiveForm;
/** /**
* @var yii\web\View $this * @var yii\web\View $this
* @var yii\widgets\ActiveForm $form * @var yii\widgets\ActiveForm $form
* @var common\models\LoginForm $model * @var \common\models\LoginForm $model
*/ */
$this->title = 'Login'; $this->title = 'Login';
$this->params['breadcrumbs'][] = $this->title; $this->params['breadcrumbs'][] = $this->title;
......
...@@ -5,7 +5,7 @@ use yii\widgets\ActiveForm; ...@@ -5,7 +5,7 @@ use yii\widgets\ActiveForm;
/** /**
* @var yii\web\View $this * @var yii\web\View $this
* @var yii\widgets\ActiveForm $form * @var yii\widgets\ActiveForm $form
* @var common\models\User $model * @var \frontend\models\PasswordResetRequestForm $model
*/ */
$this->title = 'Request password reset'; $this->title = 'Request password reset';
$this->params['breadcrumbs'][] = $this->title; $this->params['breadcrumbs'][] = $this->title;
......
...@@ -5,7 +5,7 @@ use yii\widgets\ActiveForm; ...@@ -5,7 +5,7 @@ use yii\widgets\ActiveForm;
/** /**
* @var yii\web\View $this * @var yii\web\View $this
* @var yii\widgets\ActiveForm $form * @var yii\widgets\ActiveForm $form
* @var common\models\User $model * @var \frontend\models\ResetPasswordForm $model
*/ */
$this->title = 'Reset password'; $this->title = 'Reset password';
$this->params['breadcrumbs'][] = $this->title; $this->params['breadcrumbs'][] = $this->title;
......
...@@ -5,7 +5,7 @@ use yii\widgets\ActiveForm; ...@@ -5,7 +5,7 @@ use yii\widgets\ActiveForm;
/** /**
* @var yii\web\View $this * @var yii\web\View $this
* @var yii\widgets\ActiveForm $form * @var yii\widgets\ActiveForm $form
* @var common\models\User $model * @var \frontend\models\SignupForm $model
*/ */
$this->title = 'Signup'; $this->title = 'Signup';
$this->params['breadcrumbs'][] = $this->title; $this->params['breadcrumbs'][] = $this->title;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment