├── console ├── controllers │ └── .gitkeep ├── models │ └── .gitkeep ├── config │ ├── bootstrap.php │ ├── .gitignore │ ├── params.php │ └── main.php ├── runtime │ └── .gitignore └── migrations │ ├── m130524_201442_init.php │ ├── m170916_095928_adding_first_user.php │ ├── m170916_101824_adding_sample_data_to_employee_table.php │ ├── m170916_100717_adding_employee_table.php │ └── m170916_150649_adding_oauth2_tables.php ├── backend ├── models │ ├── .gitkeep │ ├── SignupForm.php │ └── Employee.php ├── tests │ ├── _data │ │ ├── .gitignore │ │ └── login_data.php │ ├── _support │ │ ├── .gitignore │ │ ├── UnitTester.php │ │ └── FunctionalTester.php │ ├── _output │ │ └── .gitignore │ ├── unit.suite.yml │ ├── functional.suite.yml │ ├── functional │ │ ├── _bootstrap.php │ │ └── LoginCest.php │ ├── unit │ │ └── _bootstrap.php │ └── _bootstrap.php ├── config │ ├── bootstrap.php │ ├── .gitignore │ ├── params.php │ ├── test.php │ └── main.php ├── runtime │ └── .gitignore ├── web │ ├── assets │ │ └── .gitignore │ ├── .gitignore │ ├── favicon.ico │ ├── .htaccess │ └── css │ │ └── site.css ├── codeception.yml ├── assets │ └── AppAsset.php ├── views │ ├── site │ │ ├── error.php │ │ ├── login.php │ │ └── index.php │ └── layouts │ │ └── main.php ├── controllers │ ├── RestController.php │ ├── EmployeeController.php │ └── SiteController.php └── behaviours │ ├── Verbcheck.php │ └── Apiauth.php ├── frontend ├── config │ ├── bootstrap.php │ ├── .gitignore │ ├── params.php │ ├── test.php │ └── main.php ├── runtime │ └── .gitignore ├── tests │ ├── _support │ │ ├── .gitignore │ │ ├── UnitTester.php │ │ └── FunctionalTester.php │ ├── _output │ │ └── .gitignore │ ├── functional.suite.yml │ ├── unit.suite.yml │ ├── acceptance.suite.yml.example │ ├── functional │ │ ├── AboutCest.php │ │ ├── _bootstrap.php │ │ ├── HomeCest.php │ │ ├── LoginCest.php │ │ ├── SignupCest.php │ │ └── ContactCest.php │ ├── acceptance │ │ ├── _bootstrap.php │ │ └── HomeCest.php │ ├── unit │ │ ├── _bootstrap.php │ │ └── models │ │ │ ├── ContactFormTest.php │ │ │ ├── ResetPasswordFormTest.php │ │ │ ├── SignupFormTest.php │ │ │ └── PasswordResetRequestFormTest.php │ ├── _bootstrap.php │ └── _data │ │ ├── login_data.php │ │ └── user.php ├── web │ ├── assets │ │ └── .gitignore │ ├── .gitignore │ ├── favicon.ico │ ├── .htaccess │ └── css │ │ └── site.css ├── codeception.yml ├── views │ ├── site │ │ ├── about.php │ │ ├── error.php │ │ ├── resetPassword.php │ │ ├── requestPasswordResetToken.php │ │ ├── signup.php │ │ ├── login.php │ │ ├── contact.php │ │ └── index.php │ ├── user │ │ ├── create.php │ │ ├── update.php │ │ ├── _form.php │ │ ├── _search.php │ │ ├── index.php │ │ └── view.php │ ├── employee │ │ ├── create.php │ │ ├── update.php │ │ ├── _form.php │ │ ├── _search.php │ │ ├── index.php │ │ └── view.php │ └── layouts │ │ └── main.php ├── assets │ └── AppAsset.php ├── models │ ├── Employee.php │ ├── ContactForm.php │ ├── SignupForm.php │ ├── ResetPasswordForm.php │ ├── User.php │ ├── EmployeeSearch.php │ ├── PasswordResetRequestForm.php │ └── UserSearch.php └── controllers │ ├── UserController.php │ ├── EmployeeController.php │ └── SiteController.php ├── common ├── tests │ ├── _support │ │ ├── .gitignore │ │ └── UnitTester.php │ ├── _output │ │ └── .gitignore │ ├── unit.suite.yml │ ├── _bootstrap.php │ ├── _data │ │ └── user.php │ └── unit │ │ └── models │ │ └── LoginFormTest.php ├── config │ ├── .gitignore │ ├── params.php │ ├── bootstrap.php │ ├── test.php │ └── main.php ├── fixtures │ └── UserFixture.php ├── mail │ ├── passwordResetToken-text.php │ ├── layouts │ │ ├── text.php │ │ └── html.php │ └── passwordResetToken-html.php ├── codeception.yml ├── models │ ├── AccessTokens.php │ ├── AuthorizationCodes.php │ ├── LoginForm.php │ └── User.php ├── widgets │ └── Alert.php └── components │ └── Api.php ├── .bowerrc ├── environments ├── dev │ ├── backend │ │ ├── web │ │ │ ├── robots.txt │ │ │ ├── index.php │ │ │ └── index-test.php │ │ └── config │ │ │ ├── params-local.php │ │ │ ├── test-local.php │ │ │ └── main-local.php │ ├── frontend │ │ ├── web │ │ │ ├── robots.txt │ │ │ ├── index.php │ │ │ └── index-test.php │ │ └── config │ │ │ ├── params-local.php │ │ │ ├── test-local.php │ │ │ └── main-local.php │ ├── common │ │ └── config │ │ │ ├── params-local.php │ │ │ ├── test-local.php │ │ │ └── main-local.php │ ├── console │ │ └── config │ │ │ ├── params-local.php │ │ │ └── main-local.php │ ├── yii_test.bat │ ├── yii_test │ └── yii ├── prod │ ├── frontend │ │ ├── web │ │ │ ├── robots.txt │ │ │ └── index.php │ │ └── config │ │ │ ├── params-local.php │ │ │ └── main-local.php │ ├── backend │ │ ├── web │ │ │ ├── robots.txt │ │ │ └── index.php │ │ └── config │ │ │ ├── params-local.php │ │ │ └── main-local.php │ ├── common │ │ └── config │ │ │ ├── params-local.php │ │ │ └── main-local.php │ ├── console │ │ └── config │ │ │ ├── main-local.php │ │ │ └── params-local.php │ └── yii └── index.php ├── vagrant ├── config │ ├── .gitignore │ └── vagrant-local.example.yml ├── nginx │ ├── log │ │ └── .gitignore │ └── app.conf └── provision │ ├── always-as-root.sh │ ├── once-as-vagrant.sh │ └── once-as-root.sh ├── codeception.yml ├── init.bat ├── yii.bat ├── .gitignore ├── composer.json ├── LICENSE.md ├── Vagrantfile ├── README.md └── requirements.php /console/controllers/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/models/.gitkeep: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /backend/tests/_data/.gitignore: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /console/models/.gitkeep: -------------------------------------------------------------------------------- 1 | * 2 | -------------------------------------------------------------------------------- /backend/config/bootstrap.php: -------------------------------------------------------------------------------- 1 | 'admin@example.com', 4 | ]; 5 | -------------------------------------------------------------------------------- /console/config/params.php: -------------------------------------------------------------------------------- 1 | 'admin@example.com', 4 | ]; 5 | -------------------------------------------------------------------------------- /backend/tests/functional.suite.yml: -------------------------------------------------------------------------------- 1 | class_name: FunctionalTester 2 | modules: 3 | enabled: 4 | - Yii2 5 | -------------------------------------------------------------------------------- /backend/web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirinibin/Yii2-RESTful-API-with-OAuth2/HEAD/backend/web/favicon.ico -------------------------------------------------------------------------------- /frontend/web/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sirinibin/Yii2-RESTful-API-with-OAuth2/HEAD/frontend/web/favicon.ico -------------------------------------------------------------------------------- /vagrant/nginx/log/.gitignore: -------------------------------------------------------------------------------- 1 | # nginx logs 2 | backend-access.log 3 | backend-error.log 4 | frontend-access.log 5 | frontend-error.log -------------------------------------------------------------------------------- /frontend/tests/functional.suite.yml: -------------------------------------------------------------------------------- 1 | class_name: FunctionalTester 2 | modules: 3 | enabled: 4 | - Filesystem 5 | - Yii2 6 | -------------------------------------------------------------------------------- /common/tests/unit.suite.yml: -------------------------------------------------------------------------------- 1 | class_name: UnitTester 2 | bootstrap: false 3 | modules: 4 | enabled: 5 | - Yii2: 6 | part: fixtures 7 | -------------------------------------------------------------------------------- /frontend/tests/unit.suite.yml: -------------------------------------------------------------------------------- 1 | class_name: UnitTester 2 | modules: 3 | enabled: 4 | - Yii2: 5 | part: [orm, email, fixtures] 6 | - Asserts 7 | -------------------------------------------------------------------------------- /environments/dev/console/config/main-local.php: -------------------------------------------------------------------------------- 1 | ['gii'], 4 | 'modules' => [ 5 | 'gii' => 'yii\gii\Module', 6 | ], 7 | ]; 8 | -------------------------------------------------------------------------------- /common/config/params.php: -------------------------------------------------------------------------------- 1 | 'admin@example.com', 4 | 'supportEmail' => 'support@example.com', 5 | 'user.passwordResetTokenExpire' => 3600, 6 | ]; 7 | -------------------------------------------------------------------------------- /codeception.yml: -------------------------------------------------------------------------------- 1 | # global codeception file to run tests from all apps 2 | include: 3 | - common 4 | - frontend 5 | - backend 6 | paths: 7 | log: console/runtime/logs 8 | settings: 9 | colors: true -------------------------------------------------------------------------------- /common/fixtures/UserFixture.php: -------------------------------------------------------------------------------- 1 | 'admin@example.com', 4 | 'API_VERSION'=>'1', 5 | 'dev_api_url'=>'yii2-rest.com', 6 | 'prod_api_url'=>'api.yii2.nintriva.net' 7 | ]; 8 | -------------------------------------------------------------------------------- /backend/web/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine on 2 | # If a directory or a file exists, use it directly 3 | RewriteCond %{REQUEST_FILENAME} !-f 4 | RewriteCond %{REQUEST_FILENAME} !-d 5 | # Otherwise forward it to index.php 6 | RewriteRule . index.php -------------------------------------------------------------------------------- /frontend/web/.htaccess: -------------------------------------------------------------------------------- 1 | RewriteEngine on 2 | # If a directory or a file exists, use it directly 3 | RewriteCond %{REQUEST_FILENAME} !-f 4 | RewriteCond %{REQUEST_FILENAME} !-d 5 | # Otherwise forward it to index.php 6 | RewriteRule . index.php -------------------------------------------------------------------------------- /frontend/tests/acceptance.suite.yml.example: -------------------------------------------------------------------------------- 1 | class_name: AcceptanceTester 2 | modules: 3 | enabled: 4 | - WebDriver: 5 | url: http://localhost:8080 6 | browser: firefox 7 | - Yii2: 8 | part: init 9 | -------------------------------------------------------------------------------- /common/config/bootstrap.php: -------------------------------------------------------------------------------- 1 | 'app-common-tests', 4 | 'basePath' => dirname(__DIR__), 5 | 'components' => [ 6 | 'user' => [ 7 | 'class' => 'yii\web\User', 8 | 'identityClass' => 'common\models\User', 9 | ], 10 | ], 11 | ]; 12 | -------------------------------------------------------------------------------- /environments/dev/backend/config/test-local.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'request' => [ 5 | // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation 6 | 'cookieValidationKey' => '', 7 | ], 8 | ], 9 | ]; 10 | -------------------------------------------------------------------------------- /backend/config/test.php: -------------------------------------------------------------------------------- 1 | 'app-backend-tests', 4 | 'components' => [ 5 | 'assetManager' => [ 6 | 'basePath' => __DIR__ . '/../web/assets', 7 | ], 8 | 'urlManager' => [ 9 | 'showScriptName' => true, 10 | ], 11 | ], 12 | ]; 13 | -------------------------------------------------------------------------------- /environments/prod/frontend/config/main-local.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'request' => [ 5 | // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation 6 | 'cookieValidationKey' => '', 7 | ], 8 | ], 9 | ]; 10 | -------------------------------------------------------------------------------- /frontend/config/test.php: -------------------------------------------------------------------------------- 1 | 'app-frontend-tests', 4 | 'components' => [ 5 | 'assetManager' => [ 6 | 'basePath' => __DIR__ . '/../web/assets', 7 | ], 8 | 'urlManager' => [ 9 | 'showScriptName' => true, 10 | ], 11 | ], 12 | ]; 13 | -------------------------------------------------------------------------------- /frontend/tests/functional/AboutCest.php: -------------------------------------------------------------------------------- 1 | amOnRoute('site/about'); 11 | $I->see('About', 'h1'); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /vagrant/provision/always-as-root.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #== Bash helpers == 4 | 5 | function info { 6 | echo " " 7 | echo "--> $1" 8 | echo " " 9 | } 10 | 11 | #== Provision script == 12 | 13 | info "Provision-script user: `whoami`" 14 | 15 | info "Restart web-stack" 16 | service php7.0-fpm restart 17 | service nginx restart 18 | service mysql restart -------------------------------------------------------------------------------- /common/mail/passwordResetToken-text.php: -------------------------------------------------------------------------------- 1 | urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]); 7 | ?> 8 | Hello username ?>, 9 | 10 | Follow the link below to reset your password: 11 | 12 | 13 | -------------------------------------------------------------------------------- /common/codeception.yml: -------------------------------------------------------------------------------- 1 | namespace: common\tests 2 | actor: Tester 3 | paths: 4 | tests: tests 5 | log: tests/_output 6 | data: tests/_data 7 | helpers: tests/_support 8 | settings: 9 | bootstrap: _bootstrap.php 10 | colors: true 11 | memory_limit: 1024M 12 | modules: 13 | config: 14 | Yii2: 15 | configFile: 'config/test-local.php' 16 | -------------------------------------------------------------------------------- /backend/codeception.yml: -------------------------------------------------------------------------------- 1 | namespace: backend\tests 2 | actor: Tester 3 | paths: 4 | tests: tests 5 | log: tests/_output 6 | data: tests/_data 7 | helpers: tests/_support 8 | settings: 9 | bootstrap: _bootstrap.php 10 | colors: true 11 | memory_limit: 1024M 12 | modules: 13 | config: 14 | Yii2: 15 | configFile: 'config/test-local.php' 16 | -------------------------------------------------------------------------------- /frontend/codeception.yml: -------------------------------------------------------------------------------- 1 | namespace: frontend\tests 2 | actor: Tester 3 | paths: 4 | tests: tests 5 | log: tests/_output 6 | data: tests/_data 7 | helpers: tests/_support 8 | settings: 9 | bootstrap: _bootstrap.php 10 | colors: true 11 | memory_limit: 1024M 12 | modules: 13 | config: 14 | Yii2: 15 | configFile: 'config/test-local.php' 16 | -------------------------------------------------------------------------------- /common/tests/_bootstrap.php: -------------------------------------------------------------------------------- 1 | [ 8 | 'db' => [ 9 | 'dsn' => 'mysql:host=localhost;dbname=yii2advanced_test', 10 | ] 11 | ], 12 | ] 13 | ); 14 | -------------------------------------------------------------------------------- /common/mail/layouts/text.php: -------------------------------------------------------------------------------- 1 | 9 | 10 | beginPage() ?> 11 | beginBody() ?> 12 | 13 | endBody() ?> 14 | endPage() ?> 15 | -------------------------------------------------------------------------------- /backend/tests/functional/_bootstrap.php: -------------------------------------------------------------------------------- 1 | 'davert']); 9 | * ``` 10 | * 11 | * In Cests 12 | * 13 | * ```php 14 | * \Codeception\Util\Fixtures::get('user1'); 15 | * ``` 16 | */ -------------------------------------------------------------------------------- /backend/tests/unit/_bootstrap.php: -------------------------------------------------------------------------------- 1 | 'davert']); 9 | * ``` 10 | * 11 | * In Tests 12 | * 13 | * ```php 14 | * \Codeception\Util\Fixtures::get('user1'); 15 | * ``` 16 | */ 17 | -------------------------------------------------------------------------------- /frontend/tests/acceptance/_bootstrap.php: -------------------------------------------------------------------------------- 1 | 'davert']); 9 | * ``` 10 | * 11 | * In Cept 12 | * 13 | * ```php 14 | * \Codeception\Util\Fixtures::get('user1'); 15 | * ``` 16 | */ -------------------------------------------------------------------------------- /frontend/tests/functional/_bootstrap.php: -------------------------------------------------------------------------------- 1 | 'davert']); 9 | * ``` 10 | * 11 | * In Cests 12 | * 13 | * ```php 14 | * \Codeception\Util\Fixtures::get('user1'); 15 | * ``` 16 | */ -------------------------------------------------------------------------------- /frontend/tests/unit/_bootstrap.php: -------------------------------------------------------------------------------- 1 | 'davert']); 9 | * ``` 10 | * 11 | * In Tests 12 | * 13 | * ```php 14 | * \Codeception\Util\Fixtures::get('user1'); 15 | * ``` 16 | */ 17 | -------------------------------------------------------------------------------- /frontend/views/site/about.php: -------------------------------------------------------------------------------- 1 | title = 'About'; 8 | $this->params['breadcrumbs'][] = $this->title; 9 | ?> 10 |
11 |

title) ?>

12 | 13 |

This is the About page. You may modify the following file to customize its content:

14 | 15 | 16 |
17 | -------------------------------------------------------------------------------- /frontend/tests/functional/HomeCest.php: -------------------------------------------------------------------------------- 1 | amOnPage(\Yii::$app->homeUrl); 12 | $I->see('My Company'); 13 | $I->seeLink('About'); 14 | $I->click('About'); 15 | $I->see('This is the About page.'); 16 | } 17 | } -------------------------------------------------------------------------------- /common/config/main.php: -------------------------------------------------------------------------------- 1 | [ 4 | '@bower' => '@vendor/bower-asset', 5 | '@npm' => '@vendor/npm-asset', 6 | ], 7 | 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', 8 | 'components' => [ 9 | 'cache' => [ 10 | 'class' => 'yii\caching\FileCache', 11 | ], 12 | 'api' => [ 13 | 'class' => 'common\components\Api', 14 | ], 15 | ], 16 | ]; 17 | -------------------------------------------------------------------------------- /backend/tests/_bootstrap.php: -------------------------------------------------------------------------------- 1 | 'erau', 5 | 'auth_key' => 'tUu1qHcde0diwUol3xeI-18MuHkkprQI', 6 | // password_0 7 | 'password_hash' => '$2y$13$nJ1WDlBaGcbCdbNC5.5l4.sgy.OMEKCqtDQOdQ2OWpgiKRWYyzzne', 8 | 'password_reset_token' => 'RkD_Jw0_8HEedzLk7MM-ZKEFfYR7VbMr_1392559490', 9 | 'created_at' => '1392559490', 10 | 'updated_at' => '1392559490', 11 | 'email' => 'sfriesen@jenkins.info', 12 | ], 13 | ]; 14 | -------------------------------------------------------------------------------- /environments/prod/common/config/main-local.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'db' => [ 5 | 'class' => 'yii\db\Connection', 6 | 'dsn' => 'mysql:host=localhost;dbname=yii2advanced', 7 | 'username' => 'root', 8 | 'password' => '', 9 | 'charset' => 'utf8', 10 | ], 11 | 'mailer' => [ 12 | 'class' => 'yii\swiftmailer\Mailer', 13 | 'viewPath' => '@common/mail', 14 | ], 15 | ], 16 | ]; 17 | -------------------------------------------------------------------------------- /frontend/assets/AppAsset.php: -------------------------------------------------------------------------------- 1 | 'erau', 5 | 'auth_key' => 'tUu1qHcde0diwUol3xeI-18MuHkkprQI', 6 | // password_0 7 | 'password_hash' => '$2y$13$nJ1WDlBaGcbCdbNC5.5l4.sgy.OMEKCqtDQOdQ2OWpgiKRWYyzzne', 8 | 'password_reset_token' => 'RkD_Jw0_8HEedzLk7MM-ZKEFfYR7VbMr_1392559490', 9 | 'created_at' => '1392559490', 10 | 'updated_at' => '1392559490', 11 | 'email' => 'sfriesen@jenkins.info', 12 | ], 13 | ]; 14 | -------------------------------------------------------------------------------- /frontend/views/user/create.php: -------------------------------------------------------------------------------- 1 | title = 'Create User'; 10 | $this->params['breadcrumbs'][] = ['label' => 'Users', 'url' => ['index']]; 11 | $this->params['breadcrumbs'][] = $this->title; 12 | ?> 13 |
14 | 15 |

title) ?>

16 | 17 | render('_form', [ 18 | 'model' => $model, 19 | ]) ?> 20 | 21 |
22 | -------------------------------------------------------------------------------- /common/mail/passwordResetToken-html.php: -------------------------------------------------------------------------------- 1 | urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]); 8 | ?> 9 |
10 |

Hello username) ?>,

11 | 12 |

Follow the link below to reset your password:

13 | 14 |

15 |
16 | -------------------------------------------------------------------------------- /common/tests/_data/user.php: -------------------------------------------------------------------------------- 1 | 'bayer.hudson', 6 | 'auth_key' => 'HP187Mvq7Mmm3CTU80dLkGmni_FUH_lR', 7 | //password_0 8 | 'password_hash' => '$2y$13$EjaPFBnZOQsHdGuHI.xvhuDp1fHpo8hKRSk6yshqa9c5EG8s3C3lO', 9 | 'password_reset_token' => 'ExzkCOaYc1L8IOBs4wdTGGbgNiG3Wz1I_1402312317', 10 | 'created_at' => '1402312317', 11 | 'updated_at' => '1402312317', 12 | 'email' => 'nicole.paucek@schultz.info', 13 | ], 14 | ]; 15 | -------------------------------------------------------------------------------- /frontend/tests/acceptance/HomeCest.php: -------------------------------------------------------------------------------- 1 | amOnPage(Url::toRoute('/site/index')); 12 | $I->see('My Company'); 13 | 14 | $I->seeLink('About'); 15 | $I->click('About'); 16 | $I->wait(2); // wait for page to be opened 17 | 18 | $I->see('This is the About page.'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /frontend/views/employee/create.php: -------------------------------------------------------------------------------- 1 | title = 'Create Employee'; 10 | $this->params['breadcrumbs'][] = ['label' => 'Employees', 'url' => ['index']]; 11 | $this->params['breadcrumbs'][] = $this->title; 12 | ?> 13 |
14 | 15 |

title) ?>

16 | 17 | render('_form', [ 18 | 'model' => $model, 19 | ]) ?> 20 | 21 |
22 | -------------------------------------------------------------------------------- /init.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem ------------------------------------------------------------- 4 | rem Yii command line init script for Windows. 5 | rem 6 | rem @author Qiang Xue 7 | rem @link http://www.yiiframework.com/ 8 | rem @copyright Copyright (c) 2008 Yii Software LLC 9 | rem @license http://www.yiiframework.com/license/ 10 | rem ------------------------------------------------------------- 11 | 12 | @setlocal 13 | 14 | set YII_PATH=%~dp0 15 | 16 | if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe 17 | 18 | "%PHP_COMMAND%" "%YII_PATH%init" %* 19 | 20 | @endlocal 21 | -------------------------------------------------------------------------------- /yii.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem ------------------------------------------------------------- 4 | rem Yii command line bootstrap script for Windows. 5 | rem 6 | rem @author Qiang Xue 7 | rem @link http://www.yiiframework.com/ 8 | rem @copyright Copyright (c) 2008 Yii Software LLC 9 | rem @license http://www.yiiframework.com/license/ 10 | rem ------------------------------------------------------------- 11 | 12 | @setlocal 13 | 14 | set YII_PATH=%~dp0 15 | 16 | if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe 17 | 18 | "%PHP_COMMAND%" "%YII_PATH%yii" %* 19 | 20 | @endlocal 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # yii console commands 2 | /yii 3 | /yii_test 4 | /yii_test.bat 5 | 6 | # phpstorm project files 7 | .idea 8 | 9 | # netbeans project files 10 | nbproject 11 | 12 | # zend studio for eclipse project files 13 | .buildpath 14 | .project 15 | .settings 16 | 17 | # windows thumbnail cache 18 | Thumbs.db 19 | 20 | # composer vendor dir 21 | /vendor 22 | 23 | # composer itself is not needed 24 | composer.phar 25 | 26 | # Mac DS_Store Files 27 | .DS_Store 28 | 29 | # phpunit itself is not needed 30 | phpunit.phar 31 | # local phpunit config 32 | /phpunit.xml 33 | 34 | # vagrant runtime 35 | /.vagrant 36 | -------------------------------------------------------------------------------- /vagrant/config/vagrant-local.example.yml: -------------------------------------------------------------------------------- 1 | # Your personal GitHub token 2 | github_token: 3 | # Read more: https://github.com/blog/1509-personal-api-tokens 4 | # You can generate it here: https://github.com/settings/tokens 5 | 6 | # Guest OS timezone 7 | timezone: Europe/London 8 | 9 | # Are we need check box updates for every 'vagrant up'? 10 | box_check_update: false 11 | 12 | # Virtual machine name 13 | machine_name: y2aa 14 | 15 | # Virtual machine IP 16 | ip: 192.168.83.137 17 | 18 | # Virtual machine CPU cores number 19 | cpus: 1 20 | 21 | # Virtual machine RAM 22 | memory: 512 23 | -------------------------------------------------------------------------------- /environments/dev/yii_test.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | 3 | rem ------------------------------------------------------------- 4 | rem Yii command line bootstrap script for Windows. 5 | rem 6 | rem @author Qiang Xue 7 | rem @link http://www.yiiframework.com/ 8 | rem @copyright Copyright (c) 2008 Yii Software LLC 9 | rem @license http://www.yiiframework.com/license/ 10 | rem ------------------------------------------------------------- 11 | 12 | @setlocal 13 | 14 | set YII_PATH=%~dp0 15 | 16 | if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe 17 | 18 | "%PHP_COMMAND%" "%YII_PATH%yii_test" %* 19 | 20 | @endlocal 21 | -------------------------------------------------------------------------------- /frontend/views/user/update.php: -------------------------------------------------------------------------------- 1 | title = 'Update User: {nameAttribute}'; 9 | $this->params['breadcrumbs'][] = ['label' => 'Users', 'url' => ['index']]; 10 | $this->params['breadcrumbs'][] = ['label' => $model->id, 'url' => ['view', 'id' => $model->id]]; 11 | $this->params['breadcrumbs'][] = 'Update'; 12 | ?> 13 |
14 | 15 |

title) ?>

16 | 17 | render('_form', [ 18 | 'model' => $model, 19 | ]) ?> 20 | 21 |
22 | -------------------------------------------------------------------------------- /frontend/views/employee/update.php: -------------------------------------------------------------------------------- 1 | title = 'Update Employee: {nameAttribute}'; 9 | $this->params['breadcrumbs'][] = ['label' => 'Employees', 'url' => ['index']]; 10 | $this->params['breadcrumbs'][] = ['label' => $model->name, 'url' => ['view', 'id' => $model->id]]; 11 | $this->params['breadcrumbs'][] = 'Update'; 12 | ?> 13 |
14 | 15 |

title) ?>

16 | 17 | render('_form', [ 18 | 'model' => $model, 19 | ]) ?> 20 | 21 |
22 | -------------------------------------------------------------------------------- /backend/views/site/error.php: -------------------------------------------------------------------------------- 1 | title = $name; 11 | ?> 12 |
13 | 14 |

title) ?>

15 | 16 |
17 | 18 |
19 | 20 |

21 | The above error occurred while the Web server was processing your request. 22 |

23 |

24 | Please contact us if you think this is a server error. Thank you. 25 |

26 | 27 |
28 | -------------------------------------------------------------------------------- /frontend/views/site/error.php: -------------------------------------------------------------------------------- 1 | title = $name; 11 | ?> 12 |
13 | 14 |

title) ?>

15 | 16 |
17 | 18 |
19 | 20 |

21 | The above error occurred while the Web server was processing your request. 22 |

23 |

24 | Please contact us if you think this is a server error. Thank you. 25 |

26 | 27 |
28 | -------------------------------------------------------------------------------- /environments/dev/backend/web/index.php: -------------------------------------------------------------------------------- 1 | run(); 18 | -------------------------------------------------------------------------------- /environments/dev/frontend/web/index.php: -------------------------------------------------------------------------------- 1 | run(); 18 | -------------------------------------------------------------------------------- /environments/dev/backend/config/main-local.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'request' => [ 6 | // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation 7 | 'cookieValidationKey' => '', 8 | ], 9 | ], 10 | ]; 11 | 12 | if (!YII_ENV_TEST) { 13 | // configuration adjustments for 'dev' environment 14 | $config['bootstrap'][] = 'debug'; 15 | $config['modules']['debug'] = [ 16 | 'class' => 'yii\debug\Module', 17 | ]; 18 | 19 | $config['bootstrap'][] = 'gii'; 20 | $config['modules']['gii'] = [ 21 | 'class' => 'yii\gii\Module', 22 | ]; 23 | } 24 | 25 | return $config; 26 | -------------------------------------------------------------------------------- /environments/dev/frontend/config/main-local.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'request' => [ 6 | // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation 7 | 'cookieValidationKey' => '', 8 | ], 9 | ], 10 | ]; 11 | 12 | if (!YII_ENV_TEST) { 13 | // configuration adjustments for 'dev' environment 14 | $config['bootstrap'][] = 'debug'; 15 | $config['modules']['debug'] = [ 16 | 'class' => 'yii\debug\Module', 17 | ]; 18 | 19 | $config['bootstrap'][] = 'gii'; 20 | $config['modules']['gii'] = [ 21 | 'class' => 'yii\gii\Module', 22 | ]; 23 | } 24 | 25 | return $config; 26 | -------------------------------------------------------------------------------- /environments/prod/backend/web/index.php: -------------------------------------------------------------------------------- 1 | run(); 18 | -------------------------------------------------------------------------------- /environments/prod/frontend/web/index.php: -------------------------------------------------------------------------------- 1 | run(); 18 | -------------------------------------------------------------------------------- /environments/dev/backend/web/index-test.php: -------------------------------------------------------------------------------- 1 | run(); 19 | -------------------------------------------------------------------------------- /environments/dev/frontend/web/index-test.php: -------------------------------------------------------------------------------- 1 | run(); 19 | -------------------------------------------------------------------------------- /common/tests/_support/UnitTester.php: -------------------------------------------------------------------------------- 1 | [ 4 | 'db' => [ 5 | 'class' => 'yii\db\Connection', 6 | 'dsn' => 'mysql:host=localhost;dbname=yii2advanced', 7 | 'username' => 'root', 8 | 'password' => '', 9 | 'charset' => 'utf8', 10 | ], 11 | 'mailer' => [ 12 | 'class' => 'yii\swiftmailer\Mailer', 13 | 'viewPath' => '@common/mail', 14 | // send all mails to a file by default. You have to set 15 | // 'useFileTransport' to false and configure a transport 16 | // for the mailer to send real emails. 17 | 'useFileTransport' => true, 18 | ], 19 | ], 20 | ]; 21 | -------------------------------------------------------------------------------- /backend/tests/_support/UnitTester.php: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | 13 | 14 | 15 | field($model, 'name')->textInput(['maxlength' => true]) ?> 16 | 17 | field($model, 'email')->textInput(['maxlength' => true]) ?> 18 | 19 | field($model, 'created_at')->textInput() ?> 20 | 21 | field($model, 'updated_at')->textInput() ?> 22 | 23 |
24 | 'btn btn-success']) ?> 25 |
26 | 27 | 28 | 29 |
30 | -------------------------------------------------------------------------------- /common/mail/layouts/html.php: -------------------------------------------------------------------------------- 1 | 8 | beginPage() ?> 9 | 10 | 11 | 12 | 13 | <?= Html::encode($this->title) ?> 14 | head() ?> 15 | 16 | 17 | beginBody() ?> 18 | 19 | endBody() ?> 20 | 21 | 22 | endPage() ?> 23 | -------------------------------------------------------------------------------- /environments/dev/yii_test: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | run(); 27 | exit($exitCode); 28 | -------------------------------------------------------------------------------- /frontend/views/employee/_search.php: -------------------------------------------------------------------------------- 1 | 10 | 11 | 36 | -------------------------------------------------------------------------------- /frontend/tests/_data/user.php: -------------------------------------------------------------------------------- 1 | 'okirlin', 6 | 'auth_key' => 'iwTNae9t34OmnK6l4vT4IeaTk-YWI2Rv', 7 | 'password_hash' => '$2y$13$CXT0Rkle1EMJ/c1l5bylL.EylfmQ39O5JlHJVFpNn618OUS1HwaIi', 8 | 'password_reset_token' => 't5GU9NwpuGYSfb7FEZMAxqtuz2PkEvv_' . time(), 9 | 'created_at' => '1391885313', 10 | 'updated_at' => '1391885313', 11 | 'email' => 'brady.renner@rutherford.com', 12 | ], 13 | [ 14 | 'username' => 'troy.becker', 15 | 'auth_key' => 'EdKfXrx88weFMV0vIxuTMWKgfK2tS3Lp', 16 | 'password_hash' => '$2y$13$g5nv41Px7VBqhS3hVsVN2.MKfgT3jFdkXEsMC4rQJLfaMa7VaJqL2', 17 | 'password_reset_token' => '4BSNyiZNAuxjs5Mty990c47sVrgllIi_' . time(), 18 | 'created_at' => '1391885313', 19 | 'updated_at' => '1391885313', 20 | 'email' => 'nicolas.dianna@hotmail.com', 21 | 'status' => '0', 22 | ], 23 | ]; 24 | -------------------------------------------------------------------------------- /environments/dev/yii: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | run(); 28 | exit($exitCode); 29 | -------------------------------------------------------------------------------- /environments/prod/yii: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | run(); 28 | exit($exitCode); 29 | -------------------------------------------------------------------------------- /frontend/tests/_support/FunctionalTester.php: -------------------------------------------------------------------------------- 1 | see($message, '.help-block'); 27 | } 28 | 29 | public function dontSeeValidationError($message) 30 | { 31 | $this->dontSee($message, '.help-block'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /vagrant/provision/once-as-vagrant.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #== Import script args == 4 | 5 | github_token=$(echo "$1") 6 | 7 | #== Bash helpers == 8 | 9 | function info { 10 | echo " " 11 | echo "--> $1" 12 | echo " " 13 | } 14 | 15 | #== Provision script == 16 | 17 | info "Provision-script user: `whoami`" 18 | 19 | info "Configure composer" 20 | composer config --global github-oauth.github.com ${github_token} 21 | echo "Done!" 22 | 23 | info "Install project dependencies" 24 | cd /app 25 | composer --no-progress --prefer-dist install 26 | 27 | info "Init project" 28 | ./init --env=Development --overwrite=y 29 | 30 | info "Apply migrations" 31 | ./yii migrate --interactive=0 32 | ./yii_test migrate --interactive=0 33 | 34 | info "Create bash-alias 'app' for vagrant user" 35 | echo 'alias app="cd /app"' | tee /home/vagrant/.bash_aliases 36 | 37 | info "Enabling colorized prompt for guest console" 38 | sed -i "s/#force_color_prompt=yes/force_color_prompt=yes/" /home/vagrant/.bashrc 39 | -------------------------------------------------------------------------------- /frontend/views/site/resetPassword.php: -------------------------------------------------------------------------------- 1 | title = 'Reset password'; 11 | $this->params['breadcrumbs'][] = $this->title; 12 | ?> 13 |
14 |

title) ?>

15 | 16 |

Please choose your new password:

17 | 18 |
19 |
20 | 'reset-password-form']); ?> 21 | 22 | field($model, 'password')->passwordInput(['autofocus' => true]) ?> 23 | 24 |
25 | 'btn btn-primary']) ?> 26 |
27 | 28 | 29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /frontend/views/employee/index.php: -------------------------------------------------------------------------------- 1 | title = 'Employees'; 11 | $this->params['breadcrumbs'][] = $this->title; 12 | ?> 13 |
14 | 15 |

title) ?>

16 | render('_search', ['model' => $searchModel]); ?> 17 | 18 |

19 | 'btn btn-success']) ?> 20 |

21 | 22 | $dataProvider, 24 | 'filterModel' => $searchModel, 25 | 'columns' => [ 26 | ['class' => 'yii\grid\SerialColumn'], 27 | 28 | 'id', 29 | 'name', 30 | 'email:email', 31 | 'created_at', 32 | 'updated_at', 33 | 34 | ['class' => 'yii\grid\ActionColumn'], 35 | ], 36 | ]); ?> 37 |
38 | -------------------------------------------------------------------------------- /frontend/views/site/requestPasswordResetToken.php: -------------------------------------------------------------------------------- 1 | title = 'Request password reset'; 11 | $this->params['breadcrumbs'][] = $this->title; 12 | ?> 13 |
14 |

title) ?>

15 | 16 |

Please fill out your email. A link to reset password will be sent there.

17 | 18 |
19 |
20 | 'request-password-reset-form']); ?> 21 | 22 | field($model, 'email')->textInput(['autofocus' => true]) ?> 23 | 24 |
25 | 'btn btn-primary']) ?> 26 |
27 | 28 | 29 |
30 |
31 |
32 | -------------------------------------------------------------------------------- /console/config/main.php: -------------------------------------------------------------------------------- 1 | 'app-console', 11 | 'basePath' => dirname(__DIR__), 12 | 'bootstrap' => ['log'], 13 | 'controllerNamespace' => 'console\controllers', 14 | 'aliases' => [ 15 | '@bower' => '@vendor/bower-asset', 16 | '@npm' => '@vendor/npm-asset', 17 | ], 18 | 'controllerMap' => [ 19 | 'fixture' => [ 20 | 'class' => 'yii\console\controllers\FixtureController', 21 | 'namespace' => 'common\fixtures', 22 | ], 23 | ], 24 | 'components' => [ 25 | 'log' => [ 26 | 'targets' => [ 27 | [ 28 | 'class' => 'yii\log\FileTarget', 29 | 'levels' => ['error', 'warning'], 30 | ], 31 | ], 32 | ], 33 | ], 34 | 'params' => $params, 35 | ]; 36 | -------------------------------------------------------------------------------- /frontend/views/site/signup.php: -------------------------------------------------------------------------------- 1 | title = 'Signup'; 11 | $this->params['breadcrumbs'][] = $this->title; 12 | ?> 13 | 36 | -------------------------------------------------------------------------------- /frontend/views/user/_form.php: -------------------------------------------------------------------------------- 1 | 10 | 11 |
12 | 13 | 14 | 15 | field($model, 'username')->textInput(['maxlength' => true]) ?> 16 | 17 | field($model, 'auth_key')->textInput(['maxlength' => true]) ?> 18 | 19 | field($model, 'password_hash')->textInput(['maxlength' => true]) ?> 20 | 21 | field($model, 'password_reset_token')->textInput(['maxlength' => true]) ?> 22 | 23 | field($model, 'email')->textInput(['maxlength' => true]) ?> 24 | 25 | field($model, 'status')->textInput() ?> 26 | 27 | field($model, 'created_at')->textInput() ?> 28 | 29 | field($model, 'updated_at')->textInput() ?> 30 | 31 |
32 | 'btn btn-success']) ?> 33 |
34 | 35 | 36 | 37 |
38 | -------------------------------------------------------------------------------- /backend/views/site/login.php: -------------------------------------------------------------------------------- 1 | title = 'Login'; 11 | $this->params['breadcrumbs'][] = $this->title; 12 | ?> 13 | 36 | -------------------------------------------------------------------------------- /frontend/views/employee/view.php: -------------------------------------------------------------------------------- 1 | title = $model->name; 10 | $this->params['breadcrumbs'][] = ['label' => 'Employees', 'url' => ['index']]; 11 | $this->params['breadcrumbs'][] = $this->title; 12 | ?> 13 |
14 | 15 |

title) ?>

16 | 17 |

18 | $model->id], ['class' => 'btn btn-primary']) ?> 19 | $model->id], [ 20 | 'class' => 'btn btn-danger', 21 | 'data' => [ 22 | 'confirm' => 'Are you sure you want to delete this item?', 23 | 'method' => 'post', 24 | ], 25 | ]) ?> 26 |

27 | 28 | $model, 30 | 'attributes' => [ 31 | 'id', 32 | 'name', 33 | 'email:email', 34 | 'created_at', 35 | 'updated_at', 36 | ], 37 | ]) ?> 38 | 39 |
40 | -------------------------------------------------------------------------------- /frontend/views/user/_search.php: -------------------------------------------------------------------------------- 1 | 10 | 11 | 44 | -------------------------------------------------------------------------------- /frontend/views/user/index.php: -------------------------------------------------------------------------------- 1 | title = 'Users'; 11 | $this->params['breadcrumbs'][] = $this->title; 12 | ?> 13 |
14 | 15 |

title) ?>

16 | render('_search', ['model' => $searchModel]); ?> 17 | 18 |

19 | 'btn btn-success']) ?> 20 |

21 | 22 | $dataProvider, 24 | 'filterModel' => $searchModel, 25 | 'columns' => [ 26 | ['class' => 'yii\grid\SerialColumn'], 27 | 28 | 'id', 29 | 'username', 30 | 'auth_key', 31 | 'password_hash', 32 | 'password_reset_token', 33 | // 'email:email', 34 | // 'status', 35 | // 'created_at', 36 | // 'updated_at', 37 | 38 | ['class' => 'yii\grid\ActionColumn'], 39 | ], 40 | ]); ?> 41 |
42 | -------------------------------------------------------------------------------- /frontend/models/Employee.php: -------------------------------------------------------------------------------- 1 | 200], 35 | [['email'], 'string', 'max' => 100], 36 | ]; 37 | } 38 | 39 | /** 40 | * @inheritdoc 41 | */ 42 | public function attributeLabels() 43 | { 44 | return [ 45 | 'id' => 'ID', 46 | 'name' => 'Name', 47 | 'email' => 'Email', 48 | 'created_at' => 'Created At', 49 | 'updated_at' => 'Updated At', 50 | ]; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /backend/tests/functional/LoginCest.php: -------------------------------------------------------------------------------- 1 | [ 25 | 'class' => UserFixture::className(), 26 | 'dataFile' => codecept_data_dir() . 'login_data.php' 27 | ] 28 | ]; 29 | } 30 | 31 | /** 32 | * @param FunctionalTester $I 33 | */ 34 | public function loginUser(FunctionalTester $I) 35 | { 36 | $I->amOnPage('/site/login'); 37 | $I->fillField('Username', 'erau'); 38 | $I->fillField('Password', 'password_0'); 39 | $I->click('login-button'); 40 | 41 | $I->see('Logout (erau)', 'form button[type=submit]'); 42 | $I->dontSeeLink('Login'); 43 | $I->dontSeeLink('Signup'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /frontend/tests/unit/models/ContactFormTest.php: -------------------------------------------------------------------------------- 1 | attributes = [ 14 | 'name' => 'Tester', 15 | 'email' => 'tester@example.com', 16 | 'subject' => 'very important letter subject', 17 | 'body' => 'body of current message', 18 | ]; 19 | 20 | expect_that($model->sendEmail('admin@example.com')); 21 | 22 | // using Yii2 module actions to check email was sent 23 | $this->tester->seeEmailIsSent(); 24 | 25 | $emailMessage = $this->tester->grabLastSentEmail(); 26 | expect('valid email is sent', $emailMessage)->isInstanceOf('yii\mail\MessageInterface'); 27 | expect($emailMessage->getTo())->hasKey('admin@example.com'); 28 | expect($emailMessage->getFrom())->hasKey('tester@example.com'); 29 | expect($emailMessage->getSubject())->equals('very important letter subject'); 30 | expect($emailMessage->toString())->contains('body of current message'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /console/migrations/m130524_201442_init.php: -------------------------------------------------------------------------------- 1 | db->driverName === 'mysql') { 11 | // http://stackoverflow.com/questions/766809/whats-the-difference-between-utf8-general-ci-and-utf8-unicode-ci 12 | $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; 13 | } 14 | 15 | $this->createTable('{{%user}}', [ 16 | 'id' => $this->primaryKey(), 17 | 'username' => $this->string()->notNull()->unique(), 18 | 'auth_key' => $this->string(32)->notNull(), 19 | 'password_hash' => $this->string()->notNull(), 20 | 'password_reset_token' => $this->string()->unique(), 21 | 'email' => $this->string()->notNull()->unique(), 22 | 23 | 'status' => $this->smallInteger()->notNull()->defaultValue(10), 24 | 'created_at' => $this->integer()->notNull(), 25 | 'updated_at' => $this->integer()->notNull(), 26 | ], $tableOptions); 27 | } 28 | 29 | public function down() 30 | { 31 | $this->dropTable('{{%user}}'); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /frontend/views/user/view.php: -------------------------------------------------------------------------------- 1 | title = $model->id; 10 | $this->params['breadcrumbs'][] = ['label' => 'Users', 'url' => ['index']]; 11 | $this->params['breadcrumbs'][] = $this->title; 12 | ?> 13 |
14 | 15 |

title) ?>

16 | 17 |

18 | $model->id], ['class' => 'btn btn-primary']) ?> 19 | $model->id], [ 20 | 'class' => 'btn btn-danger', 21 | 'data' => [ 22 | 'confirm' => 'Are you sure you want to delete this item?', 23 | 'method' => 'post', 24 | ], 25 | ]) ?> 26 |

27 | 28 | $model, 30 | 'attributes' => [ 31 | 'id', 32 | 'username', 33 | 'auth_key', 34 | 'password_hash', 35 | 'password_reset_token', 36 | 'email:email', 37 | 'status', 38 | 'created_at', 39 | 'updated_at', 40 | ], 41 | ]) ?> 42 | 43 |
44 | -------------------------------------------------------------------------------- /frontend/tests/unit/models/ResetPasswordFormTest.php: -------------------------------------------------------------------------------- 1 | tester->haveFixtures([ 19 | 'user' => [ 20 | 'class' => UserFixture::className(), 21 | 'dataFile' => codecept_data_dir() . 'user.php' 22 | ], 23 | ]); 24 | } 25 | 26 | public function testResetWrongToken() 27 | { 28 | $this->tester->expectException('yii\base\InvalidParamException', function() { 29 | new ResetPasswordForm(''); 30 | }); 31 | 32 | $this->tester->expectException('yii\base\InvalidParamException', function() { 33 | new ResetPasswordForm('notexistingtoken_1391882543'); 34 | }); 35 | } 36 | 37 | public function testResetCorrectToken() 38 | { 39 | $user = $this->tester->grabFixture('user', 0); 40 | $form = new ResetPasswordForm($user['password_reset_token']); 41 | expect_that($form->resetPassword()); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "yiisoft/yii2-app-advanced", 3 | "description": "Yii 2 Advanced Project Template", 4 | "keywords": ["yii2", "framework", "advanced", "project template"], 5 | "homepage": "http://www.yiiframework.com/", 6 | "type": "project", 7 | "license": "BSD-3-Clause", 8 | "support": { 9 | "issues": "https://github.com/yiisoft/yii2/issues?state=open", 10 | "forum": "http://www.yiiframework.com/forum/", 11 | "wiki": "http://www.yiiframework.com/wiki/", 12 | "irc": "irc://irc.freenode.net/yii", 13 | "source": "https://github.com/yiisoft/yii2" 14 | }, 15 | "minimum-stability": "dev", 16 | "require": { 17 | "php": ">=5.4.0", 18 | "yiisoft/yii2": "~2.0.6", 19 | "yiisoft/yii2-bootstrap": "~2.0.0", 20 | "yiisoft/yii2-swiftmailer": "~2.0.0 || ~2.1.0" 21 | }, 22 | "require-dev": { 23 | "yiisoft/yii2-debug": "~2.0.0", 24 | "yiisoft/yii2-gii": "~2.0.0", 25 | "yiisoft/yii2-faker": "~2.0.0", 26 | "codeception/base": "^2.2.3", 27 | "codeception/verify": "~0.3.1" 28 | }, 29 | "config": { 30 | "process-timeout": 1800 31 | }, 32 | "repositories": [ 33 | { 34 | "type": "composer", 35 | "url": "https://asset-packagist.org" 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /frontend/views/site/login.php: -------------------------------------------------------------------------------- 1 | title = 'Login'; 11 | $this->params['breadcrumbs'][] = $this->title; 12 | ?> 13 | 40 | -------------------------------------------------------------------------------- /console/migrations/m170916_095928_adding_first_user.php: -------------------------------------------------------------------------------- 1 | db->createCommand($sql)->execute(); 19 | } 20 | 21 | /** 22 | * @inheritdoc 23 | */ 24 | public function safeDown() 25 | { 26 | // echo "m170916_095928_adding_first_user cannot be reverted.\n"; 27 | 28 | $sql="DELETE from user where username='sirinibin'"; 29 | Yii::$app->db->createCommand($sql)->execute(); 30 | 31 | } 32 | 33 | /* 34 | // Use up()/down() to run migration code without a transaction. 35 | public function up() 36 | { 37 | 38 | } 39 | 40 | public function down() 41 | { 42 | echo "m170916_095928_adding_first_user cannot be reverted.\n"; 43 | 44 | return false; 45 | } 46 | */ 47 | } 48 | -------------------------------------------------------------------------------- /console/migrations/m170916_101824_adding_sample_data_to_employee_table.php: -------------------------------------------------------------------------------- 1 | db->createCommand($sql)->execute(); 17 | 18 | } 19 | 20 | /** 21 | * @inheritdoc 22 | */ 23 | public function safeDown() 24 | { 25 | // echo "m170916_101824_adding_sample_data_to_employee_table cannot be reverted.\n"; 26 | 27 | $sql="DELETE from employee where email='john@gmail.com' or email='james@gmail.com' "; 28 | Yii::$app->db->createCommand($sql)->execute(); 29 | 30 | // return false; 31 | } 32 | 33 | /* 34 | // Use up()/down() to run migration code without a transaction. 35 | public function up() 36 | { 37 | 38 | } 39 | 40 | public function down() 41 | { 42 | echo "m170916_101824_adding_sample_data_to_employee_table cannot be reverted.\n"; 43 | 44 | return false; 45 | } 46 | */ 47 | } 48 | -------------------------------------------------------------------------------- /backend/controllers/RestController.php: -------------------------------------------------------------------------------- 1 | \yii\filters\Cors::className(), 27 | 'cors' => [ 28 | 'Origin' => ['*'], 29 | // 'Access-Control-Allow-Origin' => ['*', 'http://haikuwebapp.local.com:81','http://localhost:81'], 30 | 'Access-Control-Request-Method' => ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'], 31 | 'Access-Control-Request-Headers' => ['*'], 32 | 'Access-Control-Allow-Credentials' => null, 33 | 'Access-Control-Max-Age' => 86400, 34 | 'Access-Control-Expose-Headers' => [] 35 | ] 36 | 37 | ]; 38 | return $behaviors; 39 | } 40 | 41 | public function init() 42 | { 43 | $this->request = json_decode(file_get_contents('php://input'), true); 44 | 45 | if($this->request&&!is_array($this->request)){ 46 | Yii::$app->api->sendFailedResponse(['Invalid Json']); 47 | 48 | } 49 | 50 | } 51 | 52 | } 53 | 54 | 55 | -------------------------------------------------------------------------------- /common/models/AccessTokens.php: -------------------------------------------------------------------------------- 1 | 300], 38 | [['auth_code', 'app_id'], 'string', 'max' => 200], 39 | ]; 40 | } 41 | 42 | /** 43 | * @inheritdoc 44 | */ 45 | public function attributeLabels() 46 | { 47 | return [ 48 | 'id' => 'ID', 49 | 'token' => 'Token', 50 | 'expires_at' => 'Expires At', 51 | 'auth_code' => 'Auth Code', 52 | 'user_id' => 'User ID', 53 | 'app_id' => 'App ID', 54 | 'created_at' => 'Created At', 55 | 'updated_at' => 'Updated At', 56 | ]; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /console/migrations/m170916_100717_adding_employee_table.php: -------------------------------------------------------------------------------- 1 | db->createCommand($sql)->execute(); 45 | 46 | 47 | } 48 | 49 | /** 50 | * @inheritdoc 51 | */ 52 | public function safeDown() 53 | { 54 | $this->dropTable('{{%employee}}'); 55 | } 56 | 57 | /* 58 | // Use up()/down() to run migration code without a transaction. 59 | public function up() 60 | { 61 | 62 | } 63 | 64 | public function down() 65 | { 66 | echo "m170916_100717_adding_employee_table cannot be reverted.\n"; 67 | 68 | return false; 69 | } 70 | */ 71 | } 72 | -------------------------------------------------------------------------------- /frontend/views/site/contact.php: -------------------------------------------------------------------------------- 1 | title = 'Contact'; 12 | $this->params['breadcrumbs'][] = $this->title; 13 | ?> 14 |
15 |

title) ?>

16 | 17 |

18 | If you have business inquiries or other questions, please fill out the following form to contact us. Thank you. 19 |

20 | 21 |
22 |
23 | 'contact-form']); ?> 24 | 25 | field($model, 'name')->textInput(['autofocus' => true]) ?> 26 | 27 | field($model, 'email') ?> 28 | 29 | field($model, 'subject') ?> 30 | 31 | field($model, 'body')->textarea(['rows' => 6]) ?> 32 | 33 | field($model, 'verifyCode')->widget(Captcha::className(), [ 34 | 'template' => '
{image}
{input}
', 35 | ]) ?> 36 | 37 |
38 | 'btn btn-primary', 'name' => 'contact-button']) ?> 39 |
40 | 41 | 42 |
43 |
44 | 45 |
46 | -------------------------------------------------------------------------------- /frontend/models/ContactForm.php: -------------------------------------------------------------------------------- 1 | 'Verification Code', 42 | ]; 43 | } 44 | 45 | /** 46 | * Sends an email to the specified email address using the information collected by this model. 47 | * 48 | * @param string $email the target email address 49 | * @return bool whether the email was sent 50 | */ 51 | public function sendEmail($email) 52 | { 53 | return Yii::$app->mailer->compose() 54 | ->setTo($email) 55 | ->setFrom([$this->email => $this->name]) 56 | ->setSubject($this->subject) 57 | ->setTextBody($this->body) 58 | ->send(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /backend/behaviours/Verbcheck.php: -------------------------------------------------------------------------------- 1 | action->id; 24 | 25 | 26 | if (isset($this->actions[$action])) { 27 | $verbs = $this->actions[$action]; 28 | } elseif (isset($this->actions['*'])) { 29 | $verbs = $this->actions['*']; 30 | } else { 31 | return $event->isValid; 32 | } 33 | 34 | $verb = Yii::$app->getRequest()->getMethod(); 35 | $allowed = array_map('strtoupper', $verbs); 36 | if (!in_array($verb, $allowed)) { 37 | $event->isValid = false; 38 | // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7 39 | Yii::$app->getResponse()->getHeaders()->set('Allow', implode(', ', $allowed)); 40 | 41 | Yii::$app->api->sendFailedResponse('Method Not Allowed. This url can only handle the following request methods: ' . implode(', ', $allowed) . '.'); 42 | 43 | // throw new MethodNotAllowedHttpException('Method Not Allowed. This url can only handle the following request methods: ' . implode(', ', $allowed) . '.'); 44 | } 45 | 46 | return $event->isValid; 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /frontend/models/SignupForm.php: -------------------------------------------------------------------------------- 1 | '\common\models\User', 'message' => 'This username has already been taken.'], 26 | ['username', 'string', 'min' => 2, 'max' => 255], 27 | 28 | ['email', 'trim'], 29 | ['email', 'required'], 30 | ['email', 'email'], 31 | ['email', 'string', 'max' => 255], 32 | ['email', 'unique', 'targetClass' => '\common\models\User', 'message' => 'This email address has already been taken.'], 33 | 34 | ['password', 'required'], 35 | ['password', 'string', 'min' => 6], 36 | ]; 37 | } 38 | 39 | /** 40 | * Signs user up. 41 | * 42 | * @return User|null the saved model or null if saving fails 43 | */ 44 | public function signup() 45 | { 46 | if (!$this->validate()) { 47 | return null; 48 | } 49 | 50 | $user = new User(); 51 | $user->username = $this->username; 52 | $user->email = $this->email; 53 | $user->setPassword($this->password); 54 | $user->generateAuthKey(); 55 | 56 | return $user->save() ? $user : null; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The Yii framework is free software. It is released under the terms of 2 | the following BSD License. 3 | 4 | Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com) 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions 9 | are met: 10 | 11 | * Redistributions of source code must retain the above copyright 12 | notice, this list of conditions and the following disclaimer. 13 | * Redistributions in binary form must reproduce the above copyright 14 | notice, this list of conditions and the following disclaimer in 15 | the documentation and/or other materials provided with the 16 | distribution. 17 | * Neither the name of Yii Software LLC nor the names of its 18 | contributors may be used to endorse or promote products derived 19 | from this software without specific prior written permission. 20 | 21 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 24 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 25 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 26 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 27 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 28 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 29 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 31 | ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 32 | POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- /frontend/models/ResetPasswordForm.php: -------------------------------------------------------------------------------- 1 | _user = User::findByPasswordResetToken($token); 34 | if (!$this->_user) { 35 | throw new InvalidParamException('Wrong password reset token.'); 36 | } 37 | parent::__construct($config); 38 | } 39 | 40 | /** 41 | * @inheritdoc 42 | */ 43 | public function rules() 44 | { 45 | return [ 46 | ['password', 'required'], 47 | ['password', 'string', 'min' => 6], 48 | ]; 49 | } 50 | 51 | /** 52 | * Resets password. 53 | * 54 | * @return bool if password was reset. 55 | */ 56 | public function resetPassword() 57 | { 58 | $user = $this->_user; 59 | $user->setPassword($this->password); 60 | $user->removePasswordResetToken(); 61 | 62 | return $user->save(false); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /backend/models/SignupForm.php: -------------------------------------------------------------------------------- 1 | '\common\models\User', 'message' => 'This username has already been taken.'], 28 | ['username', 'string', 'min' => 2, 'max' => 255], 29 | 30 | ['email', 'trim'], 31 | ['email', 'required'], 32 | ['email', 'email'], 33 | ['email', 'string', 'max' => 255], 34 | ['email', 'unique', 'targetClass' => '\common\models\User', 'message' => 'This email address has already been taken.'], 35 | 36 | ['password', 'required'], 37 | ['password', 'string', 'min' => 6], 38 | ]; 39 | } 40 | 41 | /** 42 | * Signs user up. 43 | * 44 | * @return User|null the saved model or null if saving fails 45 | */ 46 | public function signup() 47 | { 48 | 49 | if (!$this->validate()) { 50 | 51 | Yii::$app->api->sendFailedResponse($this->errors); 52 | //return null; 53 | } 54 | 55 | $user = new User(); 56 | $user->username = $this->username; 57 | $user->email = $this->email; 58 | $user->setPassword($this->password); 59 | $user->generateAuthKey(); 60 | 61 | return $user->save() ? $user : null; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /frontend/models/User.php: -------------------------------------------------------------------------------- 1 | 255], 39 | [['auth_key'], 'string', 'max' => 32], 40 | [['username'], 'unique'], 41 | [['email'], 'unique'], 42 | [['password_reset_token'], 'unique'], 43 | ]; 44 | } 45 | 46 | /** 47 | * @inheritdoc 48 | */ 49 | public function attributeLabels() 50 | { 51 | return [ 52 | 'id' => 'ID', 53 | 'username' => 'Username', 54 | 'auth_key' => 'Auth Key', 55 | 'password_hash' => 'Password Hash', 56 | 'password_reset_token' => 'Password Reset Token', 57 | 'email' => 'Email', 58 | 'status' => 'Status', 59 | 'created_at' => 'Created At', 60 | 'updated_at' => 'Updated At', 61 | ]; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /common/models/AuthorizationCodes.php: -------------------------------------------------------------------------------- 1 | 150], 37 | [['app_id'], 'string', 'max' => 200], 38 | ]; 39 | } 40 | 41 | /** 42 | * @inheritdoc 43 | */ 44 | public function attributeLabels() 45 | { 46 | return [ 47 | 'id' => 'ID', 48 | 'code' => 'Code', 49 | 'expires_at' => 'Expires At', 50 | 'user_id' => 'User ID', 51 | 'app_id' => 'App ID', 52 | 'created_at' => 'Created At', 53 | 'updated_at' => 'Updated At', 54 | ]; 55 | } 56 | public static function isValid($code) 57 | { 58 | $model=static::findOne(['code' => $code]); 59 | 60 | if(!$model||$model->expires_atapi->sendFailedResponse("Authcode Expired"); 63 | return(false); 64 | } 65 | else 66 | return($model); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /frontend/tests/functional/LoginCest.php: -------------------------------------------------------------------------------- 1 | [ 21 | 'class' => UserFixture::className(), 22 | 'dataFile' => codecept_data_dir() . 'login_data.php' 23 | ] 24 | ]; 25 | } 26 | 27 | public function _before(FunctionalTester $I) 28 | { 29 | $I->amOnRoute('site/login'); 30 | } 31 | 32 | protected function formParams($login, $password) 33 | { 34 | return [ 35 | 'LoginForm[username]' => $login, 36 | 'LoginForm[password]' => $password, 37 | ]; 38 | } 39 | 40 | public function checkEmpty(FunctionalTester $I) 41 | { 42 | $I->submitForm('#login-form', $this->formParams('', '')); 43 | $I->seeValidationError('Username cannot be blank.'); 44 | $I->seeValidationError('Password cannot be blank.'); 45 | } 46 | 47 | public function checkWrongPassword(FunctionalTester $I) 48 | { 49 | $I->submitForm('#login-form', $this->formParams('admin', 'wrong')); 50 | $I->seeValidationError('Incorrect username or password.'); 51 | } 52 | 53 | public function checkValidLogin(FunctionalTester $I) 54 | { 55 | $I->submitForm('#login-form', $this->formParams('erau', 'password_0')); 56 | $I->see('Logout (erau)', 'form button[type=submit]'); 57 | $I->dontSeeLink('Login'); 58 | $I->dontSeeLink('Signup'); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /frontend/tests/unit/models/SignupFormTest.php: -------------------------------------------------------------------------------- 1 | tester->haveFixtures([ 18 | 'user' => [ 19 | 'class' => UserFixture::className(), 20 | 'dataFile' => codecept_data_dir() . 'user.php' 21 | ] 22 | ]); 23 | } 24 | 25 | public function testCorrectSignup() 26 | { 27 | $model = new SignupForm([ 28 | 'username' => 'some_username', 29 | 'email' => 'some_email@example.com', 30 | 'password' => 'some_password', 31 | ]); 32 | 33 | $user = $model->signup(); 34 | 35 | expect($user)->isInstanceOf('common\models\User'); 36 | 37 | expect($user->username)->equals('some_username'); 38 | expect($user->email)->equals('some_email@example.com'); 39 | expect($user->validatePassword('some_password'))->true(); 40 | } 41 | 42 | public function testNotCorrectSignup() 43 | { 44 | $model = new SignupForm([ 45 | 'username' => 'troy.becker', 46 | 'email' => 'nicolas.dianna@hotmail.com', 47 | 'password' => 'some_password', 48 | ]); 49 | 50 | expect_not($model->signup()); 51 | expect_that($model->getErrors('username')); 52 | expect_that($model->getErrors('email')); 53 | 54 | expect($model->getFirstError('username')) 55 | ->equals('This username has already been taken.'); 56 | expect($model->getFirstError('email')) 57 | ->equals('This email address has already been taken.'); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /frontend/tests/functional/SignupCest.php: -------------------------------------------------------------------------------- 1 | amOnRoute('site/signup'); 15 | } 16 | 17 | public function signupWithEmptyFields(FunctionalTester $I) 18 | { 19 | $I->see('Signup', 'h1'); 20 | $I->see('Please fill out the following fields to signup:'); 21 | $I->submitForm($this->formId, []); 22 | $I->seeValidationError('Username cannot be blank.'); 23 | $I->seeValidationError('Email cannot be blank.'); 24 | $I->seeValidationError('Password cannot be blank.'); 25 | 26 | } 27 | 28 | public function signupWithWrongEmail(FunctionalTester $I) 29 | { 30 | $I->submitForm( 31 | $this->formId, [ 32 | 'SignupForm[username]' => 'tester', 33 | 'SignupForm[email]' => 'ttttt', 34 | 'SignupForm[password]' => 'tester_password', 35 | ] 36 | ); 37 | $I->dontSee('Username cannot be blank.', '.help-block'); 38 | $I->dontSee('Password cannot be blank.', '.help-block'); 39 | $I->see('Email is not a valid email address.', '.help-block'); 40 | } 41 | 42 | public function signupSuccessfully(FunctionalTester $I) 43 | { 44 | $I->submitForm($this->formId, [ 45 | 'SignupForm[username]' => 'tester', 46 | 'SignupForm[email]' => 'tester.email@example.com', 47 | 'SignupForm[password]' => 'tester_password', 48 | ]); 49 | 50 | $I->seeRecord('common\models\User', [ 51 | 'username' => 'tester', 52 | 'email' => 'tester.email@example.com', 53 | ]); 54 | 55 | $I->see('Logout (tester)', 'form button[type=submit]'); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /frontend/models/EmployeeSearch.php: -------------------------------------------------------------------------------- 1 | $query, 50 | ]); 51 | 52 | $this->load($params); 53 | 54 | if (!$this->validate()) { 55 | // uncomment the following line if you do not want to return any records when validation fails 56 | // $query->where('0=1'); 57 | return $dataProvider; 58 | } 59 | 60 | // grid filtering conditions 61 | $query->andFilterWhere([ 62 | 'id' => $this->id, 63 | 'created_at' => $this->created_at, 64 | 'updated_at' => $this->updated_at, 65 | ]); 66 | 67 | $query->andFilterWhere(['like', 'name', $this->name]) 68 | ->andFilterWhere(['like', 'email', $this->email]); 69 | 70 | return $dataProvider; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /frontend/models/PasswordResetRequestForm.php: -------------------------------------------------------------------------------- 1 | '\common\models\User', 27 | 'filter' => ['status' => User::STATUS_ACTIVE], 28 | 'message' => 'There is no user with this email address.' 29 | ], 30 | ]; 31 | } 32 | 33 | /** 34 | * Sends an email with a link, for resetting the password. 35 | * 36 | * @return bool whether the email was send 37 | */ 38 | public function sendEmail() 39 | { 40 | /* @var $user User */ 41 | $user = User::findOne([ 42 | 'status' => User::STATUS_ACTIVE, 43 | 'email' => $this->email, 44 | ]); 45 | 46 | if (!$user) { 47 | return false; 48 | } 49 | 50 | if (!User::isPasswordResetTokenValid($user->password_reset_token)) { 51 | $user->generatePasswordResetToken(); 52 | if (!$user->save()) { 53 | return false; 54 | } 55 | } 56 | 57 | return Yii::$app 58 | ->mailer 59 | ->compose( 60 | ['html' => 'passwordResetToken-html', 'text' => 'passwordResetToken-text'], 61 | ['user' => $user] 62 | ) 63 | ->setFrom([Yii::$app->params['supportEmail'] => Yii::$app->name . ' robot']) 64 | ->setTo($this->email) 65 | ->setSubject('Password reset for ' . Yii::$app->name) 66 | ->send(); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /common/tests/unit/models/LoginFormTest.php: -------------------------------------------------------------------------------- 1 | [ 26 | 'class' => UserFixture::className(), 27 | 'dataFile' => codecept_data_dir() . 'user.php' 28 | ] 29 | ]; 30 | } 31 | 32 | public function testLoginNoUser() 33 | { 34 | $model = new LoginForm([ 35 | 'username' => 'not_existing_username', 36 | 'password' => 'not_existing_password', 37 | ]); 38 | 39 | expect('model should not login user', $model->login())->false(); 40 | expect('user should not be logged in', Yii::$app->user->isGuest)->true(); 41 | } 42 | 43 | public function testLoginWrongPassword() 44 | { 45 | $model = new LoginForm([ 46 | 'username' => 'bayer.hudson', 47 | 'password' => 'wrong_password', 48 | ]); 49 | 50 | expect('model should not login user', $model->login())->false(); 51 | expect('error message should be set', $model->errors)->hasKey('password'); 52 | expect('user should not be logged in', Yii::$app->user->isGuest)->true(); 53 | } 54 | 55 | public function testLoginCorrect() 56 | { 57 | $model = new LoginForm([ 58 | 'username' => 'bayer.hudson', 59 | 'password' => 'password_0', 60 | ]); 61 | 62 | expect('model should login user', $model->login())->true(); 63 | expect('error message should not be set', $model->errors)->hasntKey('password'); 64 | expect('user should be logged in', Yii::$app->user->isGuest)->false(); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /frontend/tests/unit/models/PasswordResetRequestFormTest.php: -------------------------------------------------------------------------------- 1 | tester->haveFixtures([ 21 | 'user' => [ 22 | 'class' => UserFixture::className(), 23 | 'dataFile' => codecept_data_dir() . 'user.php' 24 | ] 25 | ]); 26 | } 27 | 28 | public function testSendMessageWithWrongEmailAddress() 29 | { 30 | $model = new PasswordResetRequestForm(); 31 | $model->email = 'not-existing-email@example.com'; 32 | expect_not($model->sendEmail()); 33 | } 34 | 35 | public function testNotSendEmailsToInactiveUser() 36 | { 37 | $user = $this->tester->grabFixture('user', 1); 38 | $model = new PasswordResetRequestForm(); 39 | $model->email = $user['email']; 40 | expect_not($model->sendEmail()); 41 | } 42 | 43 | public function testSendEmailSuccessfully() 44 | { 45 | $userFixture = $this->tester->grabFixture('user', 0); 46 | 47 | $model = new PasswordResetRequestForm(); 48 | $model->email = $userFixture['email']; 49 | $user = User::findOne(['password_reset_token' => $userFixture['password_reset_token']]); 50 | 51 | expect_that($model->sendEmail()); 52 | expect_that($user->password_reset_token); 53 | 54 | $emailMessage = $this->tester->grabLastSentEmail(); 55 | expect('valid email is sent', $emailMessage)->isInstanceOf('yii\mail\MessageInterface'); 56 | expect($emailMessage->getTo())->hasKey($model->email); 57 | expect($emailMessage->getFrom())->hasKey(Yii::$app->params['supportEmail']); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /common/models/LoginForm.php: -------------------------------------------------------------------------------- 1 | hasErrors()) { 44 | $user = $this->getUser(); 45 | if (!$user || !$user->validatePassword($this->password)) { 46 | $this->addError($attribute, 'Incorrect username or password.'); 47 | } 48 | } 49 | } 50 | 51 | /** 52 | * Logs in a user using the provided username and password. 53 | * 54 | * @return bool whether the user is logged in successfully 55 | */ 56 | public function login() 57 | { 58 | if ($this->validate()) { 59 | return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0); 60 | } 61 | 62 | return false; 63 | } 64 | 65 | /** 66 | * Finds user by [[username]] 67 | * 68 | * @return User|null 69 | */ 70 | protected function getUser() 71 | { 72 | if ($this->_user === null) { 73 | $this->_user = User::findByUsername($this->username); 74 | } 75 | 76 | return $this->_user; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /environments/index.php: -------------------------------------------------------------------------------- 1 | [ 11 | * 'path' => 'directory storing the local files', 12 | * 'skipFiles' => [ 13 | * // list of files that should only copied once and skipped if they already exist 14 | * ], 15 | * 'setWritable' => [ 16 | * // list of directories that should be set writable 17 | * ], 18 | * 'setExecutable' => [ 19 | * // list of files that should be set executable 20 | * ], 21 | * 'setCookieValidationKey' => [ 22 | * // list of config files that need to be inserted with automatically generated cookie validation keys 23 | * ], 24 | * 'createSymlink' => [ 25 | * // list of symlinks to be created. Keys are symlinks, and values are the targets. 26 | * ], 27 | * ], 28 | * ]; 29 | * ``` 30 | */ 31 | return [ 32 | 'Development' => [ 33 | 'path' => 'dev', 34 | 'setWritable' => [ 35 | 'backend/runtime', 36 | 'backend/web/assets', 37 | 'frontend/runtime', 38 | 'frontend/web/assets', 39 | ], 40 | 'setExecutable' => [ 41 | 'yii', 42 | 'yii_test', 43 | ], 44 | 'setCookieValidationKey' => [ 45 | 'backend/config/main-local.php', 46 | 'frontend/config/main-local.php', 47 | ], 48 | ], 49 | 'Production' => [ 50 | 'path' => 'prod', 51 | 'setWritable' => [ 52 | 'backend/runtime', 53 | 'backend/web/assets', 54 | 'frontend/runtime', 55 | 'frontend/web/assets', 56 | ], 57 | 'setExecutable' => [ 58 | 'yii', 59 | ], 60 | 'setCookieValidationKey' => [ 61 | 'backend/config/main-local.php', 62 | 'frontend/config/main-local.php', 63 | ], 64 | ], 65 | ]; 66 | -------------------------------------------------------------------------------- /frontend/config/main.php: -------------------------------------------------------------------------------- 1 | 'app-frontend', 11 | 'basePath' => dirname(__DIR__), 12 | 'bootstrap' => ['log'], 13 | 'controllerNamespace' => 'frontend\controllers', 14 | 'defaultRoute' => 'site/index', 15 | 'components' => [ 16 | 'request' => [ 17 | 'csrfParam' => '_csrf-frontend', 18 | ], 19 | 'user' => [ 20 | 'identityClass' => 'common\models\User', 21 | 'enableAutoLogin' => true, 22 | 'identityCookie' => ['name' => '_identity-frontend', 'httpOnly' => true], 23 | ], 24 | 'session' => [ 25 | // this is the name of the session cookie used for login on the frontend 26 | 'name' => 'advanced-frontend', 27 | ], 28 | 'log' => [ 29 | 'traceLevel' => YII_DEBUG ? 3 : 0, 30 | 'targets' => [ 31 | [ 32 | 'class' => 'yii\log\FileTarget', 33 | 'levels' => ['error', 'warning'], 34 | ], 35 | ], 36 | ], 37 | 'errorHandler' => [ 38 | 'errorAction' => 'site/error', 39 | ], 40 | 'urlManager' => [ 41 | 'class' => 'yii\web\UrlManager', 42 | 'enablePrettyUrl' => true, 43 | 'showScriptName' => false, 44 | 'rules' => [ 45 | '/' => '/view', 46 | '//' => '/', 47 | '/' => '/', 48 | '//' => '//view', 49 | '///' => '//', 50 | // '//' => '//', 51 | ], 52 | ], 53 | ], 54 | 'params' => $params, 55 | ]; 56 | -------------------------------------------------------------------------------- /frontend/tests/functional/ContactCest.php: -------------------------------------------------------------------------------- 1 | amOnPage(['site/contact']); 13 | } 14 | 15 | public function checkContact(FunctionalTester $I) 16 | { 17 | $I->see('Contact', 'h1'); 18 | } 19 | 20 | public function checkContactSubmitNoData(FunctionalTester $I) 21 | { 22 | $I->submitForm('#contact-form', []); 23 | $I->see('Contact', 'h1'); 24 | $I->seeValidationError('Name cannot be blank'); 25 | $I->seeValidationError('Email cannot be blank'); 26 | $I->seeValidationError('Subject cannot be blank'); 27 | $I->seeValidationError('Body cannot be blank'); 28 | $I->seeValidationError('The verification code is incorrect'); 29 | } 30 | 31 | public function checkContactSubmitNotCorrectEmail(FunctionalTester $I) 32 | { 33 | $I->submitForm('#contact-form', [ 34 | 'ContactForm[name]' => 'tester', 35 | 'ContactForm[email]' => 'tester.email', 36 | 'ContactForm[subject]' => 'test subject', 37 | 'ContactForm[body]' => 'test content', 38 | 'ContactForm[verifyCode]' => 'testme', 39 | ]); 40 | $I->seeValidationError('Email is not a valid email address.'); 41 | $I->dontSeeValidationError('Name cannot be blank'); 42 | $I->dontSeeValidationError('Subject cannot be blank'); 43 | $I->dontSeeValidationError('Body cannot be blank'); 44 | $I->dontSeeValidationError('The verification code is incorrect'); 45 | } 46 | 47 | public function checkContactSubmitCorrectData(FunctionalTester $I) 48 | { 49 | $I->submitForm('#contact-form', [ 50 | 'ContactForm[name]' => 'tester', 51 | 'ContactForm[email]' => 'tester@example.com', 52 | 'ContactForm[subject]' => 'test subject', 53 | 'ContactForm[body]' => 'test content', 54 | 'ContactForm[verifyCode]' => 'testme', 55 | ]); 56 | $I->seeEmailIsSent(); 57 | $I->see('Thank you for contacting us. We will respond to you as soon as possible.'); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /frontend/models/UserSearch.php: -------------------------------------------------------------------------------- 1 | $query, 50 | ]); 51 | 52 | $this->load($params); 53 | 54 | if (!$this->validate()) { 55 | // uncomment the following line if you do not want to return any records when validation fails 56 | // $query->where('0=1'); 57 | return $dataProvider; 58 | } 59 | 60 | // grid filtering conditions 61 | $query->andFilterWhere([ 62 | 'id' => $this->id, 63 | 'status' => $this->status, 64 | 'created_at' => $this->created_at, 65 | 'updated_at' => $this->updated_at, 66 | ]); 67 | 68 | $query->andFilterWhere(['like', 'username', $this->username]) 69 | ->andFilterWhere(['like', 'auth_key', $this->auth_key]) 70 | ->andFilterWhere(['like', 'password_hash', $this->password_hash]) 71 | ->andFilterWhere(['like', 'password_reset_token', $this->password_reset_token]) 72 | ->andFilterWhere(['like', 'email', $this->email]); 73 | 74 | return $dataProvider; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /vagrant/provision/once-as-root.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | #== Import script args == 4 | 5 | timezone=$(echo "$1") 6 | 7 | #== Bash helpers == 8 | 9 | function info { 10 | echo " " 11 | echo "--> $1" 12 | echo " " 13 | } 14 | 15 | #== Provision script == 16 | 17 | info "Provision-script user: `whoami`" 18 | 19 | export DEBIAN_FRONTEND=noninteractive 20 | 21 | info "Configure timezone" 22 | timedatectl set-timezone ${timezone} --no-ask-password 23 | 24 | info "Prepare root password for MySQL" 25 | debconf-set-selections <<< "mysql-community-server mysql-community-server/root-pass password \"''\"" 26 | debconf-set-selections <<< "mysql-community-server mysql-community-server/re-root-pass password \"''\"" 27 | echo "Done!" 28 | 29 | info "Update OS software" 30 | apt-get update 31 | apt-get upgrade -y 32 | 33 | info "Install additional software" 34 | apt-get install -y php7.0-curl php7.0-cli php7.0-intl php7.0-mysqlnd php7.0-gd php7.0-fpm php7.0-mbstring php7.0-xml unzip nginx mysql-server-5.7 php.xdebug 35 | 36 | info "Configure MySQL" 37 | sed -i "s/.*bind-address.*/bind-address = 0.0.0.0/" /etc/mysql/mysql.conf.d/mysqld.cnf 38 | mysql -uroot <<< "CREATE USER 'root'@'%' IDENTIFIED BY ''" 39 | mysql -uroot <<< "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'" 40 | mysql -uroot <<< "DROP USER 'root'@'localhost'" 41 | mysql -uroot <<< "FLUSH PRIVILEGES" 42 | echo "Done!" 43 | 44 | info "Configure PHP-FPM" 45 | sed -i 's/user = www-data/user = vagrant/g' /etc/php/7.0/fpm/pool.d/www.conf 46 | sed -i 's/group = www-data/group = vagrant/g' /etc/php/7.0/fpm/pool.d/www.conf 47 | sed -i 's/owner = www-data/owner = vagrant/g' /etc/php/7.0/fpm/pool.d/www.conf 48 | cat << EOF > /etc/php/7.0/mods-available/xdebug.ini 49 | zend_extension=xdebug.so 50 | xdebug.remote_enable=1 51 | xdebug.remote_connect_back=1 52 | xdebug.remote_port=9000 53 | xdebug.remote_autostart=1 54 | EOF 55 | echo "Done!" 56 | 57 | info "Configure NGINX" 58 | sed -i 's/user www-data/user vagrant/g' /etc/nginx/nginx.conf 59 | echo "Done!" 60 | 61 | info "Enabling site configuration" 62 | ln -s /app/vagrant/nginx/app.conf /etc/nginx/sites-enabled/app.conf 63 | echo "Done!" 64 | 65 | info "Initailize databases for MySQL" 66 | mysql -uroot <<< "CREATE DATABASE yii2advanced" 67 | mysql -uroot <<< "CREATE DATABASE yii2advanced_test" 68 | echo "Done!" 69 | 70 | info "Install composer" 71 | curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer -------------------------------------------------------------------------------- /backend/views/layouts/main.php: -------------------------------------------------------------------------------- 1 | 15 | beginPage() ?> 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | <?= Html::encode($this->title) ?> 24 | head() ?> 25 | 26 | 27 | beginBody() ?> 28 | 29 |
30 | 'My Company', 33 | 'brandUrl' => Yii::$app->homeUrl, 34 | 'options' => [ 35 | 'class' => 'navbar-inverse navbar-fixed-top', 36 | ], 37 | ]); 38 | $menuItems = [ 39 | ['label' => 'Home', 'url' => ['/site/index']], 40 | ]; 41 | if (Yii::$app->user->isGuest) { 42 | $menuItems[] = ['label' => 'Login', 'url' => ['/site/login']]; 43 | } else { 44 | $menuItems[] = '
  • ' 45 | . Html::beginForm(['/site/logout'], 'post') 46 | . Html::submitButton( 47 | 'Logout (' . Yii::$app->user->identity->username . ')', 48 | ['class' => 'btn btn-link logout'] 49 | ) 50 | . Html::endForm() 51 | . '
  • '; 52 | } 53 | echo Nav::widget([ 54 | 'options' => ['class' => 'navbar-nav navbar-right'], 55 | 'items' => $menuItems, 56 | ]); 57 | NavBar::end(); 58 | ?> 59 | 60 |
    61 | isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], 63 | ]) ?> 64 | 65 | 66 |
    67 |
    68 | 69 |
    70 |
    71 |

    © My Company

    72 | 73 |

    74 |
    75 |
    76 | 77 | endBody() ?> 78 | 79 | 80 | endPage() ?> 81 | -------------------------------------------------------------------------------- /vagrant/nginx/app.conf: -------------------------------------------------------------------------------- 1 | server { 2 | charset utf-8; 3 | client_max_body_size 128M; 4 | sendfile off; 5 | 6 | listen 80; ## listen for ipv4 7 | #listen [::]:80 default_server ipv6only=on; ## listen for ipv6 8 | 9 | server_name y2aa-frontend.dev; 10 | root /app/frontend/web/; 11 | index index.php; 12 | 13 | access_log /app/vagrant/nginx/log/frontend-access.log; 14 | error_log /app/vagrant/nginx/log/frontend-error.log; 15 | 16 | location / { 17 | # Redirect everything that isn't a real file to index.php 18 | try_files $uri $uri/ /index.php$is_args$args; 19 | } 20 | 21 | # uncomment to avoid processing of calls to non-existing static files by Yii 22 | #location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ { 23 | # try_files $uri =404; 24 | #} 25 | #error_page 404 /404.html; 26 | 27 | location ~ \.php$ { 28 | include fastcgi_params; 29 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 30 | #fastcgi_pass 127.0.0.1:9000; 31 | fastcgi_pass unix:/var/run/php/php7.0-fpm.sock; 32 | try_files $uri =404; 33 | } 34 | 35 | location ~ /\.(ht|svn|git) { 36 | deny all; 37 | } 38 | } 39 | 40 | server { 41 | charset utf-8; 42 | client_max_body_size 128M; 43 | sendfile off; 44 | 45 | listen 80; ## listen for ipv4 46 | #listen [::]:80 default_server ipv6only=on; ## listen for ipv6 47 | 48 | server_name y2aa-backend.dev; 49 | root /app/backend/web/; 50 | index index.php; 51 | 52 | access_log /app/vagrant/nginx/log/backend-access.log; 53 | error_log /app/vagrant/nginx/log/backend-error.log; 54 | 55 | location / { 56 | # Redirect everything that isn't a real file to index.php 57 | try_files $uri $uri/ /index.php$is_args$args; 58 | } 59 | 60 | # uncomment to avoid processing of calls to non-existing static files by Yii 61 | #location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ { 62 | # try_files $uri =404; 63 | #} 64 | #error_page 404 /404.html; 65 | 66 | location ~ \.php$ { 67 | include fastcgi_params; 68 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 69 | #fastcgi_pass 127.0.0.1:9000; 70 | fastcgi_pass unix:/var/run/php/php7.0-fpm.sock; 71 | try_files $uri =404; 72 | } 73 | 74 | location ~ /\.(ht|svn|git) { 75 | deny all; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /backend/views/site/index.php: -------------------------------------------------------------------------------- 1 | title = 'My Yii Application'; 6 | ?> 7 |
    8 | 9 |
    10 |

    Congratulations!

    11 | 12 |

    You have successfully created your Yii-powered application.

    13 | 14 |

    Get started with Yii

    15 |
    16 | 17 |
    18 | 19 |
    20 |
    21 |

    Heading

    22 | 23 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et 24 | dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip 25 | ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu 26 | fugiat nulla pariatur.

    27 | 28 |

    Yii Documentation »

    29 |
    30 |
    31 |

    Heading

    32 | 33 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et 34 | dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip 35 | ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu 36 | fugiat nulla pariatur.

    37 | 38 |

    Yii Forum »

    39 |
    40 |
    41 |

    Heading

    42 | 43 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et 44 | dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip 45 | ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu 46 | fugiat nulla pariatur.

    47 | 48 |

    Yii Extensions »

    49 |
    50 |
    51 | 52 |
    53 |
    54 | -------------------------------------------------------------------------------- /frontend/views/site/index.php: -------------------------------------------------------------------------------- 1 | title = 'My Yii Application'; 6 | ?> 7 |
    8 | 9 |
    10 |

    Congratulations!

    11 | 12 |

    You have successfully created your Yii-powered application.

    13 | 14 |

    Get started with Yii

    15 |
    16 | 17 |
    18 | 19 |
    20 |
    21 |

    Heading

    22 | 23 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et 24 | dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip 25 | ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu 26 | fugiat nulla pariatur.

    27 | 28 |

    Yii Documentation »

    29 |
    30 |
    31 |

    Heading

    32 | 33 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et 34 | dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip 35 | ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu 36 | fugiat nulla pariatur.

    37 | 38 |

    Yii Forum »

    39 |
    40 |
    41 |

    Heading

    42 | 43 |

    Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et 44 | dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip 45 | ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu 46 | fugiat nulla pariatur.

    47 | 48 |

    Yii Extensions »

    49 |
    50 |
    51 | 52 |
    53 |
    54 | -------------------------------------------------------------------------------- /frontend/web/css/site.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | height: 100%; 4 | } 5 | 6 | .wrap { 7 | min-height: 100%; 8 | height: auto; 9 | margin: 0 auto -60px; 10 | padding: 0 0 60px; 11 | } 12 | 13 | .wrap > .container { 14 | padding: 70px 15px 20px; 15 | } 16 | 17 | .footer { 18 | height: 60px; 19 | background-color: #f5f5f5; 20 | border-top: 1px solid #ddd; 21 | padding-top: 20px; 22 | } 23 | 24 | .jumbotron { 25 | text-align: center; 26 | background-color: transparent; 27 | } 28 | 29 | .jumbotron .btn { 30 | font-size: 21px; 31 | padding: 14px 24px; 32 | } 33 | 34 | .not-set { 35 | color: #c55; 36 | font-style: italic; 37 | } 38 | 39 | /* add sorting icons to gridview sort links */ 40 | a.asc:after, a.desc:after { 41 | position: relative; 42 | top: 1px; 43 | display: inline-block; 44 | font-family: 'Glyphicons Halflings'; 45 | font-style: normal; 46 | font-weight: normal; 47 | line-height: 1; 48 | padding-left: 5px; 49 | } 50 | 51 | a.asc:after { 52 | content: "\e151"; 53 | } 54 | 55 | a.desc:after { 56 | content: "\e152"; 57 | } 58 | 59 | .sort-numerical a.asc:after { 60 | content: "\e153"; 61 | } 62 | 63 | .sort-numerical a.desc:after { 64 | content: "\e154"; 65 | } 66 | 67 | .sort-ordinal a.asc:after { 68 | content: "\e155"; 69 | } 70 | 71 | .sort-ordinal a.desc:after { 72 | content: "\e156"; 73 | } 74 | 75 | .grid-view td { 76 | white-space: nowrap; 77 | } 78 | 79 | .grid-view .filters input, 80 | .grid-view .filters select { 81 | min-width: 50px; 82 | } 83 | 84 | .hint-block { 85 | display: block; 86 | margin-top: 5px; 87 | color: #999; 88 | } 89 | 90 | .error-summary { 91 | color: #a94442; 92 | background: #fdf7f7; 93 | border-left: 3px solid #eed3d7; 94 | padding: 10px 20px; 95 | margin: 0 0 15px 0; 96 | } 97 | 98 | /* align the logout "link" (button in form) of the navbar */ 99 | .nav li > form > button.logout { 100 | padding: 15px; 101 | border: none; 102 | } 103 | 104 | @media(max-width:767px) { 105 | .nav li > form > button.logout { 106 | display:block; 107 | text-align: left; 108 | width: 100%; 109 | padding: 10px 15px; 110 | } 111 | } 112 | 113 | .nav > li > form > button.logout:focus, 114 | .nav > li > form > button.logout:hover { 115 | text-decoration: none; 116 | } 117 | 118 | .nav > li > form > button.logout:focus { 119 | outline: none; 120 | } 121 | -------------------------------------------------------------------------------- /backend/web/css/site.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | height: 100%; 4 | } 5 | 6 | .wrap { 7 | min-height: 100%; 8 | height: auto; 9 | margin: 0 auto -60px; 10 | padding: 0 0 60px; 11 | } 12 | 13 | .wrap > .container { 14 | padding: 70px 15px 20px; 15 | } 16 | 17 | .footer { 18 | height: 60px; 19 | background-color: #f5f5f5; 20 | border-top: 1px solid #ddd; 21 | padding-top: 20px; 22 | } 23 | 24 | .jumbotron { 25 | text-align: center; 26 | background-color: transparent; 27 | } 28 | 29 | .jumbotron .btn { 30 | font-size: 21px; 31 | padding: 14px 24px; 32 | } 33 | 34 | .not-set { 35 | color: #c55; 36 | font-style: italic; 37 | } 38 | 39 | /* add sorting icons to gridview sort links */ 40 | a.asc:after, a.desc:after { 41 | position: relative; 42 | top: 1px; 43 | display: inline-block; 44 | font-family: 'Glyphicons Halflings'; 45 | font-style: normal; 46 | font-weight: normal; 47 | line-height: 1; 48 | padding-left: 5px; 49 | } 50 | 51 | a.asc:after { 52 | content: /*"\e113"*/ "\e151"; 53 | } 54 | 55 | a.desc:after { 56 | content: /*"\e114"*/ "\e152"; 57 | } 58 | 59 | .sort-numerical a.asc:after { 60 | content: "\e153"; 61 | } 62 | 63 | .sort-numerical a.desc:after { 64 | content: "\e154"; 65 | } 66 | 67 | .sort-ordinal a.asc:after { 68 | content: "\e155"; 69 | } 70 | 71 | .sort-ordinal a.desc:after { 72 | content: "\e156"; 73 | } 74 | 75 | .grid-view td { 76 | white-space: nowrap; 77 | } 78 | 79 | .grid-view .filters input, 80 | .grid-view .filters select { 81 | min-width: 50px; 82 | } 83 | 84 | .hint-block { 85 | display: block; 86 | margin-top: 5px; 87 | color: #999; 88 | } 89 | 90 | .error-summary { 91 | color: #a94442; 92 | background: #fdf7f7; 93 | border-left: 3px solid #eed3d7; 94 | padding: 10px 20px; 95 | margin: 0 0 15px 0; 96 | } 97 | 98 | /* align the logout "link" (button in form) of the navbar */ 99 | .nav li > form > button.logout { 100 | padding: 15px; 101 | border: none; 102 | } 103 | 104 | @media(max-width:767px) { 105 | .nav li > form > button.logout { 106 | display:block; 107 | text-align: left; 108 | width: 100%; 109 | padding: 10px 15px; 110 | } 111 | } 112 | 113 | .nav > li > form > button.logout:focus, 114 | .nav > li > form > button.logout:hover { 115 | text-decoration: none; 116 | } 117 | 118 | .nav > li > form > button.logout:focus { 119 | outline: none; 120 | } 121 | -------------------------------------------------------------------------------- /common/widgets/Alert.php: -------------------------------------------------------------------------------- 1 | session->setFlash('error', 'This is the message'); 12 | * Yii::$app->session->setFlash('success', 'This is the message'); 13 | * Yii::$app->session->setFlash('info', 'This is the message'); 14 | * ``` 15 | * 16 | * Multiple messages could be set as follows: 17 | * 18 | * ```php 19 | * Yii::$app->session->setFlash('error', ['Error 1', 'Error 2']); 20 | * ``` 21 | * 22 | * @author Kartik Visweswaran 23 | * @author Alexander Makarov 24 | */ 25 | class Alert extends \yii\bootstrap\Widget 26 | { 27 | /** 28 | * @var array the alert types configuration for the flash messages. 29 | * This array is setup as $key => $value, where: 30 | * - key: the name of the session flash variable 31 | * - value: the bootstrap alert type (i.e. danger, success, info, warning) 32 | */ 33 | public $alertTypes = [ 34 | 'error' => 'alert-danger', 35 | 'danger' => 'alert-danger', 36 | 'success' => 'alert-success', 37 | 'info' => 'alert-info', 38 | 'warning' => 'alert-warning' 39 | ]; 40 | /** 41 | * @var array the options for rendering the close button tag. 42 | * Array will be passed to [[\yii\bootstrap\Alert::closeButton]]. 43 | */ 44 | public $closeButton = []; 45 | 46 | /** 47 | * {@inheritdoc} 48 | */ 49 | public function run() 50 | { 51 | $session = Yii::$app->session; 52 | $flashes = $session->getAllFlashes(); 53 | $appendClass = isset($this->options['class']) ? ' ' . $this->options['class'] : ''; 54 | 55 | foreach ($flashes as $type => $flash) { 56 | if (!isset($this->alertTypes[$type])) { 57 | continue; 58 | } 59 | 60 | foreach ((array) $flash as $i => $message) { 61 | echo \yii\bootstrap\Alert::widget([ 62 | 'body' => $message, 63 | 'closeButton' => $this->closeButton, 64 | 'options' => array_merge($this->options, [ 65 | 'id' => $this->getId() . '-' . $type . '-' . $i, 66 | 'class' => $this->alertTypes[$type] . $appendClass, 67 | ]), 68 | ]); 69 | } 70 | 71 | $session->removeFlash($type); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | require 'fileutils' 3 | 4 | domains = { 5 | frontend: 'y2aa-frontend.dev', 6 | backend: 'y2aa-backend.dev' 7 | } 8 | 9 | config = { 10 | local: './vagrant/config/vagrant-local.yml', 11 | example: './vagrant/config/vagrant-local.example.yml' 12 | } 13 | 14 | # copy config from example if local config not exists 15 | FileUtils.cp config[:example], config[:local] unless File.exist?(config[:local]) 16 | # read config 17 | options = YAML.load_file config[:local] 18 | 19 | # check github token 20 | if options['github_token'].nil? || options['github_token'].to_s.length != 40 21 | puts "You must place REAL GitHub token into configuration:\n/yii2-app-advanced/vagrant/config/vagrant-local.yml" 22 | exit 23 | end 24 | 25 | # vagrant configurate 26 | Vagrant.configure(2) do |config| 27 | # select the box 28 | config.vm.box = 'bento/ubuntu-16.04' 29 | 30 | # should we ask about box updates? 31 | config.vm.box_check_update = options['box_check_update'] 32 | 33 | config.vm.provider 'virtualbox' do |vb| 34 | # machine cpus count 35 | vb.cpus = options['cpus'] 36 | # machine memory size 37 | vb.memory = options['memory'] 38 | # machine name (for VirtualBox UI) 39 | vb.name = options['machine_name'] 40 | end 41 | 42 | # machine name (for vagrant console) 43 | config.vm.define options['machine_name'] 44 | 45 | # machine name (for guest machine console) 46 | config.vm.hostname = options['machine_name'] 47 | 48 | # network settings 49 | config.vm.network 'private_network', ip: options['ip'] 50 | 51 | # sync: folder 'yii2-app-advanced' (host machine) -> folder '/app' (guest machine) 52 | config.vm.synced_folder './', '/app', owner: 'vagrant', group: 'vagrant' 53 | 54 | # disable folder '/vagrant' (guest machine) 55 | config.vm.synced_folder '.', '/vagrant', disabled: true 56 | 57 | # hosts settings (host machine) 58 | config.vm.provision :hostmanager 59 | config.hostmanager.enabled = true 60 | config.hostmanager.manage_host = true 61 | config.hostmanager.ignore_private_ip = false 62 | config.hostmanager.include_offline = true 63 | config.hostmanager.aliases = domains.values 64 | 65 | # provisioners 66 | config.vm.provision 'shell', path: './vagrant/provision/once-as-root.sh', args: [options['timezone']] 67 | config.vm.provision 'shell', path: './vagrant/provision/once-as-vagrant.sh', args: [options['github_token']], privileged: false 68 | config.vm.provision 'shell', path: './vagrant/provision/always-as-root.sh', run: 'always' 69 | 70 | # post-install message (vagrant console) 71 | config.vm.post_up_message = "Frontend URL: http://#{domains[:frontend]}\nBackend URL: http://#{domains[:backend]}" 72 | end 73 | -------------------------------------------------------------------------------- /frontend/views/layouts/main.php: -------------------------------------------------------------------------------- 1 | 15 | beginPage() ?> 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | <?= Html::encode($this->title) ?> 24 | head() ?> 25 | 26 | 27 | beginBody() ?> 28 | 29 |
    30 | 'Yii2 RESTful API', 33 | 'brandUrl' => Yii::$app->homeUrl, 34 | 'options' => [ 35 | 'class' => 'navbar-inverse navbar-fixed-top', 36 | ], 37 | ]); 38 | $menuItems = [ 39 | ['label' => 'API 1.0 Documenation', 'url' => ['/site/index']], 40 | // ['label' => 'About', 'url' => ['/site/about']], 41 | // ['label' => 'Contact', 'url' => ['/site/contact']], 42 | ]; 43 | if (Yii::$app->user->isGuest) { 44 | $menuItems[] = ['label' => 'Signup', 'url' => ['/site/signup']]; 45 | $menuItems[] = ['label' => 'Login', 'url' => ['/site/login']]; 46 | } else { 47 | $menuItems[] = ['label' => 'Employees', 'url' => ['/employee/index']]; 48 | // $menuItems[] = ['label' => 'Users', 'url' => ['/user/index']]; 49 | 50 | $menuItems[] = '
  • ' 51 | . Html::beginForm(['/site/logout'], 'post') 52 | . Html::submitButton( 53 | 'Logout (' . Yii::$app->user->identity->username . ')', 54 | ['class' => 'btn btn-link logout'] 55 | ) 56 | . Html::endForm() 57 | . '
  • '; 58 | 59 | } 60 | echo Nav::widget([ 61 | 'options' => ['class' => 'navbar-nav navbar-right'], 62 | 'items' => $menuItems, 63 | ]); 64 | NavBar::end(); 65 | ?> 66 | 67 |
    68 | ['label'=>'','url'=>''], 70 | 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], 71 | ]) ?> 72 | 73 | 74 |
    75 |
    76 | 77 |
    78 |
    79 |

    © Yii2 RESTful API

    80 | 81 |

    82 |
    83 |
    84 | 85 | endBody() ?> 86 | 87 | 88 | endPage() ?> 89 | -------------------------------------------------------------------------------- /console/migrations/m170916_150649_adding_oauth2_tables.php: -------------------------------------------------------------------------------- 1 | db->createCommand($sql)->execute(); 79 | 80 | } 81 | 82 | /** 83 | * @inheritdoc 84 | */ 85 | public function safeDown() 86 | { 87 | // echo "m170916_150649_adding_oauth2_tables cannot be reverted.\n"; 88 | $this->dropTable('{{%access_tokens}}'); 89 | $this->dropTable('{{%authorization_codes}}'); 90 | 91 | //return false; 92 | } 93 | 94 | /* 95 | // Use up()/down() to run migration code without a transaction. 96 | public function up() 97 | { 98 | 99 | } 100 | 101 | public function down() 102 | { 103 | echo "m170916_150649_adding_oauth2_tables cannot be reverted.\n"; 104 | 105 | return false; 106 | } 107 | */ 108 | } 109 | -------------------------------------------------------------------------------- /backend/controllers/EmployeeController.php: -------------------------------------------------------------------------------- 1 | [ 26 | 'class' => Apiauth::className(), 27 | 'exclude' => [], 28 | 'callback'=>[] 29 | ], 30 | 'access' => [ 31 | 'class' => AccessControl::className(), 32 | 'only' => ['index'], 33 | 'rules' => [ 34 | [ 35 | 'actions' => [], 36 | 'allow' => true, 37 | 'roles' => ['?'], 38 | ], 39 | [ 40 | 'actions' => [ 41 | 'index' 42 | ], 43 | 'allow' => true, 44 | 'roles' => ['@'], 45 | ], 46 | [ 47 | 'actions' => [], 48 | 'allow' => true, 49 | 'roles' => ['*'], 50 | ], 51 | ], 52 | ], 53 | 'verbs' => [ 54 | 'class' => Verbcheck::className(), 55 | 'actions' => [ 56 | 'index' => ['GET', 'POST'], 57 | 'create' => ['POST'], 58 | 'update' => ['PUT'], 59 | 'view' => ['GET'], 60 | 'delete' => ['DELETE'] 61 | ], 62 | ], 63 | 64 | ]; 65 | } 66 | 67 | public function actionIndex() 68 | { 69 | $params = $this->request['search']; 70 | $response = Employee::search($params); 71 | Yii::$app->api->sendSuccessResponse($response['data'], $response['info']); 72 | } 73 | 74 | public function actionCreate() 75 | { 76 | 77 | $model = new Employee; 78 | $model->attributes = $this->request; 79 | 80 | if ($model->save()) { 81 | Yii::$app->api->sendSuccessResponse($model->attributes); 82 | } else { 83 | Yii::$app->api->sendFailedResponse($model->errors); 84 | } 85 | 86 | } 87 | 88 | public function actionUpdate($id) 89 | { 90 | 91 | $model = $this->findModel($id); 92 | $model->attributes = $this->request; 93 | 94 | if ($model->save()) { 95 | Yii::$app->api->sendSuccessResponse($model->attributes); 96 | } else { 97 | Yii::$app->api->sendFailedResponse($model->errors); 98 | } 99 | 100 | } 101 | 102 | public function actionView($id) 103 | { 104 | 105 | $model = $this->findModel($id); 106 | Yii::$app->api->sendSuccessResponse($model->attributes); 107 | } 108 | 109 | public function actionDelete($id) 110 | { 111 | 112 | $model = $this->findModel($id); 113 | $model->delete(); 114 | Yii::$app->api->sendSuccessResponse($model->attributes); 115 | } 116 | 117 | protected function findModel($id) 118 | { 119 | if (($model = Employee::findOne($id)) !== null) { 120 | return $model; 121 | } else { 122 | Yii::$app->api->sendFailedResponse("Invalid Record requested"); 123 | } 124 | } 125 | } -------------------------------------------------------------------------------- /backend/config/main.php: -------------------------------------------------------------------------------- 1 | 'app-backend', 11 | 'basePath' => dirname(__DIR__), 12 | 'controllerNamespace' => 'backend\controllers', 13 | 'bootstrap' => ['log'], 14 | 'modules' => [], 15 | 'components' => [ 16 | /* 'request' => [ 17 | 'csrfParam' => '_csrf-backend', 18 | ], */ 19 | 'request' => [ 20 | 'parsers' => [ 21 | 'application/json' => 'yii\web\JsonParser', 22 | ] 23 | ], 24 | 'response' => [ 25 | 'format' => yii\web\Response::FORMAT_JSON, 26 | 'charset' => 'UTF-8', 27 | // ... 28 | ], 29 | 'user' => [ 30 | 'identityClass' => 'common\models\User', 31 | 'enableAutoLogin' => true, 32 | 'identityCookie' => ['name' => '_identity-backend', 'httpOnly' => true], 33 | ], 34 | 'session' => [ 35 | // this is the name of the session cookie used for login on the backend 36 | 'name' => 'advanced-backend', 37 | ], 38 | 'log' => [ 39 | 'traceLevel' => YII_DEBUG ? 3 : 0, 40 | 'targets' => [ 41 | [ 42 | 'class' => 'yii\log\FileTarget', 43 | 'levels' => ['error', 'warning'], 44 | ], 45 | ], 46 | ], 47 | 'errorHandler' => [ 48 | 'errorAction' => 'site/error', 49 | ], 50 | 'urlManager' => [ 51 | 'class' => 'yii\web\UrlManager', 52 | 'enablePrettyUrl' => true, 53 | 'showScriptName' => false, 54 | 'rules' => [ 55 | '1/register'=>'site/register', 56 | '1/authorize'=>'site/authorize', 57 | '1/accesstoken'=>'site/accesstoken', 58 | '1/me'=>'site/me', 59 | '1/logout'=>'site/logout', 60 | 61 | 62 | '1/employees'=>'employee/index', 63 | '1/employees/view/'=>'employee/view', 64 | '1/employees/create'=>'employee/create', 65 | '1/employees/update/'=>'employee/update', 66 | '1/employees/delete/'=>'employee/delete', 67 | 68 | 69 | '/' => '/view', 70 | '//' => '/', 71 | '/' => '/', 72 | '//' => '//view', 73 | '///' => '//', 74 | // '//' => '//', 75 | ], 76 | 77 | ], 78 | 79 | 80 | /* 81 | 'urlManager' => [ 82 | 'enablePrettyUrl' => true, 83 | 'enableStrictParsing' => true, 84 | 'showScriptName' => false, 85 | 'rules' => [ 86 | ['class' => 'yii\rest\UrlRule', 'controller' => 'employee'], 87 | ], 88 | ], 89 | */ 90 | 91 | 92 | /* 93 | 'urlManager' => [ 94 | 'enablePrettyUrl' => true, 95 | 'showScriptName' => false, 96 | 'rules' => [ 97 | ], 98 | ], 99 | */ 100 | 101 | ], 102 | 'params' => $params, 103 | ]; 104 | -------------------------------------------------------------------------------- /backend/models/Employee.php: -------------------------------------------------------------------------------- 1 | 200], 37 | [['email'], 'string', 'max' => 100], 38 | ]; 39 | } 40 | 41 | /** 42 | * @inheritdoc 43 | */ 44 | public function attributeLabels() 45 | { 46 | return [ 47 | 'id' => 'ID', 48 | 'name' => 'Name', 49 | 'email' => 'Email', 50 | 'created_at' => 'Created At', 51 | 'updated_at' => 'Updated At', 52 | ]; 53 | } 54 | 55 | static public function search($params) 56 | { 57 | 58 | $page = Yii::$app->getRequest()->getQueryParam('page'); 59 | $limit = Yii::$app->getRequest()->getQueryParam('limit'); 60 | $order = Yii::$app->getRequest()->getQueryParam('order'); 61 | 62 | $search = Yii::$app->getRequest()->getQueryParam('search'); 63 | 64 | if(isset($search)){ 65 | $params=$search; 66 | } 67 | 68 | 69 | 70 | $limit = isset($limit) ? $limit : 10; 71 | $page = isset($page) ? $page : 1; 72 | 73 | 74 | $offset = ($page - 1) * $limit; 75 | 76 | $query = Employee::find() 77 | ->select(['id', 'name', 'email', 'created_at', 'updated_at']) 78 | ->asArray(true) 79 | ->limit($limit) 80 | ->offset($offset); 81 | 82 | if(isset($params['id'])) { 83 | $query->andFilterWhere(['id' => $params['id']]); 84 | } 85 | 86 | if(isset($params['created_at'])) { 87 | $query->andFilterWhere(['created_at' => $params['created_at']]); 88 | } 89 | if(isset($params['updated_at'])) { 90 | $query->andFilterWhere(['updated_at' => $params['updated_at']]); 91 | } 92 | if(isset($params['name'])) { 93 | $query->andFilterWhere(['like', 'name', $params['name']]); 94 | } 95 | if(isset($params['email'])){ 96 | $query->andFilterWhere(['like', 'email', $params['email']]); 97 | } 98 | 99 | 100 | if(isset($order)){ 101 | $query->orderBy($order); 102 | } 103 | 104 | 105 | $additional_info = [ 106 | 'page' => $page, 107 | 'size' => $limit, 108 | 'totalCount' => (int)$query->count() 109 | ]; 110 | 111 | return [ 112 | 'data' => $query->all(), 113 | 'info' => $additional_info 114 | ]; 115 | } 116 | 117 | public function beforeSave($insert) 118 | { 119 | 120 | if (parent::beforeSave($insert)) { 121 | 122 | if ($this->isNewRecord) { 123 | $this->created_at = date("Y-m-d H:i:s", time()); 124 | $this->updated_at = date("Y-m-d H:i:s", time()); 125 | 126 | } else { 127 | 128 | $this->updated_at = date("Y-m-d H:i:s", time()); 129 | } 130 | return true; 131 | } else { 132 | return false; 133 | } 134 | 135 | 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /frontend/controllers/UserController.php: -------------------------------------------------------------------------------- 1 | [ 25 | 'class' => VerbFilter::className(), 26 | 'actions' => [ 27 | 'delete' => ['post'], 28 | ], 29 | ], 30 | 'access' => [ 31 | 'class' => AccessControl::className(), 32 | 'only' => [], 33 | 'rules' => [ 34 | [ 35 | 'actions' => [], 36 | 'allow' => true, 37 | 'roles' => ['?'], 38 | ], 39 | [ 40 | 'actions' => ['create','index', 'update', 'view', 'delete'], 41 | 'allow' => true, 42 | 'roles' => ['@'], 43 | ], 44 | ], 45 | ], 46 | ]; 47 | } 48 | 49 | /** 50 | * Lists all User models. 51 | * @return mixed 52 | */ 53 | public function actionIndex() 54 | { 55 | $searchModel = new UserSearch(); 56 | $dataProvider = $searchModel->search(Yii::$app->request->queryParams); 57 | 58 | return $this->render('index', [ 59 | 'searchModel' => $searchModel, 60 | 'dataProvider' => $dataProvider, 61 | ]); 62 | } 63 | 64 | /** 65 | * Displays a single User model. 66 | * @param integer $id 67 | * @return mixed 68 | */ 69 | public function actionView($id) 70 | { 71 | return $this->render('view', [ 72 | 'model' => $this->findModel($id), 73 | ]); 74 | } 75 | 76 | /** 77 | * Creates a new User model. 78 | * If creation is successful, the browser will be redirected to the 'view' page. 79 | * @return mixed 80 | */ 81 | public function actionCreate() 82 | { 83 | exit; 84 | $model = new User(); 85 | 86 | if ($model->load(Yii::$app->request->post()) && $model->save()) { 87 | return $this->redirect(['view', 'id' => $model->id]); 88 | } 89 | 90 | return $this->render('create', [ 91 | 'model' => $model, 92 | ]); 93 | } 94 | 95 | /** 96 | * Updates an existing User model. 97 | * If update is successful, the browser will be redirected to the 'view' page. 98 | * @param integer $id 99 | * @return mixed 100 | */ 101 | public function actionUpdate($id) 102 | { 103 | exit; 104 | $model = $this->findModel($id); 105 | 106 | if ($model->load(Yii::$app->request->post()) && $model->save()) { 107 | return $this->redirect(['view', 'id' => $model->id]); 108 | } 109 | 110 | return $this->render('update', [ 111 | 'model' => $model, 112 | ]); 113 | } 114 | 115 | /** 116 | * Deletes an existing User model. 117 | * If deletion is successful, the browser will be redirected to the 'index' page. 118 | * @param integer $id 119 | * @return mixed 120 | */ 121 | public function actionDelete($id) 122 | { 123 | exit; 124 | $this->findModel($id)->delete(); 125 | 126 | return $this->redirect(['index']); 127 | } 128 | 129 | /** 130 | * Finds the User model based on its primary key value. 131 | * If the model is not found, a 404 HTTP exception will be thrown. 132 | * @param integer $id 133 | * @return User the loaded model 134 | * @throws NotFoundHttpException if the model cannot be found 135 | */ 136 | protected function findModel($id) 137 | { 138 | if (($model = User::findOne($id)) !== null) { 139 | return $model; 140 | } 141 | 142 | throw new NotFoundHttpException('The requested page does not exist.'); 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /frontend/controllers/EmployeeController.php: -------------------------------------------------------------------------------- 1 | [ 25 | 'class' => VerbFilter::className(), 26 | 'actions' => [ 27 | 'delete' => ['post'], 28 | ], 29 | ], 30 | 'access' => [ 31 | 'class' => AccessControl::className(), 32 | 'only' => [], 33 | 'rules' => [ 34 | [ 35 | 'actions' => [], 36 | 'allow' => false, 37 | 'roles' => ['?'], 38 | ], 39 | [ 40 | 'actions' => ['create', 'update', 'index', 'view', 'delete'], 41 | 'allow' => true, 42 | 'roles' => ['@'], 43 | ], 44 | ], 45 | ], 46 | ]; 47 | } 48 | 49 | /** 50 | * Lists all Employee models. 51 | * @return mixed 52 | */ 53 | public function actionIndex() 54 | { 55 | $searchModel = new EmployeeSearch(); 56 | $dataProvider = $searchModel->search(Yii::$app->request->queryParams); 57 | 58 | return $this->render('index', [ 59 | 'searchModel' => $searchModel, 60 | 'dataProvider' => $dataProvider, 61 | ]); 62 | } 63 | 64 | /** 65 | * Displays a single Employee model. 66 | * @param integer $id 67 | * @return mixed 68 | */ 69 | public function actionView($id) 70 | { 71 | return $this->render('view', [ 72 | 'model' => $this->findModel($id), 73 | ]); 74 | } 75 | 76 | /** 77 | * Creates a new Employee model. 78 | * If creation is successful, the browser will be redirected to the 'view' page. 79 | * @return mixed 80 | */ 81 | public function actionCreate() 82 | { 83 | $model = new Employee(); 84 | 85 | if ($model->load(Yii::$app->request->post()) && $model->save()) { 86 | return $this->redirect(['view', 'id' => $model->id]); 87 | } 88 | 89 | return $this->render('create', [ 90 | 'model' => $model, 91 | ]); 92 | } 93 | 94 | /** 95 | * Updates an existing Employee model. 96 | * If update is successful, the browser will be redirected to the 'view' page. 97 | * @param integer $id 98 | * @return mixed 99 | */ 100 | public function actionUpdate($id) 101 | { 102 | $model = $this->findModel($id); 103 | 104 | if ($model->load(Yii::$app->request->post()) && $model->save()) { 105 | return $this->redirect(['view', 'id' => $model->id]); 106 | } 107 | 108 | return $this->render('update', [ 109 | 'model' => $model, 110 | ]); 111 | } 112 | 113 | /** 114 | * Deletes an existing Employee model. 115 | * If deletion is successful, the browser will be redirected to the 'index' page. 116 | * @param integer $id 117 | * @return mixed 118 | */ 119 | public function actionDelete($id) 120 | { 121 | $this->findModel($id)->delete(); 122 | 123 | return $this->redirect(['index']); 124 | } 125 | 126 | /** 127 | * Finds the Employee model based on its primary key value. 128 | * If the model is not found, a 404 HTTP exception will be thrown. 129 | * @param integer $id 130 | * @return Employee the loaded model 131 | * @throws NotFoundHttpException if the model cannot be found 132 | */ 133 | protected function findModel($id) 134 | { 135 | if (($model = Employee::findOne($id)) !== null) { 136 | return $model; 137 | } 138 | 139 | throw new NotFoundHttpException('The requested page does not exist.'); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /backend/behaviours/Apiauth.php: -------------------------------------------------------------------------------- 1 | 18 | * @since 2.0 19 | */ 20 | class Apiauth extends AuthMethod 21 | { 22 | /** 23 | * @var string the parameter name for passing the access token 24 | */ 25 | public $tokenParam = 'access-token'; 26 | 27 | public $exclude = []; 28 | public $callback = []; 29 | 30 | 31 | /** 32 | * @inheritdoc 33 | */ 34 | public function authenticate($user, $request, $response) 35 | { 36 | $headers = Yii::$app->getRequest()->getHeaders(); 37 | 38 | $accessToken=NULL; 39 | if(isset($_GET['access_token'])){ 40 | $accessToken=$_GET['access_token']; 41 | }else { 42 | $accessToken = $headers->get('x-access_token'); 43 | } 44 | 45 | if(empty($accessToken)){ 46 | 47 | if(isset($_GET['access-token'])){ 48 | $accessToken=$_GET['access-token']; 49 | }else { 50 | $accessToken = $headers->get('x-access-token'); 51 | } 52 | } 53 | 54 | // $accessToken = $request->get($this->tokenParam); 55 | 56 | /* 57 | if(isset($_POST['access-token'])) { 58 | 59 | $accessToken = $_POST['access-token']; 60 | //echo $accessToken; 61 | //exit; 62 | } 63 | */ 64 | 65 | //echo $accessToken; 66 | //exit; 67 | 68 | 69 | 70 | /* 71 | if(isset($_SERVER['HTTP_X_ACCESS_TOKEN'])) { 72 | 73 | $accessToken=$_SERVER['HTTP_X_ACCESS_TOKEN']; 74 | } 75 | */ 76 | //echo "AT:".$accessToken; 77 | // exit; 78 | if (is_string($accessToken)) { 79 | $identity = $user->loginByAccessToken($accessToken, get_class($this)); 80 | if ($identity !== null) { 81 | return $identity; 82 | } 83 | } 84 | if ($accessToken !== null) { 85 | 86 | Yii::$app->api->sendFailedResponse('Invalid Access token'); 87 | 88 | // $this->handleFailure($response); 89 | } 90 | 91 | 92 | return null; 93 | } 94 | 95 | public function beforeAction($action) 96 | { 97 | //echo "okk"; 98 | //exit; 99 | 100 | 101 | if (in_array($action->id, $this->exclude)&& 102 | !isset($_GET['access-token'])) 103 | { 104 | //Yii::$app->api->sendFailedResponse("error1"); 105 | // Yii::$app->api->sendSuccessResponse(["nice1"]); 106 | // exit; 107 | return true; 108 | } 109 | 110 | //if (!$this->verifyApp()) 111 | // Yii::$app->api->sendFailedResponse('Invalid Request(App not verified)'); 112 | 113 | 114 | if (in_array($action->id, $this->callback)&& 115 | !isset($_GET['access-token'])) 116 | { 117 | //Yii::$app->api->sendFailedResponse("error1"); 118 | // Yii::$app->api->sendSuccessResponse(["nice1"]); 119 | // exit; 120 | return true; 121 | } 122 | 123 | 124 | 125 | $response = $this->response ?: Yii::$app->getResponse(); 126 | 127 | $identity = $this->authenticate( 128 | $this->user ?: Yii::$app->getUser(), 129 | $this->request ?: Yii::$app->getRequest(), 130 | $response 131 | ); 132 | 133 | if ($identity !== null) { 134 | return true; 135 | } else { 136 | $this->challenge($response); 137 | $this->handleFailure($response); 138 | 139 | Yii::$app->api->sendFailedResponse('Invalid Request'); 140 | //return false; 141 | } 142 | } 143 | 144 | /** 145 | * @inheritdoc 146 | */ 147 | public function handleFailure($response) 148 | { 149 | Yii::$app->api->sendFailedResponse('Invalid Access token'); 150 | //throw new UnauthorizedHttpException('You are requesting with an invalid credential.'); 151 | } 152 | 153 | } 154 | -------------------------------------------------------------------------------- /common/components/Api.php: -------------------------------------------------------------------------------- 1 | setHeader(400); 22 | 23 | echo json_encode(array('status' => 0, 'error_code' => 400, 'errors' => $message), JSON_PRETTY_PRINT); 24 | 25 | Yii::$app->end(); 26 | } 27 | 28 | public function sendSuccessResponse($data = false,$additional_info = false) 29 | { 30 | 31 | $this->setHeader(200); 32 | 33 | $response = []; 34 | $response['status'] = 1; 35 | 36 | if (is_array($data)) 37 | $response['data'] = $data; 38 | 39 | if ($additional_info) { 40 | $response = array_merge($response, $additional_info); 41 | } 42 | 43 | $response = Json::encode($response, JSON_PRETTY_PRINT); 44 | 45 | 46 | if (isset($_GET['callback'])) { 47 | /* this is required for angularjs1.0 client factory API calls to work */ 48 | $response = $_GET['callback'] . "(" . $response . ")"; 49 | 50 | echo $response; 51 | } else { 52 | echo $response; 53 | } 54 | 55 | Yii::$app->end(); 56 | 57 | } 58 | 59 | protected function setHeader($status) 60 | { 61 | 62 | $text = $this->_getStatusCodeMessage($status); 63 | 64 | Yii::$app->response->setStatusCode($status, $text); 65 | 66 | $status_header = 'HTTP/1.1 ' . $status . ' ' . $text; 67 | $content_type = "application/json; charset=utf-8"; 68 | 69 | 70 | header($status_header); 71 | header('Content-type: ' . $content_type); 72 | header('X-Powered-By: ' . "Your Company "); 73 | header('Access-Control-Allow-Origin:*'); 74 | 75 | 76 | } 77 | 78 | protected function _getStatusCodeMessage($status) 79 | { 80 | // these could be stored in a .ini file and loaded 81 | // via parse_ini_file()... however, this will suffice 82 | // for an example 83 | $codes = Array( 84 | 200 => 'OK', 85 | 400 => 'Bad Request', 86 | 401 => 'Unauthorized', 87 | 402 => 'Payment Required', 88 | 403 => 'Forbidden', 89 | 404 => 'Not Found', 90 | 500 => 'Internal Server Error', 91 | 501 => 'Not Implemented', 92 | ); 93 | return (isset($codes[$status])) ? $codes[$status] : ''; 94 | } 95 | 96 | public function createAuthorizationCode($user_id) 97 | { 98 | $model = new AuthorizationCodes; 99 | 100 | $model->code = md5(uniqid()); 101 | 102 | $model->expires_at = time() + (60 * 5); 103 | 104 | $model->user_id = $user_id; 105 | 106 | if (isset($_SERVER['HTTP_X_HAIKUJAM_APPLICATION_ID'])) 107 | $app_id = $_SERVER['HTTP_X_HAIKUJAM_APPLICATION_ID']; 108 | else 109 | $app_id = null; 110 | 111 | $model->app_id = $app_id; 112 | 113 | $model->created_at = time(); 114 | 115 | $model->updated_at = time(); 116 | 117 | $model->save(false); 118 | 119 | return ($model); 120 | 121 | } 122 | 123 | public function createAccesstoken($authorization_code) 124 | { 125 | 126 | $auth_code = AuthorizationCodes::findOne(['code' => $authorization_code]); 127 | 128 | $model = new AccessTokens(); 129 | 130 | $model->token = md5(uniqid()); 131 | 132 | $model->auth_code = $auth_code->code; 133 | 134 | $model->expires_at = time() + (60 * 60 * 24 * 60); // 60 days 135 | 136 | // $model->expires_at=time()+(60 * 2);// 2 minutes 137 | 138 | $model->user_id = $auth_code->user_id; 139 | 140 | $model->created_at = time(); 141 | 142 | $model->updated_at = time(); 143 | 144 | $model->save(false); 145 | 146 | return ($model); 147 | 148 | } 149 | 150 | public function refreshAccesstoken($token) 151 | { 152 | $access_token = AccessTokens::findOne(['token' => $token]); 153 | if ($access_token) { 154 | 155 | $access_token->delete(); 156 | $new_access_token = $this->createAccesstoken($access_token->auth_code); 157 | return ($new_access_token); 158 | } else { 159 | 160 | Yii::$app->api->sendFailedResponse("Invalid Access token2"); 161 | } 162 | } 163 | 164 | } 165 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

    2 | 3 | 4 | 5 |

    Yii 2 RESTful API with OAuth 2.0

    6 |
    7 |

    8 | 9 | This is a RESTful API with OAuth2 authentication/security developed using Yii2 framework. 10 | You can use this if you want to quick start developing your own custom RESTful API by skipping 95% of your scratch works. 11 | Hopefully this will save lot of your time as this API includes all the basic stuffs you need to get started. 12 | 13 | This API also includes a developer dashboard with the API documentation which is developed in Yii2. This will be useful to manage your developers access to the API documentation. 14 | 15 | [DEMO](http://developers.yii2.nintriva.net) 16 | ------------------- 17 | ``` 18 | http://developers.yii2.nintriva.net 19 | Login: developer/developer 20 | ``` 21 | 22 | ## Official Documentation 23 | 24 | Documentation for this RESTful API can be found on the [Yii2 RESTful API with OAuth2 Documenation](http://developers.yii2.nintriva.net/). 25 | 26 | ## Security Vulnerabilities 27 | 28 | If you discover a security vulnerability within this template, please send an e-mail to Sirin k at sirin@nintriva.com. All security vulnerabilities will be promptly addressed. 29 | 30 | ## License 31 | 32 | The Lumen framework is open-sourced software licensed under the [MIT license](http://opensource.org/licenses/MIT) 33 | 34 | 35 | 36 | INSTALLATION 37 | 38 | 39 | ``` 40 | Step1: Create a database named yii2_rest 41 | Step2:Clone the source code 42 | git clone -b master https://github.com/sirinibin/yii2-rest.git 43 | 44 | Step3: cd yii2-rest 45 | Step4:composer install 46 | Step5: ./init 47 | Step6: vim common/config/main-local.php 48 | change db information 49 | 'db' => [ 50 | 51 | 'class' => 'yii\db\Connection', 52 | 53 | 'dsn' => 'mysql:host=127.0.0.1;dbname=yii2_rest', 54 | 55 | 'username' => 'root', 56 | 57 | 'password' => '123', 58 | 59 | 'charset' => 'utf8', 60 | 61 | ], 62 | 63 | Step7: Run db migration 64 | cd /var/www/yii2-rest 65 | ./yii migrate 66 | 67 | Step8: 68 | point API end point URL to backend 69 | /var/www/yii2-rest/backend/web 70 | 71 | 72 | point frontend URL to frontend 73 | /var/www/yii2-rest/frontend/web 74 | ``` 75 | 76 | DIRECTORY STRUCTURE 77 | ------------------- 78 | 79 | ``` 80 | common 81 | config/ contains shared configurations 82 | mail/ contains view files for e-mails 83 | models/ contains model classes used in both backend and frontend 84 | tests/ contains tests for common classes 85 | console 86 | config/ contains console configurations 87 | controllers/ contains console controllers (commands) 88 | migrations/ contains database migrations 89 | models/ contains console-specific model classes 90 | runtime/ contains files generated during runtime 91 | backend 92 | assets/ contains application assets such as JavaScript and CSS 93 | config/ contains backend configurations 94 | controllers/ contains Web controller classes 95 | models/ contains backend-specific model classes 96 | runtime/ contains files generated during runtime 97 | tests/ contains tests for backend application 98 | views/ contains view files for the Web application 99 | web/ contains the entry script and Web resources 100 | frontend 101 | assets/ contains application assets such as JavaScript and CSS 102 | config/ contains frontend configurations 103 | controllers/ contains Web controller classes 104 | models/ contains frontend-specific model classes 105 | runtime/ contains files generated during runtime 106 | tests/ contains tests for frontend application 107 | views/ contains view files for the Web application 108 | web/ contains the entry script and Web resources 109 | widgets/ contains frontend widgets 110 | vendor/ contains dependent 3rd-party packages 111 | environments/ contains environment-based overrides 112 | ``` 113 | 114 | [![Latest Stable Version](https://poser.pugx.org/yiisoft/yii2-app-advanced/v/stable.png)](https://packagist.org/packages/yiisoft/yii2-app-advanced) 115 | [![Total Downloads](https://poser.pugx.org/yiisoft/yii2-app-advanced/downloads.png)](https://packagist.org/packages/yiisoft/yii2-app-advanced) 116 | [![Build Status](https://travis-ci.org/yiisoft/yii2-app-advanced.svg?branch=master)](https://travis-ci.org/yiisoft/yii2-app-advanced) 117 | -------------------------------------------------------------------------------- /requirements.php: -------------------------------------------------------------------------------- 1 | Error'; 18 | echo '

    The path to yii framework seems to be incorrect.

    '; 19 | echo '

    You need to install Yii framework via composer or adjust the framework path in file ' . basename(__FILE__) . '.

    '; 20 | echo '

    Please refer to the README on how to install Yii.

    '; 21 | } 22 | 23 | require_once $frameworkPath . '/requirements/YiiRequirementChecker.php'; 24 | $requirementsChecker = new YiiRequirementChecker(); 25 | 26 | $gdMemo = $imagickMemo = 'Either GD PHP extension with FreeType support or ImageMagick PHP extension with PNG support is required for image CAPTCHA.'; 27 | $gdOK = $imagickOK = false; 28 | 29 | if (extension_loaded('imagick')) { 30 | $imagick = new Imagick(); 31 | $imagickFormats = $imagick->queryFormats('PNG'); 32 | if (in_array('PNG', $imagickFormats)) { 33 | $imagickOK = true; 34 | } else { 35 | $imagickMemo = 'Imagick extension should be installed with PNG support in order to be used for image CAPTCHA.'; 36 | } 37 | } 38 | 39 | if (extension_loaded('gd')) { 40 | $gdInfo = gd_info(); 41 | if (!empty($gdInfo['FreeType Support'])) { 42 | $gdOK = true; 43 | } else { 44 | $gdMemo = 'GD extension should be installed with FreeType support in order to be used for image CAPTCHA.'; 45 | } 46 | } 47 | 48 | /** 49 | * Adjust requirements according to your application specifics. 50 | */ 51 | $requirements = array( 52 | // Database : 53 | array( 54 | 'name' => 'PDO extension', 55 | 'mandatory' => true, 56 | 'condition' => extension_loaded('pdo'), 57 | 'by' => 'All DB-related classes', 58 | ), 59 | array( 60 | 'name' => 'PDO SQLite extension', 61 | 'mandatory' => false, 62 | 'condition' => extension_loaded('pdo_sqlite'), 63 | 'by' => 'All DB-related classes', 64 | 'memo' => 'Required for SQLite database.', 65 | ), 66 | array( 67 | 'name' => 'PDO MySQL extension', 68 | 'mandatory' => false, 69 | 'condition' => extension_loaded('pdo_mysql'), 70 | 'by' => 'All DB-related classes', 71 | 'memo' => 'Required for MySQL database.', 72 | ), 73 | array( 74 | 'name' => 'PDO PostgreSQL extension', 75 | 'mandatory' => false, 76 | 'condition' => extension_loaded('pdo_pgsql'), 77 | 'by' => 'All DB-related classes', 78 | 'memo' => 'Required for PostgreSQL database.', 79 | ), 80 | // Cache : 81 | array( 82 | 'name' => 'Memcache extension', 83 | 'mandatory' => false, 84 | 'condition' => extension_loaded('memcache') || extension_loaded('memcached'), 85 | 'by' => 'MemCache', 86 | 'memo' => extension_loaded('memcached') ? 'To use memcached set MemCache::useMemcached to true.' : '' 87 | ), 88 | array( 89 | 'name' => 'APC extension', 90 | 'mandatory' => false, 91 | 'condition' => extension_loaded('apc'), 92 | 'by' => 'ApcCache', 93 | ), 94 | // CAPTCHA: 95 | array( 96 | 'name' => 'GD PHP extension with FreeType support', 97 | 'mandatory' => false, 98 | 'condition' => $gdOK, 99 | 'by' => 'Captcha', 100 | 'memo' => $gdMemo, 101 | ), 102 | array( 103 | 'name' => 'ImageMagick PHP extension with PNG support', 104 | 'mandatory' => false, 105 | 'condition' => $imagickOK, 106 | 'by' => 'Captcha', 107 | 'memo' => $imagickMemo, 108 | ), 109 | // PHP ini : 110 | 'phpExposePhp' => array( 111 | 'name' => 'Expose PHP', 112 | 'mandatory' => false, 113 | 'condition' => $requirementsChecker->checkPhpIniOff("expose_php"), 114 | 'by' => 'Security reasons', 115 | 'memo' => '"expose_php" should be disabled at php.ini', 116 | ), 117 | 'phpAllowUrlInclude' => array( 118 | 'name' => 'PHP allow url include', 119 | 'mandatory' => false, 120 | 'condition' => $requirementsChecker->checkPhpIniOff("allow_url_include"), 121 | 'by' => 'Security reasons', 122 | 'memo' => '"allow_url_include" should be disabled at php.ini', 123 | ), 124 | 'phpSmtp' => array( 125 | 'name' => 'PHP mail SMTP', 126 | 'mandatory' => false, 127 | 'condition' => strlen(ini_get('SMTP')) > 0, 128 | 'by' => 'Email sending', 129 | 'memo' => 'PHP mail SMTP server required', 130 | ), 131 | ); 132 | $requirementsChecker->checkYii()->check($requirements)->render(); 133 | -------------------------------------------------------------------------------- /backend/controllers/SiteController.php: -------------------------------------------------------------------------------- 1 | [ 29 | 'class' => Apiauth::className(), 30 | 'exclude' => ['authorize', 'register', 'accesstoken','index'], 31 | ], 32 | 'access' => [ 33 | 'class' => AccessControl::className(), 34 | 'only' => ['logout', 'signup'], 35 | 'rules' => [ 36 | [ 37 | 'actions' => ['signup'], 38 | 'allow' => true, 39 | 'roles' => ['?'], 40 | ], 41 | [ 42 | 'actions' => ['logout', 'me'], 43 | 'allow' => true, 44 | 'roles' => ['@'], 45 | ], 46 | [ 47 | 'actions' => ['authorize', 'register', 'accesstoken'], 48 | 'allow' => true, 49 | 'roles' => ['*'], 50 | ], 51 | ], 52 | ], 53 | 'verbs' => [ 54 | 'class' => Verbcheck::className(), 55 | 'actions' => [ 56 | 'logout' => ['GET'], 57 | 'authorize' => ['POST'], 58 | 'register' => ['POST'], 59 | 'accesstoken' => ['POST'], 60 | 'me' => ['GET'], 61 | ], 62 | ], 63 | ]; 64 | } 65 | 66 | 67 | /** 68 | * @inheritdoc 69 | */ 70 | public function actions() 71 | { 72 | return [ 73 | 'error' => [ 74 | 'class' => 'yii\web\ErrorAction', 75 | ], 76 | ]; 77 | } 78 | 79 | /** 80 | * Displays homepage. 81 | * 82 | * @return string 83 | */ 84 | public function actionIndex() 85 | { 86 | Yii::$app->api->sendSuccessResponse(['Yii2 RESTful API with OAuth2']); 87 | // return $this->render('index'); 88 | } 89 | 90 | public function actionRegister() 91 | { 92 | 93 | $model = new SignupForm(); 94 | $model->attributes = $this->request; 95 | 96 | if ($user = $model->signup()) { 97 | 98 | $data=$user->attributes; 99 | unset($data['auth_key']); 100 | unset($data['password_hash']); 101 | unset($data['password_reset_token']); 102 | 103 | Yii::$app->api->sendSuccessResponse($data); 104 | 105 | } 106 | 107 | } 108 | 109 | 110 | public function actionMe() 111 | { 112 | $data = Yii::$app->user->identity; 113 | $data = $data->attributes; 114 | unset($data['auth_key']); 115 | unset($data['password_hash']); 116 | unset($data['password_reset_token']); 117 | 118 | Yii::$app->api->sendSuccessResponse($data); 119 | } 120 | 121 | public function actionAccesstoken() 122 | { 123 | 124 | if (!isset($this->request["authorization_code"])) { 125 | Yii::$app->api->sendFailedResponse("Authorization code missing"); 126 | } 127 | 128 | $authorization_code = $this->request["authorization_code"]; 129 | 130 | $auth_code = AuthorizationCodes::isValid($authorization_code); 131 | if (!$auth_code) { 132 | Yii::$app->api->sendFailedResponse("Invalid Authorization Code"); 133 | } 134 | 135 | $accesstoken = Yii::$app->api->createAccesstoken($authorization_code); 136 | 137 | $data = []; 138 | $data['access_token'] = $accesstoken->token; 139 | $data['expires_at'] = $accesstoken->expires_at; 140 | Yii::$app->api->sendSuccessResponse($data); 141 | 142 | } 143 | 144 | public function actionAuthorize() 145 | { 146 | $model = new LoginForm(); 147 | 148 | $model->attributes = $this->request; 149 | 150 | 151 | if ($model->validate() && $model->login()) { 152 | 153 | $auth_code = Yii::$app->api->createAuthorizationCode(Yii::$app->user->identity['id']); 154 | 155 | $data = []; 156 | $data['authorization_code'] = $auth_code->code; 157 | $data['expires_at'] = $auth_code->expires_at; 158 | 159 | Yii::$app->api->sendSuccessResponse($data); 160 | } else { 161 | Yii::$app->api->sendFailedResponse($model->errors); 162 | } 163 | } 164 | 165 | public function actionLogout() 166 | { 167 | $headers = Yii::$app->getRequest()->getHeaders(); 168 | $access_token = $headers->get('x-access-token'); 169 | 170 | if(!$access_token){ 171 | $access_token = Yii::$app->getRequest()->getQueryParam('access-token'); 172 | } 173 | 174 | $model = AccessTokens::findOne(['token' => $access_token]); 175 | 176 | if ($model->delete()) { 177 | 178 | Yii::$app->api->sendSuccessResponse(["Logged Out Successfully"]); 179 | 180 | } else { 181 | Yii::$app->api->sendFailedResponse("Invalid Request"); 182 | } 183 | 184 | 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /common/models/User.php: -------------------------------------------------------------------------------- 1 | self::STATUS_ACTIVE], 55 | ['status', 'in', 'range' => [self::STATUS_ACTIVE, self::STATUS_DELETED]], 56 | ]; 57 | } 58 | 59 | /** 60 | * @inheritdoc 61 | */ 62 | public static function findIdentity($id) 63 | { 64 | return static::findOne(['id' => $id, 'status' => self::STATUS_ACTIVE]); 65 | } 66 | 67 | /** 68 | * @inheritdoc 69 | */ 70 | /* 71 | public static function findIdentityByAccessToken($token, $type = null) 72 | { 73 | throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.'); 74 | } 75 | */ 76 | 77 | public static function findIdentityByAccessToken($token, $type = null) 78 | { 79 | $access_token = AccessTokens::findOne(['token' => $token]); 80 | if ($access_token) { 81 | if ($access_token->expires_at < time()) { 82 | Yii::$app->api->sendFailedResponse('Access token expired'); 83 | } 84 | 85 | return static::findOne(['id' => $access_token->user_id]); 86 | } else { 87 | return (false); 88 | } 89 | //throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.'); 90 | } 91 | 92 | /** 93 | * Finds user by username 94 | * 95 | * @param string $username 96 | * @return static|null 97 | */ 98 | public static function findByUsername($username) 99 | { 100 | return static::findOne(['username' => $username, 'status' => self::STATUS_ACTIVE]); 101 | } 102 | 103 | /** 104 | * Finds user by password reset token 105 | * 106 | * @param string $token password reset token 107 | * @return static|null 108 | */ 109 | public static function findByPasswordResetToken($token) 110 | { 111 | if (!static::isPasswordResetTokenValid($token)) { 112 | return null; 113 | } 114 | 115 | return static::findOne([ 116 | 'password_reset_token' => $token, 117 | 'status' => self::STATUS_ACTIVE, 118 | ]); 119 | } 120 | 121 | /** 122 | * Finds out if password reset token is valid 123 | * 124 | * @param string $token password reset token 125 | * @return bool 126 | */ 127 | public static function isPasswordResetTokenValid($token) 128 | { 129 | if (empty($token)) { 130 | return false; 131 | } 132 | 133 | $timestamp = (int) substr($token, strrpos($token, '_') + 1); 134 | $expire = Yii::$app->params['user.passwordResetTokenExpire']; 135 | return $timestamp + $expire >= time(); 136 | } 137 | 138 | /** 139 | * @inheritdoc 140 | */ 141 | public function getId() 142 | { 143 | return $this->getPrimaryKey(); 144 | } 145 | 146 | /** 147 | * @inheritdoc 148 | */ 149 | public function getAuthKey() 150 | { 151 | return $this->auth_key; 152 | } 153 | 154 | /** 155 | * @inheritdoc 156 | */ 157 | public function validateAuthKey($authKey) 158 | { 159 | return $this->getAuthKey() === $authKey; 160 | } 161 | 162 | /** 163 | * Validates password 164 | * 165 | * @param string $password password to validate 166 | * @return bool if password provided is valid for current user 167 | */ 168 | public function validatePassword($password) 169 | { 170 | return Yii::$app->security->validatePassword($password, $this->password_hash); 171 | } 172 | 173 | /** 174 | * Generates password hash from password and sets it to the model 175 | * 176 | * @param string $password 177 | */ 178 | public function setPassword($password) 179 | { 180 | $this->password_hash = Yii::$app->security->generatePasswordHash($password); 181 | } 182 | 183 | /** 184 | * Generates "remember me" authentication key 185 | */ 186 | public function generateAuthKey() 187 | { 188 | $this->auth_key = Yii::$app->security->generateRandomString(); 189 | } 190 | 191 | /** 192 | * Generates new password reset token 193 | */ 194 | public function generatePasswordResetToken() 195 | { 196 | $this->password_reset_token = Yii::$app->security->generateRandomString() . '_' . time(); 197 | } 198 | 199 | /** 200 | * Removes password reset token 201 | */ 202 | public function removePasswordResetToken() 203 | { 204 | $this->password_reset_token = null; 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /frontend/controllers/SiteController.php: -------------------------------------------------------------------------------- 1 | [ 28 | 'class' => AccessControl::className(), 29 | 'only' => ['logout', 'signup','index'], 30 | 'rules' => [ 31 | [ 32 | 'actions' => ['signup'], 33 | 'allow' => true, 34 | 'roles' => ['?'], 35 | ], 36 | [ 37 | 'actions' => ['logout','index'], 38 | 'allow' => true, 39 | 'roles' => ['@'], 40 | ], 41 | ], 42 | ], 43 | 'verbs' => [ 44 | 'class' => VerbFilter::className(), 45 | 'actions' => [ 46 | 'logout' => ['post'], 47 | ], 48 | ], 49 | ]; 50 | } 51 | 52 | /** 53 | * @inheritdoc 54 | */ 55 | public function actions() 56 | { 57 | return [ 58 | 'error' => [ 59 | 'class' => 'yii\web\ErrorAction', 60 | ], 61 | 'captcha' => [ 62 | 'class' => 'yii\captcha\CaptchaAction', 63 | 'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null, 64 | ], 65 | ]; 66 | } 67 | 68 | /** 69 | * Displays homepage. 70 | * 71 | * @return mixed 72 | */ 73 | public function actionIndex() 74 | { 75 | if(YII_ENV=='dev') 76 | { 77 | $api_host=Yii::$app->params['dev_api_url']; 78 | } 79 | else if(YII_ENV=='prod') 80 | { 81 | $api_host=Yii::$app->params['prod_api_url']; 82 | } 83 | 84 | 85 | return $this->render('apidoc',['api_host'=>$api_host]); 86 | } 87 | 88 | /** 89 | * Logs in a user. 90 | * 91 | * @return mixed 92 | */ 93 | public function actionLogin() 94 | { 95 | if (!Yii::$app->user->isGuest) { 96 | return $this->goHome(); 97 | } 98 | 99 | $model = new LoginForm(); 100 | if ($model->load(Yii::$app->request->post()) && $model->login()) { 101 | return $this->goBack(); 102 | } else { 103 | return $this->render('login', [ 104 | 'model' => $model, 105 | ]); 106 | } 107 | } 108 | 109 | /** 110 | * Logs out the current user. 111 | * 112 | * @return mixed 113 | */ 114 | public function actionLogout() 115 | { 116 | Yii::$app->user->logout(); 117 | 118 | return $this->goHome(); 119 | } 120 | 121 | /** 122 | * Displays contact page. 123 | * 124 | * @return mixed 125 | */ 126 | public function actionContact() 127 | { 128 | $model = new ContactForm(); 129 | if ($model->load(Yii::$app->request->post()) && $model->validate()) { 130 | if ($model->sendEmail(Yii::$app->params['adminEmail'])) { 131 | Yii::$app->session->setFlash('success', 'Thank you for contacting us. We will respond to you as soon as possible.'); 132 | } else { 133 | Yii::$app->session->setFlash('error', 'There was an error sending your message.'); 134 | } 135 | 136 | return $this->refresh(); 137 | } else { 138 | return $this->render('contact', [ 139 | 'model' => $model, 140 | ]); 141 | } 142 | } 143 | 144 | /** 145 | * Displays about page. 146 | * 147 | * @return mixed 148 | */ 149 | public function actionAbout() 150 | { 151 | return $this->render('about'); 152 | } 153 | 154 | /** 155 | * Signs user up. 156 | * 157 | * @return mixed 158 | */ 159 | public function actionSignup() 160 | { 161 | $model = new SignupForm(); 162 | if ($model->load(Yii::$app->request->post())) { 163 | if ($user = $model->signup()) { 164 | if (Yii::$app->getUser()->login($user)) { 165 | return $this->goHome(); 166 | } 167 | } 168 | } 169 | 170 | return $this->render('signup', [ 171 | 'model' => $model, 172 | ]); 173 | } 174 | 175 | /** 176 | * Requests password reset. 177 | * 178 | * @return mixed 179 | */ 180 | public function actionRequestPasswordReset() 181 | { 182 | $model = new PasswordResetRequestForm(); 183 | if ($model->load(Yii::$app->request->post()) && $model->validate()) { 184 | if ($model->sendEmail()) { 185 | Yii::$app->session->setFlash('success', 'Check your email for further instructions.'); 186 | 187 | return $this->goHome(); 188 | } else { 189 | Yii::$app->session->setFlash('error', 'Sorry, we are unable to reset password for the provided email address.'); 190 | } 191 | } 192 | 193 | return $this->render('requestPasswordResetToken', [ 194 | 'model' => $model, 195 | ]); 196 | } 197 | 198 | /** 199 | * Resets password. 200 | * 201 | * @param string $token 202 | * @return mixed 203 | * @throws BadRequestHttpException 204 | */ 205 | public function actionResetPassword($token) 206 | { 207 | try { 208 | $model = new ResetPasswordForm($token); 209 | } catch (InvalidParamException $e) { 210 | throw new BadRequestHttpException($e->getMessage()); 211 | } 212 | 213 | if ($model->load(Yii::$app->request->post()) && $model->validate() && $model->resetPassword()) { 214 | Yii::$app->session->setFlash('success', 'New password saved.'); 215 | 216 | return $this->goHome(); 217 | } 218 | 219 | return $this->render('resetPassword', [ 220 | 'model' => $model, 221 | ]); 222 | } 223 | } 224 | --------------------------------------------------------------------------------