diff --git a/.bowerrc b/.bowerrc
index 1669168f2..a39b5b0fb 100644
--- a/.bowerrc
+++ b/.bowerrc
@@ -1,3 +1,3 @@
{
- "directory" : "vendor/bower"
+ "directory" : "vendor/bower-asset"
}
diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs
new file mode 100644
index 000000000..011c056bb
--- /dev/null
+++ b/.git-blame-ignore-revs
@@ -0,0 +1,8 @@
+# Reformat code te be PSR-2 compatible
+93cc82c4bf42cea403e1acaab201338bea304b6e
+# Convert to short syntax (array)
+332030325fbad38a64c5e60980d3b14b6434d6dd
+# Convert to short syntax
+b0fcdfab1aecaeb620f75a83f15aa02bc25765a0
+# Fix codestyle
+0f91f32ecff52ef479addb0a5013342d59fe0697
diff --git a/.gitattributes b/.gitattributes
index 3e9092c68..705fb391d 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,6 +1,7 @@
# Ignore all test and documentation for archive
-/.github export-ignore
-/.gitattributes export-ignore
-/.scrutinizer.yml export-ignore
-/.travis.yml export-ignore
-/docs export-ignore
+/.github export-ignore
+/.git-blame-ignore-revs export-ignore
+/.gitattributes export-ignore
+/.scrutinizer.yml export-ignore
+/.travis.yml export-ignore
+/docs export-ignore
diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md
index 3d8d32c25..ea75bda5f 100644
--- a/.github/CONTRIBUTING.md
+++ b/.github/CONTRIBUTING.md
@@ -3,5 +3,5 @@ Contributing to Yii2
- [Report an issue](https://github.com/yiisoft/yii2/blob/master/docs/internals/report-an-issue.md)
- [Translate documentation or messages](https://github.com/yiisoft/yii2/blob/master/docs/internals/translation-workflow.md)
-- [Give us feedback or start a design discussion](http://www.yiiframework.com/forum/index.php/forum/42-general-discussions-for-yii-20/)
+- [Give us feedback or start a design discussion](https://www.yiiframework.com/forum/index.php/forum/42-general-discussions-for-yii-20/)
- [Contribute to the core code or fix bugs](https://github.com/yiisoft/yii2/blob/master/docs/internals/git-workflow.md)
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 000000000..692239301
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,4 @@
+# These are supported funding model platforms
+
+open_collective: yiisoft
+tidelift: "packagist/yiisoft/yii2-app-basic"
diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
index 52ec8692f..a2ee92439 100644
--- a/.github/ISSUE_TEMPLATE.md
+++ b/.github/ISSUE_TEMPLATE.md
@@ -9,6 +9,6 @@
| Q | A
| ---------------- | ---
-| Yii vesion |
+| Yii version |
| PHP version |
| Operating system |
diff --git a/.github/SECURITY.md b/.github/SECURITY.md
new file mode 100644
index 000000000..405acca4e
--- /dev/null
+++ b/.github/SECURITY.md
@@ -0,0 +1,6 @@
+# Security Policy
+
+Please use the [security issue form](https://www.yiiframework.com/security) to report to us any security issue you find in Yii.
+DO NOT use the issue tracker or discuss it in the public forum as it will cause more damage than help.
+
+Please note that as a non-commercial OpenSource project we are not able to pay bounties at the moment.
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
new file mode 100644
index 000000000..a5beac653
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,83 @@
+on:
+ - pull_request
+ - push
+
+name: build
+
+jobs:
+ tests:
+ name: PHP ${{ matrix.php }} - ${{ matrix.os }}
+
+ env:
+ extensions: dom, json, gd, imagick
+ key: cache-v4
+
+ runs-on: ${{ matrix.os }}
+
+ strategy:
+ matrix:
+ os:
+ - ubuntu-latest
+ - windows-latest
+
+ php:
+ - "7.4"
+ - "8.0"
+ - "8.1"
+ - "8.2"
+ - "8.3"
+ - "8.4"
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v5
+
+ - name: Setup cache environment
+ id: cache-env
+ uses: shivammathur/cache-extensions@v1
+ with:
+ php-version: ${{ matrix.php }}
+ extensions: ${{ env.extensions }}
+ key: ${{ env.key }}
+
+ - name: Cache extensions
+ uses: actions/cache@v4
+ with:
+ path: ${{ steps.cache-env.outputs.dir }}
+ key: ${{ steps.cache-env.outputs.key }}
+ restore-keys: ${{ steps.cache-env.outputs.key }}
+
+ - name: Install PHP with extensions
+ uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ extensions: ${{ env.extensions }}
+ ini-values: date.timezone='UTC'
+
+ - name: Determine composer cache directory on Linux
+ if: matrix.os == 'ubuntu-latest'
+ run: |
+ echo "COMPOSER_CACHE_DIR=$(composer config cache-dir)" >> $GITHUB_ENV
+
+ - name: Determine composer cache directory on Windows
+ if: matrix.os == 'windows-latest'
+ run: |
+ echo "COMPOSER_CACHE_DIR=~\AppData\Local\Composer" >> $GITHUB_ENV
+
+ - name: Cache dependencies installed with composer
+ uses: actions/cache@v4
+ with:
+ path: ${{ steps.cache-env.outputs.dir }}
+ key: php${{ matrix.php }}-composer-${{ matrix.dependencies }}-${{ hashFiles('**/composer.json') }}
+ restore-keys: |
+ php${{ matrix.php }}-composer-${{ matrix.dependencies }}-
+
+ - name: Install dependencies with composer php PHP [5.6 - 8.0]
+ run: composer update --prefer-dist --no-interaction --no-progress --optimize-autoloader --ansi
+
+ - name: Run tests with codeception
+ run: |
+ sed -i "s/'cookieValidationKey' => ''/'cookieValidationKey' => 'testkey'/" config/web.php
+ php -S 127.0.0.1:8080 -t public > ./runtime/yii.log 2>&1 &
+ vendor/bin/codecept run
+ shell: bash
diff --git a/.gitignore b/.gitignore
index 6084c1da5..05fb29d1b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,9 @@
# phpstorm project files
.idea
+# visual studio code project files
+.vscode
+
# netbeans project files
nbproject
@@ -27,4 +30,7 @@ phpunit.phar
/phpunit.xml
tests/_output/*
-tests/_support/_generated
\ No newline at end of file
+tests/_support/_generated
+
+#vagrant folder
+/.vagrant
\ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 03f4b5d18..000000000
--- a/.travis.yml
+++ /dev/null
@@ -1,29 +0,0 @@
-language: php
-
-php:
- - 5.4
- - 5.5
- - 5.6
- - 7.0
-# - hhvm
-
-# faster builds on new travis setup not using sudo
-sudo: false
-
-# cache vendor dirs
-cache:
- directories:
- - $HOME/.composer/cache
-
-install:
- - travis_retry composer self-update && composer --version
- - travis_retry composer global require "fxp/composer-asset-plugin:~1.1.1"
- - travis_retry composer update --dev --prefer-dist --no-interaction
-# setup application:
- - |
- sed -i "s/'cookieValidationKey' => ''/'cookieValidationKey' => 'testkey'/" config/web.php
-
-script:
- - |
- php -S localhost:8080 -t web > /dev/null 2>&1 &
- composer exec codecept run
\ No newline at end of file
diff --git a/LICENSE.md b/LICENSE.md
index e98f03df8..ee872b9ab 100644
--- a/LICENSE.md
+++ b/LICENSE.md
@@ -1,6 +1,3 @@
-The Yii framework is free software. It is released under the terms of
-the following BSD License.
-
Copyright © 2008 by Yii Software LLC (http://www.yiisoft.com)
All rights reserved.
diff --git a/README.md b/README.md
index c398b58ff..981a7f9d9 100644
--- a/README.md
+++ b/README.md
@@ -1,16 +1,21 @@
-Yii 2 Basic Project Template
-============================
-
-Yii 2 Basic Project Template is a skeleton [Yii 2](http://www.yiiframework.com/) application best for
+
+
+
+
+
Yii 2 Basic Project Template
+
+
+
+Yii 2 Basic Project Template is a skeleton [Yii 2](https://www.yiiframework.com/) application best for
rapidly creating small projects.
The template contains the basic features including user login/logout and a contact page.
It includes all commonly used configurations that would allow you to focus on adding new
features to your application.
-[](https://packagist.org/packages/yiisoft/yii2-app-basic)
-[](https://packagist.org/packages/yiisoft/yii2-app-basic)
-[](https://travis-ci.org/yiisoft/yii2-app-basic)
+[](https://packagist.org/packages/yiisoft/yii2-app-basic)
+[](https://packagist.org/packages/yiisoft/yii2-app-basic)
+[](https://github.com/yiisoft/yii2-app-basic/actions?query=workflow%3Abuild)
DIRECTORY STRUCTURE
-------------------
@@ -32,7 +37,7 @@ DIRECTORY STRUCTURE
REQUIREMENTS
------------
-The minimum requirement by this project template that your Web server supports PHP 5.4.0.
+The minimum requirement by this project template that your Web server supports PHP 7.4.
INSTALLATION
@@ -40,14 +45,13 @@ INSTALLATION
### Install via Composer
-If you do not have [Composer](http://getcomposer.org/), you may install it by following the instructions
-at [getcomposer.org](http://getcomposer.org/doc/00-intro.md#installation-nix).
+If you do not have [Composer](https://getcomposer.org/), you may install it by following the instructions
+at [getcomposer.org](https://getcomposer.org/doc/00-intro.md#installation-nix).
You can then install this project template using the following command:
~~~
-php composer.phar global require "fxp/composer-asset-plugin:^1.2.0"
-php composer.phar create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic basic
+composer create-project --prefer-dist yiisoft/yii2-app-basic basic
~~~
Now you should be able to access the application through the following URL, assuming `basic` is the directory
@@ -57,10 +61,9 @@ directly under the Web root.
http://localhost/basic/web/
~~~
-
### Install from an Archive File
-Extract the archive file downloaded from [yiiframework.com](http://www.yiiframework.com/download/) to
+Extract the archive file downloaded from [yiiframework.com](https://www.yiiframework.com/download/) to
a directory named `basic` that is directly under the Web root.
Set cookie validation key in `config/web.php` file to some random secret string:
@@ -79,6 +82,29 @@ http://localhost/basic/web/
~~~
+### Install with Docker
+
+Update your vendor packages
+
+ docker-compose run --rm php composer update --prefer-dist
+
+Run the installation triggers (creating cookie validation code)
+
+ docker-compose run --rm php composer install
+
+Start the container
+
+ docker-compose up -d
+
+You can then access the application through the following URL:
+
+ http://127.0.0.1:8000
+
+**NOTES:**
+- Minimum required Docker engine version `17.04` for development (see [Performance tuning for volume mounts](https://docs.docker.com/docker-for-mac/osxfs-caching/))
+- The default configuration uses a host-volume in your home directory `.docker-composer` for composer caches
+
+
CONFIGURATION
-------------
@@ -102,12 +128,11 @@ return [
- Refer to the README in the `tests` directory for information specific to basic application tests.
-
TESTING
-------
-Tests are located in `tests` directory. They are developed with [Codeception PHP Testing Framework](http://codeception.com/).
-By default there are 3 test suites:
+Tests are located in `tests` directory. They are developed with [Codeception PHP Testing Framework](https://codeception.com/).
+By default, there are 3 test suites:
- `unit`
- `functional`
@@ -116,8 +141,8 @@ By default there are 3 test suites:
Tests can be executed by running
```
-composer exec codecept run
-```
+vendor/bin/codecept run
+```
The command above will execute unit and functional tests. Unit tests are testing the system components, while functional
tests are for testing user interaction. Acceptance tests are disabled by default as they require additional setup since
@@ -130,7 +155,7 @@ To execute acceptance tests do the following:
1. Rename `tests/acceptance.suite.yml.example` to `tests/acceptance.suite.yml` to enable suite configuration
-2. Replace `codeception/base` package in `composer.json` with `codeception/codeception` to install full featured
+2. Replace `codeception/base` package in `composer.json` with `codeception/codeception` to install full-featured
version of Codeception
3. Update dependencies with Composer
@@ -139,13 +164,29 @@ To execute acceptance tests do the following:
composer update
```
-4. Download [Selenium Server](http://www.seleniumhq.org/download/) and launch it:
+4. Download [Selenium Server](https://www.seleniumhq.org/download/) and launch it:
```
java -jar ~/selenium-server-standalone-x.xx.x.jar
+ ```
+
+ 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:
+
+ ```
+ # for Firefox
+ java -jar -Dwebdriver.gecko.driver=~/geckodriver ~/selenium-server-standalone-3.xx.x.jar
+
+ # for Google Chrome
+ java -jar -Dwebdriver.chrome.driver=~/chromedriver ~/selenium-server-standalone-3.xx.x.jar
```
+
+ As an alternative way you can use already configured Docker container with older versions of Selenium and Firefox:
+
+ ```
+ docker run --net=host selenium/standalone-firefox:2.53.0
+ ```
-5. (Optional) Create `yii2_basic_tests` database and update it by applying migrations if you have them.
+5. (Optional) Create `yii2basic_test` database and update it by applying migrations if you have them.
```
tests/bin/yii migrate
@@ -164,13 +205,13 @@ To execute acceptance tests do the following:
```
# run all available tests
- composer exec codecept run
+ vendor/bin/codecept run
# run acceptance tests
- composer exec codecept run acceptance
+ vendor/bin/codecept run acceptance
# run only unit and functional tests
- composer exec codecept run unit,functional
+ vendor/bin/codecept run unit,functional
```
### Code coverage support
@@ -180,13 +221,13 @@ to collect code coverage. You can run your tests and collect coverage with the f
```
#collect coverage for all tests
-composer exec codecept run -- --coverage-html --coverage-xml
+vendor/bin/codecept run --coverage --coverage-html --coverage-xml
#collect coverage only for unit tests
-composer exec codecept run unit -- --coverage-html --coverage-xml
+vendor/bin/codecept run unit --coverage --coverage-html --coverage-xml
#collect coverage for unit and functional tests
-composer exec codecept run functional,unit -- --coverage-html --coverage-xml
+vendor/bin/codecept run functional,unit --coverage --coverage-html --coverage-xml
```
You can see code coverage output under the `tests/_output` directory.
diff --git a/Vagrantfile b/Vagrantfile
new file mode 100644
index 000000000..258aea601
--- /dev/null
+++ b/Vagrantfile
@@ -0,0 +1,92 @@
+require 'yaml'
+require 'fileutils'
+
+required_plugins_installed = nil
+required_plugins = %w( vagrant-hostmanager vagrant-vbguest )
+required_plugins.each do |plugin|
+ unless Vagrant.has_plugin? plugin
+ system "vagrant plugin install #{plugin}"
+ required_plugins_installed = true
+ end
+end
+
+# IF plugin[s] was just installed - restart required
+if required_plugins_installed
+ # Get CLI command[s] and call again
+ system 'vagrant' + ARGV.to_s.gsub(/\[\"|\", \"|\"\]/, ' ')
+ exit
+end
+
+domains = {
+ app: 'yii2basic.test'
+}
+
+vagrantfile_dir_path = File.dirname(__FILE__)
+
+config = {
+ local: vagrantfile_dir_path + '/vagrant/config/vagrant-local.yml',
+ example: vagrantfile_dir_path + '/vagrant/config/vagrant-local.example.yml'
+}
+
+# copy config from example if local config not exists
+FileUtils.cp config[:example], config[:local] unless File.exist?(config[:local])
+# read config
+options = YAML.load_file config[:local]
+
+# check github token
+if options['github_token'].nil? || options['github_token'].to_s.length != 40
+ puts "You must place REAL GitHub token into configuration:\n/yii2-app-basic/vagrant/config/vagrant-local.yml"
+ exit
+end
+
+# vagrant configurate
+Vagrant.configure(2) do |config|
+ # select the box
+ config.vm.box = 'bento/ubuntu-18.04'
+
+ # should we ask about box updates?
+ config.vm.box_check_update = options['box_check_update']
+
+ config.vm.provider 'virtualbox' do |vb|
+ # machine cpus count
+ vb.cpus = options['cpus']
+ # machine memory size
+ vb.memory = options['memory']
+ # machine name (for VirtualBox UI)
+ vb.name = options['machine_name']
+ end
+
+ # machine name (for vagrant console)
+ config.vm.define options['machine_name']
+
+ # machine name (for guest machine console)
+ config.vm.hostname = options['machine_name']
+
+ # network settings
+ config.vm.network 'private_network', ip: options['ip']
+
+ # sync: folder 'yii2-app-advanced' (host machine) -> folder '/app' (guest machine)
+ config.vm.synced_folder './', '/app', owner: 'vagrant', group: 'vagrant'
+
+ # disable folder '/vagrant' (guest machine)
+ config.vm.synced_folder '.', '/vagrant', disabled: true
+
+ # hosts settings (host machine)
+ config.vm.provision :hostmanager
+ config.hostmanager.enabled = true
+ config.hostmanager.manage_host = true
+ config.hostmanager.ignore_private_ip = false
+ config.hostmanager.include_offline = true
+ config.hostmanager.aliases = domains.values
+
+ # quick fix for failed guest additions installations
+ # config.vbguest.auto_update = false
+
+ # provisioners
+ config.vm.provision 'shell', path: './vagrant/provision/once-as-root.sh', args: [options['timezone'], options['ip']]
+ config.vm.provision 'shell', path: './vagrant/provision/once-as-vagrant.sh', args: [options['github_token']], privileged: false
+ config.vm.provision 'shell', path: './vagrant/provision/always-as-root.sh', run: 'always'
+
+ # post-install message (vagrant console)
+ config.vm.post_up_message = "App URL: http://#{domains[:app]}"
+end
diff --git a/assets/AppAsset.php b/assets/AppAsset.php
index 0e495a8f8..d9d4cc430 100644
--- a/assets/AppAsset.php
+++ b/assets/AppAsset.php
@@ -1,8 +1,9 @@
* @since 2.0
*/
@@ -24,6 +27,6 @@ class AppAsset extends AssetBundle
];
public $depends = [
'yii\web\YiiAsset',
- 'yii\bootstrap\BootstrapAsset',
+ 'yii\bootstrap5\BootstrapAsset'
];
}
diff --git a/codeception.yml b/codeception.yml
index 2513bee79..dd8febcff 100644
--- a/codeception.yml
+++ b/codeception.yml
@@ -1,37 +1,27 @@
actor: Tester
+bootstrap: _bootstrap.php
paths:
tests: tests
- log: tests/_output
+ output: tests/_output
data: tests/_data
helpers: tests/_support
settings:
- bootstrap: _bootstrap.php
memory_limit: 1024M
colors: true
modules:
config:
Yii2:
configFile: 'config/test.php'
- cleanup: false
# To enable code coverage:
#coverage:
# #c3_url: http://localhost:8080/index-test.php/
# enabled: true
# #remote: true
-# #remote_config: '../tests/codeception.yml'
+# #remote_config: '../codeception.yml'
# whitelist:
# include:
# - models/*
# - controllers/*
# - commands/*
# - mail/*
-# blacklist:
-# include:
-# - assets/*
-# - config/*
-# - runtime/*
-# - vendor/*
-# - views/*
-# - web/*
-# - tests/*
diff --git a/commands/HelloController.php b/commands/HelloController.php
index 86ab8b853..28dba79cd 100644
--- a/commands/HelloController.php
+++ b/commands/HelloController.php
@@ -1,13 +1,15 @@
=5.4.0",
- "yiisoft/yii2": "~2.0.5",
- "yiisoft/yii2-bootstrap": "~2.0.0",
- "yiisoft/yii2-swiftmailer": "~2.0.0"
+ "php": ">=7.4.0",
+ "yiisoft/yii2": "~2.0.45",
+ "yiisoft/yii2-bootstrap5": "~2.0.2",
+ "yiisoft/yii2-symfonymailer": "~2.0.3"
},
"require-dev": {
- "yiisoft/yii2-debug": "~2.0.0",
- "yiisoft/yii2-gii": "~2.0.0",
+ "yiisoft/yii2-debug": "~2.1.0",
+ "yiisoft/yii2-gii": "~2.2.0",
"yiisoft/yii2-faker": "~2.0.0",
-
- "codeception/base": "^2.2.3",
- "codeception/verify": "~0.3.1",
- "codeception/specify": "~0.4.3"
+ "codeception/codeception": "^5.0.0 || ^4.0",
+ "codeception/lib-innerbrowser": "^4.0 || ^3.0 || ^1.1",
+ "codeception/module-asserts": "^3.0 || ^1.1",
+ "codeception/module-yii2": "^1.1",
+ "codeception/module-filesystem": "^3.0 || ^2.0 || ^1.1",
+ "codeception/verify": "^3.0 || ^2.2"
},
"config": {
- "process-timeout": 1800
+ "allow-plugins": {
+ "yiisoft/yii2-composer" : true
+ },
+ "process-timeout": 1800,
+ "fxp-asset": {
+ "enabled": false
+ }
},
"scripts": {
+ "post-install-cmd": [
+ "yii\\composer\\Installer::postInstall"
+ ],
"post-create-project-cmd": [
- "yii\\composer\\Installer::postCreateProject"
+ "yii\\composer\\Installer::postCreateProject",
+ "yii\\composer\\Installer::postInstall"
]
},
"extra": {
@@ -44,14 +56,18 @@
"web/assets": "0777",
"yii": "0755"
}
- ],
+ ]
+ },
+ "yii\\composer\\Installer::postInstall": {
"generateCookieValidationKey": [
"config/web.php"
]
- },
- "asset-installer-paths": {
- "npm-asset-library": "vendor/npm",
- "bower-asset-library": "vendor/bower"
}
- }
+ },
+ "repositories": [
+ {
+ "type": "composer",
+ "url": "https://asset-packagist.org/"
+ }
+ ]
}
diff --git a/config/__autocomplete.php b/config/__autocomplete.php
new file mode 100644
index 000000000..e5e1c00d7
--- /dev/null
+++ b/config/__autocomplete.php
@@ -0,0 +1,35 @@
+ 'basic-console',
'basePath' => dirname(__DIR__),
'bootstrap' => ['log'],
'controllerNamespace' => 'app\commands',
+ 'aliases' => [
+ '@bower' => '@vendor/bower-asset',
+ '@npm' => '@vendor/npm-asset',
+ '@tests' => '@app/tests',
+ ],
'components' => [
'cache' => [
'class' => 'yii\caching\FileCache',
@@ -38,6 +43,14 @@
$config['modules']['gii'] = [
'class' => 'yii\gii\Module',
];
+ // configuration adjustments for 'dev' environment
+ // requires version `2.1.21` of yii2-debug module
+ $config['bootstrap'][] = 'debug';
+ $config['modules']['debug'] = [
+ 'class' => 'yii\debug\Module',
+ // uncomment the following to add your IP if you are not connecting from localhost.
+ //'allowedIPs' => ['127.0.0.1', '::1'],
+ ];
}
return $config;
diff --git a/config/db.php b/config/db.php
index c4c12529c..bc75e616f 100644
--- a/config/db.php
+++ b/config/db.php
@@ -6,4 +6,9 @@
'username' => 'root',
'password' => '',
'charset' => 'utf8',
+
+ // Schema cache options (for production environment)
+ //'enableSchemaCache' => true,
+ //'schemaCacheDuration' => 60,
+ //'schemaCache' => 'cache',
];
diff --git a/config/params.php b/config/params.php
index 6ebf2792b..981c621ac 100644
--- a/config/params.php
+++ b/config/params.php
@@ -2,4 +2,6 @@
return [
'adminEmail' => 'admin@example.com',
+ 'senderEmail' => 'noreply@example.com',
+ 'senderName' => 'Example.com mailer',
];
diff --git a/config/test.php b/config/test.php
index a92c2fcde..fb5e2929e 100644
--- a/config/test.php
+++ b/config/test.php
@@ -1,20 +1,29 @@
'basic-tests',
- 'basePath' => dirname(__DIR__),
+ 'basePath' => dirname(__DIR__),
+ 'aliases' => [
+ '@bower' => '@vendor/bower-asset',
+ '@npm' => '@vendor/npm-asset',
+ ],
'language' => 'en-US',
'components' => [
- 'db' => $dbParams,
+ 'db' => $db,
'mailer' => [
+ 'class' => \yii\symfonymailer\Mailer::class,
+ 'viewPath' => '@app/mail',
+ // send all mails to a file by default.
'useFileTransport' => true,
+ 'messageClass' => 'yii\symfonymailer\Message'
],
- 'assetManager' => [
+ 'assetManager' => [
'basePath' => __DIR__ . '/../web/assets',
],
'urlManager' => [
@@ -22,7 +31,7 @@
],
'user' => [
'identityClass' => 'app\models\User',
- ],
+ ],
'request' => [
'cookieValidationKey' => 'test',
'enableCsrfValidation' => false,
@@ -32,7 +41,7 @@
'domain' => 'localhost',
],
*/
- ],
+ ],
],
'params' => $params,
];
diff --git a/config/test_db.php b/config/test_db.php
index c951052e3..f10835fe1 100644
--- a/config/test_db.php
+++ b/config/test_db.php
@@ -1,6 +1,7 @@
'basic',
'basePath' => dirname(__DIR__),
'bootstrap' => ['log'],
+ 'aliases' => [
+ '@bower' => '@vendor/bower-asset',
+ '@npm' => '@vendor/npm-asset',
+ ],
'components' => [
'request' => [
// !!! insert a secret key in the following (if it is empty) - this is required by cookie validation
@@ -22,10 +27,9 @@
'errorAction' => 'site/error',
],
'mailer' => [
- 'class' => 'yii\swiftmailer\Mailer',
- // send all mails to a file by default. You have to set
- // 'useFileTransport' to false and configure a transport
- // for the mailer to send real emails.
+ 'class' => \yii\symfonymailer\Mailer::class,
+ 'viewPath' => '@app/mail',
+ // send all mails to a file by default.
'useFileTransport' => true,
],
'log' => [
@@ -37,7 +41,7 @@
],
],
],
- 'db' => require(__DIR__ . '/db.php'),
+ 'db' => $db,
/*
'urlManager' => [
'enablePrettyUrl' => true,
diff --git a/controllers/SiteController.php b/controllers/SiteController.php
index 36dc9591b..67c3f50f8 100644
--- a/controllers/SiteController.php
+++ b/controllers/SiteController.php
@@ -5,6 +5,7 @@
use Yii;
use yii\filters\AccessControl;
use yii\web\Controller;
+use yii\web\Response;
use yii\filters\VerbFilter;
use app\models\LoginForm;
use app\models\ContactForm;
@@ -12,13 +13,13 @@
class SiteController extends Controller
{
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function behaviors()
{
return [
'access' => [
- 'class' => AccessControl::className(),
+ 'class' => AccessControl::class,
'only' => ['logout'],
'rules' => [
[
@@ -29,7 +30,7 @@ public function behaviors()
],
],
'verbs' => [
- 'class' => VerbFilter::className(),
+ 'class' => VerbFilter::class,
'actions' => [
'logout' => ['post'],
],
@@ -38,7 +39,7 @@ public function behaviors()
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function actions()
{
@@ -66,7 +67,7 @@ public function actionIndex()
/**
* Login action.
*
- * @return string
+ * @return Response|string
*/
public function actionLogin()
{
@@ -78,6 +79,8 @@ public function actionLogin()
if ($model->load(Yii::$app->request->post()) && $model->login()) {
return $this->goBack();
}
+
+ $model->password = '';
return $this->render('login', [
'model' => $model,
]);
@@ -86,7 +89,7 @@ public function actionLogin()
/**
* Logout action.
*
- * @return string
+ * @return Response
*/
public function actionLogout()
{
@@ -98,7 +101,7 @@ public function actionLogout()
/**
* Displays contact page.
*
- * @return string
+ * @return Response|string
*/
public function actionContact()
{
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 000000000..86be3bd0d
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,9 @@
+version: '2'
+services:
+ php:
+ image: yiisoftware/yii2-php:7.4-apache
+ volumes:
+ - ~/.composer-docker/cache:/root/.composer/cache:delegated
+ - ./:/app:delegated
+ ports:
+ - '8000:80'
diff --git a/mail/layouts/html.php b/mail/layouts/html.php
index bddbc6129..95732cdfa 100644
--- a/mail/layouts/html.php
+++ b/mail/layouts/html.php
@@ -1,9 +1,9 @@
beginPage() ?>
diff --git a/mail/layouts/text.php b/mail/layouts/text.php
new file mode 100644
index 000000000..0873d7728
--- /dev/null
+++ b/mail/layouts/text.php
@@ -0,0 +1,13 @@
+beginPage();
+$this->beginBody();
+echo $content;
+$this->endBody();
+$this->endPage();
diff --git a/models/ContactForm.php b/models/ContactForm.php
index 0b13cb05d..f001d2192 100644
--- a/models/ContactForm.php
+++ b/models/ContactForm.php
@@ -52,7 +52,8 @@ public function contact($email)
if ($this->validate()) {
Yii::$app->mailer->compose()
->setTo($email)
- ->setFrom([$this->email => $this->name])
+ ->setFrom([Yii::$app->params['senderEmail'] => Yii::$app->params['senderName']])
+ ->setReplyTo([$this->email => $this->name])
->setSubject($this->subject)
->setTextBody($this->body)
->send();
diff --git a/models/LoginForm.php b/models/LoginForm.php
index cc6af264c..5304eecf4 100644
--- a/models/LoginForm.php
+++ b/models/LoginForm.php
@@ -8,7 +8,7 @@
/**
* LoginForm is the model behind the login form.
*
- * @property User|null $user This property is read-only.
+ * @property-read User|null $user
*
*/
class LoginForm extends Model
@@ -60,7 +60,7 @@ public function validatePassword($attribute, $params)
public function login()
{
if ($this->validate()) {
- return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600*24*30 : 0);
+ return Yii::$app->user->login($this->getUser(), $this->rememberMe ? 3600 * 24 * 30 : 0);
}
return false;
}
diff --git a/models/User.php b/models/User.php
index 250fb3962..2e3fb25ed 100644
--- a/models/User.php
+++ b/models/User.php
@@ -2,7 +2,7 @@
namespace app\models;
-class User extends \yii\base\Object implements \yii\web\IdentityInterface
+class User extends \yii\base\BaseObject implements \yii\web\IdentityInterface
{
public $id;
public $username;
@@ -29,7 +29,7 @@ class User extends \yii\base\Object implements \yii\web\IdentityInterface
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public static function findIdentity($id)
{
@@ -37,7 +37,7 @@ public static function findIdentity($id)
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public static function findIdentityByAccessToken($token, $type = null)
{
@@ -68,7 +68,7 @@ public static function findByUsername($username)
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getId()
{
@@ -76,7 +76,7 @@ public function getId()
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function getAuthKey()
{
@@ -84,7 +84,7 @@ public function getAuthKey()
}
/**
- * @inheritdoc
+ * {@inheritdoc}
*/
public function validateAuthKey($authKey)
{
diff --git a/requirements.php b/requirements.php
index 5d1973268..db69a36ee 100644
--- a/requirements.php
+++ b/requirements.php
@@ -1,4 +1,5 @@
Error';
- echo 'The path to yii framework seems to be incorrect.
';
- echo 'You need to install Yii framework via composer or adjust the framework path in file ' . basename(__FILE__) . '.
';
- echo 'Please refer to the README on how to install Yii.
';
+if (!isset($frameworkPath)) {
+ $searchPaths = array(
+ dirname(__FILE__) . '/vendor/yiisoft/yii2',
+ dirname(__FILE__) . '/../vendor/yiisoft/yii2',
+ );
+ foreach ($searchPaths as $path) {
+ if (is_dir($path)) {
+ $frameworkPath = $path;
+ break;
+ }
+ }
+}
+
+if (!isset($frameworkPath) || !is_dir($frameworkPath)) {
+ $message = "Error
\n\n"
+ . "The path to yii framework seems to be incorrect.
\n"
+ . 'You need to install Yii framework via composer or adjust the framework path in file ' . basename(__FILE__) . ".
\n"
+ . 'Please refer to the README on how to install Yii.
\n";
+
+ if (!empty($_SERVER['argv'])) {
+ // do not print HTML when used in console mode
+ echo strip_tags($message);
+ } else {
+ echo $message;
+ }
+ exit(1);
}
require_once($frameworkPath . '/requirements/YiiRequirementChecker.php');
@@ -82,22 +106,22 @@
'name' => 'Memcache extension',
'mandatory' => false,
'condition' => extension_loaded('memcache') || extension_loaded('memcached'),
- 'by' => 'MemCache',
- 'memo' => extension_loaded('memcached') ? 'To use memcached set MemCache::useMemcached to true.' : ''
+ 'by' => 'MemCache',
+ 'memo' => extension_loaded('memcached') ? 'To use memcached set MemCache::useMemcached to true.' : ''
),
// CAPTCHA:
array(
'name' => 'GD PHP extension with FreeType support',
'mandatory' => false,
'condition' => $gdOK,
- 'by' => 'Captcha',
+ 'by' => 'Captcha',
'memo' => $gdMemo,
),
array(
'name' => 'ImageMagick PHP extension with PNG support',
'mandatory' => false,
'condition' => $imagickOK,
- 'by' => 'Captcha',
+ 'by' => 'Captcha',
'memo' => $imagickMemo,
),
// PHP ini :
@@ -130,8 +154,10 @@
'name' => 'APC extension',
'mandatory' => false,
'condition' => extension_loaded('apc'),
- 'by' => 'ApcCache',
+ 'by' => 'ApcCache',
);
}
-$requirementsChecker->checkYii()->check($requirements)->render();
+$result = $requirementsChecker->checkYii()->check($requirements)->getResult();
+$requirementsChecker->render();
+exit($result['summary']['errors'] === 0 ? 0 : 1);
diff --git a/tests/_bootstrap.php b/tests/_bootstrap.php
index 969c089b9..4b8424f73 100644
--- a/tests/_bootstrap.php
+++ b/tests/_bootstrap.php
@@ -1,6 +1,7 @@
amOnPage(Url::toRoute('/site/contact'));
}
-
+
public function contactPageWorks(AcceptanceTester $I)
{
$I->wantTo('ensure that contact page works');
@@ -25,7 +25,7 @@ public function contactFormCanBeSubmitted(AcceptanceTester $I)
$I->fillField('#contactform-verifycode', 'testme');
$I->click('contact-button');
-
+
$I->wait(2); // wait for button to be clicked
$I->dontSeeElement('#contact-form');
diff --git a/tests/acceptance/HomeCest.php b/tests/acceptance/HomeCest.php
index cb8faea71..98fb6b9f7 100644
--- a/tests/acceptance/HomeCest.php
+++ b/tests/acceptance/HomeCest.php
@@ -1,17 +1,18 @@
amOnPage(Url::toRoute('/site/index'));
+ $I->amOnPage(Url::toRoute('/site/index'));
$I->see('My Company');
-
+
$I->seeLink('About');
$I->click('About');
$I->wait(2); // wait for page to be opened
-
+
$I->see('This is the About page.');
}
}
diff --git a/tests/acceptance/LoginCest.php b/tests/acceptance/LoginCest.php
index 9ffc395ba..6f5cb2f38 100644
--- a/tests/acceptance/LoginCest.php
+++ b/tests/acceptance/LoginCest.php
@@ -1,5 +1,6 @@
[
- 'db' => require(__DIR__ . '/../../config/test_db.php')
+ 'db' => require __DIR__ . '/../../config/test_db.php'
]
]
);
@@ -26,4 +26,4 @@ $config = yii\helpers\ArrayHelper::merge(
$application = new yii\console\Application($config);
$exitCode = $application->run();
-exit($exitCode);
\ No newline at end of file
+exit($exitCode);
diff --git a/tests/bin/yii.bat b/tests/bin/yii.bat
index d516b3a19..ce14c92bc 100644
--- a/tests/bin/yii.bat
+++ b/tests/bin/yii.bat
@@ -4,9 +4,9 @@ rem -------------------------------------------------------------
rem Yii command line bootstrap script for Windows.
rem
rem @author Qiang Xue
-rem @link http://www.yiiframework.com/
+rem @link https://www.yiiframework.com/
rem @copyright Copyright (c) 2008 Yii Software LLC
-rem @license http://www.yiiframework.com/license/
+rem @license https://www.yiiframework.com/license/
rem -------------------------------------------------------------
@setlocal
diff --git a/tests/functional.suite.yml b/tests/functional.suite.yml
index 374c6df45..9d8cf149c 100644
--- a/tests/functional.suite.yml
+++ b/tests/functional.suite.yml
@@ -6,8 +6,9 @@
# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
#basic/web/index.php
-class_name: FunctionalTester
+actor: FunctionalTester
modules:
enabled:
- Filesystem
- Yii2
+ - Asserts
diff --git a/tests/functional/ContactFormCest.php b/tests/functional/ContactFormCest.php
index 7f198bf6d..f1ccaf18f 100644
--- a/tests/functional/ContactFormCest.php
+++ b/tests/functional/ContactFormCest.php
@@ -1,14 +1,15 @@
amOnPage(['site/contact']);
+ $I->amOnRoute('site/contact');
}
public function openContactPage(\FunctionalTester $I)
{
- $I->see('Contact', 'h1');
+ $I->see('Contact', 'h1');
}
public function submitEmptyForm(\FunctionalTester $I)
@@ -37,7 +38,7 @@ public function submitFormWithIncorrectEmail(\FunctionalTester $I)
$I->see('Email is not a valid email address.');
$I->dontSee('Subject cannot be blank', '.help-inline');
$I->dontSee('Body cannot be blank', '.help-inline');
- $I->dontSee('The verification code is incorrect', '.help-inline');
+ $I->dontSee('The verification code is incorrect', '.help-inline');
}
public function submitFormSuccessfully(\FunctionalTester $I)
@@ -51,6 +52,6 @@ public function submitFormSuccessfully(\FunctionalTester $I)
]);
$I->seeEmailIsSent();
$I->dontSeeElement('#contact-form');
- $I->see('Thank you for contacting us. We will respond to you as soon as possible.');
+ $I->see('Thank you for contacting us. We will respond to you as soon as possible.');
}
}
diff --git a/tests/functional/LoginFormCest.php b/tests/functional/LoginFormCest.php
index 9ed74b113..a1545a706 100644
--- a/tests/functional/LoginFormCest.php
+++ b/tests/functional/LoginFormCest.php
@@ -1,4 +1,5 @@
see('Login', 'h1');
-
}
// demonstrates `amLoggedInAs` method
@@ -53,6 +53,6 @@ public function loginSuccessfully(\FunctionalTester $I)
'LoginForm[password]' => 'admin',
]);
$I->see('Logout (admin)');
- $I->dontSeeElement('form#login-form');
+ $I->dontSeeElement('form#login-form');
}
-}
\ No newline at end of file
+}
diff --git a/tests/unit.suite.yml b/tests/unit.suite.yml
index b1efaba99..c14e49c37 100644
--- a/tests/unit.suite.yml
+++ b/tests/unit.suite.yml
@@ -3,9 +3,9 @@
# suite for unit (internal) tests.
# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
-class_name: UnitTester
+actor: UnitTester
modules:
enabled:
- Asserts
- Yii2:
- part: [orm, email]
\ No newline at end of file
+ part: [orm, email, fixtures]
diff --git a/tests/unit/models/ContactFormTest.php b/tests/unit/models/ContactFormTest.php
index 81a67e9cc..1bb1bb2d0 100644
--- a/tests/unit/models/ContactFormTest.php
+++ b/tests/unit/models/ContactFormTest.php
@@ -1,12 +1,12 @@
model = $this->getMockBuilder('app\models\ContactForm')
- ->setMethods(['validate'])
- ->getMock();
+ $model = new ContactForm();
- $this->model->expects($this->once())
- ->method('validate')
- ->will($this->returnValue(true));
-
- $this->model->attributes = [
+ $model->attributes = [
'name' => 'Tester',
'email' => 'tester@example.com',
'subject' => 'very important letter subject',
'body' => 'body of current message',
+ 'verifyCode' => 'testme',
];
- expect_that($this->model->contact('admin@example.com'));
+ verify($model->contact('admin@example.com'))->notEmpty();
// using Yii2 module actions to check email was sent
$this->tester->seeEmailIsSent();
+ /** @var MessageInterface $emailMessage */
$emailMessage = $this->tester->grabLastSentEmail();
- expect('valid email is sent', $emailMessage)->isInstanceOf('yii\mail\MessageInterface');
- expect($emailMessage->getTo())->hasKey('admin@example.com');
- expect($emailMessage->getFrom())->hasKey('tester@example.com');
- expect($emailMessage->getSubject())->equals('very important letter subject');
- expect($emailMessage->toString())->contains('body of current message');
+ verify($emailMessage)->instanceOf('yii\mail\MessageInterface');
+ verify($emailMessage->getTo())->arrayHasKey('admin@example.com');
+ verify($emailMessage->getFrom())->arrayHasKey('noreply@example.com');
+ verify($emailMessage->getReplyTo())->arrayHasKey('tester@example.com');
+ verify($emailMessage->getSubject())->equals('very important letter subject');
+ verify($emailMessage->toString())->stringContainsString('body of current message');
}
}
diff --git a/tests/unit/models/LoginFormTest.php b/tests/unit/models/LoginFormTest.php
index 46190cd05..6f96c2377 100644
--- a/tests/unit/models/LoginFormTest.php
+++ b/tests/unit/models/LoginFormTest.php
@@ -1,9 +1,8 @@
'not_existing_password',
]);
- expect_not($this->model->login());
- expect_that(\Yii::$app->user->isGuest);
+ verify($this->model->login())->false();
+ verify(\Yii::$app->user->isGuest)->true();
}
public function testLoginWrongPassword()
@@ -32,9 +31,9 @@ public function testLoginWrongPassword()
'password' => 'wrong_password',
]);
- expect_not($this->model->login());
- expect_that(\Yii::$app->user->isGuest);
- expect($this->model->errors)->hasKey('password');
+ verify($this->model->login())->false();
+ verify(\Yii::$app->user->isGuest)->true();
+ verify($this->model->errors)->arrayHasKey('password');
}
public function testLoginCorrect()
@@ -44,9 +43,8 @@ public function testLoginCorrect()
'password' => 'demo',
]);
- expect_that($this->model->login());
- expect_not(\Yii::$app->user->isGuest);
- expect($this->model->errors)->hasntKey('password');
+ verify($this->model->login())->true();
+ verify(\Yii::$app->user->isGuest)->false();
+ verify($this->model->errors)->arrayHasNotKey('password');
}
-
}
diff --git a/tests/unit/models/UserTest.php b/tests/unit/models/UserTest.php
index 2187ab8ca..3db9772a3 100644
--- a/tests/unit/models/UserTest.php
+++ b/tests/unit/models/UserTest.php
@@ -1,42 +1,43 @@
username)->equals('admin');
+ verify($user = User::findIdentity(100))->notEmpty();
+ verify($user->username)->equals('admin');
- expect_not(User::findIdentity(999));
+ verify(User::findIdentity(999))->empty();
}
public function testFindUserByAccessToken()
{
- expect_that($user = User::findIdentityByAccessToken('100-token'));
- expect($user->username)->equals('admin');
+ verify($user = User::findIdentityByAccessToken('100-token'))->notEmpty();
+ verify($user->username)->equals('admin');
- expect_not(User::findIdentityByAccessToken('non-existing'));
+ verify(User::findIdentityByAccessToken('non-existing'))->empty();
}
public function testFindUserByUsername()
{
- expect_that($user = User::findByUsername('admin'));
- expect_not(User::findByUsername('not-admin'));
+ verify($user = User::findByUsername('admin'))->notEmpty();
+ verify(User::findByUsername('not-admin'))->empty();
}
/**
* @depends testFindUserByUsername
*/
- public function testValidateUser($user)
+ public function testValidateUser()
{
$user = User::findByUsername('admin');
- expect_that($user->validateAuthKey('test100key'));
- expect_not($user->validateAuthKey('test102key'));
+ verify($user->validateAuthKey('test100key'))->notEmpty();
+ verify($user->validateAuthKey('test102key'))->empty();
- expect_that($user->validatePassword('admin'));
- expect_not($user->validatePassword('123456'));
+ verify($user->validatePassword('admin'))->notEmpty();
+ verify($user->validatePassword('123456'))->empty();
}
-
}
diff --git a/tests/unit/widgets/AlertTest.php b/tests/unit/widgets/AlertTest.php
new file mode 100644
index 000000000..af2db5d21
--- /dev/null
+++ b/tests/unit/widgets/AlertTest.php
@@ -0,0 +1,263 @@
+session->setFlash('error', $message);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($message);
+ verify($renderingResult)->stringContainsString('alert-danger');
+
+ verify($renderingResult)->stringNotContainsString('alert-success');
+ verify($renderingResult)->stringNotContainsString('alert-info');
+ verify($renderingResult)->stringNotContainsString('alert-warning');
+ }
+
+ public function testMultipleErrorMessages()
+ {
+ $firstMessage = 'This is the first error message';
+ $secondMessage = 'This is the second error message';
+
+ Yii::$app->session->setFlash('error', [$firstMessage, $secondMessage]);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($firstMessage);
+ verify($renderingResult)->stringContainsString($secondMessage);
+ verify($renderingResult)->stringContainsString('alert-danger');
+
+ verify($renderingResult)->stringNotContainsString('alert-success');
+ verify($renderingResult)->stringNotContainsString('alert-info');
+ verify($renderingResult)->stringNotContainsString('alert-warning');
+ }
+
+ public function testSingleDangerMessage()
+ {
+ $message = 'This is a danger message';
+
+ Yii::$app->session->setFlash('danger', $message);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($message);
+ verify($renderingResult)->stringContainsString('alert-danger');
+
+ verify($renderingResult)->stringNotContainsString('alert-success');
+ verify($renderingResult)->stringNotContainsString('alert-info');
+ verify($renderingResult)->stringNotContainsString('alert-warning');
+ }
+
+ public function testMultipleDangerMessages()
+ {
+ $firstMessage = 'This is the first danger message';
+ $secondMessage = 'This is the second danger message';
+
+ Yii::$app->session->setFlash('danger', [$firstMessage, $secondMessage]);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($firstMessage);
+ verify($renderingResult)->stringContainsString($secondMessage);
+ verify($renderingResult)->stringContainsString('alert-danger');
+
+ verify($renderingResult)->stringNotContainsString('alert-success');
+ verify($renderingResult)->stringNotContainsString('alert-info');
+ verify($renderingResult)->stringNotContainsString('alert-warning');
+ }
+
+ public function testSingleSuccessMessage()
+ {
+ $message = 'This is a success message';
+
+ Yii::$app->session->setFlash('success', $message);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($message);
+ verify($renderingResult)->stringContainsString('alert-success');
+
+ verify($renderingResult)->stringNotContainsString('alert-danger');
+ verify($renderingResult)->stringNotContainsString('alert-info');
+ verify($renderingResult)->stringNotContainsString('alert-warning');
+ }
+
+ public function testMultipleSuccessMessages()
+ {
+ $firstMessage = 'This is the first danger message';
+ $secondMessage = 'This is the second danger message';
+
+ Yii::$app->session->setFlash('success', [$firstMessage, $secondMessage]);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($firstMessage);
+ verify($renderingResult)->stringContainsString($secondMessage);
+ verify($renderingResult)->stringContainsString('alert-success');
+
+ verify($renderingResult)->stringNotContainsString('alert-danger');
+ verify($renderingResult)->stringNotContainsString('alert-info');
+ verify($renderingResult)->stringNotContainsString('alert-warning');
+ }
+
+ public function testSingleInfoMessage()
+ {
+ $message = 'This is an info message';
+
+ Yii::$app->session->setFlash('info', $message);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($message);
+ verify($renderingResult)->stringContainsString('alert-info');
+
+ verify($renderingResult)->stringNotContainsString('alert-danger');
+ verify($renderingResult)->stringNotContainsString('alert-success');
+ verify($renderingResult)->stringNotContainsString('alert-warning');
+ }
+
+ public function testMultipleInfoMessages()
+ {
+ $firstMessage = 'This is the first info message';
+ $secondMessage = 'This is the second info message';
+
+ Yii::$app->session->setFlash('info', [$firstMessage, $secondMessage]);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($firstMessage);
+ verify($renderingResult)->stringContainsString($secondMessage);
+ verify($renderingResult)->stringContainsString('alert-info');
+
+ verify($renderingResult)->stringNotContainsString('alert-danger');
+ verify($renderingResult)->stringNotContainsString('alert-success');
+ verify($renderingResult)->stringNotContainsString('alert-warning');
+ }
+
+ public function testSingleWarningMessage()
+ {
+ $message = 'This is a warning message';
+
+ Yii::$app->session->setFlash('warning', $message);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($message);
+ verify($renderingResult)->stringContainsString('alert-warning');
+
+ verify($renderingResult)->stringNotContainsString('alert-danger');
+ verify($renderingResult)->stringNotContainsString('alert-success');
+ verify($renderingResult)->stringNotContainsString('alert-info');
+ }
+
+ public function testMultipleWarningMessages()
+ {
+ $firstMessage = 'This is the first warning message';
+ $secondMessage = 'This is the second warning message';
+
+ Yii::$app->session->setFlash('warning', [$firstMessage, $secondMessage]);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($firstMessage);
+ verify($renderingResult)->stringContainsString($secondMessage);
+ verify($renderingResult)->stringContainsString('alert-warning');
+
+ verify($renderingResult)->stringNotContainsString('alert-danger');
+ verify($renderingResult)->stringNotContainsString('alert-success');
+ verify($renderingResult)->stringNotContainsString('alert-info');
+ }
+
+ public function testSingleMixedMessages()
+ {
+ $errorMessage = 'This is an error message';
+ $dangerMessage = 'This is a danger message';
+ $successMessage = 'This is a success message';
+ $infoMessage = 'This is a info message';
+ $warningMessage = 'This is a warning message';
+
+ Yii::$app->session->setFlash('error', $errorMessage);
+ Yii::$app->session->setFlash('danger', $dangerMessage);
+ Yii::$app->session->setFlash('success', $successMessage);
+ Yii::$app->session->setFlash('info', $infoMessage);
+ Yii::$app->session->setFlash('warning', $warningMessage);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($errorMessage);
+ verify($renderingResult)->stringContainsString($dangerMessage);
+ verify($renderingResult)->stringContainsString($successMessage);
+ verify($renderingResult)->stringContainsString($infoMessage);
+ verify($renderingResult)->stringContainsString($warningMessage);
+
+ verify($renderingResult)->stringContainsString('alert-danger');
+ verify($renderingResult)->stringContainsString('alert-success');
+ verify($renderingResult)->stringContainsString('alert-info');
+ verify($renderingResult)->stringContainsString('alert-warning');
+ }
+
+ public function testMultipleMixedMessages()
+ {
+ $firstErrorMessage = 'This is the first error message';
+ $secondErrorMessage = 'This is the second error message';
+ $firstDangerMessage = 'This is the first danger message';
+ $secondDangerMessage = 'This is the second';
+ $firstSuccessMessage = 'This is the first success message';
+ $secondSuccessMessage = 'This is the second success message';
+ $firstInfoMessage = 'This is the first info message';
+ $secondInfoMessage = 'This is the second info message';
+ $firstWarningMessage = 'This is the first warning message';
+ $secondWarningMessage = 'This is the second warning message';
+
+ Yii::$app->session->setFlash('error', [$firstErrorMessage, $secondErrorMessage]);
+ Yii::$app->session->setFlash('danger', [$firstDangerMessage, $secondDangerMessage]);
+ Yii::$app->session->setFlash('success', [$firstSuccessMessage, $secondSuccessMessage]);
+ Yii::$app->session->setFlash('info', [$firstInfoMessage, $secondInfoMessage]);
+ Yii::$app->session->setFlash('warning', [$firstWarningMessage, $secondWarningMessage]);
+
+ $renderingResult = Alert::widget();
+
+ verify($renderingResult)->stringContainsString($firstErrorMessage);
+ verify($renderingResult)->stringContainsString($secondErrorMessage);
+ verify($renderingResult)->stringContainsString($firstDangerMessage);
+ verify($renderingResult)->stringContainsString($secondDangerMessage);
+ verify($renderingResult)->stringContainsString($firstSuccessMessage);
+ verify($renderingResult)->stringContainsString($secondSuccessMessage);
+ verify($renderingResult)->stringContainsString($firstInfoMessage);
+ verify($renderingResult)->stringContainsString($secondInfoMessage);
+ verify($renderingResult)->stringContainsString($firstWarningMessage);
+ verify($renderingResult)->stringContainsString($secondWarningMessage);
+
+ verify($renderingResult)->stringContainsString('alert-danger');
+ verify($renderingResult)->stringContainsString('alert-success');
+ verify($renderingResult)->stringContainsString('alert-info');
+ verify($renderingResult)->stringContainsString('alert-warning');
+ }
+
+ public function testFlashIntegrity()
+ {
+ $errorMessage = 'This is an error message';
+ $unrelatedMessage = 'This is a message that is not related to the alert widget';
+
+ Yii::$app->session->setFlash('error', $errorMessage);
+ Yii::$app->session->setFlash('unrelated', $unrelatedMessage);
+
+ Alert::widget();
+
+ // Simulate redirect
+ Yii::$app->session->close();
+ Yii::$app->session->open();
+
+ verify(Yii::$app->session->getFlash('error'))->empty();
+ verify(Yii::$app->session->getFlash('unrelated'))->equals($unrelatedMessage);
+ }
+}
diff --git a/vagrant/config/.gitignore b/vagrant/config/.gitignore
new file mode 100644
index 000000000..0685a56ca
--- /dev/null
+++ b/vagrant/config/.gitignore
@@ -0,0 +1,2 @@
+# local configuration
+vagrant-local.yml
\ No newline at end of file
diff --git a/vagrant/config/vagrant-local.example.yml b/vagrant/config/vagrant-local.example.yml
new file mode 100644
index 000000000..919aec0e8
--- /dev/null
+++ b/vagrant/config/vagrant-local.example.yml
@@ -0,0 +1,22 @@
+# Your personal GitHub token
+github_token:
+# Read more: https://github.com/blog/1509-personal-api-tokens
+# You can generate it here: https://github.com/settings/tokens
+
+# Guest OS timezone
+timezone: Europe/London
+
+# Are we need check box updates for every 'vagrant up'?
+box_check_update: false
+
+# Virtual machine name
+machine_name: yii2basic
+
+# Virtual machine IP
+ip: 192.168.83.137
+
+# Virtual machine CPU cores number
+cpus: 1
+
+# Virtual machine RAM
+memory: 1024
diff --git a/vagrant/nginx/app.conf b/vagrant/nginx/app.conf
new file mode 100644
index 000000000..1bfc0d583
--- /dev/null
+++ b/vagrant/nginx/app.conf
@@ -0,0 +1,38 @@
+server {
+ charset utf-8;
+ client_max_body_size 128M;
+ sendfile off;
+
+ listen 80; ## listen for ipv4
+ #listen [::]:80 default_server ipv6only=on; ## listen for ipv6
+
+ server_name yii2basic.test;
+ root /app/web/;
+ index index.php;
+
+ access_log /app/vagrant/nginx/log/yii2basic.access.log;
+ error_log /app/vagrant/nginx/log/yii2basic.error.log;
+
+ location / {
+ # Redirect everything that isn't a real file to index.php
+ try_files $uri $uri/ /index.php$is_args$args;
+ }
+
+ # uncomment to avoid processing of calls to non-existing static files by Yii
+ #location ~ \.(js|css|png|jpg|gif|swf|ico|pdf|mov|fla|zip|rar)$ {
+ # try_files $uri =404;
+ #}
+ #error_page 404 /404.html;
+
+ location ~ \.php$ {
+ include fastcgi_params;
+ fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
+ #fastcgi_pass 127.0.0.1:9000;
+ fastcgi_pass unix:/var/run/php/php7.2-fpm.sock;
+ try_files $uri =404;
+ }
+
+ location ~ /\.(ht|svn|git) {
+ deny all;
+ }
+}
diff --git a/vagrant/nginx/log/.gitignore b/vagrant/nginx/log/.gitignore
new file mode 100644
index 000000000..c0fbfdd8d
--- /dev/null
+++ b/vagrant/nginx/log/.gitignore
@@ -0,0 +1,3 @@
+#nginx logs
+yii2basic.access.log
+yii2basic.error.log
\ No newline at end of file
diff --git a/vagrant/provision/always-as-root.sh b/vagrant/provision/always-as-root.sh
new file mode 100644
index 000000000..17fb58421
--- /dev/null
+++ b/vagrant/provision/always-as-root.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+
+#== Bash helpers ==
+
+function info {
+ echo " "
+ echo "--> $1"
+ echo " "
+}
+
+#== Provision script ==
+
+info "Provision-script user: `whoami`"
+
+info "Restart web-stack"
+service php7.2-fpm restart
+service nginx restart
+service mysql restart
diff --git a/vagrant/provision/once-as-root.sh b/vagrant/provision/once-as-root.sh
new file mode 100644
index 000000000..1d2917016
--- /dev/null
+++ b/vagrant/provision/once-as-root.sh
@@ -0,0 +1,79 @@
+#!/usr/bin/env bash
+
+#== Import script args ==
+
+timezone=$(echo "$1")
+readonly IP=$2
+
+#== Bash helpers ==
+
+function info {
+ echo " "
+ echo "--> $1"
+ echo " "
+}
+
+#== Provision script ==
+
+info "Provision-script user: `whoami`"
+
+export DEBIAN_FRONTEND=noninteractive
+
+info "Configure timezone"
+timedatectl set-timezone ${timezone} --no-ask-password
+
+info "Add the VM IP to the list of allowed IPs"
+awk -v ip=$IP -f /app/vagrant/provision/provision.awk /app/config/web.php
+
+info "Prepare root password for MySQL"
+debconf-set-selections <<< 'mariadb-server mysql-server/root_password password'
+debconf-set-selections <<< 'mariadb-server mysql-server/root_password_again password'
+echo "Done!"
+
+info "Update OS software"
+apt-get update
+apt-get upgrade -y
+
+info "Install additional software"
+apt-get install -y php7.2-curl php7.2-cli php7.2-intl php7.2-mysqlnd php7.2-gd php7.2-fpm php7.2-mbstring php7.2-xml unzip nginx mariadb-server-10.1 php.xdebug
+
+info "Configure MySQL"
+sed -i 's/.*bind-address.*/bind-address = 0.0.0.0/' /etc/mysql/mariadb.conf.d/50-server.cnf
+mysql <<< "CREATE USER 'root'@'%' IDENTIFIED BY ''"
+mysql <<< "GRANT ALL PRIVILEGES ON *.* TO 'root'@'%'"
+mysql <<< "DROP USER 'root'@'localhost'"
+mysql <<< 'FLUSH PRIVILEGES'
+echo "Done!"
+
+info "Configure PHP-FPM"
+sed -i 's/user = www-data/user = vagrant/g' /etc/php/7.2/fpm/pool.d/www.conf
+sed -i 's/group = www-data/group = vagrant/g' /etc/php/7.2/fpm/pool.d/www.conf
+sed -i 's/owner = www-data/owner = vagrant/g' /etc/php/7.2/fpm/pool.d/www.conf
+cat << EOF > /etc/php/7.2/mods-available/xdebug.ini
+zend_extension=xdebug.so
+xdebug.remote_enable=1
+xdebug.remote_connect_back=1
+xdebug.remote_port=9000
+xdebug.remote_autostart=1
+EOF
+echo "Done!"
+
+info "Configure NGINX"
+sed -i 's/user www-data/user vagrant/g' /etc/nginx/nginx.conf
+echo "Done!"
+
+info "Enabling site configuration"
+ln -s /app/vagrant/nginx/app.conf /etc/nginx/sites-enabled/app.conf
+echo "Done!"
+
+info "Removing default site configuration"
+rm /etc/nginx/sites-enabled/default
+echo "Done!"
+
+info "Initialize databases for MySQL"
+mysql <<< 'CREATE DATABASE yii2basic'
+mysql <<< 'CREATE DATABASE yii2basic_test'
+echo "Done!"
+
+info "Install composer"
+curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
diff --git a/vagrant/provision/once-as-vagrant.sh b/vagrant/provision/once-as-vagrant.sh
new file mode 100644
index 000000000..8da47217c
--- /dev/null
+++ b/vagrant/provision/once-as-vagrant.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+
+#== Import script args ==
+
+github_token=$(echo "$1")
+
+#== Bash helpers ==
+
+function info {
+ echo " "
+ echo "--> $1"
+ echo " "
+}
+
+#== Provision script ==
+
+info "Provision-script user: `whoami`"
+
+info "Configure composer"
+composer config --global github-oauth.github.com ${github_token}
+echo "Done!"
+
+info "Install project dependencies"
+cd /app
+composer --no-progress --prefer-dist install
+
+info "Create bash-alias 'app' for vagrant user"
+echo 'alias app="cd /app"' | tee /home/vagrant/.bash_aliases
+
+info "Enabling colorized prompt for guest console"
+sed -i "s/#force_color_prompt=yes/force_color_prompt=yes/" /home/vagrant/.bashrc
diff --git a/vagrant/provision/provision.awk b/vagrant/provision/provision.awk
new file mode 100644
index 000000000..bcf44d3be
--- /dev/null
+++ b/vagrant/provision/provision.awk
@@ -0,0 +1,50 @@
+###
+# Modifying Yii2's files for Vagrant VM
+#
+# @author HA3IK
+# @version 1.0.0
+
+BEGIN {
+ print "AWK BEGINs its work:"
+ IGNORECASE = 1
+
+ # Correct IP - wildcard last octet
+ match(ip, /(([0-9]+\.)+)/, arr)
+ ip = arr[1] "*"
+}
+# BODY
+{
+ # Check if it's the same file
+ if (FILENAME != isFile["same"]){
+ msg = "- Work with: " FILENAME
+ # Close a previous file
+ close(isFile["same"])
+ # Delete previous data
+ delete isFile
+ # Save current file
+ isFile["same"] = FILENAME
+ # Define array index for the file
+ switch (FILENAME){
+ case /config\/web\.php$/:
+ isFile["IsConfWeb"] = 1
+ msg = msg " - add allowed IP: " ip
+ break
+ }
+ # Print the concatenated message for the file
+ print msg
+ }
+
+ # IF config/web.php
+ if (isFile["IsConfWeb"]){
+ # IF line has "allowedIPs" and doesn't has our IP
+ if (match($0, "allowedIPs") && !match($0, ip)){
+ match($0, /([^\]]+)(.+)/, arr)
+ $0 = sprintf("%s, '%s'%s", arr[1], ip, arr[2])
+ }
+ # Rewrite the file
+ print $0 > FILENAME
+ }
+}
+END {
+ print "AWK ENDs its work."
+}
diff --git a/views/layouts/main.php b/views/layouts/main.php
index 4d256e45b..c204435c0 100644
--- a/views/layouts/main.php
+++ b/views/layouts/main.php
@@ -1,74 +1,79 @@
registerCsrfMetaTags();
+$this->registerMetaTag(['charset' => Yii::$app->charset], 'charset');
+$this->registerMetaTag(['name' => 'viewport', 'content' => 'width=device-width, initial-scale=1, shrink-to-fit=no']);
+$this->registerMetaTag(['name' => 'description', 'content' => $this->params['meta_description'] ?? '']);
+$this->registerMetaTag(['name' => 'keywords', 'content' => $this->params['meta_keywords'] ?? '']);
+$this->registerLinkTag(['rel' => 'icon', 'type' => 'image/x-icon', 'href' => Yii::getAlias('@web/favicon.ico')]);
?>
beginPage() ?>
-
+
-
-
- = Html::csrfMetaTags() ?>
= Html::encode($this->title) ?>
head() ?>
-
+
beginBody() ?>
-
+
+
- = Breadcrumbs::widget([
- 'links' => isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [],
- ]) ?>
+ params['breadcrumbs'])): ?>
+ = Breadcrumbs::widget(['links' => $this->params['breadcrumbs']]) ?>
+
+ = Alert::widget() ?>
= $content ?>
-
+
-