14 |
= Html::encode($this->title) ?>
15 |
16 |
Please fill out the following fields to login:
17 |
18 | 'login-form',
20 | 'layout' => 'horizontal',
21 | 'fieldConfig' => [
22 | 'template' => "{label}\n
{input}
\n
{error}
",
23 | 'labelOptions' => ['class' => 'col-lg-1 control-label'],
24 | ],
25 | ]); ?>
26 |
27 | = $form->field($model, 'username')->textInput(['autofocus' => true]) ?>
28 |
29 | = $form->field($model, 'password')->passwordInput() ?>
30 |
31 | = $form->field($model, 'rememberMe')->checkbox([
32 | 'template' => "
{input} {label}
\n
{error}
",
33 | ]) ?>
34 |
35 |
40 |
41 |
42 |
43 |
44 | You may login with admin/admin or demo/demo.
45 | To modify the username/password, please check out the code app\models\User::$users.
46 |
47 |
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 | = Html::csrfMetaTags() ?>
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 | = Breadcrumbs::widget([
62 | 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
63 | ]) ?>
64 | = $content ?>
65 |
66 |
67 |
68 |
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 |
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 |
4 |
5 |
6 | Yii 2 Practical-B Project Template
7 |
8 |
10 |
11 |
12 |
13 |
14 | [](https://packagist.org/packages/kartik-v/yii2-app-practical-b)
15 | [](https://packagist.org/packages/kartik-v/yii2-app-practical-b)
16 | [](https://packagist.org/packages/kartik-v/yii2-app-practical-b)
17 | [](https://packagist.org/packages/kartik-v/yii2-app-practical-b)
18 | [](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 |
--------------------------------------------------------------------------------