Merge branch 'master' into static-code-analyzer

This commit is contained in:
Lorenzo Milesi
2022-08-11 09:50:37 +02:00
committed by GitHub
77 changed files with 2260 additions and 411 deletions

3
.gitignore vendored
View File

@ -47,8 +47,7 @@ codeception.yml
# Code Style Checkers and Mess Detectors
/phpcs.xml
/.php_cs.cache
/.php_cs
.php-cs-fixer.cache
# composer
composer.lock

43
.php-cs-fixer.dist.php Normal file
View File

@ -0,0 +1,43 @@
<?php
$header = <<<'EOF'
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.
EOF;
$finder = PhpCsFixer\Finder::create()
->exclude(['resources'])
->in("src/User")
;
$config = new PhpCsFixer\Config();
return $config->setRules([
'@PSR1' => true,
'@PSR2' => true,
'array_syntax' => ['syntax' => 'short'],
'header_comment' => ['header' => $header],
'combine_consecutive_unsets' => true,
'no_extra_blank_lines' => [
'tokens' => ['break', 'case', 'continue', 'curly_brace_block', 'default', 'extra', 'parenthesis_brace_block', 'return', 'square_brace_block', 'switch', 'throw', 'use', 'use_trait',],
],
'no_useless_else' => true,
'no_useless_return' => true,
'ordered_class_elements' => true,
'ordered_imports' => true,
'phpdoc_add_missing_param_annotation' => true,
'phpdoc_order' => true,
'phpdoc_align' => true,
'no_trailing_whitespace' => true,
'no_whitespace_in_blank_line' => true,
'no_trailing_comma_in_singleline_array' => true,
'no_whitespace_before_comma_in_array' => true,
'trim_array_spaces' => true,
'explicit_string_variable' => true,
'binary_operator_spaces' => true,
])
->setFinder($finder)
;

View File

@ -1,49 +0,0 @@
<?php
$header = <<<'EOF'
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.
EOF;
$finder = PhpCsFixer\Finder::create()
->exclude([
'.github',
'docs',
'temp',
'tests',
'vendor',
'src/User/resources'
])
->in(__DIR__);
return PhpCsFixer\Config::create()
->setRules(array(
'@PSR1' => true,
'@PSR2' => true,
'header_comment' => ['header' => $header],
'array_syntax' => array('syntax' => 'short'),
'combine_consecutive_unsets' => true,
'no_extra_consecutive_blank_lines' => array(
'break',
'continue',
'extra',
'return',
'throw',
'use',
'parenthesis_brace_block',
'square_brace_block',
'curly_brace_block'
),
'no_useless_else' => true,
'no_useless_return' => true,
'ordered_class_elements' => true,
'ordered_imports' => true,
'phpdoc_add_missing_param_annotation' => true,
'phpdoc_order' => true,
'phpdoc_align' => true
))
->setFinder($finder);

View File

@ -1,73 +1,89 @@
# CHANGELOG
## 1.6.0 work in progress
- Fix replace non-working travis build with working github actions build (TonisOrmisson)
- Fix user login events not triggered on ajax requests (TonisOrmisson)
- Enh: Added minimum requirements when a new password is automatically generated (MatteoF96)
- Fix #380: Avoid rewriting AccessRule::matchRole (maxxer)
- Fix #378: Add module attribute 'disableIpLogging' (jkmssoft)
- Enh #387: Added Persian translation (hadi-aj)
- Fix #384: Delete flash messages after consuming (cgsmith)
- Enh: Added SK translations (snickom)
- Fix: allow `password_changed_at` to be saved when reseting password (p4blojf)
- Fix #430: Moved `EVENT_BEFORE_PROFILE_UPDATE` to correct place (eluhr)
- Ehn #456: Added filter to allow forcing 2FA for specific user roles (acordeddu)
**WARNING**: this release (long time due) makes a step forward in PHP
compatibility, leaving behind obsolete versions. While yii2-usuario should
still work without issues on 5.6, from now on testing and development will
look forward and manitain only >=7.4 versions.
- Ehn: update welcome and confirmation email ending line (maxxer)
- Ehn #361: Record and manage user session history (maranqz)
- Fix: replace non-working travis build with working github actions build (TonisOrmisson)
- Fix: user login events not triggered on ajax requests (TonisOrmisson)
- Enh: Added minimum requirements when a new password is automatically generated (MatteoF96)
- Fix #380: Avoid rewriting AccessRule::matchRole (maxxer)
- Fix #378: Add module attribute 'disableIpLogging' (jkmssoft)
- Enh #387: Added Persian translation (hadi-aj)
- Fix #384: Delete flash messages after consuming (cgsmith)
- Fix #381: Renamed events in `UserEvent` to avoid conflicts with events in `FormEvent` (Slayvin)
- Enh: Added SK translations (snickom)
- Fix: allow `password_changed_at` to be saved when reseting password (p4blojf)
- Fix #430: Moved `EVENT_BEFORE_PROFILE_UPDATE` to correct place (eluhr)
- Ehn #456: Added filter to allow forcing 2FA for specific user roles (acordeddu)
- Ehn #412: Allow role names to support UTF-8 chars (4khobta)
- Ehn #448: Remove deprecated SwiftMailer, use SymfonyMailer instead (TonisOrmisson)
- Ehn #428: Translations of the placeholders in the login widget (anapaulaxenon)
- Update PHP-CS-Fixer configuration to new version (maxxer)
## 1.5.1 April 5, 2020
- Fix #370: Extending view fix (effsoft)
- Fix #306: Add event for failed login (ivan-cc)
- Fix #347: Only pass fields known to User model in registrationControl->actionRegister() (BillHeaton)
- Fix #346: Update ReCaptcha guide to not use AJAX (BillHeaton)
- Fix #345: Update ReCaptcha guide to add scenarios() in recoveryForm (BillHeaton)
- Fix #307: Fix French translation (arollmann)
- Fix #316: Fix new response from Google OAuth Api (Julian-B90)
- Fix #321: Fix new response from LinkedIn OAuth Api (tonydspaniard)
- Fix #322: Fix boolean values in migrations for SQL server (tsdogs)
- Enh #325: Added support for sqlite3 (santilin)
- Fix #326: Fix rule for the user auth_tf_enabled field (santilin)
- Fix #290: Fix wrong email message for resending confirmation (tonydspaniard)
- Enh #269: Added help documentation to console commands (tonydspaniard)
- Fix #244: Fix forced inclusion of a suggested class (tonydspaniard)
- Fix user event triggering in admin controller (maxxer)
- Enh #331: Added Ukrainian translations (kwazaro)
- Enh #324: Added option to restrict user assignments to roles only (CheckeredFlag)
- Enh #224: Added option to require consent (eseperio)
- Enh: Added classMap for MailService (necrox87)
- Fix #370: Extending view fix (effsoft)
- Fix #306: Add event for failed login (ivan-cc)
- Fix #347: Only pass fields known to User model in registrationControl->actionRegister() (BillHeaton)
- Fix #346: Update ReCaptcha guide to not use AJAX (BillHeaton)
- Fix #345: Update ReCaptcha guide to add scenarios() in recoveryForm (BillHeaton)
- Fix #307: Fix French translation (arollmann)
- Fix #316: Fix new response from Google OAuth Api (Julian-B90)
- Fix #321: Fix new response from LinkedIn OAuth Api (tonydspaniard)
- Fix #322: Fix boolean values in migrations for SQL server (tsdogs)
- Enh #325: Added support for sqlite3 (santilin)
- Fix #326: Fix rule for the user auth_tf_enabled field (santilin)
- Fix #290: Fix wrong email message for resending confirmation (tonydspaniard)
- Enh #269: Added help documentation to console commands (tonydspaniard)
- Fix #244: Fix forced inclusion of a suggested class (tonydspaniard)
- Fix user event triggering in admin controller (maxxer)
- Enh #331: Added Ukrainian translations (kwazaro)
- Enh #324: Added option to restrict user assignments to roles only (CheckeredFlag)
- Enh #224: Added option to require consent (eseperio)
- Enh: Added classMap for MailService (necrox87)
## 1.5.0 April 19, 2019
- Fix: Fix condition in EmailChangeService (it was always false) (borisaeric)
- Fix #198: Updated translations by quique, bizley, TonisOrmisson, guogan, Dezinger, maxxer, wautvda, mrbig00, fabiomlferreira, WeeSee
- Fix #209: Doc fix. allowAccountDelete default value is false (Dezinger)
- Fix #211: Migration boolean default value set to FALSE instead 0 (Dezinger)
- Fix #213: Migration sql syntax fix (Dezinger)
- Ehn #131: 2FA libraries now optional (maxxer)
- Ehn #187: Add GDPR features (Eseperio)
- Enh #184: Add `last-login-ip` capture capability (kartik-v)
- Enh: Changed `View::render()` calls in views to use absolute paths (ajmedway)
- Fix #169: Fix bug in ReCaptchaComponent (BuTaMuH)
- Fix #168: Fix spelling in russian language (EvgenyOrekhov)
- Fix #195: UserCreateService: check if we're from web before setting flash message (maxxer)
- Enh: Improvements to the admin responsive design (wautvda)
- Enh: Add controller module class reference (TonisOrmisson)
- Enh: Replace the deprecated InvalidParamException in ClassMapHelper (TonisOrmisson)
- Fix #242: Add POST filter for `admin/force-password-change` action (bscheshirwork)
- Enh #251: Use `asset-packagist` instead of `fxp-asset` if you run it as a module without having a project around (bscheshirwork)
- Fix #252: Delete check for unexpected property `allowPasswordRecovery` for resend email by admin (bscheshirwork)
- Fix #254: Rename `GDPR` properties to `lowerCamelCase` style (bscheshirwork)
- Enh #253: Add PHPDoc for events class (bscheshirwork)
- Fix #258: Rename `GDPR` delete action to `lowerCamelCase`/`dash` style (bscheshirwork)
- Fix #271: Add closure support for `from` email address; Change default sender to `supportEmail` (bscheshirwork)
- Fix #276: Fix missing translatable strings
- Enh #249: Show message `email send if possible` any time on reset password request (bscheshirwork)
- Enh #282: Allows customization of controller namespace (maxxer)
- Enh #303: Added French translation (pde159)
- Fix #304: Fixed broken regex character class (CheckeredFlag)
- Fix: Fix condition in EmailChangeService (it was always false) (borisaeric)
- Fix #198: Updated translations by quique, bizley, TonisOrmisson, guogan, Dezinger, maxxer, wautvda, mrbig00, fabiomlferreira, WeeSee
- Fix #209: Doc fix. allowAccountDelete default value is false (Dezinger)
- Fix #211: Migration boolean default value set to FALSE instead 0 (Dezinger)
- Fix #213: Migration sql syntax fix (Dezinger)
- Ehn #131: 2FA libraries now optional (maxxer)
- Ehn #187: Add GDPR features (Eseperio)
- Enh #184: Add `last-login-ip` capture capability (kartik-v)
- Enh: Changed `View::render()` calls in views to use absolute paths (ajmedway)
- Fix #169: Fix bug in ReCaptchaComponent (BuTaMuH)
- Fix #168: Fix spelling in russian language (EvgenyOrekhov)
- Fix #195: UserCreateService: check if we're from web before setting flash message (maxxer)
- Enh: Improvements to the admin responsive design (wautvda)
- Enh: Add controller module class reference (TonisOrmisson)
- Enh: Replace the deprecated InvalidParamException in ClassMapHelper (TonisOrmisson)
- Fix #242: Add POST filter for `admin/force-password-change` action (bscheshirwork)
- Enh #251: Use `asset-packagist` instead of `fxp-asset` if you run it as a module without having a project around (bscheshirwork)
- Fix #252: Delete check for unexpected property `allowPasswordRecovery` for resend email by admin (bscheshirwork)
- Fix #254: Rename `GDPR` properties to `lowerCamelCase` style (bscheshirwork)
- Enh #253: Add PHPDoc for events class (bscheshirwork)
- Fix #258: Rename `GDPR` delete action to `lowerCamelCase`/`dash` style (bscheshirwork)
- Fix #271: Add closure support for `from` email address; Change default sender to `supportEmail` (bscheshirwork)
- Fix #276: Fix missing translatable strings
- Enh #249: Show message `email send if possible` any time on reset password request (bscheshirwork)
- Enh #282: Allows customization of controller namespace (maxxer)
- Enh #303: Added French translation (pde159)
- Fix #304: Fixed broken regex character class (CheckeredFlag)
## 1.1.4 - February 19, 2018
- Enh: Check enableEmailConfirmation on registration (faenir)
- Fix #154: Fix DateTime constructor with Unix timestamps (tonydspaniard)
## 1.1.2-3 - February 9, 2018
- Bug: Bugfix for Model events UserEvent::EVENT_BEFORE_CONFIRMATION and UserEvent::EVENT_AFTER_CONFIRMATION (ajmedway)
- Bug: Bugfix for Model events UserEvent::EVENT_BEFORE_CREATE and UserEvent::EVENT_AFTER_CREATE (ajmedway)
- Enh #137: Added the ability to make `enableAutologin` configurable (pappfer)
@ -82,6 +98,7 @@
- Bug #110: Honor `enableFlashMessages` in `PasswordRecoveryService` (maxxer)
## 1.1.1 - November 27, 2017
- Bug #115: Convert client_id to string because pgsql fail with type convertion (Dezinger)
- Bug #119: Security fix: add AccessControl to RuleController (Dezinger)
- Enh #120: 2FA i18n russian translation (Dezinger)
@ -94,6 +111,7 @@
- Enh #109: Make use of better classes names (tonydspaniard)
## 1.1.0 - October 22, 2017
- Enh #91: Documentation for Mail events (kartik-v)
- Enh #79: Enhancements to Mailer exception handling and events (kartik-v)
- Fix #85: External links should open in a new tab|window (eseperio)
@ -115,12 +133,14 @@
- Fix #86: Fix view location bug (tonydspaniard)
## 1.0.13 - August 12, 2017
- Fix #49: Fix wrong call of method make() for set attributes (MKiselev)
- Enh #46: Use safeUp()/safeDown() instead up()/down() in migrations (MKiselev)
- Fix #51: Typo fix rememberLoginLifeSpan to rememberLoginLifespan (MKiselev)
- Fix #58: Last login fix (pappfer)
## 1.0.12 - August 6, 2017
- Bug Fix: Modify ResetPasswordService to forcely update password_hash field (tonydspaniard)
- Bug Fix: Fixed wrong routing misspell (tonydspaniard)
- Enh #41: Remove deprecated package yii2-codeception (tonydspaniard)
@ -130,12 +150,14 @@
- Fix #42: Allow setting permissions as children to roles (kurounin)
## 1.0.10-11 - July 25, 2017
- Fix #37: Fix bower alias in test environment (tekord)
- Enh #32: Added Italian Translation (maxxer)
- Fix #30: Prefill username and email in SettingsForm (mattheobjornson)
- Enh #39: Added `last_login_at` field to user table (pappfer)
## 1.0.9 - July 19, 2017
- Enh #22: Added impersonation feature (tonydspaniard)
## 1.0.8 - July 16, 2017

View File

@ -7,7 +7,6 @@ Yii 2 Usuario Extension
[![Latest Stable Version](https://poser.pugx.org/2amigos/yii2-usuario/version)](https://packagist.org/packages/2amigos/yii2-usuario)
[![Total Downloads](https://poser.pugx.org/2amigos/yii2-usuario/downloads)](https://packagist.org/packages/2amigos/yii2-usuario)
[![Build Status](https://github.com/2amigos/yii2-usuario/actions/workflows/php.yml/badge.svg)](https://github.com/2amigos/yii2-usuario/actions/)
[![Latest Unstable Version](https://poser.pugx.org/2amigos/yii2-usuario/v/unstable)](//packagist.org/packages/2amigos/yii2-usuario)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/2amigos/yii2-usuario/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/2amigos/yii2-usuario/?branch=master)

View File

@ -44,17 +44,19 @@
"2amigos/yii2-selectize-widget": "^1.1",
"yiisoft/yii2-authclient": "^2.1",
"yiisoft/yii2-httpclient": "^2.0",
"yiisoft/yii2-bootstrap": "^2.0",
"yiisoft/yii2-swiftmailer": "^2.0"
"yiisoft/yii2-bootstrap": "^2.0"
},
"suggest": {
"yiisoft/yii2-symfonymailer": "A mailer driver is needed to send e-mails. Older versions use abandoned Swiftmailer which can be replaced with symfonymailer",
"2amigos/2fa-library": "Needed if you want to enable 2 Factor Authentication. Require version ^1.0",
"2amigos/qrcode-library": "Needed if you want to enable 2FA with QR Code generation. Require version ^1.1"
},
"require-dev": {
"2amigos/2fa-library": "^2.0",
"2amigos/qrcode-library": "^2.0",
"friendsofphp/php-cs-fixer": "^2.3",
"friendsofphp/php-cs-fixer": "^3",
"php": ">=7.4",
"yiisoft/yii2-symfonymailer": "~2.0.0",
"squizlabs/php_codesniffer": "*",
"phpmd/phpmd": "@stable",
"codeception/verify": "^0.3.3",

6
docs/events/user-events.md Normal file → Executable file
View File

@ -21,6 +21,8 @@ On Controllers
- **UserEvent::EVENT_AFTER_UNBLOCK**: Occurs after a user is being un-blocked
- **UserEvent::EVENT_BEFORE_SWITCH_IDENTITY**: Occurs before a user is being impersonated by admin
- **UserEvent::EVENT_AFTER_SWITCH_IDENTITY**: Occurs after a user his being impersonated by admin
- **SessionEvent::EVENT_BEFORE_TERMINATE_USER_SESSIONS**
- **SessionEvent::EVENT_AFTER_TERMINATE_USER_SESSIONS**
- **RegistrationController**
@ -41,6 +43,10 @@ On Controllers
- **UserEvent::EVENT_BEFORE_DELETE**: Occurs before the user account is deleted
- **UserEvent::EVENT_AFTER_DELETE**: Occurs after the user account is deleted
- **SessionController**
- **SessionEvent::EVENT_BEFORE_TERMINATE_USER_SESSIONS**: Occurs before the user sessions is terminated
- **SessionEvent::EVENT_AFTER_TERMINATE_USER_SESSIONS**: Occurs after the user sessions is terminated
On Models
---------

View File

@ -0,0 +1,60 @@
How to enable session history
============================
Session history list user sessions.
User can delete all sessions except current.
Configure Module and Application
--------------------------------
```php
// ...
'modules' => [
'user' => [
'class' => Da\User\Module::class,
'enableSessionHistory' => true,
]
],
// ...
'components' => [
'session' => Da\User\Service\SessionHistory\SessionHistoryDecorator::class,
]
// ...
'container' => [
'singletons' => [
Da\User\Service\SessionHistory\TerminateSessionsServiceInterface::class => Da\User\Service\SessionHistory\TerminateSessionsService::class
]
]
// ...
'controllerMap' => [
'migrate' => [
...
'migrationNamespaces' => [
'Da\User\Migration\Session',
],
],
],
```
Additionally for upping migration can use
```
./yii migrate --migrationNamespaces=Da\\User\\Migration\Session
```
Setting user screenshot:
![Settings user screenshot](./session-history/settings.png)
Admin screenshot:
![Admin screenshot](./session-history/admin.png)
© [2amigos](http://www.2amigos.us/) 2013-2019

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

1
docs/index.md Normal file → Executable file
View File

@ -188,6 +188,7 @@ Helpful Guides
- [How to Switch Identities](helpful-guides/how-to-switch-identities.md)
- [Separate Frontend and Backend Sessions](helpful-guides/separate-frontend-and-backend-sessions.md)
- [Social Network Authentication](helpful-guides/social-network-authentication.md)
- [How to Enable session history](helpful-guides/how-to-use-session-history.md)
Contributing
------------

16
docs/installation/configuration-options.md Normal file → Executable file
View File

@ -3,6 +3,22 @@ Configuration Options
The module comes with a set of attributes to configure. The following is the list of all available options:
#### enableSessionHistory (Type: `boolean, integer`, Default value: `false`)
If this option is to `true`, session history will be kept, [more](../helpful-guides/how-to-use-session-history.md).
#### numberSessionHistory (Type: `boolean, integer`, Default value: `false`)
Number of expired storing records `session history`, values:
- `false` Store all records without deleting
- `integer` Count of records for storing
#### timeoutSessionHistory (Type: `boolean, integer`, Default value: `false`)
How long store `session history` after expiring, values:
- `false` Store all records without deleting
- `integer` Time for storing after expiring in seconds
#### enableTwoFactorAuthentication (type: `boolean`, default: `false`)
Setting this attribute will allow users to configure their login process with two-factor authentication.

10
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',
@ -359,7 +365,7 @@ class Bootstrap implements BootstrapInterface
$mapping = array_merge($defaults, $userClassMap);
foreach ($mapping as $name => $definition) {
$map[$this->getRoute($routes, $name) . "\\$name"] = $definition;
$map[$this->getRoute($routes, $name) . "\\{$name}"] = $definition;
}
return $map;

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'));
}
}

View File

@ -110,13 +110,13 @@ class RegistrationController extends Controller
// Create a temporay $user so we can get the attributes, then get
// the intersection between the $form fields and the $user fields.
$user = $this->make(User::class, [] );
$user = $this->make(User::class, []);
$fields = array_intersect_key($form->attributes, $user->attributes);
// Becomes password_hash
// Becomes password_hash
$fields['password'] = $form['password'];
$user = $this->make(User::class, [], $fields );
$user = $this->make(User::class, [], $fields);
$user->setScenario('register');
$mailService = MailFactory::makeWelcomeMailerService($user);

55
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' => ['@'],
],
],
],
];
@ -175,7 +183,8 @@ class SettingsController extends Controller
throw new NotFoundHttpException();
}
return $this->render(
'privacy', [
'privacy',
[
'module' => $this->module
]
);
@ -249,7 +258,8 @@ class SettingsController extends Controller
}
return $this->render(
'gdpr-delete', [
'gdpr-delete',
[
'model' => $form,
]
);
@ -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(),
]
@ -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

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

@ -23,8 +23,8 @@ class UserEvent extends Event
const EVENT_AFTER_CREATE = 'afterCreate';
const EVENT_BEFORE_DELETE = 'beforeDelete';
const EVENT_AFTER_DELETE = 'afterDelete';
const EVENT_BEFORE_REGISTER = 'beforeRegister';
const EVENT_AFTER_REGISTER = 'afterRegister';
const EVENT_BEFORE_REGISTER = 'beforeRegistration';
const EVENT_AFTER_REGISTER = 'afterRegistration';
const EVENT_BEFORE_ACCOUNT_UPDATE = 'beforeAccountUpdate';
const EVENT_AFTER_ACCOUNT_UPDATE = 'afterAccountUpdate';
const EVENT_BEFORE_PROFILE_UPDATE = 'beforeProfileUpdate';

View File

@ -37,7 +37,7 @@ class AccessRuleFilter extends AccessRule
/** @var User $identity */
$identity = $user->identity;
if (!$identity->gdpr_consent) {
Yii::$app->response->redirect([ "/$consentAction"])->send();
Yii::$app->response->redirect(["/{$consentAction}"])->send();
}
}
}

View File

@ -13,8 +13,8 @@ namespace Da\User\Helper;
use Yii;
use yii\base\Exception;
use yii\base\Security;
use yii\base\InvalidConfigException;
use yii\base\Security;
class SecurityHelper
{
@ -76,8 +76,7 @@ class SecurityHelper
if (!isset($minPasswordRequirements)) {
if (isset(Yii::$app->getModule('user')->minPasswordRequirements)) {
$minPasswordRequirements = Yii::$app->getModule('user')->minPasswordRequirements;
}
else {
} else {
$minPasswordRequirements = [
'lower' => 1,
'digit' => 1,

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);
}
}

View File

@ -97,7 +97,7 @@ abstract class AbstractAuthItem extends Model
return [
['itemName', 'safe'],
['name', 'required'],
['name', 'match', 'pattern' => '/^\w[\w.:\-]+\w$/'],
['name', 'match', 'pattern' => '/^\w[\w.:\-]+\w$/u'],
[['name', 'description', 'rule'], 'trim'],
[
'name',

View File

@ -25,7 +25,7 @@ use yii\base\InvalidParamException;
use yii\db\ActiveRecord;
/**
* @property int $user_id
* @property int $user_id
* @property string $name
* @property string $public_email
* @property string $gravatar_email
@ -34,7 +34,7 @@ use yii\db\ActiveRecord;
* @property string $website
* @property string $bio
* @property string $timezone
* @property User $user
* @property User $user
*/
class Profile extends ActiveRecord
{

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);
}
}

View File

@ -22,17 +22,17 @@ use yii\helpers\Url;
/**
* /**
* @property int $id Id
* @property int $user_id User id, null if account is not bind to user
* @property string $provider Name of service
* @property string $client_id Account id
* @property string $data Account properties returned by social network (json encoded)
* @property string $decodedData Json-decoded properties
* @property int $id Id
* @property int $user_id User id, null if account is not bind to user
* @property string $provider Name of service
* @property string $client_id Account id
* @property string $data Account properties returned by social network (json encoded)
* @property string $decodedData Json-decoded properties
* @property string $code
* @property string $email
* @property string $username
* @property int $created_at
* @property User $user User that this account is connected for
* @property int $created_at
* @property User $user User that this account is connected for
*/
class SocialNetworkAccount extends ActiveRecord
{

View File

@ -24,13 +24,13 @@ use yii\helpers\Url;
/**
* Token Active Record model.
*
* @property int $user_id
* @property int $user_id
* @property string $code
* @property int $type
* @property int $type
* @property string $url
* @property bool $isExpired
* @property int $created_at
* @property User $user
* @property bool $isExpired
* @property int $created_at
* @property User $user
*/
class Token extends ActiveRecord
{

View File

@ -31,33 +31,33 @@ use yii\web\IdentityInterface;
*
* @property bool $isAdmin
* @property bool $isBlocked
* @property bool $isConfirmed whether user account has been confirmed or not
* @property bool $gdpr_deleted whether user requested deletion of his account
* @property bool $gdpr_consent whether user has consent personal data processing
* @property bool $isConfirmed whether user account has been confirmed or not
* @property bool $gdpr_deleted whether user requested deletion of his account
* @property bool $gdpr_consent whether user has consent personal data processing
*
* Database fields:
* @property int $id
* @property string $username
* @property string $email
* @property string $unconfirmed_email
* @property string $password_hash
* @property string $auth_key
* @property string $auth_tf_key
* @property int $auth_tf_enabled
* @property string $registration_ip
* @property int $confirmed_at
* @property int $blocked_at
* @property int $flags
* @property int $created_at
* @property int $updated_at
* @property int $last_login_at
* @property int $gdpr_consent_date date of agreement of data processing
* @property string $last_login_ip
* @property int $password_changed_at
* @property int $password_age
* Defined relations:
* @property int $id
* @property string $username
* @property string $email
* @property string $unconfirmed_email
* @property string $password_hash
* @property string $auth_key
* @property string $auth_tf_key
* @property int $auth_tf_enabled
* @property string $registration_ip
* @property int $confirmed_at
* @property int $blocked_at
* @property int $flags
* @property int $created_at
* @property int $updated_at
* @property int $last_login_at
* @property int $gdpr_consent_date date of agreement of data processing
* @property string $last_login_ip
* @property int $password_changed_at
* @property int $password_age
* Defined relations:
* @property SocialNetworkAccount[] $socialNetworkAccounts
* @property Profile $profile
* @property Profile $profile
*/
class User extends ActiveRecord implements IdentityInterface
{

33
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.
@ -217,7 +232,7 @@ class Module extends BaseModule
/**
* @var array Minimum requirements when a new password is automatically generated.
* Array structure: `requirement => minimum number characters`.
* Array structure: `requirement => minimum number characters`.
*
* Possible array keys:
* - lower: minimum number of lowercase characters;
@ -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,13 +115,16 @@ 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' => '',
'If you haven\'t received a password, you can reset it at' => '',
'Impersonate this user' => '',
'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 +134,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 +193,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 +243,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' => '',
@ -259,6 +271,7 @@ return [
'You can connect multiple accounts to be able to log in using them' => '',
'You cannot remove your own account' => '',
'You need to confirm your email address' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'Your account details have been updated' => '',
'Your account has been blocked' => '',
'Your account has been blocked.' => '',
@ -279,6 +292,4 @@ return [
'{0, date, MMM dd, YYYY HH:mm}' => '',
'{0, date, MMMM dd, YYYY HH:mm}' => '',
'{0} cannot be blank.' => '',
'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,14 +114,17 @@ 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' => '',
'If you did not make this request you can ignore this email' => '',
'If you haven\'t received a password, you can reset it at' => '',
'Impersonate this user' => '',
'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 +134,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 +193,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 +243,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' => '',
@ -259,6 +271,7 @@ return [
'You can connect multiple accounts to be able to log in using them' => '',
'You cannot remove your own account' => '',
'You need to confirm your email address' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'Your account details have been updated' => '',
'Your account has been blocked' => '',
'Your account has been blocked.' => '',
@ -279,6 +292,4 @@ return [
'{0, date, MMM dd, YYYY HH:mm}' => '',
'{0, date, MMMM dd, YYYY HH:mm}' => '',
'{0} cannot be blank.' => '',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'Now you can resume the login process' => '@@@@',
];

View File

@ -275,11 +275,30 @@ return [
'{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.' => '',
'Data privacy' => '',
'If you haven\'t received a password, you can reset it at' => '',
'Submit' => '',
'Unfortunately, you can not work with this site without giving us consent to process your data.' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'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. ' => '@@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,8 +277,25 @@ 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' => '@@@@',
'Now you can resume the login process' => '@@@@',
'If you haven\'t received a password, you can reset it at' => '',
'Information' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
];

View File

@ -276,11 +276,22 @@ 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.',
'If you haven\'t received a password, you can reset it at' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'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}' => '',
'An email has been sent with instructions for resetting your password' => '@@Se ha enviado un correo electrónico con instrucciones para restablecer su contraseña@@',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'Now you can resume the login process' => '@@@@',
'Two factor authentication protects you against stolen credentials' => '@@La autenticación de dos factores le protege del robo de credenciales@@',
];

View File

@ -268,17 +268,28 @@ 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' => '',
'If you haven\'t received a password, you can reset it at' => '',
'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' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'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?' => '',
@ -194,13 +201,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' => '',
@ -228,6 +245,9 @@ return [
'Scan the QrCode with Google Authenticator App, then insert its temporary code on the box and submit.' => '',
'Select rule...' => '',
'Send password recovery email' => '',
'Sign in' => '',
'Sign up' => '',
'Something went wrong' => '',
'Submit' => '',
'Switch identities is disabled.' => '',
'The "recaptcha" component must be configured.' => '',
@ -259,7 +279,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' => '',
@ -270,6 +292,7 @@ return [
'We have sent confirmation links to both old and new email addresses. You must click both links to complete your request.' => '',
'You are about to delete all your personal data from this site.' => '',
'You cannot remove your own account' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'Your account has been completely deleted' => '',
'Your consent is required to register' => '',
'Your consent is required to work with this site' => '',
@ -279,8 +302,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,13 +115,16 @@ 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' => '',
'If you haven\'t received a password, you can reset it at' => '',
'Impersonate this user' => '',
'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 +134,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 +193,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 +243,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' => '',
@ -259,6 +271,7 @@ return [
'You can connect multiple accounts to be able to log in using them' => '',
'You cannot remove your own account' => '',
'You need to confirm your email address' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'Your account details have been updated' => '',
'Your account has been blocked' => '',
'Your account has been blocked.' => '',
@ -279,6 +292,4 @@ return [
'{0, date, MMM dd, YYYY HH:mm}' => '',
'{0, date, MMMM dd, YYYY HH:mm}' => '',
'{0} cannot be blank.' => '',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'Now you can resume the login process' => '@@@@',
];

View File

@ -273,12 +273,22 @@ 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' => '',
'If you haven\'t received a password, you can reset it at' => '',
'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,14 +114,17 @@ 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' => '',
'If you did not make this request you can ignore this email' => '',
'If you haven\'t received a password, you can reset it at' => '',
'Impersonate this user' => '',
'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 +134,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 +193,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 +243,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' => '',
@ -259,6 +271,7 @@ return [
'You can connect multiple accounts to be able to log in using them' => '',
'You cannot remove your own account' => '',
'You need to confirm your email address' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'Your account details have been updated' => '',
'Your account has been blocked' => '',
'Your account has been blocked.' => '',
@ -279,6 +292,4 @@ return [
'{0, date, MMM dd, YYYY HH:mm}' => '',
'{0, date, MMMM dd, YYYY HH:mm}' => '',
'{0} cannot be blank.' => '',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'Now you can resume the login process' => '@@@@',
];

View File

@ -270,35 +270,26 @@ 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' => '',
'If you haven\'t received a password, you can reset it at' => '',
'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' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'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.@@',
'An email has been sent with instructions for resetting your password' => '@@E-mailt küldtek a jelszó visszaállításával kapcsolatos utasításokkal@@',
'Awesome, almost there. ' => '@@Hurrá, majdnem kész.@@',
'Disable Two-Factor Auth' => '@@Letiltja a kétütemű hitelesítést@@',
'Enable Two-factor auth' => '@@Engedélyezze a kétütemű hitelesítést@@',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'I aggree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => '@@Aggregálom a személyes adataim feldolgozását és a cookie-k használatát a webhely működésének megkönnyítése érdekében. További információért olvassa el a {privacyPolicy}@@',
'Invalid two-factor code' => '@@Érvénytelen kétütemű kód@@',
'Last login' => '@@Utolsó bejelentkezés@@',
'Now you can resume the login process' => '@@@@',
'This will disable two-factor auth. Are you sure?' => '@@Ez letiltja a kétütemű hitelesítést. biztos vagy ebben?@@',
'Two Factor Authentication' => '@@Két tényező hitelesítés@@',
'Two factor authentication protects you against stolen credentials' => '@@Két tényező-hitelesítés megvédi az ellopott hitelesítő adatokat@@',
'Two factor successfully enabled.' => '@@Két tényező sikeresen bekapcsolt.@@',
'Two-Factor Authentication' => '@@Két faktoros hitelesítés@@',
'Two-factor auth protects you against stolen credentials' => '@@A kétütemű auth védelmet nyújt az ellopott hitelesítő adatok ellen@@',
'Two-factor authentication code' => '@@Kétszeres hitelesítési kód@@',
'Two-factor authorization has been disabled.' => '@@A kétütemű engedélyezés le van tiltva.@@',
'Two-factor code' => '@@Kétszámjegyű kód@@',
'Unable to disable two-factor authorization.' => '@@Nem sikerült letiltani a kétütemű engedélyezést.@@',
'We couldn\'t re-send the mail to confirm your address. ' => '@@A cím megerősítéséhez nem tudtuk újra elküldeni az e-mailt.@@',
'We have sent confirmation links to both old and new email addresses. ' => '@@Megerősítő linkeket küldtünk régi és új e-mail címekre.@@',
];

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.',
'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@@',
'If you haven\'t received a password, you can reset it at' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'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}' => '',
];

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,14 +114,18 @@ 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}' => '',
'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' => '',
'If you did not make this request you can ignore this email' => '',
'If you haven\'t received a password, you can reset it at' => '',
'Impersonate this user' => '',
'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 +135,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 +194,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 +244,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' => '',
@ -259,6 +272,7 @@ return [
'You can connect multiple accounts to be able to log in using them' => '',
'You cannot remove your own account' => '',
'You need to confirm your email address' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'Your account details have been updated' => '',
'Your account has been blocked' => '',
'Your account has been blocked.' => '',
@ -279,6 +293,4 @@ return [
'{0, date, MMM dd, YYYY HH:mm}' => '',
'{0, date, MMMM dd, YYYY HH:mm}' => '',
'{0} cannot be blank.' => '',
'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' => '',
@ -113,13 +115,16 @@ 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' => '',
'If you haven\'t received a password, you can reset it at' => '',
'Impersonate this user' => '',
'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 +134,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 +193,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 +243,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' => '',
@ -259,6 +271,7 @@ return [
'You can connect multiple accounts to be able to log in using them' => '',
'You cannot remove your own account' => '',
'You need to confirm your email address' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'Your account details have been updated' => '',
'Your account has been blocked' => '',
'Your account has been blocked.' => '',
@ -279,6 +292,4 @@ return [
'{0, date, MMM dd, YYYY HH:mm}' => '',
'{0, date, MMMM dd, YYYY HH:mm}' => '',
'{0} cannot be blank.' => '',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'Now you can resume the login process' => '@@@@',
];

View File

@ -270,41 +270,25 @@ 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' => '',
'If you haven\'t received a password, you can reset it at' => '',
'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@@',
'An email has been sent with instructions for resetting your password' => '@@Er werd een email verstuurd met instructies om jouw wachtwoord te resetten@@',
'Awesome, almost there. ' => '@@Super, bijna klaar.@@',
'Class "{0}" does not exist' => '@@Class "{0} bestaat niet@@',
'Disable Two-Factor Auth' => '@@Tweetraps authenticatie uitschakelen@@',
'Enable Two-factor auth' => '@@Tweetraps authenticatie inschakelen@@',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'I aggree processing of my personal data and the use of cookies
to facilitate the operation of this site. For more information read our {privacyPolicy}' => '@@Ik ga akkoord dat mijn persoonlijke data en cookies worden verwerkt voor het gebruik van deze website. Voor meer informatie lees onze {privacyPolicy}@@',
'I aggree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => '@@Ik ga akkoord dat mijn persoonlijke data en cookies worden verwerkt voor het gebruik van deze website. Voor meer informatie lees onze {privacyPolicy}@@',
'Invalid two-factor code' => '@@Ongeldige tweetraps authenticatie code@@',
'Last login' => '@@Laatste login@@',
'Now you can resume the login process' => '@@@@',
'Registration ip' => '@@Registratie IP@@',
'Rule class can not be instantiated' => '@@Registratie IP@@',
'Rule class must extend "yii\\rbac\\Rule"' => '@@Regel klasse moet worden uitgebreid met "yii\\rbac\\Rule"@@',
'This will disable two-factor auth. Are you sure?' => '@@Dit zal de tweetraps authenticatie uitschakelen. Ben je zeker?@@',
'Two Factor Authentication' => '@@Tweetraps authenticatie@@',
'Two factor authentication protects you against stolen credentials' => '@@Tweetraps authenticatie beschermt je tegen gestolen inloggegevens@@',
'Two factor successfully enabled.' => '@@Tweetraps authenticatie ingeschakeld@@',
'Two-Factor Authentication' => '@@Tweetraps authenticatie@@',
'Two-factor auth protects you against stolen credentials' => '@@Tweetraps authenticatie beschermt je tegen gestolen authenticatie gegevens@@',
'Two-factor authentication code' => '@@Tweetraps authenticatie code@@',
'Two-factor authorization has been disabled.' => '@@Tweetraps authenticatie werd uitgeschakeld.@@',
'Two-factor code' => '@@Tweetraps authenticatie code@@',
'Unable to disable two-factor authorization.' => '@@Tweetraps authenticatie kon niet worden uitgeschakeld@@',
'We couldn\'t re-send the mail to confirm your address. ' => '@@Wij konden de email bevestigingsmail niet opnieuw naar jouw adres verzenden.@@',
'We have sent confirmation links to both old and new email addresses. ' => '@@We hebben de bevestigingsmail naar zowel jouw oud als nieuw emailadres verzonden.@@',
];

View File

@ -270,30 +270,26 @@ 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' => '',
'If you haven\'t received a password, you can reset it at' => '',
'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' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'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@@',
'Disable Two-Factor Auth' => '@@Wyłącz uwierzytelnianie dwuetapowe@@',
'Enable Two-factor auth' => '@@Włącz uwierzytelnianie dwuetapowe@@',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'Invalid two-factor code' => '@@Nieprawidłowy kod uwierzytelniania dwuetapowego@@',
'Last login' => '@@Data ostatniego logowania@@',
'Now you can resume the login process' => '@@@@',
'This will disable two-factor auth. Are you sure?' => '@@To wyłączy uwierzytelnianie dwuetapowe. Czy jesteś pewny?@@',
'Two Factor Authentication' => '@@Uwierzytelnianie dwuetapowe@@',
'Two factor authentication protects you against stolen credentials' => '@@Uwierzytelnianie dwuetapowe chroni Cię przed kradzieżą danych logowania@@',
'Two factor successfully enabled.' => '@@Dwuetapowe uwierzytelnianie poprawnie włączone.@@',
'Two-Factor Authentication' => '@@Uwierzytelnianie dwuetapowe@@',
'Two-factor auth protects you against stolen credentials' => '@@Uwierzytelnianie dwuetapowe chroni Cię przed kradzieżą danych logowania@@',
'Two-factor authentication code' => '@@Kod uwierzytelniania dwuetapowego@@',
'Two-factor authorization has been disabled.' => '@@Dwuetapowa autoryzacja została wyłączona.@@',
'Two-factor code' => '@@Kod dwuetapowy@@',
'Unable to disable two-factor authorization.' => '@@Nie można wyłączyć dwuetapowej autoryzacji.@@',
];

View File

@ -270,41 +270,26 @@ 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' => '',
'If you haven\'t received a password, you can reset it at' => '',
'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' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'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.@@',
'An email has been sent with instructions for resetting your password' => '@@Um e-mail foi enviado com instruções para redefinir sua senha@@',
'Awesome, almost there. ' => '@@Incrível, quase lá.@@',
'Class "{0}" does not exist' => '@@A classe "{0}" não existe@@',
'Disable Two-Factor Auth' => '@@Desabilitar autenticação em dois fatores@@',
'Enable Two-factor auth' => '@@Habilitar autenticação em dois fatores@@',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'I aggree processing of my personal data and the use of cookies
to facilitate the operation of this site. For more information read our {privacyPolicy}' => '@@Concordo com o processamento de meus dados pessoais e o uso de cookies para facilitar a operação deste site. Para mais informações, leia nosso {privacyPolicy}@@',
'I aggree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => '@@Concordo com o processamento de meus dados pessoais e o uso de cookies para facilitar a operação deste site. Para mais informações, leia nosso {privacyPolicy}@@',
'Invalid two-factor code' => '@@Código de dois fatores inválido@@',
'Last login' => '@@Último login@@',
'Now you can resume the login process' => '@@@@',
'Registration ip' => '@@IP de registro@@',
'Rule class can not be instantiated' => '@@A classe de regras não pode ser instanciada@@',
'Rule class must extend "yii\\rbac\\Rule"' => '@@A classe de regras deve estender de "yii\\rbac\\Rule"@@',
'This will disable two-factor auth. Are you sure?' => '@@Isso desativará a autenticação de dois fatores. Você tem certeza?@@',
'Two Factor Authentication' => '@@Autenticação de dois fatores@@',
'Two factor authentication protects you against stolen credentials' => '@@A autenticação de dois fatores protege você contra credenciais roubadas@@',
'Two factor successfully enabled.' => '@@Dois fatores habilitados com sucesso.@@',
'Two-Factor Authentication' => '@@Autenticação de dois fatores@@',
'Two-factor auth protects you against stolen credentials' => '@@Autenticação de dois fatores protege você contra credenciais roubadas@@',
'Two-factor authentication code' => '@@Código de autenticação de dois fatores@@',
'Two-factor authorization has been disabled.' => '@@A autorização de dois fatores foi desabilitada.@@',
'Two-factor code' => '@@Código de dois fatores@@',
'Unable to disable two-factor authorization.' => '@@Não é possível desabilitar a autorização de dois fatores.@@',
'We couldn\'t re-send the mail to confirm your address. ' => '@@Não poderíamos re-enviar o correio para confirmar o seu endereço.@@',
'We have sent confirmation links to both old and new email addresses. ' => '@@Enviamos links de confirmação para endereços de e-mail antigo e novo.@@',
];

View File

@ -257,28 +257,39 @@ 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' => '',
'If you haven\'t received a password, you can reset it at' => '',
'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' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'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

@ -270,37 +270,26 @@ 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' => '',
'If you haven\'t received a password, you can reset it at' => '',
'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' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'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.@@',
'An email has been sent with instructions for resetting your password' => '@@A fost trimis un e-mail cu instrucțiuni pentru resetarea parolei@@',
'Awesome, almost there. ' => '@@Minunat, aproape gata.@@',
'Disable Two-Factor Auth' => '@@Dezactivați autentificarea cu două factori@@',
'Enable Two-factor auth' => '@@Activați Auth@@',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'I aggree processing of my personal data and the use of cookies
to facilitate the operation of this site. For more information read our {privacyPolicy}' => '@@Am agregat prelucrarea datelor mele personale și utilizarea cookie-urilor pentru a facilita funcționarea acestui site. Pentru mai multe informații, citiți {privacyPolicy}@@',
'I aggree processing of my personal data and the use of cookies to facilitate the operation of this site. For more information read our {privacyPolicy}' => '@@Am agregat prelucrarea datelor mele personale și utilizarea cookie-urilor pentru a facilita funcționarea acestui site. Pentru mai multe informații, citiți {privacyPolicy}@@',
'Invalid two-factor code' => '@@Cod de două factori nevalid@@',
'Last login' => '@@Ultima logare@@',
'Now you can resume the login process' => '@@@@',
'This will disable two-factor auth. Are you sure?' => '@@Aceasta va dezactiva auth-ul cu două factori. Esti sigur?@@',
'Two Factor Authentication' => '@@Două autentificare cu factori@@',
'Two factor authentication protects you against stolen credentials' => '@@Autentificarea cu două factori vă protejează împotriva acreditărilor furate@@',
'Two factor successfully enabled.' => '@@Doi factori activat cu succes.@@',
'Two-Factor Authentication' => '@@Două factori de autentificare@@',
'Two-factor auth protects you against stolen credentials' => '@@Autostradă cu două factori vă protejează împotriva acreditărilor furate@@',
'Two-factor authentication code' => '@@Cod de autentificare cu două factori@@',
'Two-factor authorization has been disabled.' => '@@A fost dezactivată autorizația cu două factori.@@',
'Two-factor code' => '@@Cod de două factori@@',
'Unable to disable two-factor authorization.' => '@@Imposibil de dezactivat autorizația cu două factori.@@',
'We couldn\'t re-send the mail to confirm your address. ' => '@@Nu am putut retrimite mesajul pentru a vă confirma adresa.@@',
'We have sent confirmation links to both old and new email addresses. ' => '@@Am trimis legături de confirmare adreselor de e-mail vechi și noi.@@',
];

15
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' => 'Ваша персональная информация удалена',
@ -275,8 +288,10 @@ return [
'{0} cannot be blank.' => '{0} не может быть пустым.',
'According to the European General Data Protection Regulation (GDPR) we need your consent to work with your personal data.' => '',
'Data privacy' => '',
'If you haven\'t received a password, you can reset it at' => '',
'Submit' => '',
'Unfortunately, you can not work with this site without giving us consent to process your data.' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'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' => '',
@ -112,14 +114,17 @@ 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' => '',
'If you did not make this request you can ignore this email' => '',
'If you haven\'t received a password, you can reset it at' => '',
'Impersonate this user' => '',
'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 +134,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 +193,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 +243,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' => '',
@ -259,6 +271,7 @@ return [
'You can connect multiple accounts to be able to log in using them' => '',
'You cannot remove your own account' => '',
'You need to confirm your email address' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'Your account details have been updated' => '',
'Your account has been blocked' => '',
'Your account has been blocked.' => '',
@ -279,6 +292,4 @@ return [
'{0, date, MMM dd, YYYY HH:mm}' => '',
'{0, date, MMMM dd, YYYY HH:mm}' => '',
'{0} cannot be blank.' => '',
'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' => '',
@ -113,13 +115,16 @@ 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' => '',
'If you haven\'t received a password, you can reset it at' => '',
'Impersonate this user' => '',
'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 +134,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 +193,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 +243,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' => '',
@ -259,6 +271,7 @@ return [
'You can connect multiple accounts to be able to log in using them' => '',
'You cannot remove your own account' => '',
'You need to confirm your email address' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'Your account details have been updated' => '',
'Your account has been blocked' => '',
'Your account has been blocked.' => '',
@ -279,6 +292,4 @@ return [
'{0, date, MMM dd, YYYY HH:mm}' => '',
'{0, date, MMMM dd, YYYY HH:mm}' => '',
'{0} cannot be blank.' => '',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'Now you can resume the login process' => '@@@@',
];

View File

@ -273,36 +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. ' => '@@На вашу електронну адресу надіслано повідомлення@@',
'An email has been sent with instructions for resetting your password' => '@@Лист з інструкціями по зміні пароля надіслано на електронну адресу@@',
'Awesome, almost there. ' => '@@Чудово, майже все.@@',
'Class "{0}" does not exist' => '@@Клас "{0}" не існує@@',
'Disable Two-Factor Auth' => '@@Вимкнути двофакторну аутентифікацію@@',
'Enable Two-factor auth' => '@@Увімкнути двофакторну аутентифікацію@@',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'I aggree 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}@@',
'Invalid two-factor code' => '@@Невірний код двофакторної авторизації@@',
'Last login' => '@@Останній вхід@@',
'Now you can resume the login process' => '@@@@',
'Registration ip' => '@@IP реєстрації@@',
'Rule class can not be instantiated' => '@@Клас Правила не може бути ініційований@@',
'Rule class must extend "yii\\rbac\\Rule"' => '@@Клас Правила має розширювати "yii\\rbac\\Rule"@@',
'This will disable two-factor auth. Are you sure?' => '@@Це вимкне двофакторну аутентифікацію. Ви впевнені?@@',
'Two Factor Authentication' => '@@Двофакторна аутентифікація@@',
'Two factor authentication protects you against stolen credentials' => '@@Двофакторна аутентифікація дає Вам додатковий захист даних авторизації@@',
'Two factor successfully enabled.' => '@@Двофакторна аутентифікація успішно підключена@@',
'Two-Factor Authentication' => '@@Двофакторна аутентифікація@@',
'Two-factor auth protects you against stolen credentials' => '@@Двофакторна аутентифікація дає Вам додатковий захист даних авторизації@@',
'Two-factor authentication code' => '@@Код двофакторної аутентифікації@@',
'Two-factor authorization has been disabled.' => '@@Двофакторна аутентифікація відключена@@',
'Two-factor code' => '@@Двофакторний код@@',
'Unable to disable two-factor authorization.' => '@@Неможливо відключити двофакторну авторизацію.@@',
'We couldn\'t re-send the mail to confirm your address. ' => '@@Ми не змогли надіслати email для підтвердження Вашої електронної адреси.@@',
'We have sent confirmation links to both old and new email addresses. ' => '@@Ми надіслали повідомлення з підтверджуючими посиланнями на обидві Ваші електронні адреси: стару і нову.@@',
];

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,13 +115,16 @@ 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' => '',
'If you haven\'t received a password, you can reset it at' => '',
'Impersonate this user' => '',
'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 +134,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 +193,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 +243,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' => '',
@ -259,6 +271,7 @@ return [
'You can connect multiple accounts to be able to log in using them' => '',
'You cannot remove your own account' => '',
'You need to confirm your email address' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'Your account details have been updated' => '',
'Your account has been blocked' => '',
'Your account has been blocked.' => '',
@ -279,6 +292,4 @@ return [
'{0, date, MMM dd, YYYY HH:mm}' => '',
'{0, date, MMMM dd, YYYY HH:mm}' => '',
'{0} cannot be blank.' => '',
'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' => '',
@ -113,13 +115,16 @@ 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' => '',
'If you haven\'t received a password, you can reset it at' => '',
'Impersonate this user' => '',
'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 +134,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 +193,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 +243,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' => '',
@ -259,6 +271,7 @@ return [
'You can connect multiple accounts to be able to log in using them' => '',
'You cannot remove your own account' => '',
'You need to confirm your email address' => '',
'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}' => '',
'Your account details have been updated' => '',
'Your account has been blocked' => '',
'Your account has been blocked.' => '',
@ -279,6 +292,4 @@ return [
'{0, date, MMM dd, YYYY HH:mm}' => '',
'{0, date, MMMM dd, YYYY HH:mm}' => '',
'{0} cannot be blank.' => '',
'Every user having your role has two factor authentication mandatory, you must enable it' => '@@@@',
'Now you can resume the login process' => '@@@@',
];

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'),

View File

@ -30,5 +30,5 @@ use yii\helpers\Html;
<?= Yii::t('usuario', 'If you cannot click the link, please try pasting the text into your browser') ?>.
</p>
<p style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.6; font-weight: normal; margin: 0 0 10px; padding: 0;">
<?= Yii::t('usuario', 'If you did not make this request you can ignore this email') ?>.
<?= Yii::t('usuario', 'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}', ['app_name' => Yii::$app->name]) ?>.
</p>

View File

@ -22,4 +22,4 @@
<?= Yii::t('usuario', 'If you cannot click the link, please try pasting the text into your browser') ?>.
<?= Yii::t('usuario', 'If you did not make this request you can ignore this email') ?>.
<?= Yii::t('usuario', 'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}', ['app_name' => Yii::$app->name]) ?>.

View File

@ -30,4 +30,4 @@
<?= Yii::t('usuario', 'If you cannot click the link, please try pasting the text into your browser') ?>.
<?php endif ?>
<?= Yii::t('usuario', 'If you did not make this request you can ignore this email') ?>.
<?= Yii::t('usuario', 'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}', ['app_name' => Yii::$app->name]) ?>.

View File

@ -10,6 +10,7 @@
*/
use yii\helpers\Html;
use yii\helpers\Url;
/**
* @var \Da\User\Module $module
@ -28,6 +29,9 @@ use yii\helpers\Html;
<?php if ($showPassword || $module->generatePasswords): ?>
<?= Yii::t('usuario', 'We have generated a password for you') ?>: <strong><?= $user->password ?></strong>
<?php endif ?>
<?php if ($module->allowPasswordRecovery): ?>
<?= Yii::t('usuario', 'If you haven\'t received a password, you can reset it at') ?>: <strong><?= Html::a(Html::encode(Url::to(['/user/forgot'], true)), Url::to(['/user/forgot'], true)) ?></strong>
<?php endif ?>
</p>
@ -44,5 +48,5 @@ use yii\helpers\Html;
<?php endif ?>
<p style="font-family: 'Helvetica Neue', 'Helvetica', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.6; font-weight: normal; margin: 0 0 10px; padding: 0;">
<?= Yii::t('usuario', 'If you did not make this request you can ignore this email') ?>.
<?= Yii::t('usuario', 'You received this email because someone, possibly you or someone on your behalf, have created an account at {app_name}', ['app_name' => Yii::$app->name]) ?>.
</p>

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>

View File

@ -35,9 +35,9 @@ use yii\widgets\ActiveForm;
]
) ?>
<?= $form->field($model, 'login')->textInput(['placeholder' => 'Login']) ?>
<?= $form->field($model, 'login')->textInput(['placeholder' => Yii::t('usuario', 'Login')]) ?>
<?= $form->field($model, 'password')->passwordInput(['placeholder' => 'Password']) ?>
<?= $form->field($model, 'password')->passwordInput(['placeholder' => Yii::t('usuario', 'Password')]) ?>
<?= $form->field($model, 'rememberMe')->checkbox() ?>

View File

@ -26,7 +26,11 @@ return [
],
'db' => require __DIR__ . '/db.php',
'mailer' => [
'useFileTransport' => true,
'messageClass' => \yii\symfonymailer\Message::class,
[
'class' => \yii\symfonymailer\Mailer::class,
],
'useFileTransport' => false
],
'urlManager' => [
'showScriptName' => true,

View File

@ -97,12 +97,12 @@ class GdprCest
$I->amOnRoute('/user/registration/register');
$this->register($I, 'tester@example.com', 'tester', 'tester');
$I->see('Your account has been created and a message with further instructions has been sent to your email');
$user = $I->grabRecord(User::className(), ['email' => 'tester@example.com']);
$token = $I->grabRecord(Token::className(), ['user_id' => $user->id, 'type' => Token::TYPE_CONFIRMATION]);
/** @var yii\swiftmailer\Message $message */
$user = $I->grabRecord(User::class, ['email' => 'tester@example.com']);
$token = $I->grabRecord(Token::class, ['user_id' => $user->id, 'type' => Token::TYPE_CONFIRMATION]);
/** @var \yii\mail\MessageInterface $message */
$message = $I->grabLastSentEmail();
$I->assertArrayHasKey($user->email, $message->getTo());
$I->assertStringContainsString(Html::encode($token->getUrl()), utf8_encode(quoted_printable_decode($message->getSwiftMessage()->toString())));
$I->assertStringContainsString(Html::encode($token->getUrl()), utf8_encode(quoted_printable_decode($message->toString())));
$I->assertFalse($user->isConfirmed);
}
@ -118,12 +118,12 @@ class GdprCest
$I->amOnRoute('/user/registration/register');
$this->register($I, 'tester@example.com', 'tester');
$I->see('Your account has been created');
$user = $I->grabRecord(User::className(), ['email' => 'tester@example.com']);
$user = $I->grabRecord(User::class, ['email' => 'tester@example.com']);
$I->assertEquals('tester', $user->username);
/** @var yii\swiftmailer\Message $message */
/** @var \yii\mail\MessageInterface $message */
$message = $I->grabLastSentEmail();
$I->assertArrayHasKey($user->email, $message->getTo());
$I->assertStringContainsString('We have generated a password for you', utf8_encode(quoted_printable_decode($message->getSwiftMessage()->toString())));
$I->assertStringContainsString('We have generated a password for you', utf8_encode(quoted_printable_decode($message->toString())));
}

View File

@ -36,14 +36,14 @@ $I->fillField('#recoveryform-email', $user->email);
$I->click('Continue');
$I->see('An email with instructions to create a new password has been sent to ' . $user->email);
$user = $I->grabRecord(User::className(), ['email' => $user->email]);
$token = $I->grabRecord(Token::className(), ['user_id' => $user->id, 'type' => Token::TYPE_RECOVERY]);
/** @var yii\swiftmailer\Message $message */
$user = $I->grabRecord(User::class, ['email' => $user->email]);
$token = $I->grabRecord(Token::class, ['user_id' => $user->id, 'type' => Token::TYPE_RECOVERY]);
/** @var \yii\mail\MessageInterface $message */
$message = $I->grabLastSentEmail();
$I->assertArrayHasKey($user->email, $message->getTo());
$I->assertStringContainsString(
Html::encode($token->getUrl()),
utf8_encode(quoted_printable_decode($message->getSwiftMessage()->toString()))
utf8_encode(quoted_printable_decode($message->toString()))
);
$I->amGoingTo('reset password with invalid token');

View File

@ -68,12 +68,12 @@ class RegistrationCest
$I->amOnRoute('/user/registration/register');
$this->register($I, 'tester@example.com', 'tester', 'tester');
$I->see('Your account has been created and a message with further instructions has been sent to your email');
$user = $I->grabRecord(User::className(), ['email' => 'tester@example.com']);
$token = $I->grabRecord(Token::className(), ['user_id' => $user->id, 'type' => Token::TYPE_CONFIRMATION]);
/** @var yii\swiftmailer\Message $message */
$user = $I->grabRecord(User::class, ['email' => 'tester@example.com']);
$token = $I->grabRecord(Token::class, ['user_id' => $user->id, 'type' => Token::TYPE_CONFIRMATION]);
/** @var \yii\mail\MessageInterface $message */
$message = $I->grabLastSentEmail();
$I->assertArrayHasKey($user->email, $message->getTo());
$I->assertStringContainsString(Html::encode($token->getUrl()), utf8_encode(quoted_printable_decode($message->getSwiftMessage()->toString())));
$I->assertStringContainsString(Html::encode($token->getUrl()), utf8_encode(quoted_printable_decode($message->toString())));
$I->assertFalse($user->isConfirmed);
}
@ -91,10 +91,10 @@ class RegistrationCest
$I->see('Your account has been created');
$user = $I->grabRecord(User::className(), ['email' => 'tester@example.com']);
$I->assertEquals('tester', $user->username);
/** @var yii\swiftmailer\Message $message */
/** @var \yii\mail\MessageInterface $message */
$message = $I->grabLastSentEmail();
$I->assertArrayHasKey($user->email, $message->getTo());
$I->assertStringContainsString('We have generated a password for you', utf8_encode(quoted_printable_decode($message->getSwiftMessage()->toString())));
$I->assertStringContainsString('We have generated a password for you', utf8_encode(quoted_printable_decode($message->toString())));
}
protected function register(FunctionalTester $I, $email, $username = null, $password = null) {

View File

@ -34,12 +34,12 @@ $I->click('Save');
$I->seeRecord(User::className(), ['email' => $user->email, 'unconfirmed_email' => 'new_user@example.com']);
$I->see('A confirmation message has been sent to your new email address');
$user = $I->grabRecord(User::className(), ['id' => $user->id]);
$token = $I->grabRecord(Token::className(), ['user_id' => $user->id, 'type' => Token::TYPE_CONFIRM_NEW_EMAIL]);
/** @var yii\swiftmailer\Message $message */
$user = $I->grabRecord(User::class, ['id' => $user->id]);
$token = $I->grabRecord(Token::class, ['user_id' => $user->id, 'type' => Token::TYPE_CONFIRM_NEW_EMAIL]);
/** @var \yii\mail\MessageInterface $message */
$message = $I->grabLastSentEmail();
$I->assertArrayHasKey($user->unconfirmed_email, $message->getTo());
$I->assertStringContainsString(Html::encode($token->getUrl()), utf8_encode(quoted_printable_decode($message->getSwiftMessage()->toString())));
$I->assertStringContainsString(Html::encode($token->getUrl()), utf8_encode(quoted_printable_decode($message->toString())));
Yii::$app->user->logout();
$I->amGoingTo('log in using new email address before clicking the confirmation link');