add admin controller and many updates

This commit is contained in:
Antonio Ramirez
2016-12-04 23:10:06 +01:00
parent abe959d8cc
commit bda69d38af
20 changed files with 601 additions and 117 deletions

View File

@ -3,6 +3,7 @@
namespace Da\User;
use Da\User\Helper\ClassMapHelper;
use Da\User\Model\Profile;
use Yii;
use yii\authclient\Collection;
use yii\base\Application;
@ -49,7 +50,7 @@ class Bootstrap implements BootstrapInterface
$di->set(Strategy\InsecureEmailChangeStrategy::class);
$di->set(Strategy\SecureEmailChangeStrategy::class);
// models + active query classes
// models + classMap
$modelClassMap = [];
foreach ($map as $class => $definition) {
@ -57,16 +58,36 @@ class Bootstrap implements BootstrapInterface
$model = is_array($definition) ? $definition['class'] : $definition;
$name = (substr($class, strrpos($class, '\\') + 1));
$modelClassMap[$name] = $model;
}
if (in_array($name, ['User', 'Profile', 'Token', 'Account'])) {
// query classes
$di->set(
$name . 'Query',
function () use ($model) {
return $model->find();
Query\ProfileQuery::class,
function () {
return Model\Profile::find();
}
);
$di->set(
Query\SocialNetworkAccountQuery::class,
function () {
return Model\SocialNetworkAccount::find();
}
);
$di->set(
Query\TokenQuery::class,
function () {
return Model\Token::find();
}
);
$di->set(
Query\UserQuery::class,
function () {
return Model\User::find();
}
);
// search class
$di->set(Search\UserSearch::class, [$di->get('UserQuery')]);
// helpers
$di->set(Helper\AuthHelper::class);
@ -86,6 +107,8 @@ class Bootstrap implements BootstrapInterface
// services
$di->set(Service\UserCreateService::class);
$di->set(Service\UserRegisterService::class);
$di->set(Service\UserConfirmationService::class);
// events
$di->set(Event\FormEvent::class);
@ -147,6 +170,7 @@ class Bootstrap implements BootstrapInterface
protected function initMailServiceConfiguration(Application $app, Module $module)
{
$defaults = [
'fromEmail' => 'no-reply@example.com',
'welcomeMailSubject' => Yii::t('user', 'Welcome to {0}', $app->name),
'confirmationMailSubject' => Yii::t('user', 'Confirm account on {0}', $app->name),
'reconfirmationMailSubject' => Yii::t('user', 'Confirm email change on {0}', $app->name),

View File

@ -2,21 +2,62 @@
namespace Da\User\Controller;
use Da\User\Event\UserEvent;
use Da\User\Factory\MailFactory;
use Da\User\Filter\AccessRuleFilter;
use Da\User\Model\Profile;
use Da\User\Model\User;
use Da\User\Query\UserQuery;
use Da\User\Search\UserSearch;
use Da\User\Service\UserBlockService;
use Da\User\Service\UserConfirmationService;
use Da\User\Service\UserCreateService;
use Da\User\Traits\ContainerTrait;
use Da\User\Traits\ModuleTrait;
use Da\User\Validator\AjaxRequestModelValidator;
use Yii;
use yii\base\Module;
use yii\db\ActiveRecord;
use yii\filters\AccessControl;
use yii\filters\VerbFilter;
use yii\helpers\Url;
use yii\web\Controller;
use Yii;
class AdminController extends Controller
{
use ModuleTrait;
use ContainerTrait;
protected $userQuery;
/**
* AdminController constructor.
*
* @param string $id
* @param Module $module
* @param UserQuery $userQuery
* @param array $config
*/
public function __construct($id, Module $module, UserQuery $userQuery, array $config)
{
$this->userQuery = $userQuery;
parent::__construct($id, $module, $config);
}
/**
* @param \yii\base\Action $action
*
* @return bool
*/
public function beforeAction($action)
{
if (in_array($action->id, ['index', 'update', 'update-profile', 'info', 'assingments'])) {
Url::remember('', 'actions-redirect');
}
return parent::beforeAction($action);
}
/**
* @inheritdoc
*/
@ -46,6 +87,20 @@ class AdminController extends Controller
];
}
public function actionIndex()
{
$searchModel = $this->make(UserSearch::class);
$dataProvider = $searchModel->search(Yii::$app->request->get());
return $this->render(
'index',
[
'dataProvider' => $dataProvider,
'searchModel' => $searchModel
]
);
}
public function actionCreate()
{
/** @var User $user */
@ -59,17 +114,155 @@ class AdminController extends Controller
$this->trigger(UserEvent::EVENT_BEFORE_CREATE, $event);
if ($user->load(Yii::$app->request->post())) {
/** @var UserCreateService $userCreateService */
$userCreateService = $this->make(UserCreateService::class, [$user]);
$userCreateService->run();
$mailService = MailFactory::makeWelcomeMailerService($user);
$this->make(UserCreateService::class, [$user, $mailService])->run();
$this->trigger(UserEvent::EVENT_AFTER_CREATE, $event);
return $this->redirect(['update', 'id' => $user->id]);
}
return $this->render('create', [
return $this->render('create', ['user' => $user]);
}
public function actionUpdate($id)
{
$user = $this->userQuery->where(['id' => $id])->one();
$user->setScenario('update');
/** @var UserEvent $event */
$event = $this->make(UserEvent::class, [$user]);
$this->make(AjaxRequestModelValidator::class, [$user])->validate();
$this->trigger(ActiveRecord::EVENT_BEFORE_UPDATE, $event);
if ($user->load(Yii::$app->request->post()) && $user->save()) {
Yii::$app->getSession()->setFlash('success', Yii::t('user', 'Account details have been updated'));
$this->trigger(ActiveRecord::EVENT_AFTER_UPDATE, $event);
return $this->refresh();
}
return $this->render('_account', ['user' => $user]);
}
public function actionUpdateProfile($id)
{
/** @var User $user */
$user = $this->userQuery->where(['id' => $id])->one();
$profile = $user->profile;
if ($profile === null) {
$profile = $this->make(Profile::class);
$profile->link($user);
}
/** @var UserEvent $event */
$event = $this->make(UserEvent::class, [$user]);
$this->make(AjaxRequestModelValidator::class, [$user])->validate();
$this->trigger(UserEvent::EVENT_BEFORE_PROFILE_UPDATE, $event);
if ($profile->load(Yii::$app->request->post()) && $profile->save()) {
Yii::$app->getSession()->setFlash('success', Yii::t('user', 'Profile details have been updated'));
$this->trigger(UserEvent::EVENT_AFTER_PROFILE_UPDATE, $event);
return $this->refresh();
}
return $this->render(
'_profile',
[
'user' => $user,
]);
'profile' => $profile
]
);
}
public function actionInfo($id)
{
/** @var User $user */
$user = $this->userQuery->where(['id' => $id])->one();
return $this->render(
'_info',
[
'user' => $user,
]
);
}
public function actionAssignments($id)
{
/** @var User $user */
$user = $this->userQuery->where(['id' => $id])->one();
return $this->render(
'_assignments',
[
'user' => $user,
]
);
}
public function actionConfirm($id)
{
/** @var User $user */
$user = $this->userQuery->where(['id' => $id])->one();
/** @var UserEvent $event */
$event = $this->make(UserEvent::class, [$user]);
$this->trigger(UserEvent::EVENT_BEFORE_CONFIRMATION, $event);
if ($this->make(UserConfirmationService::class, [$user])->run()) {
Yii::$app->getSession()->setFlash('success', Yii::t('user', 'User has been confirmed'));
$this->trigger(UserEvent::EVENT_AFTER_CONFIRMATION, $event);
} else {
Yii::$app->getSession()->setFlash('warning', Yii::t('user', 'Unable to confirm user. Please, try again.'));
}
return $this->redirect(Url::previous('actions-redirect'));
}
public function actionDelete($id)
{
if ($id === Yii::$app->user->getId()) {
Yii::$app->getSession()->setFlash('danger', Yii::t('user', 'You cannot remove your own account'));
} else {
/** @var User $user */
$user = $this->userQuery->where(['id' => $id])->one();
/** @var UserEvent $event */
$event = $this->make(UserEvent::class, [$user]);
$this->trigger(ActiveRecord::EVENT_BEFORE_DELETE, $event);
if ($user->delete()) {
Yii::$app->getSession()->setFlash('success', \Yii::t('user', 'User has been deleted'));
$this->trigger(ActiveRecord::EVENT_AFTER_DELETE, $event);
} else {
Yii::$app->getSession()->setFlash(
'warning',
Yii::t('user', 'Unable to delete user. Please, try again later.')
);
}
}
return $this->redirect(['index']);
}
public function actionBlock($id)
{
if ($id === Yii::$app->user->getId()) {
Yii::$app->getSession()->setFlash('danger', Yii::t('user', 'You cannot remove your own account'));
} else {
/** @var User $user */
$user = $this->userQuery->where(['id' => $id])->one();
/** @var UserEvent $event */
$event = $this->make(UserEvent::class, [$user]);
if ($this->make(UserBlockService::class, [$user, $event])->run()) {
Yii::$app->getSession()->setFlash('success', Yii::t('user', 'User block status has been updated.'));
} else {
Yii::$app->getSession()->setFlash('danger', Yii::t('user', 'Unable to update block status.'));
}
}
return $this->redirect(Url::previous('actions-redirect'));
}
}

View File

@ -8,6 +8,12 @@ class UserEvent extends Event
{
const EVENT_BEFORE_CREATE = 'beforeCreate';
const EVENT_AFTER_CREATE = 'afterCreate';
const EVENT_BEFORE_REGISTER = 'beforeRegister';
const EVENT_AFTER_REGISTER = 'afterRegister';
const EVENT_BEFORE_PROFILE_UPDATE = 'beforeProfileUpdate';
const EVENT_AFTER_PROFILE_UPDATE = 'afterProfileUpdate';
const EVENT_BEFORE_CONFIRMATION = 'beforeConfirmation';
const EVENT_AFTER_CONFIRMATION = 'afterConfirmation';
protected $user;

View File

@ -0,0 +1,44 @@
<?php
namespace Da\User\Factory;
use Da\User\Model\User;
use Da\User\Module;
use Da\User\Service\MailService;
use Da\User\Traits\ModuleTrait;
use Yii;
class MailFactory
{
public static function makeWelcomeMailerService(User $user)
{
/** @var Module $module */
$module = Yii::$app->getModule('user');
$to = $user->email;
$from = $module->mailParams['fromEmail'];
$subject = $module->mailParams['welcomeMailSubject'];
$params = [
'user' => $user,
'token' => null,
'module' => $module,
'showPassword' => false
];
return static::makeMailerService($from, $to, $subject, 'welcome', $params);
}
/**
* Builds a MailerService
*
* @param string $from
* @param string $to
* @param string $subject
* @param string $view
* @param array $params
*
* @return MailService
*/
public static function makeMailerService($from, $to, $subject, $view, array $params = [])
{
return Yii::$container->get(MailService::class, [$from, $to, $subject, $view, $params]);
}
}

View File

@ -32,6 +32,11 @@ class AuthHelper
return false;
}
/**
* @param $username
*
* @return bool
*/
public function isAdmin($username)
{
/** @var Module $module */

View File

@ -1,15 +1,17 @@
<?php
namespace Da\User\Model;
/**
*
* Profile.php
*
* Date: 4/12/16
* Time: 14:57
* @author Antonio Ramirez <hola@2amigos.us>
*/
class Profile
{
use Da\User\Query\ProfileQuery;
use yii\db\ActiveRecord;
class Profile extends ActiveRecord
{
/**
* @return ProfileQuery
*/
public static function find()
{
return new ProfileQuery(static::class);
}
}

View File

@ -1,15 +1,16 @@
<?php
namespace Da\User\Model;
/**
*
* SocialNetworkAccount.php
*
* Date: 4/12/16
* Time: 14:56
* @author Antonio Ramirez <hola@2amigos.us>
*/
class SocialNetworkAccount
{
use Da\User\Query\SocialNetworkAccountQuery;
use yii\db\ActiveRecord;
class SocialNetworkAccount extends ActiveRecord
{
/**
* @return SocialNetworkAccountQuery
*/
public static function find()
{
return new SocialNetworkAccountQuery(static::class);
}
}

View File

@ -1,15 +1,22 @@
<?php
namespace Da\User\Model;
/**
*
* Token.php
*
* Date: 4/12/16
* Time: 15:17
* @author Antonio Ramirez <hola@2amigos.us>
*/
class Token
{
use Da\User\Query\TokenQuery;
use yii\db\ActiveRecord;
class Token extends ActiveRecord
{
const TYPE_CONFIRMATION = 0;
const TYPE_RECOVERY = 1;
const TYPE_CONFIRM_NEW_EMAIL = 2;
const TYPE_CONFIRM_OLD_EMAIL = 3;
/**
* @return TokenQuery
*/
public static function find()
{
return new TokenQuery(static::class);
}
}

View File

@ -1,15 +1,15 @@
<?php
namespace Da\User\Model;
use Da\User\Helper\AuthHelper;
use Da\User\Traits\ModuleTrait;
use Da\User\Query\UserQuery;
use Da\User\Traits\ContainerTrait;
use Da\User\Traits\ModuleTrait;
use Yii;
use yii\base\NotSupportedException;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use yii\helpers\ArrayHelper;
use yii\web\IdentityInterface;
use Yii;
/**
* User ActiveRecord model.
@ -47,6 +47,10 @@ class User extends ActiveRecord implements IdentityInterface
* @var string Plain password. Used for model validation.
*/
public $password;
/**
* @var array connected account list
*/
protected $connectedAccounts;
/**
* @inheritdoc
@ -178,7 +182,7 @@ class User extends ActiveRecord implements IdentityInterface
*/
public function getIsAdmin()
{
return $this->getAuthHelper()->isAdmin($this->username);
return $this->getAuth()->isAdmin($this->username);
}
/**
@ -190,7 +194,7 @@ class User extends ActiveRecord implements IdentityInterface
*/
public function hasRole($role)
{
return $this->getAuthHelper()->hasRole($this->id, $role);
return $this->getAuth()->hasRole($this->id, $role);
}
/**
@ -198,20 +202,17 @@ class User extends ActiveRecord implements IdentityInterface
*/
public function getProfile()
{
return $this->hasOne($this->getClassMapHelper()->get('Profile'), ['user_id' => 'id']);
return $this->hasOne($this->getClassMap()->get('Profile'), ['user_id' => 'id']);
}
protected $connectedAccounts;
/**
* @return SocialNetworkAccount[] social connected accounts [ 'providerName' => socialAccountModel ]
*/
public function getSocialNetworkAccounts()
{
if ($this->connectedAccounts == null) {
$accounts = $this->connectedAccounts = $this
->hasMany($this->getClassMapHelper()->get('Account'), ['user_id' => 'id'])
->all();
/** @var SocialNetworkAccount[] $accounts */
$accounts = $this->hasMany($this->getClassMap()->get('Account'), ['user_id' => 'id'])->all();
foreach($accounts as $account) {
$this->connectedAccounts[$account->provider] = $account;
@ -219,4 +220,12 @@ class User extends ActiveRecord implements IdentityInterface
}
return $this->connectedAccounts;
}
/**
* @return UserQuery
*/
public static function find()
{
return new UserQuery(static::class);
}
}

View File

@ -9,15 +9,15 @@ class Module extends \yii\base\Module
/**
* @var bool whether to allow registration process or not.
*/
public $allowRegistration = true;
public $enableRegistration = true;
/**
* @var bool whether to force email confirmation to.
*/
public $enableEmailConfirmation = true;
/**
* @var bool whether to generate passwords automatically and remove the password field from the registration form.
*/
public $generatePasswords = false;
/**
* @var bool whether to force email confirmation to.
*/
public $forceEmailConfirmation = true;
/**
* @var bool whether to allow login accounts with unconfirmed emails.
*/
@ -50,12 +50,6 @@ class Module extends \yii\base\Module
* @var string the administrator permission name
*/
public $administratorPermissionName;
/**
* @var array the class map used by the module.
*
* @see Bootstrap
*/
public $classmap = [];
/**
* @var string the route prefix
*/

View File

@ -1,15 +0,0 @@
<?php
namespace Da\User\Query;
/**
*
* AccountQuery.php
*
* Date: 3/12/16
* Time: 15:23
* @author Antonio Ramirez <hola@2amigos.us>
*/
class AccountQuery
{
}

View File

@ -1,15 +1,9 @@
<?php
namespace Da\User\Query;
/**
*
* ProfileQuery.php
*
* Date: 3/12/16
* Time: 15:23
* @author Antonio Ramirez <hola@2amigos.us>
*/
class ProfileQuery
use yii\db\ActiveQuery;
class ProfileQuery extends ActiveQuery
{
}

View File

@ -0,0 +1,9 @@
<?php
namespace Da\User\Query;
use yii\db\ActiveQuery;
class SocialNetworkAccountQuery extends ActiveQuery
{
}

View File

@ -1,15 +1,9 @@
<?php
namespace Da\User\Query;
/**
*
* TokenQuery.php
*
* Date: 3/12/16
* Time: 15:23
* @author Antonio Ramirez <hola@2amigos.us>
*/
class TokenQuery
use yii\db\ActiveQuery;
class TokenQuery extends ActiveQuery
{
}

View File

@ -1,15 +1,10 @@
<?php
namespace Da\User\Query;
/**
*
* UserQuery.php
*
* Date: 3/12/16
* Time: 15:22
* @author Antonio Ramirez <hola@2amigos.us>
*/
class UserQuery
use yii\db\ActiveQuery;
class UserQuery extends ActiveQuery
{
}

View File

@ -0,0 +1,98 @@
<?php
namespace Da\User\Search;
use Da\User\Query\UserQuery;
use Yii;
use yii\base\Model;
use yii\data\ActiveDataProvider;
class UserSearch extends Model
{
/**
* @var string
*/
public $username;
/**
* @var string
*/
public $email;
/**
* @var int
*/
public $created_at;
/**
* @var string
*/
public $registration_ip;
/**
* @var UserQuery
*/
protected $query;
/**
* UserSearch constructor.
*
* @param UserQuery $query
* @param array $config
*/
public function __construct(UserQuery $query, $config = [])
{
$this->query = $query;
parent::__construct($config);
}
/**
* @return array
*/
public function rules()
{
return [
'safeFields' => [['username', 'email', 'registration_ip', 'created_at'], 'safe'],
'createdDefault' => ['created_at', 'default', 'value' => null],
];
}
/**
* @return array
*/
public function attributeLabels()
{
return [
'username' => Yii::t('user', 'Username'),
'email' => Yii::t('user', 'Email'),
'created_at' => Yii::t('user', 'Registration time'),
'registration_ip' => Yii::t('user', 'Registration ip'),
];
}
/**
* @param $params
*
* @return ActiveDataProvider
*/
public function search($params)
{
$query = $this->query;
$dataProvider = new ActiveDataProvider([
'query' => $query,
]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
if ($this->created_at !== null) {
$date = strtotime($this->created_at);
$query->andFilterWhere(['between', 'created_at', $date, $date + 3600 * 24]);
}
$query
->andFilterWhere(['like', 'username', $this->username])
->andFilterWhere(['like', 'email', $this->email])
->andFilterWhere(['registration_ip' => $this->registration_ip]);
return $dataProvider;
}
}

View File

@ -0,0 +1,23 @@
<?php
namespace Da\User\Service;
use Da\User\Contracts\ServiceInterface;
use Da\User\Event\UserEvent;
use Da\User\Model\User;
class UserBlockService implements ServiceInterface
{
protected $model;
protected $event;
public function __construct(User $model, UserEvent $event)
{
$this->model = $model;
$this->event = $event;
}
public function run()
{
}
}

View File

@ -0,0 +1,25 @@
<?php
namespace Da\User\Service;
use Da\User\Contracts\ServiceInterface;
use Da\User\Event\UserEvent;
use Da\User\Model\User;
class UserConfirmationService implements ServiceInterface
{
protected $model;
public function __construct(User $model)
{
$this->model = $model;
}
public function run()
{
$this->model->trigger(UserEvent::EVENT_BEFORE_CONFIRMATION);
$result = (bool) $this->model->updateAttributes(['confirmed_at' => time()]);
$this->model->trigger(UserEvent::EVENT_AFTER_CONFIRMATION);
return $result;
}
}

View File

@ -0,0 +1,73 @@
<?php
namespace Da\User\Service;
use Da\User\Contracts\ServiceInterface;
use Da\User\Event\UserEvent;
use Da\User\Helper\SecurityHelper;
use Da\User\Model\Token;
use Da\User\Model\User;
use yii\base\InvalidCallException;
use yii\log\Logger;
use Exception;
class UserRegisterService implements ServiceInterface
{
protected $model;
protected $securityHelper;
protected $mailService;
protected $logger;
public function __construct(User $model, MailService $mailService, SecurityHelper $securityHelper, Logger $logger)
{
$this->model = $model;
$this->mailService = $mailService;
$this->securityHelper = $securityHelper;
$this->logger = $logger;
}
public function run()
{
$model = $this->model;
if ($model->getIsNewRecord() === false) {
throw new InvalidCallException('Cannot register user from an existing one.');
}
$transaction = $model->getDb()->beginTransaction();
try {
$model->confirmed_at = $this->model->module->enableEmailConfirmation ? null : time();
$model->password = $this->model->module->generatePasswords
? $this->securityHelper->generatePassword(8)
: $model->password;
$model->trigger(UserEvent::EVENT_BEFORE_REGISTER);
if(!$model->save()) {
$transaction->rollBack();
return false;
}
if($model->module->enableEmailConfirmation) {
$token = $model->make(Token::class, ['type' => Token::TYPE_CONFIRMATION]);
$token->link('user', $model);
}
$this->mailService->run();
$model->trigger(UserEvent::EVENT_AFTER_REGISTER);
$transaction->commit();
return true;
} catch(Exception $e) {
$transaction->rollBack();
$this->logger->log($e->getMessage(), Logger::LEVEL_WARNING);
return false;
}
}
}

View File

@ -8,8 +8,11 @@ use Yii;
use yii\di\Container;
/**
*
* @property-read Container $di
* @property-ready Da\User\Helper\AuthHelper $authHelper
* @property-ready Da\User\Helper\AuthHelper $auth
* @property-ready Da\User\Helper\ClassMapHelper $classMap
*
*/
trait ContainerTrait
{
@ -38,17 +41,17 @@ trait ContainerTrait
/**
* @return \Da\User\Helper\AuthHelper
*/
public function getAuthHelper()
public function getAuth()
{
return Yii::$container->get(AuthHelper::class);
return $this->getDi()->get(AuthHelper::class);
}
/**
* @return \Da\User\Helper\ClassMapHelper
*/
public function getClassMapHelper()
public function getClassMap()
{
return Yii::$container->get(ClassMapHelper::class);
return $this->getDi()->get(ClassMapHelper::class);
}
}