Merge branch 'rules_agreement'

This commit is contained in:
ErickSkrauch 2016-08-06 20:48:05 +03:00
commit c752a5afee
14 changed files with 223 additions and 4 deletions

View File

@ -1,6 +1,7 @@
<?php <?php
namespace api\controllers; namespace api\controllers;
use api\models\profile\AcceptRulesForm;
use api\models\profile\ChangeEmail\ConfirmNewEmailForm; use api\models\profile\ChangeEmail\ConfirmNewEmailForm;
use api\models\profile\ChangeEmail\InitStateForm; use api\models\profile\ChangeEmail\InitStateForm;
use api\models\profile\ChangeEmail\NewEmailForm; use api\models\profile\ChangeEmail\NewEmailForm;
@ -21,7 +22,7 @@ class AccountsController extends Controller {
'class' => AccessControl::class, 'class' => AccessControl::class,
'rules' => [ 'rules' => [
[ [
'actions' => ['current'], 'actions' => ['current', 'accept-rules'],
'allow' => true, 'allow' => true,
'roles' => ['@'], 'roles' => ['@'],
], ],
@ -37,9 +38,10 @@ class AccountsController extends Controller {
'allow' => true, 'allow' => true,
'roles' => ['@'], 'roles' => ['@'],
'matchCallback' => function() { 'matchCallback' => function() {
/** @var Account $account */
$account = Yii::$app->user->identity; $account = Yii::$app->user->identity;
return $account->status > Account::STATUS_REGISTERED;
return $account->status > Account::STATUS_REGISTERED
&& $account->isAgreedWithActualRules();
}, },
], ],
], ],
@ -56,6 +58,7 @@ class AccountsController extends Controller {
'change-email-submit-new-email' => ['POST'], 'change-email-submit-new-email' => ['POST'],
'change-email-confirm-new-email' => ['POST'], 'change-email-confirm-new-email' => ['POST'],
'change-lang' => ['POST'], 'change-lang' => ['POST'],
'accept-rules' => ['POST'],
]; ];
} }
@ -72,6 +75,7 @@ class AccountsController extends Controller {
'isActive' => $account->status === Account::STATUS_ACTIVE, 'isActive' => $account->status === Account::STATUS_ACTIVE,
'passwordChangedAt' => $account->password_changed_at, 'passwordChangedAt' => $account->password_changed_at,
'hasMojangUsernameCollision' => $account->hasMojangUsernameCollision(), 'hasMojangUsernameCollision' => $account->hasMojangUsernameCollision(),
'shouldAcceptRules' => !$account->isAgreedWithActualRules(),
]; ];
} }
@ -183,4 +187,20 @@ class AccountsController extends Controller {
]; ];
} }
public function actionAcceptRules() {
$account = Yii::$app->user->identity;
$model = new AcceptRulesForm($account);
$model->load(Yii::$app->request->post());
if (!$model->agreeWithLatestRules()) {
return [
'success' => false,
'errors' => $this->normalizeModelErrors($model->getErrors()),
];
}
return [
'success' => true,
];
}
} }

View File

@ -16,6 +16,7 @@ use Ramsey\Uuid\Uuid;
use Yii; use Yii;
use yii\base\ErrorException; use yii\base\ErrorException;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
use const common\LATEST_RULES_VERSION;
class RegistrationForm extends ApiForm { class RegistrationForm extends ApiForm {
@ -92,6 +93,7 @@ class RegistrationForm extends ApiForm {
$account->password = $this->password; $account->password = $this->password;
$account->lang = $this->lang; $account->lang = $this->lang;
$account->status = Account::STATUS_REGISTERED; $account->status = Account::STATUS_REGISTERED;
$account->rules_agreement_version = LATEST_RULES_VERSION;
if (!$account->save()) { if (!$account->save()) {
throw new ErrorException('Account not created.'); throw new ErrorException('Account not created.');
} }

View File

@ -0,0 +1,35 @@
<?php
namespace api\models\profile;
use api\models\base\ApiForm;
use common\models\Account;
use yii\base\ErrorException;
use const \common\LATEST_RULES_VERSION;
class AcceptRulesForm extends ApiForm {
/**
* @var Account
*/
private $account;
public function __construct(Account $account, array $config = []) {
$this->account = $account;
parent::__construct($config);
}
public function agreeWithLatestRules() : bool {
$account = $this->getAccount();
$account->rules_agreement_version = LATEST_RULES_VERSION;
if (!$account->save()) {
throw new ErrorException('Cannot set user rules version');
}
return true;
}
public function getAccount() : Account {
return $this->account;
}
}

5
common/consts.php Normal file
View File

@ -0,0 +1,5 @@
<?php
namespace common;
const LATEST_RULES_VERSION = 1;
const RULES_APPLY_FROM = '2016-08-06T18:00:00+03:00';

View File

@ -9,6 +9,7 @@ use Yii;
use yii\base\InvalidConfigException; use yii\base\InvalidConfigException;
use yii\behaviors\TimestampBehavior; use yii\behaviors\TimestampBehavior;
use yii\db\ActiveRecord; use yii\db\ActiveRecord;
use const common\LATEST_RULES_VERSION;
/** /**
* Поля модели: * Поля модели:
@ -20,6 +21,7 @@ use yii\db\ActiveRecord;
* @property integer $password_hash_strategy * @property integer $password_hash_strategy
* @property string $lang * @property string $lang
* @property integer $status * @property integer $status
* @property integer $rules_agreement_version
* @property integer $created_at * @property integer $created_at
* @property integer $updated_at * @property integer $updated_at
* @property integer $password_changed_at * @property integer $password_changed_at
@ -164,6 +166,7 @@ class Account extends ActiveRecord {
/** /**
* Выполняет проверку, принадлежит ли этому нику аккаунт у Mojang * Выполняет проверку, принадлежит ли этому нику аккаунт у Mojang
*
* @return bool * @return bool
*/ */
public function hasMojangUsernameCollision() : bool { public function hasMojangUsernameCollision() : bool {
@ -172,8 +175,27 @@ class Account extends ActiveRecord {
->exists(); ->exists();
} }
/**
* Т.к. у нас нет инфы по static_url пользователя, то пока генерируем самый простой вариант
* с ссылкой на профиль по id. На Ely он всё равно редиректнется на static, а мы так или
* иначе обеспечим отдачу этой инфы.
*
* @return string
*/
public function getProfileLink() : string { public function getProfileLink() : string {
return 'http://ely.by/u' . $this->id; return 'http://ely.by/u' . $this->id;
} }
/**
* При создании структуры БД все аккаунты получают null значение в это поле, однако оно
* обязательно для заполнения. Все мигрировавшие с Ely аккаунты будут иметь null значение,
* а актуальной версией будет 1 версия правил сайта (т.к. раньше их просто не было). Ну а
* дальше уже будем инкрементить.
*
* @return bool
*/
public function isAgreedWithActualRules() : bool {
return $this->rules_agreement_version === LATEST_RULES_VERSION;
}
} }

View File

@ -51,5 +51,10 @@
], ],
"scripts": { "scripts": {
"phploc" : "phploc ./api ./common ./console" "phploc" : "phploc ./api ./common ./console"
},
"autoload": {
"files": [
"common/consts.php"
]
} }
} }

View File

@ -0,0 +1,15 @@
<?php
use console\db\Migration;
class m160806_144759_account_rules_agreement_version extends Migration {
public function safeUp() {
$this->addColumn('{{%accounts}}', 'rules_agreement_version', $this->smallInteger()->unsigned()->after('status'));
}
public function safeDown() {
$this->dropColumn('{{%accounts}}', 'rules_agreement_version');
}
}

View File

@ -59,4 +59,9 @@ class AccountsRoute extends BasePage {
]); ]);
} }
public function acceptRules() {
$this->route = ['accounts/accept-rules'];
$this->actor->sendPOST($this->getUrl());
}
} }

View File

@ -0,0 +1,28 @@
<?php
namespace tests\codeception\api\functional;
use tests\codeception\api\_pages\AccountsRoute;
use tests\codeception\api\FunctionalTester;
class AccountsAcceptRulesCest {
/**
* @var AccountsRoute
*/
private $route;
public function _before(FunctionalTester $I) {
$this->route = new AccountsRoute($I);
}
public function testCurrent(FunctionalTester $I) {
$I->loggedInAsActiveAccount('Veleyaba', 'password_0');
$this->route->acceptRules();
$I->canSeeResponseCodeIs(200);
$I->canSeeResponseIsJson();
$I->canSeeResponseContainsJson([
'success' => true,
]);
}
}

View File

@ -1,7 +1,6 @@
<?php <?php
namespace tests\codeception\api\functional; namespace tests\codeception\api\functional;
use Codeception\Specify;
use tests\codeception\api\_pages\AccountsRoute; use tests\codeception\api\_pages\AccountsRoute;
use tests\codeception\api\FunctionalTester; use tests\codeception\api\FunctionalTester;
@ -30,6 +29,7 @@ class AccountsCurrentCest {
'shouldChangePassword' => false, 'shouldChangePassword' => false,
'isActive' => true, 'isActive' => true,
'hasMojangUsernameCollision' => false, 'hasMojangUsernameCollision' => false,
'shouldAcceptRules' => false,
]); ]);
$I->canSeeResponseJsonMatchesJsonPath('$.passwordChangedAt'); $I->canSeeResponseJsonMatchesJsonPath('$.passwordChangedAt');
} }

View File

@ -8,6 +8,7 @@ use common\models\EmailActivation;
use tests\codeception\api\unit\DbTestCase; use tests\codeception\api\unit\DbTestCase;
use tests\codeception\common\fixtures\AccountFixture; use tests\codeception\common\fixtures\AccountFixture;
use Yii; use Yii;
use const common\LATEST_RULES_VERSION;
/** /**
* @property array $accounts * @property array $accounts
@ -99,6 +100,7 @@ class RegistrationFormTest extends DbTestCase {
expect('user should be valid', $account)->isInstanceOf(Account::class); expect('user should be valid', $account)->isInstanceOf(Account::class);
expect('password should be correct', $account->validatePassword('some_password'))->true(); expect('password should be correct', $account->validatePassword('some_password'))->true();
expect('uuid is set', $account->uuid)->notEmpty(); expect('uuid is set', $account->uuid)->notEmpty();
expect('actual rules version is set', $account->rules_agreement_version)->equals(LATEST_RULES_VERSION);
expect('user model exists in database', Account::find()->andWhere([ expect('user model exists in database', Account::find()->andWhere([
'username' => 'some_username', 'username' => 'some_username',
'email' => 'some_email@example.com', 'email' => 'some_email@example.com',

View File

@ -0,0 +1,33 @@
<?php
namespace codeception\api\unit\models\profile;
use api\models\profile\AcceptRulesForm;
use Codeception\Specify;
use common\models\Account;
use tests\codeception\api\unit\DbTestCase;
use tests\codeception\common\fixtures\AccountFixture;
use const common\LATEST_RULES_VERSION;
/**
* @property AccountFixture $accounts
*/
class AcceptRulesFormTest extends DbTestCase {
use Specify;
public function fixtures() {
return [
'accounts' => AccountFixture::class,
];
}
public function testApplyLanguage() {
$this->specify('rules version bumped to latest', function() {
/** @var Account $account */
$account = Account::findOne($this->accounts['account-with-old-rules-version']);
$model = new AcceptRulesForm($account);
expect($model->agreeWithLatestRules())->true();
expect($account->rules_agreement_version)->equals(LATEST_RULES_VERSION);
});
}
}

View File

@ -9,6 +9,7 @@ return [
'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2, 'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2,
'lang' => 'en', 'lang' => 'en',
'status' => \common\models\Account::STATUS_ACTIVE, 'status' => \common\models\Account::STATUS_ACTIVE,
'rules_agreement_version' => \common\LATEST_RULES_VERSION,
'created_at' => 1451775316, 'created_at' => 1451775316,
'updated_at' => 1451775316, 'updated_at' => 1451775316,
'password_changed_at' => 1451775316, 'password_changed_at' => 1451775316,
@ -22,6 +23,7 @@ return [
'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_OLD_ELY, 'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_OLD_ELY,
'lang' => 'en', 'lang' => 'en',
'status' => \common\models\Account::STATUS_ACTIVE, 'status' => \common\models\Account::STATUS_ACTIVE,
'rules_agreement_version' => \common\LATEST_RULES_VERSION,
'created_at' => 1385225069, 'created_at' => 1385225069,
'updated_at' => 1385225069, 'updated_at' => 1385225069,
'password_changed_at' => 1385225069, 'password_changed_at' => 1385225069,
@ -35,6 +37,7 @@ return [
'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2, 'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2,
'lang' => 'en', 'lang' => 'en',
'status' => \common\models\Account::STATUS_REGISTERED, 'status' => \common\models\Account::STATUS_REGISTERED,
'rules_agreement_version' => \common\LATEST_RULES_VERSION,
'created_at' => 1453146616, 'created_at' => 1453146616,
'updated_at' => 1453146616, 'updated_at' => 1453146616,
'password_changed_at' => 1453146616, 'password_changed_at' => 1453146616,
@ -48,6 +51,7 @@ return [
'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2, 'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2,
'lang' => 'en', 'lang' => 'en',
'status' => \common\models\Account::STATUS_REGISTERED, 'status' => \common\models\Account::STATUS_REGISTERED,
'rules_agreement_version' => \common\LATEST_RULES_VERSION,
'created_at' => 1457890086, 'created_at' => 1457890086,
'updated_at' => 1457890086, 'updated_at' => 1457890086,
], ],
@ -60,6 +64,7 @@ return [
'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2, 'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2,
'lang' => 'en', 'lang' => 'en',
'status' => \common\models\Account::STATUS_ACTIVE, 'status' => \common\models\Account::STATUS_ACTIVE,
'rules_agreement_version' => \common\LATEST_RULES_VERSION,
'created_at' => 1462891432, 'created_at' => 1462891432,
'updated_at' => 1462891432, 'updated_at' => 1462891432,
], ],
@ -72,6 +77,7 @@ return [
'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2, 'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2,
'lang' => 'en', 'lang' => 'en',
'status' => \common\models\Account::STATUS_ACTIVE, 'status' => \common\models\Account::STATUS_ACTIVE,
'rules_agreement_version' => \common\LATEST_RULES_VERSION,
'created_at' => 1462891612, 'created_at' => 1462891612,
'updated_at' => 1462891612, 'updated_at' => 1462891612,
], ],
@ -84,6 +90,7 @@ return [
'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2, 'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2,
'lang' => 'en', 'lang' => 'en',
'status' => \common\models\Account::STATUS_ACTIVE, 'status' => \common\models\Account::STATUS_ACTIVE,
'rules_agreement_version' => \common\LATEST_RULES_VERSION,
'created_at' => 1463427287, 'created_at' => 1463427287,
'updated_at' => 1463427287, 'updated_at' => 1463427287,
], ],
@ -96,7 +103,21 @@ return [
'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2, 'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2,
'lang' => 'en', 'lang' => 'en',
'status' => \common\models\Account::STATUS_ACTIVE, 'status' => \common\models\Account::STATUS_ACTIVE,
'rules_agreement_version' => \common\LATEST_RULES_VERSION,
'created_at' => 1463349615, 'created_at' => 1463349615,
'updated_at' => 1463349615, 'updated_at' => 1463349615,
], ],
'account-with-old-rules-version' => [
'id' => 9,
'uuid' => '410462d3-8e71-47cc-bac6-64f77f88cf80',
'username' => 'Veleyaba',
'email' => 'veleyaba@gmail.com',
'password_hash' => '$2y$13$2rYkap5T6jG8z/mMK8a3Ou6aZxJcmAaTha6FEuujvHEmybSHRzW5e', # password_0
'password_hash_strategy' => \common\models\Account::PASS_HASH_STRATEGY_YII2,
'lang' => 'en',
'status' => \common\models\Account::STATUS_ACTIVE,
'rules_agreement_version' => null,
'created_at' => 1470499952,
'updated_at' => 1470499952,
],
]; ];

View File

@ -8,6 +8,7 @@ use tests\codeception\common\fixtures\AccountFixture;
use tests\codeception\common\fixtures\MojangUsernameFixture; use tests\codeception\common\fixtures\MojangUsernameFixture;
use tests\codeception\common\unit\DbTestCase; use tests\codeception\common\unit\DbTestCase;
use Yii; use Yii;
use const common\LATEST_RULES_VERSION;
/** /**
* @property array $accounts * @property array $accounts
@ -176,4 +177,29 @@ class AccountTest extends DbTestCase {
}); });
} }
public function testGetProfileLink() {
$model = new Account();
$model->id = '123';
$this->assertEquals('http://ely.by/u123', $model->getProfileLink());
}
public function testIsAgreedWithActualRules() {
$this->specify('get false, if rules field set in null', function() {
$model = new Account();
expect($model->isAgreedWithActualRules())->false();
});
$this->specify('get false, if rules field have version less, then actual', function() {
$model = new Account();
$model->rules_agreement_version = 0;
expect($model->isAgreedWithActualRules())->false();
});
$this->specify('get true, if rules field have equals rules version', function() {
$model = new Account();
$model->rules_agreement_version = LATEST_RULES_VERSION;
expect($model->isAgreedWithActualRules())->true();
});
}
} }