├── tests ├── _data │ └── .gitkeep ├── _output │ └── .gitignore ├── acceptance │ ├── _bootstrap.php │ ├── AboutCest.php │ ├── HomeCest.php │ ├── LoginCest.php │ └── ContactCest.php ├── functional │ ├── _bootstrap.php │ ├── LoginFormCest.php │ └── ContactFormCest.php ├── unit │ ├── _bootstrap.php │ └── models │ │ ├── UserTest.php │ │ ├── LoginFormTest.php │ │ └── ContactFormTest.php ├── _bootstrap.php ├── unit.suite.yml ├── acceptance.suite.yml.example ├── functional.suite.yml ├── bin │ ├── yii.bat │ └── yii └── _support │ ├── FunctionalTester.php │ ├── UnitTester.php │ └── AcceptanceTester.php ├── robots.txt ├── runtime └── .gitignore ├── assets └── .gitignore ├── .bowerrc ├── favicon.ico ├── config ├── params.php ├── test_db.php ├── db.php ├── test.php ├── console.php └── web.php ├── index.php ├── views ├── site │ ├── about.php │ ├── error.php │ ├── login.php │ ├── index.php │ └── contact.php └── layouts │ └── main.php ├── .gitignore ├── index-test.php ├── yii.bat ├── yii ├── assets_b ├── AppAsset.php └── css │ └── site.css ├── mail └── layouts │ └── html.php ├── commands └── HelloController.php ├── codeception.yml ├── CHANGE.md ├── LICENSE.md ├── models ├── ContactForm.php ├── LoginForm.php └── User.php ├── .htaccess ├── composer.json ├── controllers └── SiteController.php ├── requirements.php └── README.md /tests/_data/.gitkeep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: -------------------------------------------------------------------------------- /runtime/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /assets/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /tests/_output/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore -------------------------------------------------------------------------------- /tests/acceptance/_bootstrap.php: -------------------------------------------------------------------------------- 1 | 'admin@example.com', 5 | ]; 6 | -------------------------------------------------------------------------------- /tests/_bootstrap.php: -------------------------------------------------------------------------------- 1 | 'yii\db\Connection', 5 | 'dsn' => 'mysql:host=localhost;dbname=yii2practicalb', 6 | 'username' => 'root', 7 | 'password' => '', 8 | 'charset' => 'utf8', 9 | ]; 10 | -------------------------------------------------------------------------------- /tests/acceptance/AboutCest.php: -------------------------------------------------------------------------------- 1 | amOnPage(Url::toRoute('/site/about')); 9 | $I->see('About', 'h1'); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/unit.suite.yml: -------------------------------------------------------------------------------- 1 | # Codeception Test Suite Configuration 2 | 3 | # suite for unit (internal) tests. 4 | # RUN `build` COMMAND AFTER ADDING/REMOVING MODULES. 5 | 6 | class_name: UnitTester 7 | modules: 8 | enabled: 9 | - Asserts 10 | - Yii2: 11 | part: [orm, email] -------------------------------------------------------------------------------- /tests/acceptance.suite.yml.example: -------------------------------------------------------------------------------- 1 | class_name: AcceptanceTester 2 | modules: 3 | enabled: 4 | - WebDriver: 5 | url: http://127.0.0.1:8080/ 6 | browser: firefox 7 | - Yii2: 8 | part: orm 9 | entryScript: index-test.php 10 | cleanup: false 11 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | run(); -------------------------------------------------------------------------------- /tests/functional.suite.yml: -------------------------------------------------------------------------------- 1 | # Codeception Test Suite Configuration 2 | 3 | # suite for functional (integration) tests. 4 | # emulate web requests and make application process them. 5 | # (tip: better to use with frameworks). 6 | 7 | # RUN `build` COMMAND AFTER ADDING/REMOVING MODULES. 8 | #basic/web/index.php 9 | class_name: FunctionalTester 10 | modules: 11 | enabled: 12 | - Filesystem 13 | - Yii2 14 | -------------------------------------------------------------------------------- /views/site/about.php: -------------------------------------------------------------------------------- 1 | title = 'About'; 8 | $this->params['breadcrumbs'][] = $this->title; 9 | ?> 10 |
11 |

title) ?>

12 | 13 |

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

16 | 17 | 18 |
19 | -------------------------------------------------------------------------------- /tests/acceptance/HomeCest.php: -------------------------------------------------------------------------------- 1 | amOnPage(Url::toRoute('/site/index')); 9 | $I->see('My Company'); 10 | 11 | $I->seeLink('About'); 12 | $I->click('About'); 13 | $I->wait(2); // wait for page to be opened 14 | 15 | $I->see('This is the About page.'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # phpstorm project files 2 | .idea 3 | 4 | # netbeans project files 5 | nbproject 6 | 7 | # zend studio for eclipse project files 8 | .buildpath 9 | .project 10 | .settings 11 | 12 | # windows thumbnail cache 13 | Thumbs.db 14 | 15 | # composer vendor dir 16 | /vendor 17 | 18 | # composer itself is not needed 19 | composer.phar 20 | 21 | # Mac DS_Store Files 22 | .DS_Store 23 | 24 | # phpunit itself is not needed 25 | phpunit.phar 26 | # local phpunit config 27 | /phpunit.xml 28 | 29 | tests/_output/* 30 | tests/_support/_generated -------------------------------------------------------------------------------- /index-test.php: -------------------------------------------------------------------------------- 1 | run(); 17 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/bin/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 | -------------------------------------------------------------------------------- /yii: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | run(); 21 | exit($exitCode); 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/acceptance/LoginCest.php: -------------------------------------------------------------------------------- 1 | amOnPage(Url::toRoute('/site/login')); 9 | $I->see('Login', 'h1'); 10 | 11 | $I->amGoingTo('try to login with correct credentials'); 12 | $I->fillField('input[name="LoginForm[username]"]', 'admin'); 13 | $I->fillField('input[name="LoginForm[password]"]', 'admin'); 14 | $I->click('login-button'); 15 | $I->wait(2); // wait for button to be clicked 16 | 17 | $I->expectTo('see user info'); 18 | $I->see('Logout'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tests/_support/FunctionalTester.php: -------------------------------------------------------------------------------- 1 | 14 | * @author Kartik Visweswaran 15 | * @since 2.0 16 | */ 17 | class AppAsset extends AssetBundle 18 | { 19 | public $basePath = '@webroot'; 20 | public $baseUrl = '@web/assets_b'; 21 | public $css = [ 22 | 'css/site.css', 23 | ]; 24 | public $js = [ 25 | ]; 26 | public $depends = [ 27 | 'yii\web\YiiAsset', 28 | 'yii\bootstrap\BootstrapAsset', 29 | ]; 30 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /tests/bin/yii: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | [ 21 | 'db' => require(__DIR__ . '/../../config/test_db.php') 22 | ] 23 | ] 24 | ); 25 | 26 | 27 | $application = new yii\console\Application($config); 28 | $exitCode = $application->run(); 29 | exit($exitCode); -------------------------------------------------------------------------------- /commands/HelloController.php: -------------------------------------------------------------------------------- 1 | 18 | * @since 2.0 19 | */ 20 | class HelloController extends Controller 21 | { 22 | /** 23 | * This command echoes what you have entered as the message. 24 | * @param string $message the message to be echoed. 25 | */ 26 | public function actionIndex($message = 'hello world') 27 | { 28 | echo $message . "\n"; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /codeception.yml: -------------------------------------------------------------------------------- 1 | actor: Tester 2 | paths: 3 | tests: tests 4 | log: tests/_output 5 | data: tests/_data 6 | helpers: tests/_support 7 | settings: 8 | bootstrap: _bootstrap.php 9 | memory_limit: 1024M 10 | colors: true 11 | modules: 12 | config: 13 | Yii2: 14 | configFile: 'config/test.php' 15 | cleanup: false 16 | 17 | # To enable code coverage: 18 | #coverage: 19 | # #c3_url: http://localhost:8080/index-test.php/ 20 | # enabled: true 21 | # #remote: true 22 | # #remote_config: '../codeception.yml' 23 | # whitelist: 24 | # include: 25 | # - models/* 26 | # - controllers/* 27 | # - commands/* 28 | # - mail/* 29 | # blacklist: 30 | # include: 31 | # - assets/* 32 | # - config/* 33 | # - runtime/* 34 | # - vendor/* 35 | # - views/* 36 | # - web/* 37 | # - tests/* 38 | -------------------------------------------------------------------------------- /config/test.php: -------------------------------------------------------------------------------- 1 | 'practical-b-tests', 10 | 'basePath' => dirname(__DIR__), 11 | 'language' => 'en-US', 12 | 'components' => [ 13 | 'db' => $db, 14 | 'mailer' => [ 15 | 'useFileTransport' => true, 16 | ], 17 | 'assetManager' => [ 18 | 'basePath' => __DIR__ . '/../web/assets', 19 | ], 20 | 'urlManager' => [ 21 | 'showScriptName' => true, 22 | ], 23 | 'user' => [ 24 | 'identityClass' => 'app\models\User', 25 | ], 26 | 'request' => [ 27 | 'cookieValidationKey' => 'test', 28 | 'enableCsrfValidation' => false, 29 | // but if you absolutely need it set cookie domain to localhost 30 | /* 31 | 'csrfCookie' => [ 32 | 'domain' => 'localhost', 33 | ], 34 | */ 35 | ], 36 | ], 37 | 'params' => $params, 38 | ]; 39 | -------------------------------------------------------------------------------- /tests/acceptance/ContactCest.php: -------------------------------------------------------------------------------- 1 | amOnPage(Url::toRoute('/site/contact')); 10 | } 11 | 12 | public function contactPageWorks(AcceptanceTester $I) 13 | { 14 | $I->wantTo('ensure that contact page works'); 15 | $I->see('Contact', 'h1'); 16 | } 17 | 18 | public function contactFormCanBeSubmitted(AcceptanceTester $I) 19 | { 20 | $I->amGoingTo('submit contact form with correct data'); 21 | $I->fillField('#contactform-name', 'tester'); 22 | $I->fillField('#contactform-email', 'tester@example.com'); 23 | $I->fillField('#contactform-subject', 'test subject'); 24 | $I->fillField('#contactform-body', 'test content'); 25 | $I->fillField('#contactform-verifycode', 'testme'); 26 | 27 | $I->click('contact-button'); 28 | 29 | $I->wait(2); // wait for button to be clicked 30 | 31 | $I->dontSeeElement('#contact-form'); 32 | $I->see('Thank you for contacting us. We will respond to you as soon as possible.'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /config/console.php: -------------------------------------------------------------------------------- 1 | 'practical-b-console', 8 | 'basePath' => dirname(__DIR__), 9 | 'bootstrap' => ['log'], 10 | 'controllerNamespace' => 'app\commands', 11 | 'components' => [ 12 | 'cache' => [ 13 | 'class' => 'yii\caching\FileCache', 14 | ], 15 | 'log' => [ 16 | 'targets' => [ 17 | [ 18 | 'class' => 'yii\log\FileTarget', 19 | 'levels' => ['error', 'warning'], 20 | ], 21 | ], 22 | ], 23 | 'db' => $db, 24 | ], 25 | 'params' => $params, 26 | /* 27 | 'controllerMap' => [ 28 | 'fixture' => [ // Fixture generation command line. 29 | 'class' => 'yii\faker\FixtureController', 30 | ], 31 | ], 32 | */ 33 | ]; 34 | 35 | if (YII_ENV_DEV) { 36 | // configuration adjustments for 'dev' environment 37 | $config['bootstrap'][] = 'gii'; 38 | $config['modules']['gii'] = [ 39 | 'class' => 'yii\gii\Module', 40 | ]; 41 | } 42 | 43 | return $config; 44 | -------------------------------------------------------------------------------- /tests/unit/models/UserTest.php: -------------------------------------------------------------------------------- 1 | username)->equals('admin'); 11 | 12 | expect_not(User::findIdentity(999)); 13 | } 14 | 15 | public function testFindUserByAccessToken() 16 | { 17 | expect_that($user = User::findIdentityByAccessToken('100-token')); 18 | expect($user->username)->equals('admin'); 19 | 20 | expect_not(User::findIdentityByAccessToken('non-existing')); 21 | } 22 | 23 | public function testFindUserByUsername() 24 | { 25 | expect_that($user = User::findByUsername('admin')); 26 | expect_not(User::findByUsername('not-admin')); 27 | } 28 | 29 | /** 30 | * @depends testFindUserByUsername 31 | */ 32 | public function testValidateUser($user) 33 | { 34 | $user = User::findByUsername('admin'); 35 | expect_that($user->validateAuthKey('test100key')); 36 | expect_not($user->validateAuthKey('test102key')); 37 | 38 | expect_that($user->validatePassword('admin')); 39 | expect_not($user->validatePassword('123456')); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /tests/unit/models/LoginFormTest.php: -------------------------------------------------------------------------------- 1 | user->logout(); 15 | } 16 | 17 | public function testLoginNoUser() 18 | { 19 | $this->model = new LoginForm([ 20 | 'username' => 'not_existing_username', 21 | 'password' => 'not_existing_password', 22 | ]); 23 | 24 | expect_not($this->model->login()); 25 | expect_that(\Yii::$app->user->isGuest); 26 | } 27 | 28 | public function testLoginWrongPassword() 29 | { 30 | $this->model = new LoginForm([ 31 | 'username' => 'demo', 32 | 'password' => 'wrong_password', 33 | ]); 34 | 35 | expect_not($this->model->login()); 36 | expect_that(\Yii::$app->user->isGuest); 37 | expect($this->model->errors)->hasKey('password'); 38 | } 39 | 40 | public function testLoginCorrect() 41 | { 42 | $this->model = new LoginForm([ 43 | 'username' => 'demo', 44 | 'password' => 'demo', 45 | ]); 46 | 47 | expect_that($this->model->login()); 48 | expect_not(\Yii::$app->user->isGuest); 49 | expect($this->model->errors)->hasntKey('password'); 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /tests/unit/models/ContactFormTest.php: -------------------------------------------------------------------------------- 1 | model = $this->getMockBuilder('app\models\ContactForm') 19 | ->setMethods(['validate']) 20 | ->getMock(); 21 | 22 | $this->model->expects($this->once()) 23 | ->method('validate') 24 | ->will($this->returnValue(true)); 25 | 26 | $this->model->attributes = [ 27 | 'name' => 'Tester', 28 | 'email' => 'tester@example.com', 29 | 'subject' => 'very important letter subject', 30 | 'body' => 'body of current message', 31 | ]; 32 | 33 | expect_that($this->model->contact('admin@example.com')); 34 | 35 | // using Yii2 module actions to check email was sent 36 | $this->tester->seeEmailIsSent(); 37 | 38 | $emailMessage = $this->tester->grabLastSentEmail(); 39 | expect('valid email is sent', $emailMessage)->isInstanceOf('yii\mail\MessageInterface'); 40 | expect($emailMessage->getTo())->hasKey('admin@example.com'); 41 | expect($emailMessage->getFrom())->hasKey('tester@example.com'); 42 | expect($emailMessage->getSubject())->equals('very important letter subject'); 43 | expect($emailMessage->toString())->contains('body of current message'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /CHANGE.md: -------------------------------------------------------------------------------- 1 | Change Log: `yii2-app-practical-b` 2 | ================================== 3 | 4 | ## Version 1.6.3 5 | 6 | **Date:** 2017-06-04 7 | 8 | - Update to latest yii2-basic-app as of 04-Jun-2017. 9 | 10 | ## Version 1.6.2 11 | 12 | **Date:** 2016-04-08 13 | 14 | - Updates to `composer.json` and `.gitignore`. 15 | - Update to latest yii2-basic-app as of 08-Apr-2016. 16 | 17 | ## Version 1.6.1 18 | 19 | **Date:** 2015-07-09 20 | 21 | - (enh #8): Update to latest yii2-basic-app as of 09-Jul-2015. 22 | - (enh #7): Improve security for .htaccess. 23 | 24 | ## Version 1.6.0 25 | 26 | **Date:** 2014-12-19 27 | 28 | - Update to latest yii2-basic-app as of 19-Dec-2014. 29 | 30 | 31 | ## Version 1.5.0 32 | 33 | **Date:** 2014-11-22 34 | 35 | - (bug # 5): Correct loading of site.css and app assets. 36 | - Update to latest yii2-basic-app as of 22-Nov-2014. 37 | - Set release to stable. 38 | 39 | ## Version 1.4.0 40 | 41 | **Date:** 2014-11-04 42 | 43 | - (enh # 4): Set write permissions for assets folder. 44 | 45 | ## Version 1.3.0 46 | 47 | **Date:** 2014-10-14 48 | 49 | - Update to latest yii2-basic-app as of 14-Oct-2014. 50 | - (enh # 3): Easier AppAsset baseUrl setting 51 | 52 | 53 | ## Version 1.2.0 54 | 55 | **Date:** 2014-09-09 56 | 57 | - Update to latest yii2-basic-app as of 09-Sep-2014. 58 | - (enh #2): PrettyURL enabled by default 59 | 60 | ## Version 1.1.0 61 | 62 | **Date:** 2014-06-30 63 | 64 | - Update to latest yii2-basic-app as of 30-Jun-2014. 65 | 66 | ## Version 1.0.0 67 | 68 | **Date:** 2014-06-01 69 | 70 | - Update to latest yii2-basic-app as of 31-May-2014. -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | * @param string $email the target email address 48 | * @return bool whether the model passes validation 49 | */ 50 | public function contact($email) 51 | { 52 | if ($this->validate()) { 53 | 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 | return true; 61 | } 62 | return false; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /views/site/login.php: -------------------------------------------------------------------------------- 1 | title = 'Login'; 11 | $this->params['breadcrumbs'][] = $this->title; 12 | ?> 13 | 48 | -------------------------------------------------------------------------------- /tests/functional/LoginFormCest.php: -------------------------------------------------------------------------------- 1 | amOnRoute('site/login'); 7 | } 8 | 9 | public function openLoginPage(\FunctionalTester $I) 10 | { 11 | $I->see('Login', 'h1'); 12 | 13 | } 14 | 15 | // demonstrates `amLoggedInAs` method 16 | public function internalLoginById(\FunctionalTester $I) 17 | { 18 | $I->amLoggedInAs(100); 19 | $I->amOnPage('/'); 20 | $I->see('Logout (admin)'); 21 | } 22 | 23 | // demonstrates `amLoggedInAs` method 24 | public function internalLoginByInstance(\FunctionalTester $I) 25 | { 26 | $I->amLoggedInAs(\app\models\User::findByUsername('admin')); 27 | $I->amOnPage('/'); 28 | $I->see('Logout (admin)'); 29 | } 30 | 31 | public function loginWithEmptyCredentials(\FunctionalTester $I) 32 | { 33 | $I->submitForm('#login-form', []); 34 | $I->expectTo('see validations errors'); 35 | $I->see('Username cannot be blank.'); 36 | $I->see('Password cannot be blank.'); 37 | } 38 | 39 | public function loginWithWrongCredentials(\FunctionalTester $I) 40 | { 41 | $I->submitForm('#login-form', [ 42 | 'LoginForm[username]' => 'admin', 43 | 'LoginForm[password]' => 'wrong', 44 | ]); 45 | $I->expectTo('see validations errors'); 46 | $I->see('Incorrect username or password.'); 47 | } 48 | 49 | public function loginSuccessfully(\FunctionalTester $I) 50 | { 51 | $I->submitForm('#login-form', [ 52 | 'LoginForm[username]' => 'admin', 53 | 'LoginForm[password]' => 'admin', 54 | ]); 55 | $I->see('Logout (admin)'); 56 | $I->dontSeeElement('form#login-form'); 57 | } 58 | } -------------------------------------------------------------------------------- /.htaccess: -------------------------------------------------------------------------------- 1 | # ---------------------------------------------------------------------- 2 | # Adds some security for the Apache server configuration for use with 3 | # https://github.com/kartik-v/yii2-app-practical-b template. 4 | # @author Kartik Visweswaran 5 | # @see http://demos.krajee.com/app-practical-b 6 | # ---------------------------------------------------------------------- 7 | 8 | # "-Indexes" will have Apache block users from browsing folders without a default document 9 | # Usually you should leave this activated, because you shouldn't allow everybody to surf through 10 | # every folder on your server (which includes rather private places like CMS system folders). 11 | 12 | Options -Indexes 13 | 14 | 15 | 16 | # Block access to "hidden" directories whose names begin with a period. This 17 | # includes directories used by version control systems such as Subversion or Git. 18 | 19 | RewriteCond %{SCRIPT_FILENAME} -d 20 | RewriteCond %{SCRIPT_FILENAME} -f 21 | RewriteRule "(^|/)\." - [F] 22 | 23 | 24 | 25 | # Block access to backup and source files 26 | # These files may be left by some text/html editors and 27 | # pose a great security danger, when someone can access them 28 | 29 | Order allow,deny 30 | Deny from all 31 | Satisfy All 32 | 33 | 34 | # Increase cookie security 35 | 36 | php_value session.cookie_httponly true 37 | 38 | 39 | # Settings to hide index.php and ensure pretty urls 40 | RewriteEngine on 41 | 42 | # if a directory or a file exists, use it directly 43 | RewriteCond %{REQUEST_FILENAME} !-f 44 | RewriteCond %{REQUEST_FILENAME} !-d 45 | 46 | # otherwise forward it to index.php 47 | RewriteRule . index.php -------------------------------------------------------------------------------- /tests/functional/ContactFormCest.php: -------------------------------------------------------------------------------- 1 | amOnPage(['site/contact']); 7 | } 8 | 9 | public function openContactPage(\FunctionalTester $I) 10 | { 11 | $I->see('Contact', 'h1'); 12 | } 13 | 14 | public function submitEmptyForm(\FunctionalTester $I) 15 | { 16 | $I->submitForm('#contact-form', []); 17 | $I->expectTo('see validations errors'); 18 | $I->see('Contact', 'h1'); 19 | $I->see('Name cannot be blank'); 20 | $I->see('Email cannot be blank'); 21 | $I->see('Subject cannot be blank'); 22 | $I->see('Body cannot be blank'); 23 | $I->see('The verification code is incorrect'); 24 | } 25 | 26 | public function submitFormWithIncorrectEmail(\FunctionalTester $I) 27 | { 28 | $I->submitForm('#contact-form', [ 29 | 'ContactForm[name]' => 'tester', 30 | 'ContactForm[email]' => 'tester.email', 31 | 'ContactForm[subject]' => 'test subject', 32 | 'ContactForm[body]' => 'test content', 33 | 'ContactForm[verifyCode]' => 'testme', 34 | ]); 35 | $I->expectTo('see that email address is wrong'); 36 | $I->dontSee('Name cannot be blank', '.help-inline'); 37 | $I->see('Email is not a valid email address.'); 38 | $I->dontSee('Subject cannot be blank', '.help-inline'); 39 | $I->dontSee('Body cannot be blank', '.help-inline'); 40 | $I->dontSee('The verification code is incorrect', '.help-inline'); 41 | } 42 | 43 | public function submitFormSuccessfully(\FunctionalTester $I) 44 | { 45 | $I->submitForm('#contact-form', [ 46 | 'ContactForm[name]' => 'tester', 47 | 'ContactForm[email]' => 'tester@example.com', 48 | 'ContactForm[subject]' => 'test subject', 49 | 'ContactForm[body]' => 'test content', 50 | 'ContactForm[verifyCode]' => 'testme', 51 | ]); 52 | $I->seeEmailIsSent(); 53 | $I->dontSeeElement('#contact-form'); 54 | $I->see('Thank you for contacting us. We will respond to you as soon as possible.'); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kartik-v/yii2-app-practical-b", 3 | "description": "Yii 2 Practical-B Application Template", 4 | "keywords": ["yii2", "framework", "practical", "practical-b", "basic", "application template"], 5 | "homepage": "http://demos.krajee.com/app-practical-b", 6 | "type": "project", 7 | "license": "BSD-3-Clause", 8 | "authors": [ 9 | { 10 | "name": "Kartik Visweswaran", 11 | "email": "kartikv2@gmail.com", 12 | "homepage": "http://www.krajee.com/" 13 | } 14 | ], 15 | "support": { 16 | "issues": "https://github.com/yiisoft/yii2/issues?state=open", 17 | "forum": "http://www.yiiframework.com/forum/", 18 | "wiki": "http://www.yiiframework.com/wiki/", 19 | "irc": "irc://irc.freenode.net/yii", 20 | "source": "https://github.com/yiisoft/yii2" 21 | }, 22 | "minimum-stability": "dev", 23 | "require": { 24 | "php": ">=5.4.0", 25 | "yiisoft/yii2": "~2.0.5", 26 | "yiisoft/yii2-bootstrap": "~2.0.0", 27 | "yiisoft/yii2-swiftmailer": "~2.0.0" 28 | }, 29 | "require-dev": { 30 | "yiisoft/yii2-debug": "~2.0.0", 31 | "yiisoft/yii2-gii": "~2.0.0", 32 | "yiisoft/yii2-faker": "~2.0.0", 33 | 34 | "codeception/base": "^2.2.3", 35 | "codeception/verify": "~0.3.1", 36 | "codeception/specify": "~0.4.3" 37 | }, 38 | "config": { 39 | "process-timeout": 1800, 40 | "fxp-asset":{ 41 | "installer-paths": { 42 | "npm-asset-library": "vendor/npm", 43 | "bower-asset-library": "vendor/bower" 44 | } 45 | } 46 | }, 47 | "scripts": { 48 | "post-create-project-cmd": [ 49 | "yii\\composer\\Installer::postCreateProject" 50 | ] 51 | }, 52 | "extra": { 53 | "yii\\composer\\Installer::postCreateProject": { 54 | "setPermission": [ 55 | { 56 | "runtime": "0777", 57 | "assets": "0777", 58 | "yii": "0755" 59 | } 60 | ], 61 | "generateCookieValidationKey": [ 62 | "config/web.php" 63 | ] 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /models/LoginForm.php: -------------------------------------------------------------------------------- 1 | hasErrors()) { 48 | $user = $this->getUser(); 49 | 50 | if (!$user || !$user->validatePassword($this->password)) { 51 | $this->addError($attribute, 'Incorrect username or password.'); 52 | } 53 | } 54 | } 55 | 56 | /** 57 | * Logs in a user using the provided username and password. 58 | * @return bool whether the user is logged in successfully 59 | */ 60 | public function login() 61 | { 62 | if ($this->validate()) { 63 | return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0); 64 | } 65 | return false; 66 | } 67 | 68 | /** 69 | * Finds user by [[username]] 70 | * 71 | * @return User|null 72 | */ 73 | public function getUser() 74 | { 75 | if ($this->_user === false) { 76 | $this->_user = User::findByUsername($this->username); 77 | } 78 | 79 | return $this->_user; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /config/web.php: -------------------------------------------------------------------------------- 1 | 'practical-b', 8 | 'basePath' => dirname(__DIR__), 9 | 'bootstrap' => ['log'], 10 | 'components' => [ 11 | 'request' => [ 12 | // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation 13 | 'cookieValidationKey' => 'PEi6ICsok3vWiJSJJtQV2JZ6D-jk5gkh', 14 | ], 15 | 'cache' => [ 16 | 'class' => 'yii\caching\FileCache', 17 | ], 18 | 'user' => [ 19 | 'identityClass' => 'app\models\User', 20 | 'enableAutoLogin' => true, 21 | ], 22 | 'errorHandler' => [ 23 | 'errorAction' => 'site/error', 24 | ], 25 | 'mailer' => [ 26 | 'class' => 'yii\swiftmailer\Mailer', 27 | // send all mails to a file by default. You have to set 28 | // 'useFileTransport' to false and configure a transport 29 | // for the mailer to send real emails. 30 | 'useFileTransport' => true, 31 | ], 32 | 'log' => [ 33 | 'traceLevel' => YII_DEBUG ? 3 : 0, 34 | 'targets' => [ 35 | [ 36 | 'class' => 'yii\log\FileTarget', 37 | 'levels' => ['error', 'warning'], 38 | ], 39 | ], 40 | ], 41 | 'db' => $db, 42 | 'urlManager' => [ 43 | 'enablePrettyUrl' => true, 44 | 'showScriptName' => false, 45 | 'rules' => [ 46 | ], 47 | ], 48 | ], 49 | 'params' => $params, 50 | ]; 51 | 52 | if (YII_ENV_DEV) { 53 | // configuration adjustments for 'dev' environment 54 | $config['bootstrap'][] = 'debug'; 55 | $config['modules']['debug'] = [ 56 | 'class' => 'yii\debug\Module', 57 | // uncomment the following to add your IP if you are not connecting from localhost. 58 | //'allowedIPs' => ['127.0.0.1', '::1'], 59 | ]; 60 | 61 | $config['bootstrap'][] = 'gii'; 62 | $config['modules']['gii'] = [ 63 | 'class' => 'yii\gii\Module', 64 | // uncomment the following to add your IP if you are not connecting from localhost. 65 | //'allowedIPs' => ['127.0.0.1', '::1'], 66 | ]; 67 | } 68 | 69 | return $config; 70 | -------------------------------------------------------------------------------- /assets_b/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 th { 76 | white-space: nowrap; 77 | } 78 | 79 | .hint-block { 80 | display: block; 81 | margin-top: 5px; 82 | color: #999; 83 | } 84 | 85 | .error-summary { 86 | color: #a94442; 87 | background: #fdf7f7; 88 | border-left: 3px solid #eed3d7; 89 | padding: 10px 20px; 90 | margin: 0 0 15px 0; 91 | } 92 | 93 | /* align the logout "link" (button in form) of the navbar */ 94 | .nav li > form > button.logout { 95 | padding: 15px; 96 | border: none; 97 | } 98 | 99 | @media(max-width:767px) { 100 | .nav li > form > button.logout { 101 | display:block; 102 | text-align: left; 103 | width: 100%; 104 | padding: 10px 15px; 105 | } 106 | } 107 | 108 | .nav > li > form > button.logout:focus, 109 | .nav > li > form > button.logout:hover { 110 | text-decoration: none; 111 | } 112 | 113 | .nav > li > form > button.logout:focus { 114 | outline: none; 115 | } 116 | -------------------------------------------------------------------------------- /views/layouts/main.php: -------------------------------------------------------------------------------- 1 | 14 | beginPage() ?> 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | <?= Html::encode($this->title) ?> 23 | head() ?> 24 | 25 | 26 | beginBody() ?> 27 | 28 |
29 | 'My Company', 32 | 'brandUrl' => Yii::$app->homeUrl, 33 | 'options' => [ 34 | 'class' => 'navbar-inverse navbar-fixed-top', 35 | ], 36 | ]); 37 | echo Nav::widget([ 38 | 'options' => ['class' => 'navbar-nav navbar-right'], 39 | 'items' => [ 40 | ['label' => 'Home', 'url' => ['/site/index']], 41 | ['label' => 'About', 'url' => ['/site/about']], 42 | ['label' => 'Contact', 'url' => ['/site/contact']], 43 | Yii::$app->user->isGuest ? ( 44 | ['label' => 'Login', 'url' => ['/site/login']] 45 | ) : ( 46 | '
  • ' 47 | . Html::beginForm(['/site/logout'], 'post') 48 | . Html::submitButton( 49 | 'Logout (' . Yii::$app->user->identity->username . ')', 50 | ['class' => 'btn btn-link logout'] 51 | ) 52 | . Html::endForm() 53 | . '
  • ' 54 | ) 55 | ], 56 | ]); 57 | NavBar::end(); 58 | ?> 59 | 60 |
    61 | isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], 63 | ]) ?> 64 | 65 |
    66 |
    67 | 68 |
    69 |
    70 |

    © My Company

    71 | 72 |

    73 |
    74 |
    75 | 76 | endBody() ?> 77 | 78 | 79 | endPage() ?> 80 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /views/site/contact.php: -------------------------------------------------------------------------------- 1 | title = 'Contact'; 12 | $this->params['breadcrumbs'][] = $this->title; 13 | ?> 14 |
    15 |

    title) ?>

    16 | 17 | session->hasFlash('contactFormSubmitted')): ?> 18 | 19 |
    20 | Thank you for contacting us. We will respond to you as soon as possible. 21 |
    22 | 23 |

    24 | Note that if you turn on the Yii debugger, you should be able 25 | to view the mail message on the mail panel of the debugger. 26 | mailer->useFileTransport): ?> 27 | Because the application is in development mode, the email is not sent but saved as 28 | a file under mailer->fileTransportPath) ?>. 29 | Please configure the useFileTransport property of the mail 30 | application component to be false to enable email sending. 31 | 32 |

    33 | 34 | 35 | 36 |

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

    40 | 41 |
    42 |
    43 | 44 | 'contact-form']); ?> 45 | 46 | field($model, 'name')->textInput(['autofocus' => true]) ?> 47 | 48 | field($model, 'email') ?> 49 | 50 | field($model, 'subject') ?> 51 | 52 | field($model, 'body')->textarea(['rows' => 6]) ?> 53 | 54 | field($model, 'verifyCode')->widget(Captcha::className(), [ 55 | 'template' => '
    {image}
    {input}
    ', 56 | ]) ?> 57 | 58 |
    59 | 'btn btn-primary', 'name' => 'contact-button']) ?> 60 |
    61 | 62 | 63 | 64 |
    65 |
    66 | 67 | 68 |
    69 | -------------------------------------------------------------------------------- /models/User.php: -------------------------------------------------------------------------------- 1 | [ 15 | 'id' => '100', 16 | 'username' => 'admin', 17 | 'password' => 'admin', 18 | 'authKey' => 'test100key', 19 | 'accessToken' => '100-token', 20 | ], 21 | '101' => [ 22 | 'id' => '101', 23 | 'username' => 'demo', 24 | 'password' => 'demo', 25 | 'authKey' => 'test101key', 26 | 'accessToken' => '101-token', 27 | ], 28 | ]; 29 | 30 | 31 | /** 32 | * @inheritdoc 33 | */ 34 | public static function findIdentity($id) 35 | { 36 | return isset(self::$users[$id]) ? new static(self::$users[$id]) : null; 37 | } 38 | 39 | /** 40 | * @inheritdoc 41 | */ 42 | public static function findIdentityByAccessToken($token, $type = null) 43 | { 44 | foreach (self::$users as $user) { 45 | if ($user['accessToken'] === $token) { 46 | return new static($user); 47 | } 48 | } 49 | 50 | return null; 51 | } 52 | 53 | /** 54 | * Finds user by username 55 | * 56 | * @param string $username 57 | * @return static|null 58 | */ 59 | public static function findByUsername($username) 60 | { 61 | foreach (self::$users as $user) { 62 | if (strcasecmp($user['username'], $username) === 0) { 63 | return new static($user); 64 | } 65 | } 66 | 67 | return null; 68 | } 69 | 70 | /** 71 | * @inheritdoc 72 | */ 73 | public function getId() 74 | { 75 | return $this->id; 76 | } 77 | 78 | /** 79 | * @inheritdoc 80 | */ 81 | public function getAuthKey() 82 | { 83 | return $this->authKey; 84 | } 85 | 86 | /** 87 | * @inheritdoc 88 | */ 89 | public function validateAuthKey($authKey) 90 | { 91 | return $this->authKey === $authKey; 92 | } 93 | 94 | /** 95 | * Validates password 96 | * 97 | * @param string $password password to validate 98 | * @return bool if password provided is valid for current user 99 | */ 100 | public function validatePassword($password) 101 | { 102 | return $this->password === $password; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /controllers/SiteController.php: -------------------------------------------------------------------------------- 1 | [ 22 | 'class' => AccessControl::className(), 23 | 'only' => ['logout'], 24 | 'rules' => [ 25 | [ 26 | 'actions' => ['logout'], 27 | 'allow' => true, 28 | 'roles' => ['@'], 29 | ], 30 | ], 31 | ], 32 | 'verbs' => [ 33 | 'class' => VerbFilter::className(), 34 | 'actions' => [ 35 | 'logout' => ['post'], 36 | ], 37 | ], 38 | ]; 39 | } 40 | 41 | /** 42 | * @inheritdoc 43 | */ 44 | public function actions() 45 | { 46 | return [ 47 | 'error' => [ 48 | 'class' => 'yii\web\ErrorAction', 49 | ], 50 | 'captcha' => [ 51 | 'class' => 'yii\captcha\CaptchaAction', 52 | 'fixedVerifyCode' => YII_ENV_TEST ? 'testme' : null, 53 | ], 54 | ]; 55 | } 56 | 57 | /** 58 | * Displays homepage. 59 | * 60 | * @return string 61 | */ 62 | public function actionIndex() 63 | { 64 | return $this->render('index'); 65 | } 66 | 67 | /** 68 | * Login action. 69 | * 70 | * @return Response|string 71 | */ 72 | public function actionLogin() 73 | { 74 | if (!Yii::$app->user->isGuest) { 75 | return $this->goHome(); 76 | } 77 | 78 | $model = new LoginForm(); 79 | if ($model->load(Yii::$app->request->post()) && $model->login()) { 80 | return $this->goBack(); 81 | } 82 | return $this->render('login', [ 83 | 'model' => $model, 84 | ]); 85 | } 86 | 87 | /** 88 | * Logout action. 89 | * 90 | * @return Response 91 | */ 92 | public function actionLogout() 93 | { 94 | Yii::$app->user->logout(); 95 | 96 | return $this->goHome(); 97 | } 98 | 99 | /** 100 | * Displays contact page. 101 | * 102 | * @return Response|string 103 | */ 104 | public function actionContact() 105 | { 106 | $model = new ContactForm(); 107 | if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app->params['adminEmail'])) { 108 | Yii::$app->session->setFlash('contactFormSubmitted'); 109 | 110 | return $this->refresh(); 111 | } 112 | return $this->render('contact', [ 113 | 'model' => $model, 114 | ]); 115 | } 116 | 117 | /** 118 | * Displays about page. 119 | * 120 | * @return string 121 | */ 122 | public function actionAbout() 123 | { 124 | return $this->render('about'); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /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 | // CAPTCHA: 89 | array( 90 | 'name' => 'GD PHP extension with FreeType support', 91 | 'mandatory' => false, 92 | 'condition' => $gdOK, 93 | 'by' => 'Captcha', 94 | 'memo' => $gdMemo, 95 | ), 96 | array( 97 | 'name' => 'ImageMagick PHP extension with PNG support', 98 | 'mandatory' => false, 99 | 'condition' => $imagickOK, 100 | 'by' => 'Captcha', 101 | 'memo' => $imagickMemo, 102 | ), 103 | // PHP ini : 104 | 'phpExposePhp' => array( 105 | 'name' => 'Expose PHP', 106 | 'mandatory' => false, 107 | 'condition' => $requirementsChecker->checkPhpIniOff("expose_php"), 108 | 'by' => 'Security reasons', 109 | 'memo' => '"expose_php" should be disabled at php.ini', 110 | ), 111 | 'phpAllowUrlInclude' => array( 112 | 'name' => 'PHP allow url include', 113 | 'mandatory' => false, 114 | 'condition' => $requirementsChecker->checkPhpIniOff("allow_url_include"), 115 | 'by' => 'Security reasons', 116 | 'memo' => '"allow_url_include" should be disabled at php.ini', 117 | ), 118 | 'phpSmtp' => array( 119 | 'name' => 'PHP mail SMTP', 120 | 'mandatory' => false, 121 | 'condition' => strlen(ini_get('SMTP')) > 0, 122 | 'by' => 'Email sending', 123 | 'memo' => 'PHP mail SMTP server required', 124 | ), 125 | ); 126 | 127 | // OPcache check 128 | if (!version_compare(phpversion(), '5.5', '>=')) { 129 | $requirements[] = array( 130 | 'name' => 'APC extension', 131 | 'mandatory' => false, 132 | 'condition' => extension_loaded('apc'), 133 | 'by' => 'ApcCache', 134 | ); 135 | } 136 | 137 | $requirementsChecker->checkYii()->check($requirements)->render(); 138 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

    2 | 3 | Krajee Logo 4 | 5 |
    6 | Yii 2 Practical-B Project Template 7 |
    8 | 10 | Donate 11 | 12 |

    13 | 14 | [![Latest Stable Version](https://poser.pugx.org/kartik-v/yii2-app-practical-b/v/stable.svg)](https://packagist.org/packages/kartik-v/yii2-app-practical-b) 15 | [![License](https://poser.pugx.org/kartik-v/yii2-app-practical-b/license.svg)](https://packagist.org/packages/kartik-v/yii2-app-practical-b) 16 | [![Total Downloads](https://poser.pugx.org/kartik-v/yii2-app-practical-b/downloads.svg)](https://packagist.org/packages/kartik-v/yii2-app-practical-b) 17 | [![Monthly Downloads](https://poser.pugx.org/kartik-v/yii2-app-practical-b/d/monthly.png)](https://packagist.org/packages/kartik-v/yii2-app-practical-b) 18 | [![Daily Downloads](https://poser.pugx.org/kartik-v/yii2-app-practical-b/d/daily.png)](https://packagist.org/packages/kartik-v/yii2-app-practical-b) 19 | 20 | The Yii 2 Practical-B Application Template is a skeleton Yii 2 application based on the 21 | [yii2-basic template](https://github.com/yiisoft/yii2-app-basic/) best for 22 | rapidly creating small projects. The template allows a **practical** method to directly 23 | access the application from the app root. 24 | 25 | The template contains the basic features including user login/logout and a contact page. 26 | It includes all commonly used configurations that would allow you to focus on adding new 27 | features to your application. 28 | 29 | 30 | Why yii2-practical-b? 31 | --------------------- 32 | 33 | After installing a `app`, in the yii2-basic application you normally would access the 34 | frontend by: 35 | 36 | ``` 37 | http://domain/app/web 38 | ``` 39 | 40 | However, in many **practical** scenarios (especially on shared and single domain hosts) one 41 | would want their users to directly access the app as: 42 | 43 | ``` 44 | http://domain/app 45 | ``` 46 | 47 | The `yii2-app-practical-b` enables you to achieve just that by carefully moving and rearranging the 48 | bootstrap files and web components of frontend to work directly out of the app root. The 49 | `web` folder is entirely eliminated and one can directly access the application frontend 50 | this way: 51 | 52 | ``` 53 | http://domain/app 54 | ``` 55 | 56 | All other aspects of the app configuration remain the same as the **yii2-basic** app. The original `assets` folder 57 | in the approot is renamed to `assets_b`, while the `web/assets` folder moves to app root. 58 | 59 | SOME KEY ADDITIONS 60 | ------------------- 61 | 62 | 1. The template has some security preconfigured for users with Apache web servers. It has a default `.htaccess` security configuration setup. 63 | 2. The template has prettyUrl enabled by default and the changes have been made to `.htaccess` as well as `urlManager` 64 | component config in the config directory. 65 | 66 | DIRECTORY STRUCTURE 67 | ------------------- 68 | 69 | ``` 70 | / contains the entry script and web resources 71 | assets/ contains the web runtime assets 72 | assets_b/ contains application assets such as JavaScript and CSS 73 | commands/ contains console commands (controllers) 74 | config/ contains application configurations 75 | controllers/ contains Web controller classes 76 | mail/ contains view files for e-mails 77 | models/ contains model classes 78 | runtime/ contains files generated during runtime 79 | tests/ contains various tests for the yii2-practical-b application 80 | vendor/ contains dependent 3rd-party packages 81 | views/ contains view files for the Web application 82 | ``` 83 | 84 | REQUIREMENTS 85 | ------------ 86 | 87 | The minimum requirement by this project template that your Web server supports PHP 5.4.0. 88 | 89 | 90 | INSTALLATION 91 | ------------ 92 | 93 | ### Install via Composer 94 | 95 | If you do not have [Composer](http://getcomposer.org/), you may install it by following the instructions 96 | at [getcomposer.org](http://getcomposer.org/doc/00-intro.md#installation-nix). 97 | 98 | You can then install this project template using the following command: 99 | 100 | ~~~ 101 | php composer.phar global require "fxp/composer-asset-plugin:^1.3.1" 102 | php composer.phar create-project --prefer-dist --stability=dev kartik-v/yii2-app-practical-b practical-b 103 | ~~~ 104 | 105 | Now you should be able to access the application through the following URL, assuming `practical-b` is the directory 106 | directly under the Web root. 107 | 108 | ~~~ 109 | http://localhost/practical-b 110 | ~~~ 111 | 112 | ### Install from an Archive File 113 | 114 | Extract the archive file downloaded from [yiiframework.com](http://www.yiiframework.com/download/) to 115 | a directory named `basic` that is directly under the Web root. 116 | 117 | Set cookie validation key in `config/web.php` file to some random secret string: 118 | 119 | ```php 120 | 'request' => [ 121 | // !!! insert a secret key in the following (if it is empty) - this is required by cookie validation 122 | 'cookieValidationKey' => '', 123 | ], 124 | ``` 125 | 126 | You can then access the application through the following URL: 127 | 128 | ~~~ 129 | http://localhost/practical-b/ 130 | ~~~ 131 | 132 | 133 | CONFIGURATION 134 | ------------- 135 | 136 | ### Database 137 | 138 | Edit the file `config/db.php` with real data, for example: 139 | 140 | ```php 141 | return [ 142 | 'class' => 'yii\db\Connection', 143 | 'dsn' => 'mysql:host=localhost;dbname=yii2basic', 144 | 'username' => 'root', 145 | 'password' => '1234', 146 | 'charset' => 'utf8', 147 | ]; 148 | ``` 149 | 150 | **NOTES:** 151 | - Yii won't create the database for you, this has to be done manually before you can access it. 152 | - Check and edit the other files in the `config/` directory to customize your application as required. 153 | - Refer to the README in the `tests` directory for information specific to basic application tests. 154 | 155 | TESTING 156 | ------- 157 | 158 | Tests are located in `tests` directory. They are developed with [Codeception PHP Testing Framework](http://codeception.com/). 159 | By default there are 3 test suites: 160 | 161 | - `unit` 162 | - `functional` 163 | - `acceptance` 164 | 165 | Tests can be executed by running 166 | 167 | ``` 168 | vendor/bin/codecept run 169 | ``` 170 | 171 | The command above will execute unit and functional tests. Unit tests are testing the system components, while functional 172 | tests are for testing user interaction. Acceptance tests are disabled by default as they require additional setup since 173 | they perform testing in real browser. 174 | 175 | 176 | ### Running acceptance tests 177 | 178 | To execute acceptance tests do the following: 179 | 180 | 1. Rename `tests/acceptance.suite.yml.example` to `tests/acceptance.suite.yml` to enable suite configuration 181 | 182 | 2. Replace `codeception/base` package in `composer.json` with `codeception/codeception` to install full featured 183 | version of Codeception 184 | 185 | 3. Update dependencies with Composer 186 | 187 | ``` 188 | composer update 189 | ``` 190 | 191 | 4. Download [Selenium Server](http://www.seleniumhq.org/download/) and launch it: 192 | 193 | ``` 194 | java -jar ~/selenium-server-standalone-x.xx.x.jar 195 | ``` 196 | 197 | In case of using Selenium Server 3.0 with Firefox browser since v48 or Google Chrome since v53 you must download [GeckoDriver](https://github.com/mozilla/geckodriver/releases) or [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/downloads) and launch Selenium with it: 198 | 199 | ``` 200 | # for Firefox 201 | java -jar -Dwebdriver.gecko.driver=~/geckodriver ~/selenium-server-standalone-3.xx.x.jar 202 | 203 | # for Google Chrome 204 | java -jar -Dwebdriver.chrome.driver=~/chromedriver ~/selenium-server-standalone-3.xx.x.jar 205 | ``` 206 | 207 | As an alternative way you can use already configured Docker container with older versions of Selenium and Firefox: 208 | 209 | ``` 210 | docker run --net=host selenium/standalone-firefox:2.53.0 211 | ``` 212 | 213 | 5. (Optional) Create `yii2_basic_tests` database and update it by applying migrations if you have them. 214 | 215 | ``` 216 | tests/bin/yii migrate 217 | ``` 218 | 219 | The database configuration can be found at `config/test_db.php`. 220 | 221 | 222 | 6. Start web server: 223 | 224 | ``` 225 | tests/bin/yii serve 226 | ``` 227 | 228 | 7. Now you can run all available tests 229 | 230 | ``` 231 | # run all available tests 232 | vendor/bin/codecept run 233 | 234 | # run acceptance tests 235 | vendor/bin/codecept run acceptance 236 | 237 | # run only unit and functional tests 238 | vendor/bin/codecept run unit,functional 239 | ``` 240 | 241 | ### Code coverage support 242 | 243 | By default, code coverage is disabled in `codeception.yml` configuration file, you should uncomment needed rows to be able 244 | to collect code coverage. You can run your tests and collect coverage with the following command: 245 | 246 | ``` 247 | #collect coverage for all tests 248 | vendor/bin/codecept run -- --coverage-html --coverage-xml 249 | 250 | #collect coverage only for unit tests 251 | vendor/bin/codecept run unit -- --coverage-html --coverage-xml 252 | 253 | #collect coverage for unit and functional tests 254 | vendor/bin/codecept run functional,unit -- --coverage-html --coverage-xml 255 | ``` 256 | 257 | You can see code coverage output under the `tests/_output` directory. 258 | --------------------------------------------------------------------------------