This commit is contained in:
Lorenzo Milesi
2022-08-11 09:08:25 +02:00
51 changed files with 2004 additions and 40 deletions

8
src/User/Bootstrap.php Normal file → Executable file
View File

@ -16,7 +16,9 @@ use Da\User\Contracts\AuthManagerInterface;
use Da\User\Controller\SecurityController;
use Da\User\Event\FormEvent;
use Da\User\Helper\ClassMapHelper;
use Da\User\Model\SessionHistory;
use Da\User\Model\User;
use Da\User\Search\SessionHistorySearch;
use Yii;
use yii\authclient\Collection;
use yii\base\Application;
@ -128,7 +130,7 @@ class Bootstrap implements BootstrapInterface
$model = is_array($definition) ? $definition['class'] : $definition;
$name = substr($class, strrpos($class, '\\') + 1);
$modelClassMap[$class] = $model;
if (in_array($name, ['User', 'Profile', 'Token', 'SocialNetworkAccount'])) {
if (in_array($name, ['User', 'Profile', 'Token', 'SocialNetworkAccount', 'SessionHistory'])) {
$di->set(
"Da\\User\\Query\\{$name}Query",
function () use ($model) {
@ -315,10 +317,12 @@ class Bootstrap implements BootstrapInterface
'Assignment' => 'Da\User\Model\Assignment',
'Permission' => 'Da\User\Model\Permission',
'Role' => 'Da\User\Model\Role',
'SessionHistory' => SessionHistory::class,
// --- search
'UserSearch' => 'Da\User\Search\UserSearch',
'PermissionSearch' => 'Da\User\Search\PermissionSearch',
'RoleSearch' => 'Da\User\Search\RoleSearch',
'SessionHistorySearch' => SessionHistorySearch::class,
// --- forms
'RegistrationForm' => 'Da\User\Form\RegistrationForm',
'ResendForm' => 'Da\User\Form\ResendForm',
@ -338,11 +342,13 @@ class Bootstrap implements BootstrapInterface
'Assignment',
'Permission',
'Role',
'SessionHistory'
],
'Da\User\Search' => [
'UserSearch',
'PermissionSearch',
'RoleSearch',
'SessionHistorySearch',
],
'Da\User\Form' => [
'RegistrationForm',

39
src/User/Controller/AdminController.php Normal file → Executable file
View File

@ -17,9 +17,11 @@ use Da\User\Filter\AccessRuleFilter;
use Da\User\Model\Profile;
use Da\User\Model\User;
use Da\User\Query\UserQuery;
use Da\User\Search\SessionHistorySearch;
use Da\User\Search\UserSearch;
use Da\User\Service\PasswordExpireService;
use Da\User\Service\PasswordRecoveryService;
use Da\User\Service\SessionHistory\TerminateUserSessionsService;
use Da\User\Service\SwitchIdentityService;
use Da\User\Service\UserBlockService;
use Da\User\Service\UserConfirmationService;
@ -66,7 +68,7 @@ class AdminController extends Controller
*/
public function beforeAction($action)
{
if (in_array($action->id, ['index', 'update', 'update-profile', 'info', 'assignments'], true)) {
if (in_array($action->id, ['index', 'update', 'update-profile', 'info', 'assignments', 'session-history'], true)) {
Url::remember('', 'actions-redirect');
}
@ -88,6 +90,7 @@ class AdminController extends Controller
'switch-identity' => ['post'],
'password-reset' => ['post'],
'force-password-change' => ['post'],
'terminate-sessions' => ['post'],
],
],
'access' => [
@ -101,6 +104,11 @@ class AdminController extends Controller
'actions' => ['switch-identity'],
'roles' => ['@'],
],
[
'allow' => $this->getModule()->enableSessionHistory,
'actions' => ['session-history', 'terminate-sessions'],
'roles' => ['admin'],
],
[
'allow' => true,
'roles' => ['admin'],
@ -346,4 +354,33 @@ class AdminController extends Controller
}
$this->redirect(['index']);
}
/**
* Display list session history
*/
public function actionSessionHistory($id)
{
$searchModel = new SessionHistorySearch([
'user_id' => $id,
]);
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
$user = $this->userQuery->where(['id' => $id])->one();
return $this->render('_session-history', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
'user' => $user,
]);
}
/**
* Terminate all session user
*/
public function actionTerminateSessions($id)
{
$this->make(TerminateUserSessionsService::class, [$id])->run();
return $this->redirect(Url::previous('actions-redirect'));
}
}

93
src/User/Controller/SettingsController.php Normal file → Executable file
View File

@ -26,7 +26,9 @@ use Da\User\Module;
use Da\User\Query\ProfileQuery;
use Da\User\Query\SocialNetworkAccountQuery;
use Da\User\Query\UserQuery;
use Da\User\Search\SessionHistorySearch;
use Da\User\Service\EmailChangeService;
use Da\User\Service\SessionHistory\TerminateUserSessionsService;
use Da\User\Service\TwoFactorQrCodeUriGeneratorService;
use Da\User\Traits\ContainerAwareTrait;
use Da\User\Traits\ModuleAwareTrait;
@ -91,7 +93,8 @@ class SettingsController extends Controller
'actions' => [
'disconnect' => ['post'],
'delete' => ['post'],
'two-factor-disable' => ['post']
'two-factor-disable' => ['post'],
'terminate-sessions' => ['post'],
],
],
'access' => [
@ -111,7 +114,7 @@ class SettingsController extends Controller
'delete',
'two-factor',
'two-factor-enable',
'two-factor-disable'
'two-factor-disable',
],
'roles' => ['@'],
],
@ -119,7 +122,12 @@ class SettingsController extends Controller
'allow' => true,
'actions' => ['confirm'],
'roles' => ['?', '@'],
]
],
[
'allow' => $this->getModule()->enableSessionHistory,
'actions' => ['session-history', 'terminate-sessions'],
'roles' => ['@'],
],
],
],
];
@ -139,9 +147,9 @@ class SettingsController extends Controller
}
/**
*
*
* @var ProfileEvent $event
*
* @var ProfileEvent $event
*/
$event = $this->make(ProfileEvent::class, [$profile]);
@ -175,7 +183,8 @@ class SettingsController extends Controller
throw new NotFoundHttpException();
}
return $this->render(
'privacy', [
'privacy',
[
'module' => $this->module
]
);
@ -196,9 +205,9 @@ class SettingsController extends Controller
throw new NotFoundHttpException();
}
/**
*
*
* @var GdprDeleteForm $form
*
* @var GdprDeleteForm $form
*/
$form = $this->make(GdprDeleteForm::class);
@ -249,7 +258,8 @@ class SettingsController extends Controller
}
return $this->render(
'gdpr-delete', [
'gdpr-delete',
[
'model' => $form,
]
);
@ -258,9 +268,9 @@ class SettingsController extends Controller
public function actionGdprConsent()
{
/**
*
*
* @var User $user
*
* @var User $user
*/
$user = Yii::$app->user->identity;
if ($user->gdpr_consent) {
@ -270,7 +280,9 @@ class SettingsController extends Controller
$model->addRule('gdpr_consent', 'boolean');
$model->addRule('gdpr_consent', 'default', ['value' => 0, 'skipOnEmpty' => false]);
$model->addRule(
'gdpr_consent', 'compare', [
'gdpr_consent',
'compare',
[
'compareValue' => true,
'message' => Yii::t('usuario', 'Your consent is required to work with this site'),
'when' => function () {
@ -289,7 +301,8 @@ class SettingsController extends Controller
}
return $this->render(
'gdpr-consent', [
'gdpr-consent',
[
'model' => $model,
'gdpr_consent_hint' => $this->module->getConsentMessage(),
]
@ -345,9 +358,9 @@ class SettingsController extends Controller
public function actionAccount()
{
/**
*
*
*
* @var SettingsForm $form
* @var SettingsForm $form
*/
$form = $this->make(SettingsForm::class);
$event = $this->make(UserEvent::class, [$form->getUser()]);
@ -416,9 +429,9 @@ class SettingsController extends Controller
}
/**
*
*
* @var User $user
*
* @var User $user
*/
$user = Yii::$app->user->identity;
$event = $this->make(UserEvent::class, [$user]);
@ -436,9 +449,9 @@ class SettingsController extends Controller
public function actionTwoFactor($id)
{
/**
*
*
* @var User $user
*
* @var User $user
*/
$user = $this->userQuery->whereId($id)->one();
@ -456,9 +469,9 @@ class SettingsController extends Controller
Yii::$app->response->format = Response::FORMAT_JSON;
/**
*
*
* @var User $user
*
* @var User $user
*/
$user = $this->userQuery->whereId($id)->one();
@ -487,12 +500,12 @@ class SettingsController extends Controller
public function actionTwoFactorDisable($id)
{
/**
*
*
* @var User $user
*
* @var User $user
*/
$user = $this->userQuery->whereId($id)->one();
if (null === $user) {
throw new NotFoundHttpException();
}
@ -511,7 +524,33 @@ class SettingsController extends Controller
}
/**
* @param $id
* Display list session history.
*/
public function actionSessionHistory()
{
$searchModel = new SessionHistorySearch([
'user_id' => Yii::$app->user->id,
]);
$dataProvider = $searchModel->search(Yii::$app->request->queryParams);
return $this->render('session-history', [
'searchModel' => $searchModel,
'dataProvider' => $dataProvider,
]);
}
/**
* Terminate all session user
*/
public function actionTerminateSessions()
{
$this->make(TerminateUserSessionsService::class, [Yii::$app->user->id])->run();
return $this->redirect(['session-history']);
}
/**
* @param $id
* @throws ForbiddenHttpException
* @throws NotFoundHttpException
* @throws \Exception
@ -521,9 +560,9 @@ class SettingsController extends Controller
protected function disconnectSocialNetwork($id)
{
/**
*
*
* @var SocialNetworkAccount $account
*
* @var SocialNetworkAccount $account
*/
$account = $this->socialNetworkAccountQuery->whereId($id)->one();

37
src/User/Event/SessionEvent.php Executable file
View File

@ -0,0 +1,37 @@
<?php
/*
* This file is part of the 2amigos/yii2-usuario project.
*
* (c) 2amigOS! <http://2amigos.us/>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Da\User\Event;
use Da\User\Model\User;
use yii\base\Event;
/**
* @property-read User $user
*/
class SessionEvent extends Event
{
const EVENT_BEFORE_TERMINATE_USER_SESSIONS = 'beforeTerminateUserSessions';
const EVENT_AFTER_TERMINATE_USER_SESSIONS = 'afterTerminateUserSessions';
protected $user;
public function __construct(User $user, array $config = [])
{
$this->user = $user;
parent::__construct($config);
}
public function getUser()
{
return $this->user;
}
}

View File

@ -0,0 +1,73 @@
<?php
/*
* This file is part of the 2amigos/yii2-usuario project.
*
* (c) 2amigOS! <http://2amigos.us/>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Da\User\Migration\Session;
use Da\User\Helper\MigrationHelper;
use yii\db\Migration;
class m000000_000001_create_session_history_table extends Migration
{
const SESSION_HISTORY_TABLE = '{{%session_history}}';
const USER_TABLE = '{{%user}}';
/**
* {@inheritdoc}
*/
public function safeUp()
{
$this->createTable(self::SESSION_HISTORY_TABLE, [
'user_id' => $this->integer(),
'session_id' => $this->string()->null(),
'user_agent' => $this->string()->notNull(),
'ip' => $this->string(45)->notNull(),
'created_at' => $this->integer()->notNull(),
'updated_at' => $this->integer()->notNull(),
]);
$this->createIndex(
'{{%session_history_user_id}}',
self::SESSION_HISTORY_TABLE,
['user_id']
);
$this->createIndex(
'{{%session_history_session_id}}',
self::SESSION_HISTORY_TABLE,
['session_id']
);
$this->createIndex(
'{{%session_history_updated_at}}',
self::SESSION_HISTORY_TABLE,
['updated_at']
);
$this->addForeignKey(
'{{%fk_user_session_history}}',
self::SESSION_HISTORY_TABLE,
'user_id',
self::USER_TABLE,
'id',
'CASCADE',
MigrationHelper::isMicrosoftSQLServer($this->db->driverName) ? 'NO ACTION' : 'RESTRICT'
);
}
/**
* {@inheritdoc}
*/
public function safeDown()
{
$this->dropTable(self::SESSION_HISTORY_TABLE);
}
}

110
src/User/Model/SessionHistory.php Executable file
View File

@ -0,0 +1,110 @@
<?php
/*
* This file is part of the 2amigos/yii2-usuario project.
*
* (c) 2amigOS! <http://2amigos.us/>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Da\User\Model;
use Da\User\Module;
use Da\User\Query\SessionHistoryQuery;
use Da\User\Traits\ModuleAwareTrait;
use Yii;
use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord;
use yii\db\ActiveQuery;
/**
* @property int $user_id
* @property string $session_id
* @property string $user_agent
* @property string $ip
* @property int $created_at
* @property int $updated_at
*
* @property User $user
* @property bool $isActive
*
* Dependencies:
* @property-read Module $module
*/
class SessionHistory extends ActiveRecord
{
use ModuleAwareTrait;
/**
* {@inheritdoc}
*/
public static function tableName()
{
return '{{%session_history}}';
}
/** @inheritdoc */
public function behaviors()
{
return [
[
'class' => TimestampBehavior::class,
'updatedAtAttribute' => false,
]
];
}
/**
* {@inheritdoc}
*/
public function attributeLabels()
{
return [
'user_id' => Yii::t('usuario', 'User ID'),
'session_id' => Yii::t('usuario', 'Session ID'),
'user_agent' => Yii::t('usuario', 'User agent'),
'ip' => Yii::t('usuario', 'IP'),
'created_at' => Yii::t('usuario', 'Created at'),
'updated_at' => Yii::t('usuario', 'Last activity'),
];
}
/**
* @return bool Whether the session is an active or not.
*/
public function getIsActive()
{
return isset($this->session_id) && $this->updated_at + $this->getModule()->rememberLoginLifespan > time();
}
/**
* @return ActiveQuery
*/
public function getUser()
{
return $this->hasOne($this->module->classMap['User'], ['id' => 'user_id']);
}
/** @inheritdoc */
public function beforeSave($insert)
{
if ($insert && empty($this->session_id)) {
$this->setAttribute('session_id', Yii::$app->session->getId());
}
return parent::beforeSave($insert);
}
/** @inheritdoc */
public static function primaryKey()
{
return ['user_id', 'session_id'];
}
public static function find()
{
return new SessionHistoryQuery(static::class);
}
}

31
src/User/Module.php Normal file → Executable file
View File

@ -22,6 +22,21 @@ use yii\helpers\Html;
*/
class Module extends BaseModule
{
/**
* @var bool Enable the 'session history' function
* Using with {@see SessionHistoryDecorator}
*/
public $enableSessionHistory = false;
/**
* @var int|bool The number of 'session history' records will be stored for user
* if equals false records will not be deleted
*/
public $numberSessionHistory = false;
/**
* @var int|bool The time after which the expired 'session history' will be deleted
* if equals false records will not be deleted
*/
public $timeoutSessionHistory = false;
/**
* @var bool whether to enable european G.D.P.R. compliance.
* This will add a few elements to comply with european general data protection regulation.
@ -251,4 +266,20 @@ class Module extends BaseModule
return $this->gdprConsentMessage ?: $defaultConsentMessage;
}
/**
* @return bool
*/
public function hasNumberSessionHistory()
{
return $this->numberSessionHistory !== false && $this->numberSessionHistory > 0;
}
/**
* @return bool
*/
public function hasTimeoutSessionHistory()
{
return $this->timeoutSessionHistory !== false && $this->timeoutSessionHistory > 0;
}
}

View File

@ -0,0 +1,158 @@
<?php
/*
* This file is part of the 2amigos/yii2-usuario project.
*
* (c) 2amigOS! <http://2amigos.us/>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Da\User\Query;
use Da\User\Traits\ModuleAwareTrait;
use yii\web\Session;
use Yii;
class SessionHistoryCondition
{
use ModuleAwareTrait;
private $session;
public function __construct(Session $session)
{
$this->session = $session;
}
public function unbindSession()
{
return ['session_id' => null];
}
public function bySession($sessionId)
{
return ['session_id' => $sessionId];
}
public function byUser($userId)
{
return [
'user_id' => $userId,
];
}
public function byUserSession($userId, $sessionId)
{
return [
'user_id' => $userId,
'session_id' => $sessionId,
];
}
public function inactive($userId = null)
{
$where = [
'AND',
['session_id' => null]
];
if (isset($userId)) {
$where[] = $this->byUser($userId);
}
return $where;
}
public function expired($userId = null)
{
$where = [
'AND',
['<', 'updated_at', $this->getExpiredTime()]
];
if (isset($userId)) {
$where[] = $this->byUser($userId);
}
return $where;
}
public function expiredInactive($userId = null)
{
return [
'OR',
$this->expired($userId),
$this->inactive($userId),
];
}
public function shouldDeleteBefore($updatedAt, $userId)
{
$condition = ['<', 'updated_at', $updatedAt];
if ($updatedAt > $this->getExpiredTime()) {
$condition = [
'OR',
[
'AND',
$this->inactive(),
$condition,
],
$this->expired()
];
}
return [
'AND',
$this->byUser($userId),
$condition,
];
}
/**
* @return int
*/
public function getExpiredTime()
{
$module = $this->getModule();
$time = time() - max($module->rememberLoginLifespan, $this->session->getTimeout());
if (false === $module->hasTimeoutSessionHistory()) {
return $time;
}
return $time - $module->timeoutSessionHistory;
}
public function inactiveData()
{
return [
'session_id' => null,
];
}
/**
* @return array
*/
public function currentUserData()
{
return [
'user_id' => Yii::$app->user->id,
'session_id' => Yii::$app->session->getId(),
'user_agent' => Yii::$app->request->userAgent,
'ip' => Yii::$app->request->userIP,
];
}
/**
* @return array
*/
public function currentUserCondition()
{
return [
'user_id' => Yii::$app->user->id,
'session_id' => Yii::$app->session->getId(),
'user_agent' => Yii::$app->request->userAgent,
];
}
}

View File

@ -0,0 +1,82 @@
<?php
/*
* This file is part of the 2amigos/yii2-usuario project.
*
* (c) 2amigOS! <http://2amigos.us/>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Da\User\Query;
use Da\User\Traits\ModuleAwareTrait;
use yii\db\ActiveQuery;
use Yii;
class SessionHistoryQuery extends ActiveQuery
{
use ModuleAwareTrait;
public function whereUserId($userId)
{
return $this->andWhere($this->getCondition()->byUser($userId));
}
public function whereActive()
{
return $this->andWhere(['IS NOT', 'session_id', null]);
}
public function whereInActive($userId)
{
return $this->andWhere($this->getCondition()->inactive($userId));
}
public function whereExpired($userId)
{
return $this->andWhere($this->getCondition()->expired($userId));
}
public function whereExpiredInActive($userId)
{
return $this->andWhere($this->getCondition()->expiredInactive($userId));
}
public function selectSessionId()
{
return $this->select(['session_id']);
}
public function whereUserSession($userId, $sessionId)
{
return $this->andWhere($this->getCondition()->byUserSession(
$userId,
$sessionId
));
}
public function whereCurrentUser()
{
return $this->andWhere($this->getCondition()->currentUserCondition());
}
public function oldestUpdatedTimeActiveSession($userId)
{
return $this->whereExpiredInActive($userId)
->select(['updated_at'])
->limit(1)
->offset($this->getModule()->numberSessionHistory)
->orderBy(['updated_at' => SORT_DESC])->scalar();
}
/**
* @return SessionHistoryCondition
*/
protected function getCondition()
{
return Yii::$container->get(SessionHistoryCondition::class);
}
}

View File

@ -0,0 +1,74 @@
<?php
/*
* This file is part of the 2amigos/yii2-usuario project.
*
* (c) 2amigOS! <http://2amigos.us/>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Da\User\Search;
use Da\User\Model\SessionHistory;
use Da\User\Traits\ContainerAwareTrait;
use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
use yii\data\ActiveDataProvider;
class SessionHistorySearch extends SessionHistory
{
use ContainerAwareTrait;
/**
* {@inheritdoc}
*/
public function rules()
{
return [
[['user_agent', 'ip'], 'safe'],
];
}
/**
* @param array $params
*
* @throws InvalidConfigException
* @throws InvalidParamException
*
* @return ActiveDataProvider
*/
public function search($params)
{
$query = SessionHistory::find()->andWhere([
'user_id' => $this->user_id,
]);
/** @var ActiveDataProvider $dataProvider */
$dataProvider = $this->make(
ActiveDataProvider::class,
[],
[
'query' => $query,
'sort' => [
'defaultOrder' => [
'updated_at' => SORT_DESC
],
]
]
);
$this->load($params);
if (!$this->validate()) {
return $dataProvider;
}
$query->andFilterWhere(['like', 'user_agent', $this->user_agent])
->andFilterWhere(['like', 'ip', $this->ip]);
return $dataProvider;
}
}

View File

@ -0,0 +1,43 @@
<?php
/*
* This file is part of the 2amigos/yii2-usuario project.
*
* (c) 2amigOS! <http://2amigos.us/>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Da\User\Service\SessionHistory;
use yii\web\DbSession;
class DBTerminateSessionsService implements TerminateSessionsServiceInterface
{
protected $sessionIds;
protected $dbSession;
protected $fieldName;
public function __construct(array $sessionIds, DbSession $dbSession, $fieldName = 'id')
{
$this->sessionIds = $sessionIds;
$this->dbSession = $dbSession;
$this->fieldName = $fieldName;
}
public function run()
{
if (in_array(session_id(), $this->sessionIds)) {
session_write_close();
}
$this->dbSession->db->createCommand()->delete(
$this->dbSession->sessionTable,
[$this->fieldName => $this->sessionIds]
)->execute();
return true;
}
}

View File

@ -0,0 +1,461 @@
<?php
/*
* This file is part of the 2amigos/yii2-usuario project.
*
* (c) 2amigOS! <http://2amigos.us/>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Da\User\Service\SessionHistory;
use Da\User\Model\SessionHistory;
use Da\User\Query\SessionHistoryCondition;
use Da\User\Query\SessionHistoryQuery;
use Da\User\Traits\ModuleAwareTrait;
use Yii;
use yii\db\Exception;
use yii\web\Session;
use yii\base\InvalidArgumentException as BaseInvalidArgumentException;
/**
* Decorator for the {@see Session} class for storing the 'session history'
*
* Not decorated methods:
* {@see Session::open()}
* {@see Session::close()}
* {@see Session::destroy()}
* {@see Session::get()}
* {@see Session::set()}
*/
class SessionHistoryDecorator extends Session
{
use ModuleAwareTrait;
public $sessionHistoryTable = '{{%session_history}}';
/**
* @var Session
*/
public $session;
public $condition;
public function __construct(
Session $session,
SessionHistoryCondition $historyCondition,
$config = []
) {
$this->session = $session;
$this->condition = $historyCondition;
parent::__construct($config);
}
/** @inheritdoc */
public function getUseCustomStorage()
{
return $this->session->getUseCustomStorage();
}
/** @inheritdoc */
public function getIsActive()
{
return $this->session->getIsActive();
}
/** @inheritdoc */
public function getHasSessionId()
{
return $this->session->getHasSessionId();
}
/** @inheritdoc */
public function setHasSessionId($value)
{
return $this->session->setHasSessionId($value);
}
/** @inheritdoc */
public function getId()
{
return $this->session->getId();
}
/** @inheritdoc */
public function setId($value)
{
return $this->session->setId($value);
}
/** @inheritdoc */
public function regenerateID($deleteOldSession = false)
{
return $this->getDb()->transaction(function () use ($deleteOldSession) {
$oldSid = session_id();
if (false === $this->session->regenerateID($deleteOldSession)) {
return false;
}
if (false === $this->getModule()->enableSessionHistory) {
return true;
}
$user = Yii::$app->user;
if ($user->getIsGuest()) {
$this->unbindSessionHistory($oldSid);
} else {
$this->getDB()->createCommand()
->delete(
$this->sessionHistoryTable,
$this->condition->byUserSession($user->getId(), $oldSid)
)->execute();
}
return true;
});
}
/** @inheritdoc */
public function getName()
{
return $this->session->getName();
}
/** @inheritdoc */
public function setName($value)
{
return $this->session->setName($value);
}
/** @inheritdoc */
public function getSavePath()
{
return $this->session->getSavePath();
}
/** @inheritdoc */
public function setSavePath($value)
{
return $this->session->setSavePath($value);
}
/** @inheritdoc */
public function getCookieParams()
{
return $this->session->getCookieParams();
}
/** @inheritdoc */
public function setCookieParams(array $value)
{
return $this->session->setCookieParams($value);
}
/** @inheritdoc */
public function getUseCookies()
{
return $this->session->getUseCookies();
}
/** @inheritdoc */
public function setUseCookies($value)
{
return $this->session->setUseCookies($value);
}
/** @inheritdoc */
public function getGCProbability()
{
return $this->session->getGCProbability();
}
/** @inheritdoc */
public function setGCProbability($value)
{
return $this->session->setGCProbability($value);
}
/** @inheritdoc */
public function getUseTransparentSessionID()
{
return $this->session->getUseTransparentSessionID();
}
/** @inheritdoc */
public function setUseTransparentSessionID($value)
{
return $this->session->setUseTransparentSessionID($value);
}
/** @inheritdoc */
public function getTimeout()
{
return $this->session->getTimeout();
}
/** @inheritdoc */
public function setTimeout($value)
{
return $this->session->setTimeout($value);
}
/** @inheritdoc */
public function openSession($savePath, $sessionName)
{
return $this->session->openSession($savePath, $sessionName);
}
/** @inheritdoc */
public function closeSession()
{
return $this->session->closeSession();
}
/** @inheritdoc */
public function readSession($id)
{
return $this->session->readSession($id);
}
/** @inheritdoc */
public function writeSession($id, $data)
{
return $this->session->writeSession($id, $data) &&
(
false === $this->getModule()->enableSessionHistory ||
$this->getDb()->transaction(function () use ($id, $data) {
if (Yii::$app->user->getIsGuest()) {
return true;
}
$updatedAt = ['updated_at' => time()];
$model = $this->getHistoryQuery()
->whereCurrentUser()
->one();
if (isset($model)) {
$model->updateAttributes($updatedAt);
$result = true;
} else {
$model = Yii::createObject([
'class' => SessionHistory::class,
] + $this->condition->currentUserData() + $updatedAt);
if (!$result = $model->save()) {
throw new BaseInvalidArgumentException(
print_r($model->errors, 1)
);
}
$this->displacementHistory($model->user_id);
}
return $result;
})
);
}
/** @inheritdoc */
public function destroySession($id)
{
return $this->session->destroySession($id) &&
(
false === $this->getModule()->enableSessionHistory ||
$this->getDb()->transaction(function () use ($id) {
$this->unbindSessionHistory($id);
return true;
})
);
}
/** @inheritdoc */
public function gcSession($maxLifetime)
{
return $this->session->gcSession($maxLifetime) &&
(
false === $this->getModule()->enableSessionHistory ||
$this->getDb()->transaction(function () use ($maxLifetime) {
$this->getDb()->createCommand()->update(
$this->sessionHistoryTable,
$this->condition->inactiveData(),
$this->condition->expired()
)->execute();
return true;
})
);
}
/** @inheritdoc */
public function getIterator()
{
return $this->session->getIterator();
}
/** @inheritdoc */
public function getCount()
{
return $this->session->getCount();
}
/** @inheritdoc */
public function count()
{
return $this->session->count();
}
/** @inheritdoc */
public function remove($key)
{
return $this->session->remove($key);
}
/** @inheritdoc */
public function removeAll()
{
return $this->session->removeAll();
}
/** @inheritdoc */
public function has($key)
{
return $this->session->has($key);
}
/** @inheritdoc */
public function getFlash($key, $defaultValue = null, $delete = false)
{
return $this->session->getFlash($key, $defaultValue, $delete);
}
/** @inheritdoc */
public function getAllFlashes($delete = false)
{
return $this->session->getAllFlashes($delete);
}
/** @inheritdoc */
public function setFlash($key, $value = true, $removeAfterAccess = true)
{
return $this->session->setFlash($key, $value, $removeAfterAccess);
}
/** @inheritdoc */
public function addFlash($key, $value = true, $removeAfterAccess = true)
{
return $this->session->addFlash($key, $value, $removeAfterAccess);
}
/** @inheritdoc */
public function removeFlash($key)
{
return $this->session->removeFlash($key);
}
/** @inheritdoc */
public function removeAllFlashes()
{
return $this->session->removeAllFlashes();
}
/** @inheritdoc */
public function hasFlash($key)
{
return $this->session->hasFlash($key);
}
/** @inheritdoc */
public function offsetExists($offset)
{
return $this->session->offsetExists($offset);
}
/** @inheritdoc */
public function offsetGet($offset)
{
return $this->session->offsetGet($offset);
}
/** @inheritdoc */
public function offsetSet($offset, $item)
{
return $this->session->offsetSet($offset, $item);
}
/** @inheritdoc */
public function offsetUnset($offset)
{
return $this->session->offsetUnset($offset);
}
/** @inheritdoc */
public function setCacheLimiter($cacheLimiter)
{
return $this->session->setCacheLimiter($cacheLimiter);
}
/** @inheritdoc */
public function getCacheLimiter()
{
return $this->session->getCacheLimiter();
}
/**
* @param string $id
* @return bool
* @throws Exception
*/
protected function unbindSessionHistory($id)
{
return (bool)$this->getDb()->createCommand()->update(
$this->sessionHistoryTable,
$this->condition->unbindSession(),
$this->condition->bySession($id)
)->execute();
}
/**
*
* @param int $userId
* @return bool
* @throws Exception
*/
protected function displacementHistory($userId)
{
$module = $this->getModule();
if (false === $module->hasNumberSessionHistory()) {
return true;
}
$updatedAt = $this->getHistoryQuery()
->oldestUpdatedTimeActiveSession($userId);
if (!$updatedAt) {
return true;
}
$this->getDB()->createCommand()->delete(
$this->sessionHistoryTable,
$this->condition->shouldDeleteBefore(intval($updatedAt), $userId)
)->execute();
return true;
}
/**
* @return SessionHistoryQuery
*/
protected function getHistoryQuery()
{
return Yii::$container->get(SessionHistoryQuery::class);
}
protected function getDb()
{
return Yii::$app->getDb();
}
}

View File

@ -0,0 +1,48 @@
<?php
/*
* This file is part of the 2amigos/yii2-usuario project.
*
* (c) 2amigOS! <http://2amigos.us/>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Da\User\Service\SessionHistory;
class TerminateSessionsService implements TerminateSessionsServiceInterface
{
protected $sessionIds;
public function __construct(array $sessionIds)
{
$this->sessionIds = $sessionIds;
}
public function run()
{
$currentSessionId = session_id();
if (session_status() === PHP_SESSION_ACTIVE) {
session_write_close();
}
foreach ($this->sessionIds as $sessionId) {
if ($sessionId === $currentSessionId) {
$currentSessionId = null;
}
session_id($sessionId);
session_start();
session_destroy();
}
if ($currentSessionId) {
session_id($currentSessionId);
}
session_start();
return true;
}
}

View File

@ -0,0 +1,19 @@
<?php
/*
* This file is part of the 2amigos/yii2-usuario project.
*
* (c) 2amigOS! <http://2amigos.us/>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Da\User\Service\SessionHistory;
use Da\User\Contracts\ServiceInterface;
interface TerminateSessionsServiceInterface extends ServiceInterface
{
}

View File

@ -0,0 +1,105 @@
<?php
/*
* This file is part of the 2amigos/yii2-usuario project.
*
* (c) 2amigOS! <http://2amigos.us/>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Da\User\Service\SessionHistory;
use Da\User\Contracts\ServiceInterface;
use Da\User\Event\SessionEvent;
use Da\User\Model\SessionHistory;
use Da\User\Model\User;
use Da\User\Traits\ContainerAwareTrait;
use Da\User\Traits\ModuleAwareTrait;
use yii\web\Session;
use Yii;
class TerminateUserSessionsService implements ServiceInterface
{
use ContainerAwareTrait;
use ModuleAwareTrait;
protected $userId;
protected $session;
protected $excludeCurrentSession;
public function __construct($userId, Session $session, $excludeCurrentSession = true)
{
$this->userId = intval($userId);
$this->session = $session;
$this->excludeCurrentSession = $excludeCurrentSession;
}
public function run()
{
$user = $this->getUser($this->userId);
$sessionIds = $this->getSessionIds($user->id);
Yii::$app->db->transaction(function () use ($sessionIds, $user) {
/** @var SessionEvent $event */
$event = $this->make(SessionEvent::class, [$user]);
$user->trigger(SessionEvent::EVENT_BEFORE_TERMINATE_USER_SESSIONS, $event);
$this->make(TerminateSessionsServiceInterface::class, [$sessionIds])->run();
$user->updateAttributes([
'auth_key' => Yii::$app->security->generateRandomString(),
]);
if ($this->excludeCurrentUser()) {
Yii::$app->user->switchIdentity(
$user,
$this->getModule()->rememberLoginLifespan
);
}
$user->trigger(SessionEvent::EVENT_AFTER_TERMINATE_USER_SESSIONS, $event);
});
return true;
}
/**
* @param int $userId
* @return User
*/
protected function getUser($userId)
{
return ($this->make(User::class))::findOne($userId);
}
/**
* @param $userId
* @return int[]
*/
protected function getSessionIds($userId)
{
/** @var SessionHistory $sessionHistory */
$sessionHistory = $this->make(SessionHistory::class);
$sessionIds = $sessionHistory::find()->whereUserId($userId)->whereActive()->selectSessionId()->column();
if ($this->excludeCurrentUser()) {
foreach ($sessionIds as $key => $sessionId) {
if ($sessionId === $this->session->id) {
unset($sessionIds[$key]);
break;
}
}
}
return $sessionIds;
}
protected function excludeCurrentUser()
{
return $this->excludeCurrentSession && $this->userId === Yii::$app->user->id;
}
}

View File

@ -0,0 +1,85 @@
<?php
/*
* This file is part of the 2amigos/yii2-usuario project.
*
* (c) 2amigOS! <http://2amigos.us/>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
namespace Da\User\Widget;
use Da\User\Model\SessionHistory;
use Da\User\Traits\ContainerAwareTrait;
use Yii;
use yii\base\InvalidConfigException;
use yii\base\InvalidParamException;
use yii\base\Widget;
use yii\helpers\ArrayHelper;
class SessionStatusWidget extends Widget
{
use ContainerAwareTrait;
/**
* @var SessionHistory
*/
public $model;
/**
* {@inheritdoc}
*
* @throws InvalidConfigException
*/
public function init()
{
parent::init();
if (!$this->model instanceof SessionHistory) {
throw new InvalidConfigException(
__CLASS__ . '::$userId should be instanceof ' . SessionHistory::class
);
}
}
/**
* {@inheritdoc}
*
* @throws InvalidParamException
*/
public function run()
{
if ($this->model->getIsActive()) {
if ($this->model->session_id === Yii::$app->session->id) {
$value = Yii::t('usuario', 'Current');
} else {
$value = Yii::t('usuario', 'Active');
}
} else {
$value = Yii::t('usuario', 'Inactive');
}
return $value;
}
/**
* Returns available auth items to be attached to the user.
*
* @param int|null type of auth items or null to return all
*
* @return array
*/
protected function getAvailableItems($type = null)
{
return ArrayHelper::map(
$this->getAuthManager()->getItems($type),
'name',
function ($item) {
return empty($item->description)
? $item->name
: $item->name . ' (' . $item->description . ')';
}
);
}
}

View File

@ -28,6 +28,7 @@ return [
'Account details' => '',
'Account details have been updated' => '',
'Account settings' => '',
'Active' => '',
'Already registered? Sign in!' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'An error occurred processing your request' => '',
@ -81,6 +82,7 @@ return [
'Create new rule' => '',
'Created at' => '',
'Credentials will be sent to the user by email' => '',
'Current' => '',
'Current password' => '',
'Current password is not valid' => '',
'Data privacy' => '',
@ -113,6 +115,7 @@ return [
'Hello' => '',
'Here you can download your personal data in a comma separated values format.' => '',
'I agree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => '',
'IP' => '',
'If you already registered, sign in and connect this account on settings page' => '',
'If you cannot click the link, please try pasting the text into your browser' => '',
'If you did not make this request you can ignore this email' => '',
@ -120,6 +123,7 @@ return [
'In order to complete your registration, please click the link below' => '',
'In order to complete your request, please click the link below' => '',
'In order to finish your registration, we need you to enter following fields' => '',
'Inactive' => '',
'Information' => '',
'Invalid login or password' => '',
'Invalid or expired link' => '',
@ -129,6 +133,7 @@ return [
'It will be deleted forever' => '',
'Items' => '',
'Joined on {0, date}' => '',
'Last activity' => '',
'Last login IP' => '',
'Last login time' => '',
'Last password change' => '',
@ -187,11 +192,15 @@ return [
'Scan the QrCode with Google Authenticator App, then insert its temporary code on the box and submit.' => '',
'Select rule...' => '',
'Send password recovery email' => '',
'Session ID' => '',
'Session history' => '',
'Sign in' => '',
'Sign up' => '',
'Something went wrong' => '',
'Status' => '',
'Submit' => '',
'Switch identities is disabled.' => '',
'Terminate all sessions' => '',
'Thank you for signing up on {0}' => '',
'Thank you, registration is now complete.' => '',
'The "recaptcha" component must be configured.' => '',
@ -233,7 +242,9 @@ return [
'Update rule' => '',
'Update user account' => '',
'Updated at' => '',
'User ID' => '',
'User account could not be created.' => '',
'User agent' => '',
'User block status has been updated.' => '',
'User could not be registered.' => '',
'User has been confirmed' => '',

View File

@ -28,6 +28,7 @@ return [
'Account details' => '',
'Account details have been updated' => '',
'Account settings' => '',
'Active' => '',
'Already registered? Sign in!' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'An error occurred processing your request' => '',
@ -81,6 +82,7 @@ return [
'Create new rule' => '',
'Created at' => '',
'Credentials will be sent to the user by email' => '',
'Current' => '',
'Current password' => '',
'Current password is not valid' => '',
'Data privacy' => '',
@ -112,6 +114,7 @@ return [
'Gravatar email' => '',
'Hello' => '',
'Here you can download your personal data in a comma separated values format.' => '',
'IP' => '',
'I agree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => '',
'If you already registered, sign in and connect this account on settings page' => '',
'If you cannot click the link, please try pasting the text into your browser' => '',
@ -120,6 +123,7 @@ return [
'In order to complete your registration, please click the link below' => '',
'In order to complete your request, please click the link below' => '',
'In order to finish your registration, we need you to enter following fields' => '',
'Inactive' => '',
'Information' => '',
'Invalid login or password' => '',
'Invalid or expired link' => '',
@ -129,6 +133,7 @@ return [
'It will be deleted forever' => '',
'Items' => '',
'Joined on {0, date}' => '',
'Last activity' => '',
'Last login IP' => '',
'Last login time' => '',
'Last password change' => '',
@ -187,11 +192,15 @@ return [
'Scan the QrCode with Google Authenticator App, then insert its temporary code on the box and submit.' => '',
'Select rule...' => '',
'Send password recovery email' => '',
'Session ID' => '',
'Session history' => '',
'Sign in' => '',
'Sign up' => '',
'Something went wrong' => '',
'Status' => '',
'Submit' => '',
'Switch identities is disabled.' => '',
'Terminate all sessions' => '',
'Thank you for signing up on {0}' => '',
'Thank you, registration is now complete.' => '',
'The "recaptcha" component must be configured.' => '',
@ -233,7 +242,9 @@ return [
'Update rule' => '',
'Update user account' => '',
'Updated at' => '',
'User ID' => '',
'User account could not be created.' => '',
'User agent' => '',
'User block status has been updated.' => '',
'User could not be registered.' => '',
'User has been confirmed' => '',

View File

@ -282,4 +282,21 @@ return [
'A message has been sent to your email address. ' => '@@Eine Nachricht wurde an Deine E-Mail Adresse gesendet@@',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'Now you can resume the login process' => '@@@@',
'According to the European General Data Protection Regulation (GDPR) we need your consent to work with your personal data.' => '',
'Active' => '',
'Current' => '',
'Data privacy' => '',
'IP' => '',
'Inactive' => '',
'Last activity' => '',
'Session ID' => '',
'Session history' => '',
'Status' => '',
'Submit' => '',
'Terminate all sessions' => '',
'Unfortunately, you can not work with this site without giving us consent to process your data.' => '',
'User ID' => '',
'User agent' => '',
'Your consent is required to work with this site' => '',
'A message has been sent to your email address. ' => '@@Eine Nachricht wurde an Deine E-Mail Adresse gesendet@@',
];

View File

@ -277,6 +277,22 @@ return [
'{0, date, MMM dd, YYYY HH:mm}' => '{0, date, dd. MMM YYYY, HH:mm}',
'{0, date, MMMM dd, YYYY HH:mm}' => '{0, date, dd. MMMM YYYY, HH:mm}',
'{0} cannot be blank.' => '{0} darf nicht leer sein.',
'According to the European General Data Protection Regulation (GDPR) we need your consent to work with your personal data.' => '',
'Active' => '',
'Current' => '',
'Data privacy' => '',
'IP' => '',
'Inactive' => '',
'Last activity' => '',
'Session ID' => '',
'Session history' => '',
'Status' => '',
'Submit' => '',
'Terminate all sessions' => '',
'Unfortunately, you can not work with this site without giving us consent to process your data.' => '',
'User ID' => '',
'User agent' => '',
'Your consent is required to work with this site' => '',
'Information' => '',
'Your role requires 2FA, you won\'t be able to use the application until you enable it' => '',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',

View File

@ -276,6 +276,17 @@ return [
'privacy policy' => 'política de privacidad',
'{0, date, MMMM dd, YYYY HH:mm}' => '{0, date, dd MMMM, YYYY HH:mm}',
'{0} cannot be blank.' => '{0} no puede estar vacío.',
'Active' => '',
'Current' => '',
'IP' => '',
'Inactive' => '',
'Last activity' => '',
'Session ID' => '',
'Session history' => '',
'Status' => '',
'Terminate all sessions' => '',
'User ID' => '',
'User agent' => '',
'Your consent is required to work with this site' => '',
'Your role requires 2FA, you won\'t be able to use the application until you enable it' => '',
'{0, date, MMM dd, YYYY HH:mm}' => '',

View File

@ -268,17 +268,26 @@ return [
'privacy policy' => 'privaatsuspoliitika',
'{0} cannot be blank.' => '{0} ei või olla tühi.',
'According to the European General Data Protection Regulation (GDPR) we need your consent to work with your personal data.' => '',
'Active' => '',
'Authentication rule class {0} can not be instantiated' => '',
'Current' => '',
'Data privacy' => '',
'IP' => '',
'Inactive' => '',
'Last activity' => '',
'Rule class must extend "yii\\rbac\\Rule".' => '',
'Session ID' => '',
'Session history' => '',
'Status' => '',
'Submit' => '',
'Terminate all sessions' => '',
'Unfortunately, you can not work with this site without giving us consent to process your data.' => '',
'User ID' => '',
'User agent' => '',
'VKontakte' => '',
'Yandex' => '',
'Your consent is required to work with this site' => '',
'Your role requires 2FA, you won\'t be able to use the application until you enable it' => '',
'{0, date, MMM dd, YYYY HH:mm}' => '',
'{0, date, MMMM dd, YYYY HH:mm}' => '',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'Now you can resume the login process' => '@@@@',
];

View File

@ -151,6 +151,13 @@ return [
'Your profile has been updated' => 'پروفایل شما بروز شد',
'{0, date, MMMM dd, YYYY HH:mm}' => '{0, date, dd MMMM, YYYY HH:mm}',
'According to the European General Data Protection Regulation (GDPR) we need your consent to work with your personal data.' => '',
'Account' => '',
'Account confirmation' => '',
'Account details' => '',
'Account details have been updated' => '',
'Account settings' => '',
'Active' => '',
'Already registered? Sign in!' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'Are you sure you want to switch to this user for the rest of this Session?' => '',
'Are you sure you wish the user to change their password at next login?' => '',
@ -177,6 +184,10 @@ return [
'Create new role' => '',
'Create new rule' => '',
'Created at' => '',
'Credentials will be sent to the user by email' => '',
'Current' => '',
'Current password' => '',
'Current password is not valid' => '',
'Data privacy' => '',
'Data processing consent' => '',
'Delete account' => '',
@ -194,13 +205,23 @@ return [
'Force password change at next login' => '',
'Here you can download your personal data in a comma separated values format.' => '',
'I agree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => '',
'IP' => '',
'If you already registered, sign in and connect this account on settings page' => '',
'If you cannot click the link, please try pasting the text into your browser' => '',
'If you did not make this request you can ignore this email' => '',
'Impersonate this user' => '',
'In order to finish your registration, we need you to enter following fields' => '',
'Inactive' => '',
'Information' => '',
'Invalid login or password' => '',
'Invalid or expired link' => '',
'Invalid password' => '',
'Invalid two factor authentication code' => '',
'Invalid value' => '',
'It will be deleted forever' => '',
'Items' => '',
'Joined on {0, date}' => '',
'Last activity' => '',
'Last login IP' => '',
'Last login time' => '',
'Last password change' => '',
@ -230,6 +251,17 @@ return [
'Send password recovery email' => '',
'Submit' => '',
'Switch identities is disabled.' => '',
'Session ID' => '',
'Session history' => '',
'Sign in' => '',
'Sign up' => '',
'Something went wrong' => '',
'Status' => '',
'Submit' => '',
'Switch identities is disabled.' => '',
'Terminate all sessions' => '',
'Thank you for signing up on {0}' => '',
'Thank you, registration is now complete.' => '',
'The "recaptcha" component must be configured.' => '',
'The verification code is incorrect.' => '',
'There is neither role nor permission with name "{0}"' => '',
@ -259,7 +291,9 @@ return [
'Update role' => '',
'Update rule' => '',
'Updated at' => '',
'User ID' => '',
'User account could not be created.' => '',
'User agent' => '',
'User block status has been updated.' => '',
'User could not be registered.' => '',
'User will be required to change password at next login' => '',
@ -279,8 +313,4 @@ return [
'privacy policy' => '',
'{0, date, MMM dd, YYYY HH:mm}' => '',
'{0} cannot be blank.' => '',
'An email has been sent with instructions for resetting your password' => '@@ایمیلی حاوی راهنمایی برای تنظیم مجدد رمز عبور به شما ارسال شد@@',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'Now you can resume the login process' => '@@@@',
'Registration ip' => '@@ای پی ثبت نام@@',
];

View File

@ -28,6 +28,7 @@ return [
'Account details' => '',
'Account details have been updated' => '',
'Account settings' => '',
'Active' => '',
'Already registered? Sign in!' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'An error occurred processing your request' => '',
@ -81,6 +82,7 @@ return [
'Create new rule' => '',
'Created at' => '',
'Credentials will be sent to the user by email' => '',
'Current' => '',
'Current password' => '',
'Current password is not valid' => '',
'Data privacy' => '',
@ -113,6 +115,7 @@ return [
'Hello' => '',
'Here you can download your personal data in a comma separated values format.' => '',
'I agree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => '',
'IP' => '',
'If you already registered, sign in and connect this account on settings page' => '',
'If you cannot click the link, please try pasting the text into your browser' => '',
'If you did not make this request you can ignore this email' => '',
@ -120,6 +123,7 @@ return [
'In order to complete your registration, please click the link below' => '',
'In order to complete your request, please click the link below' => '',
'In order to finish your registration, we need you to enter following fields' => '',
'Inactive' => '',
'Information' => '',
'Invalid login or password' => '',
'Invalid or expired link' => '',
@ -129,6 +133,7 @@ return [
'It will be deleted forever' => '',
'Items' => '',
'Joined on {0, date}' => '',
'Last activity' => '',
'Last login IP' => '',
'Last login time' => '',
'Last password change' => '',
@ -187,11 +192,15 @@ return [
'Scan the QrCode with Google Authenticator App, then insert its temporary code on the box and submit.' => '',
'Select rule...' => '',
'Send password recovery email' => '',
'Session ID' => '',
'Session history' => '',
'Sign in' => '',
'Sign up' => '',
'Something went wrong' => '',
'Status' => '',
'Submit' => '',
'Switch identities is disabled.' => '',
'Terminate all sessions' => '',
'Thank you for signing up on {0}' => '',
'Thank you, registration is now complete.' => '',
'The "recaptcha" component must be configured.' => '',
@ -233,7 +242,9 @@ return [
'Update rule' => '',
'Update user account' => '',
'Updated at' => '',
'User ID' => '',
'User account could not be created.' => '',
'User agent' => '',
'User block status has been updated.' => '',
'User could not be registered.' => '',
'User has been confirmed' => '',

View File

@ -273,12 +273,21 @@ return [
'{0, date, MMMM dd, YYYY HH:mm}' => '{0, date, dd MMMM YYYY HH:mm}',
'{0} cannot be blank.' => '{0} ne peut être vide.',
'According to the European General Data Protection Regulation (GDPR) we need your consent to work with your personal data.' => '',
'Active' => '',
'Current' => '',
'Data privacy' => '',
'IP' => '',
'Inactive' => '',
'Last activity' => '',
'Session ID' => '',
'Session history' => '',
'Status' => '',
'Submit' => '',
'Terminate all sessions' => '',
'Unfortunately, you can not work with this site without giving us consent to process your data.' => '',
'User ID' => '',
'User agent' => '',
'Your consent is required to work with this site' => '',
'Your role requires 2FA, you won\'t be able to use the application until you enable it' => '',
'{0, date, MMM dd, YYYY HH:mm}' => '',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'Now you can resume the login process' => '@@@@',
];

View File

@ -28,6 +28,7 @@ return [
'Account details' => '',
'Account details have been updated' => '',
'Account settings' => '',
'Active' => '',
'Already registered? Sign in!' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'An error occurred processing your request' => '',
@ -81,6 +82,7 @@ return [
'Create new rule' => '',
'Created at' => '',
'Credentials will be sent to the user by email' => '',
'Current' => '',
'Current password' => '',
'Current password is not valid' => '',
'Data privacy' => '',
@ -112,6 +114,7 @@ return [
'Gravatar email' => '',
'Hello' => '',
'Here you can download your personal data in a comma separated values format.' => '',
'IP' => '',
'I agree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => '',
'If you already registered, sign in and connect this account on settings page' => '',
'If you cannot click the link, please try pasting the text into your browser' => '',
@ -120,6 +123,7 @@ return [
'In order to complete your registration, please click the link below' => '',
'In order to complete your request, please click the link below' => '',
'In order to finish your registration, we need you to enter following fields' => '',
'Inactive' => '',
'Information' => '',
'Invalid login or password' => '',
'Invalid or expired link' => '',
@ -129,6 +133,7 @@ return [
'It will be deleted forever' => '',
'Items' => '',
'Joined on {0, date}' => '',
'Last activity' => '',
'Last login IP' => '',
'Last login time' => '',
'Last password change' => '',
@ -187,11 +192,15 @@ return [
'Scan the QrCode with Google Authenticator App, then insert its temporary code on the box and submit.' => '',
'Select rule...' => '',
'Send password recovery email' => '',
'Session ID' => '',
'Session history' => '',
'Sign in' => '',
'Sign up' => '',
'Something went wrong' => '',
'Status' => '',
'Submit' => '',
'Switch identities is disabled.' => '',
'Terminate all sessions' => '',
'Thank you for signing up on {0}' => '',
'Thank you, registration is now complete.' => '',
'The "recaptcha" component must be configured.' => '',
@ -233,7 +242,9 @@ return [
'Update rule' => '',
'Update user account' => '',
'Updated at' => '',
'User ID' => '',
'User account could not be created.' => '',
'User agent' => '',
'User block status has been updated.' => '',
'User could not be registered.' => '',
'User has been confirmed' => '',

View File

@ -270,13 +270,24 @@ return [
'{0, date, MMMM dd, YYYY HH:mm}' => '{0, dátum, MMMM dd, ÉÉÉÉ HH: mm}',
'{0} cannot be blank.' => '{0} nem lehet üres.',
'According to the European General Data Protection Regulation (GDPR) we need your consent to work with your personal data.' => '',
'Active' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'Current' => '',
'Data privacy' => '',
'IP' => '',
'Inactive' => '',
'Last activity' => '',
'Rule class name' => '',
'Select rule...' => '',
'Session ID' => '',
'Session history' => '',
'Status' => '',
'Submit' => '',
'Terminate all sessions' => '',
'Two factor authentication protects you in case of stolen credentials' => '',
'Unfortunately, you can not work with this site without giving us consent to process your data.' => '',
'User ID' => '',
'User agent' => '',
'Your consent is required to work with this site' => '',
'Your role requires 2FA, you won\'t be able to use the application until you enable it' => '',
'A message has been sent to your email address. ' => '@@Üzenet érkezett az e-mail címedre.@@',

View File

@ -279,6 +279,24 @@ return [
'{0, date, MMM dd, YYYY HH:mm}' => '{0, date, MMM dd, YYYY HH:mm}',
'{0, date, MMMM dd, YYYY HH:mm}' => '{0, date, dd MMMM YYYY HH:mm}',
'{0} cannot be blank.' => '{0} non può essere vuoto.',
'According to the European General Data Protection Regulation (GDPR) we need your consent to work with your personal data.' => '',
'Active' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'Current' => '',
'Data privacy' => '',
'IP' => '',
'Inactive' => '',
'Last activity' => '',
'Session ID' => '',
'Session history' => '',
'Status' => '',
'Submit' => '',
'Terminate all sessions' => '',
'Unfortunately, you can not work with this site without giving us consent to process your data.' => '',
'User ID' => '',
'User agent' => '',
'Your consent is required to work with this site' => '',
'{0, date, MMM dd, YYYY HH:mm}' => '',
'An email has been sent with instructions for resetting your password' => '@@È stata inviata un\'email con le istruzioni per azzerare la tua password@@',
'Now you can resume the login process' => '@@Ora puoi riprendere il processo di autenticazione@@',
];

View File

@ -28,6 +28,7 @@ return [
'Account details' => '',
'Account details have been updated' => '',
'Account settings' => '',
'Active' => '',
'Already registered? Sign in!' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'An error occurred processing your request' => '',
@ -81,6 +82,7 @@ return [
'Create new rule' => '',
'Created at' => '',
'Credentials will be sent to the user by email' => '',
'Current' => '',
'Current password' => '',
'Current password is not valid' => '',
'Data privacy' => '',
@ -112,6 +114,7 @@ return [
'Gravatar email' => '',
'Hello' => '',
'Here you can download your personal data in a comma separated values format.' => '',
'IP' => '',
'I agree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => '',
'If you already registered, sign in and connect this account on settings page' => '',
'If you cannot click the link, please try pasting the text into your browser' => '',
@ -120,6 +123,7 @@ return [
'In order to complete your registration, please click the link below' => '',
'In order to complete your request, please click the link below' => '',
'In order to finish your registration, we need you to enter following fields' => '',
'Inactive' => '',
'Information' => '',
'Invalid login or password' => '',
'Invalid or expired link' => '',
@ -129,6 +133,7 @@ return [
'It will be deleted forever' => '',
'Items' => '',
'Joined on {0, date}' => '',
'Last activity' => '',
'Last login IP' => '',
'Last login time' => '',
'Last password change' => '',
@ -187,11 +192,15 @@ return [
'Scan the QrCode with Google Authenticator App, then insert its temporary code on the box and submit.' => '',
'Select rule...' => '',
'Send password recovery email' => '',
'Session ID' => '',
'Session history' => '',
'Sign in' => '',
'Sign up' => '',
'Something went wrong' => '',
'Status' => '',
'Submit' => '',
'Switch identities is disabled.' => '',
'Terminate all sessions' => '',
'Thank you for signing up on {0}' => '',
'Thank you, registration is now complete.' => '',
'The "recaptcha" component must be configured.' => '',
@ -233,7 +242,9 @@ return [
'Update rule' => '',
'Update user account' => '',
'Updated at' => '',
'User ID' => '',
'User account could not be created.' => '',
'User agent' => '',
'User block status has been updated.' => '',
'User could not be registered.' => '',
'User has been confirmed' => '',

View File

@ -28,6 +28,7 @@ return [
'Account details' => '',
'Account details have been updated' => '',
'Account settings' => '',
'Active' => '',
'Already registered? Sign in!' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'An error occurred processing your request' => '',
@ -81,6 +82,7 @@ return [
'Create new rule' => '',
'Created at' => '',
'Credentials will be sent to the user by email' => '',
'Current' => '',
'Current password' => '',
'Current password is not valid' => '',
'Data privacy' => '',
@ -113,6 +115,7 @@ return [
'Hello' => '',
'Here you can download your personal data in a comma separated values format.' => '',
'I agree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => '',
'IP' => '',
'If you already registered, sign in and connect this account on settings page' => '',
'If you cannot click the link, please try pasting the text into your browser' => '',
'If you did not make this request you can ignore this email' => '',
@ -120,6 +123,7 @@ return [
'In order to complete your registration, please click the link below' => '',
'In order to complete your request, please click the link below' => '',
'In order to finish your registration, we need you to enter following fields' => '',
'Inactive' => '',
'Information' => '',
'Invalid login or password' => '',
'Invalid or expired link' => '',
@ -129,6 +133,7 @@ return [
'It will be deleted forever' => '',
'Items' => '',
'Joined on {0, date}' => '',
'Last activity' => '',
'Last login IP' => '',
'Last login time' => '',
'Last password change' => '',
@ -187,11 +192,15 @@ return [
'Scan the QrCode with Google Authenticator App, then insert its temporary code on the box and submit.' => '',
'Select rule...' => '',
'Send password recovery email' => '',
'Session ID' => '',
'Session history' => '',
'Sign in' => '',
'Sign up' => '',
'Something went wrong' => '',
'Status' => '',
'Submit' => '',
'Switch identities is disabled.' => '',
'Terminate all sessions' => '',
'Thank you for signing up on {0}' => '',
'Thank you, registration is now complete.' => '',
'The "recaptcha" component must be configured.' => '',
@ -233,7 +242,9 @@ return [
'Update rule' => '',
'Update user account' => '',
'Updated at' => '',
'User ID' => '',
'User account could not be created.' => '',
'User agent' => '',
'User block status has been updated.' => '',
'User could not be registered.' => '',
'User has been confirmed' => '',

View File

@ -270,13 +270,24 @@ return [
'{0, date, MMMM dd, YYYY HH:mm}' => '{0, date, MMMM dd, YYYY HH:mm}\'',
'{0} cannot be blank.' => '{0} kan niet leeg zijn.',
'According to the European General Data Protection Regulation (GDPR) we need your consent to work with your personal data.' => '',
'Active' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'Current' => '',
'Data privacy' => '',
'IP' => '',
'Inactive' => '',
'Last activity' => '',
'Rule class name' => '',
'Select rule...' => '',
'Session ID' => '',
'Session history' => '',
'Status' => '',
'Submit' => '',
'Terminate all sessions' => '',
'Two factor authentication protects you in case of stolen credentials' => '',
'Unfortunately, you can not work with this site without giving us consent to process your data.' => '',
'User ID' => '',
'User agent' => '',
'Your consent is required to work with this site' => '',
'Your role requires 2FA, you won\'t be able to use the application until you enable it' => '',
'A message has been sent to your email address. ' => '@@Een bericht werd naar jouw emailadres verzonden@@',

View File

@ -270,13 +270,24 @@ return [
'{0, date, MMMM dd, YYYY HH:mm}' => '{0, date, dd MMMM YYYY, HH:mm}',
'{0} cannot be blank.' => '{0} nie może pozostać bez wartości',
'According to the European General Data Protection Regulation (GDPR) we need your consent to work with your personal data.' => '',
'Active' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'Current' => '',
'Data privacy' => '',
'IP' => '',
'Inactive' => '',
'Last activity' => '',
'Rule class name' => '',
'Select rule...' => '',
'Session ID' => '',
'Session history' => '',
'Status' => '',
'Submit' => '',
'Terminate all sessions' => '',
'Two factor authentication protects you in case of stolen credentials' => '',
'Unfortunately, you can not work with this site without giving us consent to process your data.' => '',
'User ID' => '',
'User agent' => '',
'Your consent is required to work with this site' => '',
'Your role requires 2FA, you won\'t be able to use the application until you enable it' => '',
'An email has been sent with instructions for resetting your password' => '@@Email z instrukcją resetowania hasła został wysłany@@',

View File

@ -270,13 +270,24 @@ return [
'{0, date, MMMM dd, YYYY HH:mm}' => '{0, date, MMMM dd, YYYY HH:mm}',
'{0} cannot be blank.' => '{0} não pode estar em branco',
'According to the European General Data Protection Regulation (GDPR) we need your consent to work with your personal data.' => '',
'Active' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'Current' => '',
'Data privacy' => '',
'IP' => '',
'Inactive' => '',
'Last activity' => '',
'Rule class name' => '',
'Select rule...' => '',
'Session ID' => '',
'Session history' => '',
'Status' => '',
'Submit' => '',
'Terminate all sessions' => '',
'Two factor authentication protects you in case of stolen credentials' => '',
'Unfortunately, you can not work with this site without giving us consent to process your data.' => '',
'User ID' => '',
'User agent' => '',
'Your consent is required to work with this site' => '',
'Your role requires 2FA, you won\'t be able to use the application until you enable it' => '',
'A message has been sent to your email address. ' => '@@Uma mensagem foi enviada para o seu endereço de e-mail.@@',

View File

@ -257,21 +257,32 @@ return [
'privacy policy' => 'politica de privacidade',
'{0} cannot be blank.' => '{0} não pode ficar vazio.',
'According to the European General Data Protection Regulation (GDPR) we need your consent to work with your personal data.' => '',
'Active' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'Awesome, almost there. Now you need to click the confirmation link sent to your new email address.' => '',
'Awesome, almost there. Now you need to click the confirmation link sent to your old email address.' => '',
'Children' => '',
'Class' => '',
'Current' => '',
'Data privacy' => '',
'Email' => '',
'Gravatar email' => '',
'IP' => '',
'Inactive' => '',
'Items' => '',
'Last activity' => '',
'Password' => '',
'Rule class name' => '',
'Select rule...' => '',
'Session ID' => '',
'Session history' => '',
'Status' => '',
'Submit' => '',
'Terminate all sessions' => '',
'Two factor authentication protects you in case of stolen credentials' => '',
'Unfortunately, you can not work with this site without giving us consent to process your data.' => '',
'User ID' => '',
'User agent' => '',
'VKontakte' => '',
'Website' => '',
'Yandex' => '',

View File

@ -270,13 +270,24 @@ return [
'{0, date, MMMM dd, YYYY HH:mm}' => '{0, data, MMMM dd, AAAA HH: mm}',
'{0} cannot be blank.' => '{0} nu poate fi gol.',
'According to the European General Data Protection Regulation (GDPR) we need your consent to work with your personal data.' => '',
'Active' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'Current' => '',
'Data privacy' => '',
'IP' => '',
'Inactive' => '',
'Last activity' => '',
'Rule class name' => '',
'Select rule...' => '',
'Session ID' => '',
'Session history' => '',
'Status' => '',
'Submit' => '',
'Terminate all sessions' => '',
'Two factor authentication protects you in case of stolen credentials' => '',
'Unfortunately, you can not work with this site without giving us consent to process your data.' => '',
'User ID' => '',
'User agent' => '',
'Your consent is required to work with this site' => '',
'Your role requires 2FA, you won\'t be able to use the application until you enable it' => '',
'A message has been sent to your email address. ' => '@@A fost trimis un mesaj la adresa dvs. de e-mail.@@',

13
src/User/resources/i18n/ru/usuario.php Normal file → Executable file
View File

@ -27,6 +27,7 @@ return [
'Account details' => 'Детали аккаунта',
'Account details have been updated' => 'Аккаунт был обновлен',
'Account settings' => 'Настройки аккаунта',
'Active' => 'Активно',
'Already registered? Sign in!' => 'Уже зарегистрированы? Войдите!',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => 'Письмо с инструкциями по созданию нового пароля было выслано на {email}, в случае если данный адрес связан с {appName} аккаунтом',
'An error occurred processing your request' => 'Во время выполнения запроса произошла ошибка',
@ -80,6 +81,7 @@ return [
'Create new rule' => 'Создать правило',
'Created at' => 'Дата создания',
'Credentials will be sent to the user by email' => 'Данные для входа будут отправлены пользователю на почту',
'Current' => 'Текущий',
'Current password' => 'Текущий пароль',
'Current password is not valid' => 'Текущий пароль введён неправильно',
'Data processing consent' => 'Cогласие на обработку данных',
@ -111,6 +113,7 @@ return [
'Hello' => 'Здравствуйте',
'Here you can download your personal data in a comma separated values format.' => 'Здесь вы можете загрузить свои персональные данные в формате значений, разделенных запятыми.',
'I agree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => 'Я соглашаюсь на обработку моих персональных данных и использование файлов cookie для облегчения работы этого сайта. Для получения дополнительной информации ознакомьтесь с нашей {privacyPolicy}',
'IP' => 'IP',
'If you already registered, sign in and connect this account on settings page' => 'Если вы уже зарегистрированы, войдите и подключите аккаунт в настройках',
'If you cannot click the link, please try pasting the text into your browser' => 'Если вы не можете нажать на ссылку, скопируйте её и вставьте в адресную строку вашего браузера',
'If you did not make this request you can ignore this email' => 'Если вы получили это сообщение по ошибке, просто проигнорируйте или удалите его',
@ -118,6 +121,7 @@ return [
'In order to complete your registration, please click the link below' => 'Чтобы активировать свой аккаунт, пожалуйста, нажмите на ссылку ниже',
'In order to complete your request, please click the link below' => 'Чтобы завершить запрос, нажмите на ссылку ниже',
'In order to finish your registration, we need you to enter following fields' => 'Чтобы закончить регистрацию, заполните следующие поля',
'Inactive' => 'Не активно',
'Information' => 'Информация',
'Invalid login or password' => 'Неправильный логин или пароль',
'Invalid or expired link' => 'Ссылка неправильна или устарела',
@ -127,6 +131,7 @@ return [
'It will be deleted forever' => 'Он будет удалён навсегда',
'Items' => 'Элементы',
'Joined on {0, date}' => 'Зарегистрирован {0, date}',
'Last activity' => 'Последняя активность',
'Last login IP' => 'IP последнего входа',
'Last login time' => 'Время последнего входа',
'Last password change' => 'Последняя смена пароля',
@ -185,10 +190,15 @@ return [
'Scan the QrCode with Google Authenticator App, then insert its temporary code on the box and submit.' => 'Просканируйте QR-код приложением Google Authenticator App, затем вставьте временный код в поле и отправьте.',
'Select rule...' => 'Выберите правило...',
'Send password recovery email' => 'Отправить письмо с восстановлением пароля',
'Session ID' => 'ID сесии',
'Session history' => 'История сессий',
'Sign in' => 'Войти',
'Sign up' => 'Зарегистрироваться',
'Something went wrong' => 'Что-то пошло не так',
'Status' => 'Статус',
'Submit' => 'Подтвердить',
'Switch identities is disabled.' => 'Переключение на другой аккаунт отключено.',
'Terminate all sessions' => 'Прекратить другие сеансы',
'Thank you for signing up on {0}' => 'Спасибо за регистрацию на сайте {0}',
'Thank you, registration is now complete.' => 'Поздравляем, регистрация успешно завершена!',
'The "recaptcha" component must be configured.' => 'Необходимо настроить компонент "recaptcha"',
@ -229,7 +239,9 @@ return [
'Update rule' => 'Изменить правило',
'Update user account' => 'Обновить аккаунт пользователя',
'Updated at' => 'Дата редактирования',
'User ID' => 'ID пользователя',
'User account could not be created.' => 'Не удалось создать аккаунт для пользователя.',
'User agent' => 'User agent',
'User block status has been updated.' => 'Статус блокировки пользователя обновлён.',
'User could not be registered.' => 'Не удалось зарегистрировать пользователя.',
'User has been confirmed' => 'Пользователь был активирован',
@ -265,6 +277,7 @@ return [
'Your account on {0} has been created' => 'Ваш аккаунт на сайте "{0}" был успешно создан',
'Your confirmation token is invalid or expired' => 'Ваша ссылка устарела или является ошибочной',
'Your consent is required to register' => 'Ваше согласие требуется для регистрации',
'Your consent is required to work with this site' => 'Ваше согласие требуется для работы с этим сайтом',
'Your email address has been changed' => 'Ваш email был успешно изменён',
'Your password has expired, you must change it now' => 'Срок действия вашего пароля истек, сейчас вы должны изменить его',
'Your personal information has been removed' => 'Ваша персональная информация удалена',

View File

@ -28,6 +28,7 @@ return [
'Account details' => '',
'Account details have been updated' => '',
'Account settings' => '',
'Active' => '',
'Already registered? Sign in!' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'An error occurred processing your request' => '',
@ -81,6 +82,7 @@ return [
'Create new rule' => '',
'Created at' => '',
'Credentials will be sent to the user by email' => '',
'Current' => '',
'Current password' => '',
'Current password is not valid' => '',
'Data privacy' => '',
@ -112,6 +114,7 @@ return [
'Gravatar email' => '',
'Hello' => '',
'Here you can download your personal data in a comma separated values format.' => '',
'IP' => '',
'I agree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => '',
'If you already registered, sign in and connect this account on settings page' => '',
'If you cannot click the link, please try pasting the text into your browser' => '',
@ -120,6 +123,7 @@ return [
'In order to complete your registration, please click the link below' => '',
'In order to complete your request, please click the link below' => '',
'In order to finish your registration, we need you to enter following fields' => '',
'Inactive' => '',
'Information' => '',
'Invalid login or password' => '',
'Invalid or expired link' => '',
@ -129,6 +133,7 @@ return [
'It will be deleted forever' => '',
'Items' => '',
'Joined on {0, date}' => '',
'Last activity' => '',
'Last login IP' => '',
'Last login time' => '',
'Last password change' => '',
@ -187,11 +192,15 @@ return [
'Scan the QrCode with Google Authenticator App, then insert its temporary code on the box and submit.' => '',
'Select rule...' => '',
'Send password recovery email' => '',
'Session ID' => '',
'Session history' => '',
'Sign in' => '',
'Sign up' => '',
'Something went wrong' => '',
'Status' => '',
'Submit' => '',
'Switch identities is disabled.' => '',
'Terminate all sessions' => '',
'Thank you for signing up on {0}' => '',
'Thank you, registration is now complete.' => '',
'The "recaptcha" component must be configured.' => '',
@ -233,7 +242,9 @@ return [
'Update rule' => '',
'Update user account' => '',
'Updated at' => '',
'User ID' => '',
'User account could not be created.' => '',
'User agent' => '',
'User block status has been updated.' => '',
'User could not be registered.' => '',
'User has been confirmed' => '',

View File

@ -28,6 +28,7 @@ return [
'Account details' => '',
'Account details have been updated' => '',
'Account settings' => '',
'Active' => '',
'Already registered? Sign in!' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'An error occurred processing your request' => '',
@ -81,6 +82,7 @@ return [
'Create new rule' => '',
'Created at' => '',
'Credentials will be sent to the user by email' => '',
'Current' => '',
'Current password' => '',
'Current password is not valid' => '',
'Data privacy' => '',
@ -113,6 +115,7 @@ return [
'Hello' => '',
'Here you can download your personal data in a comma separated values format.' => '',
'I agree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => '',
'IP' => '',
'If you already registered, sign in and connect this account on settings page' => '',
'If you cannot click the link, please try pasting the text into your browser' => '',
'If you did not make this request you can ignore this email' => '',
@ -120,6 +123,7 @@ return [
'In order to complete your registration, please click the link below' => '',
'In order to complete your request, please click the link below' => '',
'In order to finish your registration, we need you to enter following fields' => '',
'Inactive' => '',
'Information' => '',
'Invalid login or password' => '',
'Invalid or expired link' => '',
@ -129,6 +133,7 @@ return [
'It will be deleted forever' => '',
'Items' => '',
'Joined on {0, date}' => '',
'Last activity' => '',
'Last login IP' => '',
'Last login time' => '',
'Last password change' => '',
@ -187,11 +192,15 @@ return [
'Scan the QrCode with Google Authenticator App, then insert its temporary code on the box and submit.' => '',
'Select rule...' => '',
'Send password recovery email' => '',
'Session ID' => '',
'Session history' => '',
'Sign in' => '',
'Sign up' => '',
'Something went wrong' => '',
'Status' => '',
'Submit' => '',
'Switch identities is disabled.' => '',
'Terminate all sessions' => '',
'Thank you for signing up on {0}' => '',
'Thank you, registration is now complete.' => '',
'The "recaptcha" component must be configured.' => '',
@ -233,7 +242,9 @@ return [
'Update rule' => '',
'Update user account' => '',
'Updated at' => '',
'User ID' => '',
'User account could not be created.' => '',
'User agent' => '',
'User block status has been updated.' => '',
'User could not be registered.' => '',
'User has been confirmed' => '',

View File

@ -273,10 +273,21 @@ return [
'{0, date, MMMM dd, YYYY HH:mm}' => '{0, date, MMMM dd, YYYY HH:mm}',
'{0} cannot be blank.' => '{0} не може бути порожнім.',
'According to the European General Data Protection Regulation (GDPR) we need your consent to work with your personal data.' => '',
'Active' => '',
'Current' => '',
'Data privacy' => '',
'IP' => '',
'Inactive' => '',
'Last activity' => '',
'Recovery message sent' => '',
'Session ID' => '',
'Session history' => '',
'Status' => '',
'Submit' => '',
'Terminate all sessions' => '',
'Unfortunately, you can not work with this site without giving us consent to process your data.' => '',
'User ID' => '',
'User agent' => '',
'Your consent is required to work with this site' => '',
'Your role requires 2FA, you won\'t be able to use the application until you enable it' => '',
'A message has been sent to your email address. ' => '@@На вашу електронну адресу надіслано повідомлення@@',

View File

@ -28,6 +28,7 @@ return [
'Account details' => '',
'Account details have been updated' => '',
'Account settings' => '',
'Active' => '',
'Already registered? Sign in!' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'An error occurred processing your request' => '',
@ -81,6 +82,7 @@ return [
'Create new rule' => '',
'Created at' => '',
'Credentials will be sent to the user by email' => '',
'Current' => '',
'Current password' => '',
'Current password is not valid' => '',
'Data privacy' => '',
@ -113,6 +115,7 @@ return [
'Hello' => '',
'Here you can download your personal data in a comma separated values format.' => '',
'I agree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => '',
'IP' => '',
'If you already registered, sign in and connect this account on settings page' => '',
'If you cannot click the link, please try pasting the text into your browser' => '',
'If you did not make this request you can ignore this email' => '',
@ -120,6 +123,7 @@ return [
'In order to complete your registration, please click the link below' => '',
'In order to complete your request, please click the link below' => '',
'In order to finish your registration, we need you to enter following fields' => '',
'Inactive' => '',
'Information' => '',
'Invalid login or password' => '',
'Invalid or expired link' => '',
@ -129,6 +133,7 @@ return [
'It will be deleted forever' => '',
'Items' => '',
'Joined on {0, date}' => '',
'Last activity' => '',
'Last login IP' => '',
'Last login time' => '',
'Last password change' => '',
@ -187,11 +192,15 @@ return [
'Scan the QrCode with Google Authenticator App, then insert its temporary code on the box and submit.' => '',
'Select rule...' => '',
'Send password recovery email' => '',
'Session ID' => '',
'Session history' => '',
'Sign in' => '',
'Sign up' => '',
'Something went wrong' => '',
'Status' => '',
'Submit' => '',
'Switch identities is disabled.' => '',
'Terminate all sessions' => '',
'Thank you for signing up on {0}' => '',
'Thank you, registration is now complete.' => '',
'The "recaptcha" component must be configured.' => '',
@ -233,7 +242,9 @@ return [
'Update rule' => '',
'Update user account' => '',
'Updated at' => '',
'User ID' => '',
'User account could not be created.' => '',
'User agent' => '',
'User block status has been updated.' => '',
'User could not be registered.' => '',
'User has been confirmed' => '',

View File

@ -28,6 +28,7 @@ return [
'Account details' => '',
'Account details have been updated' => '',
'Account settings' => '',
'Active' => '',
'Already registered? Sign in!' => '',
'An email with instructions to create a new password has been sent to {email} if it is associated with an {appName} account. Your existing password has not been changed.' => '',
'An error occurred processing your request' => '',
@ -81,6 +82,7 @@ return [
'Create new rule' => '',
'Created at' => '',
'Credentials will be sent to the user by email' => '',
'Current' => '',
'Current password' => '',
'Current password is not valid' => '',
'Data privacy' => '',
@ -113,6 +115,7 @@ return [
'Hello' => '',
'Here you can download your personal data in a comma separated values format.' => '',
'I agree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => '',
'IP' => '',
'If you already registered, sign in and connect this account on settings page' => '',
'If you cannot click the link, please try pasting the text into your browser' => '',
'If you did not make this request you can ignore this email' => '',
@ -120,6 +123,7 @@ return [
'In order to complete your registration, please click the link below' => '',
'In order to complete your request, please click the link below' => '',
'In order to finish your registration, we need you to enter following fields' => '',
'Inactive' => '',
'Information' => '',
'Invalid login or password' => '',
'Invalid or expired link' => '',
@ -129,6 +133,7 @@ return [
'It will be deleted forever' => '',
'Items' => '',
'Joined on {0, date}' => '',
'Last activity' => '',
'Last login IP' => '',
'Last login time' => '',
'Last password change' => '',
@ -187,11 +192,15 @@ return [
'Scan the QrCode with Google Authenticator App, then insert its temporary code on the box and submit.' => '',
'Select rule...' => '',
'Send password recovery email' => '',
'Session ID' => '',
'Session history' => '',
'Sign in' => '',
'Sign up' => '',
'Something went wrong' => '',
'Status' => '',
'Submit' => '',
'Switch identities is disabled.' => '',
'Terminate all sessions' => '',
'Thank you for signing up on {0}' => '',
'Thank you, registration is now complete.' => '',
'The "recaptcha" component must be configured.' => '',
@ -233,7 +242,9 @@ return [
'Update rule' => '',
'Update user account' => '',
'Updated at' => '',
'User ID' => '',
'User account could not be created.' => '',
'User agent' => '',
'User block status has been updated.' => '',
'User could not be registered.' => '',
'User has been confirmed' => '',

View File

@ -0,0 +1,68 @@
<?php
/*
* This file is part of the 2amigos/yii2-usuario project.
*
* (c) 2amigOS! <http://2amigos.us/>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
use Da\User\Widget\SessionStatusWidget;
use yii\helpers\Html;
use yii\grid\GridView;
use yii\widgets\Pjax;
use Da\User\Model\SessionHistory;
use Da\User\Search\SessionHistorySearch;
use yii\web\View;
use yii\data\ActiveDataProvider;
/**
* @var $this View
* @var $searchModel SessionHistorySearch
* @var $dataProvider ActiveDataProvider
*/
?>
<?php $this->beginContent('@Da/User/resources/views/admin/update.php', ['user' => $user]) ?>
<div class="row">
<div class="col-xs-12">
<?= Html::a(
Yii::t('usuario', 'Terminate all sessions'),
['/user/admin/terminate-sessions', 'id' => $user->id],
[
'class' => 'btn btn-danger btn-xs pull-right',
'data-method' => 'post'
]
) ?>
</div>
</div>
<hr>
<?php Pjax::begin(); ?>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
'user_agent',
'ip',
[
'contentOptions' => [
'class' => 'text-nowrap',
],
'label' => Yii::t('usuario', 'Status'),
'value' => function (SessionHistory $model) {
return SessionStatusWidget::widget(['model' => $model]);
},
],
[
'attribute' => 'updated_at',
'format' => 'datetime'
],
],
]); ?>
<?php Pjax::end(); ?>
<?php $this->endContent() ?>

10
src/User/resources/views/admin/update.php Normal file → Executable file
View File

@ -13,6 +13,7 @@ use Da\User\Model\User;
use yii\bootstrap\Nav;
use yii\helpers\Html;
use yii\web\View;
use Da\User\Module as UserModule;
/**
* @var View $this
@ -24,12 +25,14 @@ $this->title = Yii::t('usuario', 'Update user account');
$this->params['breadcrumbs'][] = ['label' => Yii::t('usuario', 'Users'), 'url' => ['index']];
$this->params['breadcrumbs'][] = $this->title;
/** @var UserModule $module */
$module = Yii::$app->getModule('user');
?>
<div class="clearfix"></div>
<?= $this->render(
'/shared/_alert',
[
'module' => Yii::$app->getModule('user'),
'module' => $module,
]
) ?>
@ -67,6 +70,11 @@ $this->params['breadcrumbs'][] = $this->title;
'label' => Yii::t('usuario', 'Assignments'),
'url' => ['/user/admin/assignments', 'id' => $user->id],
],
[
'label' => Yii::t('usuario', 'Session history'),
'url' => ['/user/admin/session-history', 'id' => $user->id],
'visible' => $module->enableSessionHistory,
],
'<hr>',
[
'label' => Yii::t('usuario', 'Confirm'),

10
src/User/resources/views/settings/_menu.php Normal file → Executable file
View File

@ -11,9 +11,12 @@
use yii\helpers\Html;
use yii\widgets\Menu;
use Da\User\Module as UserModule;
use Da\User\Model\User;
/** @var \Da\User\Model\User $user */
/** @var User $user */
$user = Yii::$app->user->identity;
/** @var UserModule $module */
$module = Yii::$app->getModule('user');
$networksVisible = count(Yii::$app->authClientCollection->clients) > 0;
@ -41,6 +44,11 @@ $networksVisible = count(Yii::$app->authClientCollection->clients) > 0;
'items' => [
['label' => Yii::t('usuario', 'Profile'), 'url' => ['/user/settings/profile']],
['label' => Yii::t('usuario', 'Account'), 'url' => ['/user/settings/account']],
[
'label' => Yii::t('usuario', 'Session history'),
'url' => ['/user/settings/session-history'],
'visible' => $module->enableSessionHistory,
],
['label' => Yii::t('usuario', 'Privacy'),
'url' => ['/user/settings/privacy'],
'visible' => $module->enableGdprCompliance

View File

@ -0,0 +1,79 @@
<?php
/*
* This file is part of the 2amigos/yii2-usuario project.
*
* (c) 2amigOS! <http://2amigos.us/>
*
* For the full copyright and license information, please view
* the LICENSE file that was distributed with this source code.
*/
use yii\helpers\Html;
use yii\grid\GridView;
use yii\widgets\Pjax;
use Da\User\Model\SessionHistory;
use Da\User\Search\SessionHistorySearch;
use yii\web\View;
use yii\data\ActiveDataProvider;
use Da\User\Widget\SessionStatusWidget;
/**
* @var $this View
* @var $searchModel SessionHistorySearch
* @var $dataProvider ActiveDataProvider
*/
$this->title = Yii::t('usuario', 'Session history');
$this->params['breadcrumbs'][] = $this->title;
?>
<?= $this->render('/shared/_alert', ['module' => Yii::$app->getModule('user')]) ?>
<div class="row">
<div class="col-md-3">
<?= $this->render('/settings/_menu') ?>
</div>
<div class="col-md-9">
<div class="panel panel-default">
<div class="panel-heading">
<?= Html::encode($this->title) ?>
<?= Html::a(
Yii::t('usuario', 'Terminate all sessions'),
['/user/settings/terminate-sessions'],
[
'class' => 'btn btn-danger btn-xs pull-right',
'data-method' => 'post'
]
) ?>
</div>
<div class="panel-body">
<?php Pjax::begin(); ?>
<?= GridView::widget([
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => [
'user_agent',
'ip',
[
'contentOptions' => [
'class' => 'text-nowrap',
],
'label' => Yii::t('usuario', 'Status'),
'value' => function (SessionHistory $model) {
return SessionStatusWidget::widget(['model' => $model]);
},
],
[
'attribute' => 'updated_at',
'format' => 'datetime'
],
],
]); ?>
<?php Pjax::end(); ?>
</div>
</div>
</div>
</div>