diff --git a/.scrutinizer.yml b/.scrutinizer.yml
new file mode 100644
index 0000000..f6cf5d2
--- /dev/null
+++ b/.scrutinizer.yml
@@ -0,0 +1,6 @@
+imports:
+    - php
+
+tools:
+    external_code_coverage:
+        timeout: 1800 # Timeout in seconds.
diff --git a/.travis.yml b/.travis.yml
index 79253b9..1a457a9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -15,7 +15,6 @@ services:
 install:
   - composer self-update && composer --version
 # core framework:
-#  - composer require satooshi/php-coveralls 0.6.* --dev --prefer-dist
   - composer install --prefer-dist
   - tests/unit/data/travis/mongodb-setup.sh
   - tests/unit/data/travis/apc-setup.sh
@@ -35,9 +34,9 @@ before_script:
   - mongo yii2test --eval 'db.addUser("travis", "test");'
 
 script:
-#  - vendor/bin/phpunit --coverage-clover tests/unit/runtime/coveralls/clover.xml --verbose --exclude-group mssql,oci,wincache,xcache,zenddata,vendor
-  - vendor/bin/phpunit --verbose --exclude-group mssql,oci,wincache,xcache,zenddata,vendor
+  - vendor/bin/phpunit --verbose --coverage-clover=coverage.clover --exclude-group mssql,oci,wincache,xcache,zenddata
   - cd apps/basic && php vendor/bin/codecept run
 
-#after_script:
-#  - php vendor/bin/coveralls
+after_script:
+  - wget https://scrutinizer-ci.com/ocular.phar
+  - php ocular.phar code-coverage:upload --format=php-clover coverage.clover
diff --git a/README.md b/README.md
index 1c07021..cd22a26 100644
--- a/README.md
+++ b/README.md
@@ -21,6 +21,7 @@ which is the latest stable release of Yii.
 [![Latest Stable Version](https://poser.pugx.org/yiisoft/yii2/v/stable.png)](https://packagist.org/packages/yiisoft/yii2)
 [![Total Downloads](https://poser.pugx.org/yiisoft/yii2/downloads.png)](https://packagist.org/packages/yiisoft/yii2)
 [![Build Status](https://secure.travis-ci.org/yiisoft/yii2.png)](http://travis-ci.org/yiisoft/yii2)
+[![Code Coverage](https://scrutinizer-ci.com/g/yiisoft/yii2/badges/coverage.png?s=31d80f1036099e9d6a3e4d7738f6b000b3c3d10e)](https://scrutinizer-ci.com/g/yiisoft/yii2/)
 [![Dependency Status](https://www.versioneye.com/php/yiisoft:yii2/dev-master/badge.png)](https://www.versioneye.com/php/yiisoft:yii2/dev-master)
 [![Scrutinizer Quality Score](https://scrutinizer-ci.com/g/yiisoft/yii2/badges/quality-score.png?s=b1074a1ff6d0b214d54fa5ab7abbb90fc092471d)](https://scrutinizer-ci.com/g/yiisoft/yii2/)
 
@@ -49,8 +50,9 @@ DOCUMENTATION
 
 A draft of the [Definitive Guide](docs/guide/index.md) is available.
 
-API docs and a (quite bad) rendering of the definitive guide are currently
+API docs and a rendering of the definitive guide are currently
 available at http://stuff.cebe.cc/yii2docs/ (updated four times per hour).
+We will make guide and API docs available on yiiframework.com with the beta release.
 
 For 1.1 users, you may refer to [Upgrading from Yii 1.1](docs/guide/upgrade-from-v1.md)
 to have a general idea of what has changed in 2.0.
diff --git a/apps/advanced/README.md b/apps/advanced/README.md
index 3bafa19..62f090a 100644
--- a/apps/advanced/README.md
+++ b/apps/advanced/README.md
@@ -16,32 +16,37 @@ DIRECTORY STRUCTURE
 
 ```
 common
-	config/             contains shared configurations
-	models/             contains model classes used in both backend and frontend
+	config/				contains shared configurations
+	mail/				contains view files for e-mails
+	models/				contains model classes used in both backend and frontend
+	tests/				contains various tests for objects that are common among applications
 console
-	config/             contains console configurations
-	controllers/        contains console controllers (commands)
-	migrations/         contains database migrations
-	models/             contains console-specific model classes
-	runtime/            contains files generated during runtime
+	config/				contains console configurations
+	controllers/		contains console controllers (commands)
+	migrations/			contains database migrations
+	models/				contains console-specific model classes
+	runtime/			contains files generated during runtime
+	tests/				contains various tests for the console application
 backend
-	assets/             contains application assets such as JavaScript and CSS
-	config/             contains backend configurations
-	controllers/        contains Web controller classes
-	models/             contains backend-specific model classes
-	runtime/            contains files generated during runtime
-	views/              contains view files for the Web application
-	web/                contains the entry script and Web resources
+	assets/				contains application assets such as JavaScript and CSS
+	config/				contains backend configurations
+	controllers/		contains Web controller classes
+	models/				contains backend-specific model classes
+	runtime/			contains files generated during runtime
+	tests/				contains various tests for the backend application
+	views/				contains view files for the Web application
+	web/				contains the entry script and Web resources
 frontend
-	assets/             contains application assets such as JavaScript and CSS
-	config/             contains frontend configurations
-	controllers/        contains Web controller classes
-	models/             contains frontend-specific model classes
-	runtime/            contains files generated during runtime
-	views/              contains view files for the Web application
-	web/                contains the entry script and Web resources
-vendor/                 contains dependent 3rd-party packages
-environments/                contains environment-based overrides
+	assets/				contains application assets such as JavaScript and CSS
+	config/				contains frontend configurations
+	controllers/		contains Web controller classes
+	models/				contains frontend-specific model classes
+	runtime/			contains files generated during runtime
+	tests/				contains various tests for the frontend application
+	views/				contains view files for the Web application
+	web/				contains the entry script and Web resources
+vendor/					contains dependent 3rd-party packages
+environments/			contains environment-based overrides
 ```
 
 
@@ -82,8 +87,39 @@ the installed application. You only need to do these once for all.
 
 1. Run command `init` to initialize the application with a specific environment.
 2. Create a new database and adjust the `components['db']` configuration in `common/config/main-local.php` accordingly.
-3. Apply migrations with console command `yii migrate`.
+3. Apply migrations with console command `yii migrate`. This will create tables needed for the application to work.
 4. Set document roots of your Web server:
 
 - for frontend `/path/to/yii-application/frontend/web/` and using the URL `http://frontend/`
 - for backend `/path/to/yii-application/backend/web/` and using the URL `http://backend/`
+
+To login into the application, you need to first sign up, with any of your email address, username and password.
+Then, you can login into the application with same email address and password at any time.
+
+TESTING
+-------
+
+Install additional composer packages:
+* `php composer.phar require --dev "codeception/codeception: 1.8.*@dev" "codeception/specify: *" "codeception/verify: *"`
+
+This application boilerplate use database in testing, so you should create three databases that are used in tests:
+* `yii2_advanced_unit` - database for unit tests;
+* `yii2_advanced_functional` - database for functional tests;
+* `yii2_advanced_acceptance` - database for acceptance tests.
+
+To make your database up to date, you can run in needed test folder `yii migrate`, for example
+if you are starting from `frontend` tests then you should run `yii migrate` in each suite folder `acceptance`, `functional`, `unit`
+it will upgrade your database to the last state according migrations.
+
+To be able to run acceptance tests you should configure your server to point doc_root to your new created application. For example if we
+use php builtin server, then all that is needed to do is run `php -S 127.0.0.1:8080` in main project directory - directory that contains `frontend`, `backend`, `common`, `console` directories.
+
+After that is done you should be able to run your tests, for example to run `frontend` tests do:
+
+* `cd frontend`
+* `../vendor/bin/codecept build`
+* `../vendor/bin/codecept run`
+
+In similar way you can run tests for other application tiers - `backend`, `console`, `common`.
+
+You also can adjust you application suite configs and `_bootstrap.php` settings to use other urls and files, as it is can be done in `yii2-basic`.
diff --git a/apps/advanced/backend/codeception.yml b/apps/advanced/backend/codeception.yml
new file mode 100644
index 0000000..5b1f441
--- /dev/null
+++ b/apps/advanced/backend/codeception.yml
@@ -0,0 +1,18 @@
+paths:
+    tests: tests
+    log: tests/_log
+    data: tests/_data
+    helpers: tests/_helpers
+settings:
+    bootstrap: _bootstrap.php
+    suite_class: \PHPUnit_Framework_TestSuite
+    colors: true
+    memory_limit: 1024M
+    log: true
+modules:
+    config:
+        Db:
+            dsn: ''
+            user: ''
+            password: ''
+            dump: tests/_data/dump.sql
diff --git a/apps/advanced/backend/tests/_bootstrap.php b/apps/advanced/backend/tests/_bootstrap.php
new file mode 100644
index 0000000..ee6d24a
--- /dev/null
+++ b/apps/advanced/backend/tests/_bootstrap.php
@@ -0,0 +1,23 @@
+<?php
+
+// the entry script URL (without host info) for functional and acceptance tests
+// PLEASE ADJUST IT TO THE ACTUAL ENTRY SCRIPT URL
+defined('TEST_ENTRY_URL') or define('TEST_ENTRY_URL', '/backend/web/index-test.php');
+
+// the entry script file path for functional and acceptance tests
+defined('TEST_ENTRY_FILE') or define('TEST_ENTRY_FILE', dirname(__DIR__) . '/web/index-test.php');
+
+defined('YII_DEBUG') or define('YII_DEBUG', true);
+
+defined('YII_ENV') or define('YII_ENV', 'test');
+
+require_once(__DIR__ . '/../../vendor/autoload.php');
+
+require_once(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
+
+require(__DIR__ . '/../../common/config/aliases.php');
+
+// set correct script paths
+$_SERVER['SCRIPT_FILENAME'] = TEST_ENTRY_FILE;
+$_SERVER['SCRIPT_NAME'] = TEST_ENTRY_URL;
+$_SERVER['SERVER_NAME'] = 'localhost';
diff --git a/apps/advanced/backend/tests/_config.php b/apps/advanced/backend/tests/_config.php
new file mode 100644
index 0000000..d9cc356
--- /dev/null
+++ b/apps/advanced/backend/tests/_config.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * application configurations shared by all test types
+ */
+return [
+	'components' => [
+		'mail' => [
+			'useFileTransport' => true,
+		],
+		'urlManager' => [
+			'showScriptName' => true,
+		],
+	],
+];
diff --git a/apps/advanced/backend/tests/_console.php b/apps/advanced/backend/tests/_console.php
new file mode 100644
index 0000000..b38c84e
--- /dev/null
+++ b/apps/advanced/backend/tests/_console.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * Yii console bootstrap file.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+// fcgi doesn't have STDIN and STDOUT defined by default
+defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
+defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
+
+require_once(__DIR__ . '/../../vendor/autoload.php');
+require_once(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
+require_once(__DIR__ . '/../../common/config/aliases.php');
+
+defined('YII_DEBUG') or define('YII_DEBUG', true);
+defined('YII_ENV') or define('YII_ENV', 'test');
diff --git a/apps/advanced/backend/tests/_data/dump.sql b/apps/advanced/backend/tests/_data/dump.sql
new file mode 100644
index 0000000..4bc742c
--- /dev/null
+++ b/apps/advanced/backend/tests/_data/dump.sql
@@ -0,0 +1 @@
+/* Replace this file with actual dump of your database */
\ No newline at end of file
diff --git a/apps/advanced/backend/tests/_helpers/CodeHelper.php b/apps/advanced/backend/tests/_helpers/CodeHelper.php
new file mode 100644
index 0000000..972c8f3
--- /dev/null
+++ b/apps/advanced/backend/tests/_helpers/CodeHelper.php
@@ -0,0 +1,8 @@
+<?php
+namespace Codeception\Module;
+
+// here you can define custom functions for CodeGuy 
+
+class CodeHelper extends \Codeception\Module
+{
+}
diff --git a/apps/advanced/backend/tests/_helpers/TestHelper.php b/apps/advanced/backend/tests/_helpers/TestHelper.php
new file mode 100644
index 0000000..37737cd
--- /dev/null
+++ b/apps/advanced/backend/tests/_helpers/TestHelper.php
@@ -0,0 +1,8 @@
+<?php
+namespace Codeception\Module;
+
+// here you can define custom functions for TestGuy 
+
+class TestHelper extends \Codeception\Module
+{
+}
diff --git a/apps/advanced/backend/tests/_helpers/WebHelper.php b/apps/advanced/backend/tests/_helpers/WebHelper.php
new file mode 100644
index 0000000..66b070e
--- /dev/null
+++ b/apps/advanced/backend/tests/_helpers/WebHelper.php
@@ -0,0 +1,8 @@
+<?php
+namespace Codeception\Module;
+
+// here you can define custom functions for WebGuy 
+
+class WebHelper extends \Codeception\Module
+{
+}
diff --git a/apps/advanced/backend/tests/_log/.gitignore b/apps/advanced/backend/tests/_log/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/apps/advanced/backend/tests/_log/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/apps/advanced/backend/tests/acceptance.suite.yml b/apps/advanced/backend/tests/acceptance.suite.yml
new file mode 100644
index 0000000..e6c64d1
--- /dev/null
+++ b/apps/advanced/backend/tests/acceptance.suite.yml
@@ -0,0 +1,28 @@
+# Codeception Test Suite Configuration
+
+# suite for acceptance tests.
+# perform tests in browser using the Selenium-like tools.
+# powered by Mink (http://mink.behat.org).
+# (tip: that's what your customer will see).
+# (tip: test your ajax and javascript by one of Mink drivers).
+
+# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
+
+class_name: WebGuy
+modules:
+    enabled:
+        - WebHelper
+        - PhpBrowser
+        - common\tests\_helpers\FixtureHelper
+# you can use WebDriver instead of PhpBrowser to test javascript and ajax.
+# This will require you to install selenium. See http://codeception.com/docs/04-AcceptanceTests#Selenium
+# "restart" option is used by the WebDriver to start each time per test-file new session and cookies, 
+# it is useful if you want to login in your app in each test.
+#        - WebDriver
+    config:
+        PhpBrowser:
+            url: 'http://localhost:8080'
+#        WebDriver:
+#            url: 'http://localhost'
+#            browser: firefox
+#            restart: true
diff --git a/apps/advanced/backend/tests/acceptance/LoginCept.php b/apps/advanced/backend/tests/acceptance/LoginCept.php
new file mode 100644
index 0000000..62ebc0e
--- /dev/null
+++ b/apps/advanced/backend/tests/acceptance/LoginCept.php
@@ -0,0 +1,30 @@
+<?php
+
+use common\tests\_pages\LoginPage;
+
+$I = new WebGuy($scenario);
+$I->wantTo('ensure login page works');
+
+$loginPage = LoginPage::openBy($I);
+
+$I->amGoingTo('submit login form with no data');
+$loginPage->login('', '');
+$I->expectTo('see validations errors');
+$I->see('Username cannot be blank.');
+$I->see('Password cannot be blank.');
+
+$I->amGoingTo('try to login with wrong credentials');
+$I->expectTo('see validations errors');
+$loginPage->login('admin', 'wrong');
+$I->expectTo('see validations errors');
+$I->see('Incorrect username or password.');
+
+$I->amGoingTo('try to login with correct credentials');
+$loginPage->login('erau', 'password_0');
+$I->expectTo('see that user is logged');
+$I->see('Logout (erau)');
+$I->dontSee('Login');
+$I->dontSee('Signup');
+$I->click('Logout (erau)');
+$I->dontSee('Logout (erau)');
+$I->see('Login');
diff --git a/apps/advanced/backend/tests/acceptance/_bootstrap.php b/apps/advanced/backend/tests/acceptance/_bootstrap.php
new file mode 100644
index 0000000..6ce3d17
--- /dev/null
+++ b/apps/advanced/backend/tests/acceptance/_bootstrap.php
@@ -0,0 +1,3 @@
+<?php
+
+new yii\web\Application(require(__DIR__ . '/_config.php'));
diff --git a/apps/advanced/backend/tests/acceptance/_config.php b/apps/advanced/backend/tests/acceptance/_config.php
new file mode 100644
index 0000000..4a70bb9
--- /dev/null
+++ b/apps/advanced/backend/tests/acceptance/_config.php
@@ -0,0 +1,16 @@
+<?php
+
+return yii\helpers\ArrayHelper::merge(
+	require(__DIR__ . '/../../config/main.php'),
+	require(__DIR__ . '/../../config/main-local.php'),
+	require(__DIR__ . '/../../../common/config/main.php'),
+	require(__DIR__ . '/../../../common/config/main-local.php'),
+	require(__DIR__ . '/../_config.php'),
+	[
+		'components' => [
+			'db' => [
+				'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_acceptance',
+			],
+		],
+	]
+);
diff --git a/apps/advanced/backend/tests/acceptance/_console.php b/apps/advanced/backend/tests/acceptance/_console.php
new file mode 100644
index 0000000..1e1ec56
--- /dev/null
+++ b/apps/advanced/backend/tests/acceptance/_console.php
@@ -0,0 +1,15 @@
+<?php
+
+return yii\helpers\ArrayHelper::merge(
+	require(__DIR__ . '/../../../common/config/main.php'),
+	require(__DIR__ . '/../../../common/config/main-local.php'),
+	require(__DIR__ . '/../../../console/config/main.php'),
+	require(__DIR__ . '/../../../console/config/main-local.php'),
+	[
+		'components' => [
+			'db' => [
+				'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_acceptance',
+			],
+		],
+	]
+);
diff --git a/apps/advanced/backend/tests/acceptance/yii b/apps/advanced/backend/tests/acceptance/yii
new file mode 100644
index 0000000..a348a9a
--- /dev/null
+++ b/apps/advanced/backend/tests/acceptance/yii
@@ -0,0 +1,21 @@
+#!/usr/bin/env php
+<?php
+/**
+ * Yii console bootstrap file.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+// fcgi doesn't have STDIN and STDOUT defined by default
+defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
+defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
+
+require_once(__DIR__ . '/../_console.php');
+
+$config = require(__DIR__ . '/_console.php');
+
+$application = new yii\console\Application($config);
+$exitCode = $application->run();
+exit($exitCode);
diff --git a/apps/advanced/backend/tests/acceptance/yii.bat b/apps/advanced/backend/tests/acceptance/yii.bat
new file mode 100644
index 0000000..5e21e2e
--- /dev/null
+++ b/apps/advanced/backend/tests/acceptance/yii.bat
@@ -0,0 +1,20 @@
+@echo off
+
+rem -------------------------------------------------------------
+rem  Yii command line bootstrap script for Windows.
+rem
+rem  @author Qiang Xue <qiang.xue@gmail.com>
+rem  @link http://www.yiiframework.com/
+rem  @copyright Copyright &copy; 2012 Yii Software LLC
+rem  @license http://www.yiiframework.com/license/
+rem -------------------------------------------------------------
+
+@setlocal
+
+set YII_PATH=%~dp0
+
+if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
+
+"%PHP_COMMAND%" "%YII_PATH%yii" %*
+
+@endlocal
diff --git a/apps/advanced/backend/tests/functional.suite.yml b/apps/advanced/backend/tests/functional.suite.yml
new file mode 100644
index 0000000..5242b9b
--- /dev/null
+++ b/apps/advanced/backend/tests/functional.suite.yml
@@ -0,0 +1,18 @@
+# Codeception Test Suite Configuration
+
+# suite for functional (integration) tests.
+# emulate web requests and make application process them.
+# (tip: better to use with frameworks).
+
+# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
+#basic/web/index.php
+class_name: TestGuy
+modules:
+    enabled:
+      - Filesystem
+      - TestHelper
+      - Yii2
+      - common\tests\_helpers\FixtureHelper
+    config:
+        Yii2:
+            configFile: 'tests/functional/_config.php'
diff --git a/apps/advanced/backend/tests/functional/LoginCept.php b/apps/advanced/backend/tests/functional/LoginCept.php
new file mode 100644
index 0000000..1d2dc14
--- /dev/null
+++ b/apps/advanced/backend/tests/functional/LoginCept.php
@@ -0,0 +1,30 @@
+<?php
+
+use common\tests\_pages\LoginPage;
+
+$I = new TestGuy($scenario);
+$I->wantTo('ensure login page works');
+
+$loginPage = LoginPage::openBy($I);
+
+$I->amGoingTo('submit login form with no data');
+$loginPage->login('', '');
+$I->expectTo('see validations errors');
+$I->see('Username cannot be blank.');
+$I->see('Password cannot be blank.');
+
+$I->amGoingTo('try to login with wrong credentials');
+$I->expectTo('see validations errors');
+$loginPage->login('admin', 'wrong');
+$I->expectTo('see validations errors');
+$I->see('Incorrect username or password.');
+
+$I->amGoingTo('try to login with correct credentials');
+$loginPage->login('erau', 'password_0');
+$I->expectTo('see that user is logged');
+$I->see('Logout (erau)');
+$I->dontSee('Login');
+$I->dontSee('Signup');
+$I->click('Logout (erau)');
+$I->dontSee('Logout (erau)');
+$I->see('Login');
diff --git a/apps/advanced/backend/tests/functional/_bootstrap.php b/apps/advanced/backend/tests/functional/_bootstrap.php
new file mode 100644
index 0000000..6ce3d17
--- /dev/null
+++ b/apps/advanced/backend/tests/functional/_bootstrap.php
@@ -0,0 +1,3 @@
+<?php
+
+new yii\web\Application(require(__DIR__ . '/_config.php'));
diff --git a/apps/advanced/backend/tests/functional/_config.php b/apps/advanced/backend/tests/functional/_config.php
new file mode 100644
index 0000000..c1bc080
--- /dev/null
+++ b/apps/advanced/backend/tests/functional/_config.php
@@ -0,0 +1,20 @@
+<?php
+
+// set correct script paths
+$_SERVER['SCRIPT_FILENAME'] = TEST_ENTRY_FILE;
+$_SERVER['SCRIPT_NAME'] = TEST_ENTRY_URL;
+
+return yii\helpers\ArrayHelper::merge(
+	require(__DIR__ . '/../../config/main.php'),
+	require(__DIR__ . '/../../config/main-local.php'),
+	require(__DIR__ . '/../../../common/config/main.php'),
+	require(__DIR__ . '/../../../common/config/main-local.php'),
+	require(__DIR__ . '/../_config.php'),
+	[
+		'components' => [
+			'db' => [
+				'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_functional',
+			],
+		],
+	]
+);
diff --git a/apps/advanced/backend/tests/functional/_console.php b/apps/advanced/backend/tests/functional/_console.php
new file mode 100644
index 0000000..39434f7
--- /dev/null
+++ b/apps/advanced/backend/tests/functional/_console.php
@@ -0,0 +1,15 @@
+<?php
+
+return yii\helpers\ArrayHelper::merge(
+	require(__DIR__ . '/../../../common/config/main.php'),
+	require(__DIR__ . '/../../../common/config/main-local.php'),
+	require(__DIR__ . '/../../../console/config/main.php'),
+	require(__DIR__ . '/../../../console/config/main-local.php'),
+	[
+		'components' => [
+			'db' => [
+				'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_functional',
+			],
+		],
+	]
+);
diff --git a/apps/advanced/backend/tests/functional/yii b/apps/advanced/backend/tests/functional/yii
new file mode 100644
index 0000000..a348a9a
--- /dev/null
+++ b/apps/advanced/backend/tests/functional/yii
@@ -0,0 +1,21 @@
+#!/usr/bin/env php
+<?php
+/**
+ * Yii console bootstrap file.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+// fcgi doesn't have STDIN and STDOUT defined by default
+defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
+defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
+
+require_once(__DIR__ . '/../_console.php');
+
+$config = require(__DIR__ . '/_console.php');
+
+$application = new yii\console\Application($config);
+$exitCode = $application->run();
+exit($exitCode);
diff --git a/apps/advanced/backend/tests/functional/yii.bat b/apps/advanced/backend/tests/functional/yii.bat
new file mode 100644
index 0000000..5e21e2e
--- /dev/null
+++ b/apps/advanced/backend/tests/functional/yii.bat
@@ -0,0 +1,20 @@
+@echo off
+
+rem -------------------------------------------------------------
+rem  Yii command line bootstrap script for Windows.
+rem
+rem  @author Qiang Xue <qiang.xue@gmail.com>
+rem  @link http://www.yiiframework.com/
+rem  @copyright Copyright &copy; 2012 Yii Software LLC
+rem  @license http://www.yiiframework.com/license/
+rem -------------------------------------------------------------
+
+@setlocal
+
+set YII_PATH=%~dp0
+
+if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
+
+"%PHP_COMMAND%" "%YII_PATH%yii" %*
+
+@endlocal
diff --git a/apps/advanced/backend/tests/unit.suite.yml b/apps/advanced/backend/tests/unit.suite.yml
new file mode 100644
index 0000000..04873a8
--- /dev/null
+++ b/apps/advanced/backend/tests/unit.suite.yml
@@ -0,0 +1,8 @@
+# Codeception Test Suite Configuration
+
+# suite for unit (internal) tests.
+# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
+
+class_name: CodeGuy
+modules:
+    enabled: [CodeHelper]
diff --git a/apps/advanced/backend/tests/unit/DbTestCase.php b/apps/advanced/backend/tests/unit/DbTestCase.php
new file mode 100644
index 0000000..4cb8ff3
--- /dev/null
+++ b/apps/advanced/backend/tests/unit/DbTestCase.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace backend\tests\unit;
+
+class DbTestCase extends \yii\codeception\DbTestCase
+{
+	public $appConfig = '@backend/tests/unit/_config.php';
+}
diff --git a/apps/advanced/backend/tests/unit/TestCase.php b/apps/advanced/backend/tests/unit/TestCase.php
new file mode 100644
index 0000000..fdd63dd
--- /dev/null
+++ b/apps/advanced/backend/tests/unit/TestCase.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace backend\tests\unit;
+
+class TestCase extends \yii\codeception\TestCase
+{
+	public $appConfig = '@backend/tests/unit/_config.php';
+}
diff --git a/apps/advanced/backend/tests/unit/_bootstrap.php b/apps/advanced/backend/tests/unit/_bootstrap.php
new file mode 100644
index 0000000..7dfa7c3
--- /dev/null
+++ b/apps/advanced/backend/tests/unit/_bootstrap.php
@@ -0,0 +1,2 @@
+<?php
+// Here you can initialize variables that will for your tests
diff --git a/apps/advanced/backend/tests/unit/_config.php b/apps/advanced/backend/tests/unit/_config.php
new file mode 100644
index 0000000..22e5c62
--- /dev/null
+++ b/apps/advanced/backend/tests/unit/_config.php
@@ -0,0 +1,16 @@
+<?php
+
+return yii\helpers\ArrayHelper::merge(
+	require(__DIR__ . '/../../../common/config/main.php'),
+	require(__DIR__ . '/../../../common/config/main-local.php'),
+	require(__DIR__ . '/../../config/main.php'),
+	require(__DIR__ . '/../../config/main-local.php'),
+	require(__DIR__ . '/../_config.php'),
+	[
+		'components' => [
+			'db' => [
+				'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_unit',
+			],
+		],
+	]
+);
diff --git a/apps/advanced/backend/tests/unit/_console.php b/apps/advanced/backend/tests/unit/_console.php
new file mode 100644
index 0000000..a0d8d02
--- /dev/null
+++ b/apps/advanced/backend/tests/unit/_console.php
@@ -0,0 +1,15 @@
+<?php
+
+return yii\helpers\ArrayHelper::merge(
+	require(__DIR__ . '/../../../common/config/main.php'),
+	require(__DIR__ . '/../../../common/config/main-local.php'),
+	require(__DIR__ . '/../../../console/config/main.php'),
+	require(__DIR__ . '/../../../console/config/main-local.php'),
+	[
+		'components' => [
+			'db' => [
+				'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_unit',
+			],
+		],
+	]
+);
diff --git a/apps/advanced/backend/tests/unit/yii b/apps/advanced/backend/tests/unit/yii
new file mode 100644
index 0000000..a348a9a
--- /dev/null
+++ b/apps/advanced/backend/tests/unit/yii
@@ -0,0 +1,21 @@
+#!/usr/bin/env php
+<?php
+/**
+ * Yii console bootstrap file.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+// fcgi doesn't have STDIN and STDOUT defined by default
+defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
+defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
+
+require_once(__DIR__ . '/../_console.php');
+
+$config = require(__DIR__ . '/_console.php');
+
+$application = new yii\console\Application($config);
+$exitCode = $application->run();
+exit($exitCode);
diff --git a/apps/advanced/backend/tests/unit/yii.bat b/apps/advanced/backend/tests/unit/yii.bat
new file mode 100644
index 0000000..5e21e2e
--- /dev/null
+++ b/apps/advanced/backend/tests/unit/yii.bat
@@ -0,0 +1,20 @@
+@echo off
+
+rem -------------------------------------------------------------
+rem  Yii command line bootstrap script for Windows.
+rem
+rem  @author Qiang Xue <qiang.xue@gmail.com>
+rem  @link http://www.yiiframework.com/
+rem  @copyright Copyright &copy; 2012 Yii Software LLC
+rem  @license http://www.yiiframework.com/license/
+rem -------------------------------------------------------------
+
+@setlocal
+
+set YII_PATH=%~dp0
+
+if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
+
+"%PHP_COMMAND%" "%YII_PATH%yii" %*
+
+@endlocal
diff --git a/apps/advanced/backend/views/site/login.php b/apps/advanced/backend/views/site/login.php
index 60c438f..9b0ff7d 100644
--- a/apps/advanced/backend/views/site/login.php
+++ b/apps/advanced/backend/views/site/login.php
@@ -22,7 +22,7 @@ $this->params['breadcrumbs'][] = $this->title;
 				<?= $form->field($model, 'password')->passwordInput() ?>
 				<?= $form->field($model, 'rememberMe')->checkbox() ?>
 				<div class="form-group">
-					<?= Html::submitButton('Login', ['class' => 'btn btn-primary']) ?>
+					<?= Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>
 				</div>
 			<?php ActiveForm::end(); ?>
 		</div>
diff --git a/apps/advanced/common/codeception.yml b/apps/advanced/common/codeception.yml
new file mode 100644
index 0000000..5b1f441
--- /dev/null
+++ b/apps/advanced/common/codeception.yml
@@ -0,0 +1,18 @@
+paths:
+    tests: tests
+    log: tests/_log
+    data: tests/_data
+    helpers: tests/_helpers
+settings:
+    bootstrap: _bootstrap.php
+    suite_class: \PHPUnit_Framework_TestSuite
+    colors: true
+    memory_limit: 1024M
+    log: true
+modules:
+    config:
+        Db:
+            dsn: ''
+            user: ''
+            password: ''
+            dump: tests/_data/dump.sql
diff --git a/apps/advanced/common/mail/passwordResetToken.php b/apps/advanced/common/mail/passwordResetToken.php
index 88ad0e6..b8f8d87 100644
--- a/apps/advanced/common/mail/passwordResetToken.php
+++ b/apps/advanced/common/mail/passwordResetToken.php
@@ -6,7 +6,7 @@ use yii\helpers\Html;
  * @var common\models\User $user
  */
 
-$resetLink = Yii::$app->urlManager->createAbsoluteUrl('site/reset-password', ['token' => $user->password_reset_token]);
+$resetLink = Yii::$app->urlManager->createAbsoluteUrl(['site/reset-password', 'token' => $user->password_reset_token]);
 ?>
 
 Hello <?= Html::encode($user->username) ?>,
diff --git a/apps/advanced/common/models/LoginForm.php b/apps/advanced/common/models/LoginForm.php
index d948106..ec27a5a 100644
--- a/apps/advanced/common/models/LoginForm.php
+++ b/apps/advanced/common/models/LoginForm.php
@@ -23,10 +23,10 @@ class LoginForm extends Model
 		return [
 			// username and password are both required
 			[['username', 'password'], 'required'],
-			// password is validated by validatePassword()
-			['password', 'validatePassword'],
 			// rememberMe must be a boolean value
 			['rememberMe', 'boolean'],
+			// password is validated by validatePassword()
+			['password', 'validatePassword'],
 		];
 	}
 
@@ -36,9 +36,11 @@ class LoginForm extends Model
 	 */
 	public function validatePassword()
 	{
-		$user = $this->getUser();
-		if (!$user || !$user->validatePassword($this->password)) {
-			$this->addError('password', 'Incorrect username or password.');
+		if (!$this->hasErrors()) {
+			$user = $this->getUser();
+			if (!$user || !$user->validatePassword($this->password)) {
+				$this->addError('password', 'Incorrect username or password.');
+			}
 		}
 	}
 
@@ -61,7 +63,7 @@ class LoginForm extends Model
 	 *
 	 * @return User|null
 	 */
-	private function getUser()
+	public function getUser()
 	{
 		if ($this->_user === false) {
 			$this->_user = User::findByUsername($this->username);
diff --git a/apps/advanced/common/models/User.php b/apps/advanced/common/models/User.php
index 155ffc6..fc74532 100644
--- a/apps/advanced/common/models/User.php
+++ b/apps/advanced/common/models/User.php
@@ -54,10 +54,10 @@ class User extends ActiveRecord implements IdentityInterface
 	{
 		return [
 			'timestamp' => [
-				'class' => 'yii\behaviors\AutoTimestamp',
+				'class' => 'yii\behaviors\TimestampBehavior',
 				'attributes' => [
 					ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
-					ActiveRecord::EVENT_BEFORE_UPDATE => 'updated_at',
+					ActiveRecord::EVENT_BEFORE_UPDATE => ['updated_at'],
 				],
 			],
 		];
diff --git a/apps/advanced/common/tests/_bootstrap.php b/apps/advanced/common/tests/_bootstrap.php
new file mode 100644
index 0000000..c64ad3b
--- /dev/null
+++ b/apps/advanced/common/tests/_bootstrap.php
@@ -0,0 +1,23 @@
+<?php
+
+// the entry script URL (without host info) for functional and acceptance tests
+// PLEASE ADJUST IT TO THE ACTUAL ENTRY SCRIPT URL
+defined('TEST_ENTRY_URL') or define('TEST_ENTRY_URL', '/index-test.php');
+
+// the entry script file path for functional and acceptance tests
+defined('TEST_ENTRY_FILE') or define('TEST_ENTRY_FILE', dirname(__DIR__) . '/index-test.php');
+
+defined('YII_DEBUG') or define('YII_DEBUG', true);
+
+defined('YII_ENV') or define('YII_ENV', 'test');
+
+require_once(__DIR__ . '/../../vendor/autoload.php');
+
+require_once(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
+
+require(__DIR__ . '/../../common/config/aliases.php');
+
+// set correct script paths
+$_SERVER['SCRIPT_FILENAME'] = TEST_ENTRY_FILE;
+$_SERVER['SCRIPT_NAME'] = TEST_ENTRY_URL;
+$_SERVER['SERVER_NAME'] = 'localhost';
diff --git a/apps/advanced/common/tests/_config.php b/apps/advanced/common/tests/_config.php
new file mode 100644
index 0000000..d9cc356
--- /dev/null
+++ b/apps/advanced/common/tests/_config.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * application configurations shared by all test types
+ */
+return [
+	'components' => [
+		'mail' => [
+			'useFileTransport' => true,
+		],
+		'urlManager' => [
+			'showScriptName' => true,
+		],
+	],
+];
diff --git a/apps/advanced/common/tests/_console.php b/apps/advanced/common/tests/_console.php
new file mode 100644
index 0000000..b38c84e
--- /dev/null
+++ b/apps/advanced/common/tests/_console.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * Yii console bootstrap file.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+// fcgi doesn't have STDIN and STDOUT defined by default
+defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
+defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
+
+require_once(__DIR__ . '/../../vendor/autoload.php');
+require_once(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
+require_once(__DIR__ . '/../../common/config/aliases.php');
+
+defined('YII_DEBUG') or define('YII_DEBUG', true);
+defined('YII_ENV') or define('YII_ENV', 'test');
diff --git a/apps/advanced/common/tests/_data/dump.sql b/apps/advanced/common/tests/_data/dump.sql
new file mode 100644
index 0000000..4bc742c
--- /dev/null
+++ b/apps/advanced/common/tests/_data/dump.sql
@@ -0,0 +1 @@
+/* Replace this file with actual dump of your database */
\ No newline at end of file
diff --git a/apps/advanced/common/tests/_helpers/CodeHelper.php b/apps/advanced/common/tests/_helpers/CodeHelper.php
new file mode 100644
index 0000000..972c8f3
--- /dev/null
+++ b/apps/advanced/common/tests/_helpers/CodeHelper.php
@@ -0,0 +1,8 @@
+<?php
+namespace Codeception\Module;
+
+// here you can define custom functions for CodeGuy 
+
+class CodeHelper extends \Codeception\Module
+{
+}
diff --git a/apps/advanced/common/tests/_helpers/FixtureHelper.php b/apps/advanced/common/tests/_helpers/FixtureHelper.php
new file mode 100644
index 0000000..00c3a17
--- /dev/null
+++ b/apps/advanced/common/tests/_helpers/FixtureHelper.php
@@ -0,0 +1,58 @@
+<?php
+
+namespace common\tests\_helpers;
+
+use Codeception\Module;
+use yii\test\FixtureTrait;
+use common\tests\fixtures\UserFixture;
+
+/**
+ * This helper is used to populate database with needed fixtures before any tests should be run.
+ * For example - populate database with demo login user that should be used in acceptance and functional tests.
+ * All fixtures will be loaded before suite will be starded and unloaded after it.
+ */
+class FixtureHelper extends Module
+{
+
+	/**
+	 * Redeclare visibility because codeception includes all public methods that not starts from "_"
+	 * and not excluded by module settings, in guy class.
+	 */
+	use FixtureTrait {
+		loadFixtures as protected;
+		fixtures as protected;
+		globalFixtures as protected;
+		unloadFixtures as protected;
+		getFixtures as protected;
+		getFixture as protected;
+	}
+
+	/**
+	 * Method called before any suite tests run. Loads User fixture login user
+	 * to use in acceptance and functional tests.
+	 * @param array $settings
+	 */
+	public function _beforeSuite($settings = array())
+	{
+		$this->loadFixtures();
+	}
+
+	/**
+	 * Method is called after all suite tests run
+	 */
+	public function _afterSuite()
+	{
+		$this->unloadFixtures();
+	}
+
+	protected function fixtures()
+	{
+		return [
+			'user' => [
+				'class' =>  UserFixture::className(),
+				'dataFile' => '@common/tests/fixtures/data/init_login.php',
+			],
+		];
+	}
+
+}
diff --git a/apps/advanced/common/tests/_helpers/TestHelper.php b/apps/advanced/common/tests/_helpers/TestHelper.php
new file mode 100644
index 0000000..37737cd
--- /dev/null
+++ b/apps/advanced/common/tests/_helpers/TestHelper.php
@@ -0,0 +1,8 @@
+<?php
+namespace Codeception\Module;
+
+// here you can define custom functions for TestGuy 
+
+class TestHelper extends \Codeception\Module
+{
+}
diff --git a/apps/advanced/common/tests/_helpers/WebHelper.php b/apps/advanced/common/tests/_helpers/WebHelper.php
new file mode 100644
index 0000000..66b070e
--- /dev/null
+++ b/apps/advanced/common/tests/_helpers/WebHelper.php
@@ -0,0 +1,8 @@
+<?php
+namespace Codeception\Module;
+
+// here you can define custom functions for WebGuy 
+
+class WebHelper extends \Codeception\Module
+{
+}
diff --git a/apps/advanced/common/tests/_log/.gitignore b/apps/advanced/common/tests/_log/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/apps/advanced/common/tests/_log/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/apps/advanced/common/tests/_pages/LoginPage.php b/apps/advanced/common/tests/_pages/LoginPage.php
new file mode 100644
index 0000000..af95ea3
--- /dev/null
+++ b/apps/advanced/common/tests/_pages/LoginPage.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace common\tests\_pages;
+
+use yii\codeception\BasePage;
+
+class LoginPage extends BasePage
+{
+	public $route = 'site/login';
+
+	/**
+	 * @param string $username
+	 * @param string $password
+	 */
+	public function login($username, $password)
+	{
+		$this->guy->fillField('input[name="LoginForm[username]"]', $username);
+		$this->guy->fillField('input[name="LoginForm[password]"]', $password);
+		$this->guy->click('login-button');
+	}
+
+}
diff --git a/apps/advanced/common/tests/fixtures/UserFixture.php b/apps/advanced/common/tests/fixtures/UserFixture.php
new file mode 100644
index 0000000..6d69cfe
--- /dev/null
+++ b/apps/advanced/common/tests/fixtures/UserFixture.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace common\tests\fixtures;
+
+use yii\test\ActiveFixture;
+
+class UserFixture extends ActiveFixture
+{
+	public $modelClass = 'common\models\User';
+}
diff --git a/apps/advanced/common/tests/fixtures/data/init_login.php b/apps/advanced/common/tests/fixtures/data/init_login.php
new file mode 100644
index 0000000..7b4bbbe
--- /dev/null
+++ b/apps/advanced/common/tests/fixtures/data/init_login.php
@@ -0,0 +1,14 @@
+<?php
+
+return [
+	[
+		'username' => 'erau',
+		'auth_key' => 'tUu1qHcde0diwUol3xeI-18MuHkkprQI',
+		// password_0
+		'password_hash' => '$2y$13$nJ1WDlBaGcbCdbNC5.5l4.sgy.OMEKCqtDQOdQ2OWpgiKRWYyzzne',
+		'password_reset_token' => 'RkD_Jw0_8HEedzLk7MM-ZKEFfYR7VbMr_1392559490',
+		'created_at' => '1392559490',
+		'updated_at' => '1392559490',
+		'email' => 'sfriesen@jenkins.info',
+	],
+];
diff --git a/apps/advanced/common/tests/templates/fixtures/tbl_user.php b/apps/advanced/common/tests/templates/fixtures/tbl_user.php
new file mode 100644
index 0000000..4d14b2d
--- /dev/null
+++ b/apps/advanced/common/tests/templates/fixtures/tbl_user.php
@@ -0,0 +1,28 @@
+<?php
+
+use yii\helpers\Security;
+
+return [
+	'username' => 'userName',
+	'auth_key' => function ($fixture, $faker, $index) {
+		$fixture['auth_key'] = Security::generateRandomKey();
+		return $fixture;
+	},
+	'password_hash' => function ($fixture, $faker, $index) {
+		$fixture['password_hash'] = Security::generatePasswordHash('password_' . $index);
+		return $fixture;
+	},
+	'password_reset_token' => function ($fixture, $faker, $index) {
+		$fixture['password_reset_token'] = Security::generateRandomKey() . '_' . time();
+		return $fixture;
+	},
+	'created_at' => function ($fixture, $faker, $index) {
+		$fixture['created_at'] = time();
+		return $fixture;
+	},
+	'updated_at' => function ($fixture, $faker, $index) {
+		$fixture['updated_at'] = time();
+		return $fixture;
+	},
+	'email' => 'email',
+];
diff --git a/apps/advanced/common/tests/unit.suite.yml b/apps/advanced/common/tests/unit.suite.yml
new file mode 100644
index 0000000..a634606
--- /dev/null
+++ b/apps/advanced/common/tests/unit.suite.yml
@@ -0,0 +1,9 @@
+# Codeception Test Suite Configuration
+
+# suite for unit (internal) tests.
+# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
+
+class_name: CodeGuy
+modules:
+    enabled:
+      - CodeHelper
diff --git a/apps/advanced/common/tests/unit/DbTestCase.php b/apps/advanced/common/tests/unit/DbTestCase.php
new file mode 100644
index 0000000..9b5923b
--- /dev/null
+++ b/apps/advanced/common/tests/unit/DbTestCase.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace common\tests\unit;
+
+class DbTestCase extends \yii\codeception\DbTestCase
+{
+	public $appConfig = '@frontend/tests/unit/_config.php';
+}
diff --git a/apps/advanced/common/tests/unit/TestCase.php b/apps/advanced/common/tests/unit/TestCase.php
new file mode 100644
index 0000000..11024de
--- /dev/null
+++ b/apps/advanced/common/tests/unit/TestCase.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace common\tests\unit;
+
+class TestCase extends \yii\codeception\TestCase
+{
+	public $appConfig = '@common/tests/unit/_config.php';
+}
diff --git a/apps/advanced/common/tests/unit/_bootstrap.php b/apps/advanced/common/tests/unit/_bootstrap.php
new file mode 100644
index 0000000..7dfa7c3
--- /dev/null
+++ b/apps/advanced/common/tests/unit/_bootstrap.php
@@ -0,0 +1,2 @@
+<?php
+// Here you can initialize variables that will for your tests
diff --git a/apps/advanced/common/tests/unit/_config.php b/apps/advanced/common/tests/unit/_config.php
new file mode 100644
index 0000000..d47b3aa
--- /dev/null
+++ b/apps/advanced/common/tests/unit/_config.php
@@ -0,0 +1,15 @@
+<?php
+
+return yii\helpers\ArrayHelper::merge(
+	require(__DIR__ . '/../../config/main.php'),
+	require(__DIR__ . '/../../config/main-local.php'),
+	require(__DIR__ . '/../_config.php'),
+	[
+		'components' => [
+			'db' => [
+				'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_unit',
+			],
+			'id' => 'app-common',
+		],
+	]
+);
diff --git a/apps/advanced/common/tests/unit/_console.php b/apps/advanced/common/tests/unit/_console.php
new file mode 100644
index 0000000..2c9aaff
--- /dev/null
+++ b/apps/advanced/common/tests/unit/_console.php
@@ -0,0 +1,15 @@
+<?php
+
+return yii\helpers\ArrayHelper::merge(
+	require(__DIR__ . '/../../../console/config/main.php'),
+	require(__DIR__ . '/../../../console/config/main-local.php'),
+	require(__DIR__ . '/../../config/main.php'),
+	require(__DIR__ . '/../../config/main-local.php'),
+	[
+		'components' => [
+			'db' => [
+				'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_unit',
+			],
+		],
+	]
+);
diff --git a/apps/advanced/common/tests/unit/models/LoginFormTest.php b/apps/advanced/common/tests/unit/models/LoginFormTest.php
new file mode 100644
index 0000000..2ececd6
--- /dev/null
+++ b/apps/advanced/common/tests/unit/models/LoginFormTest.php
@@ -0,0 +1,69 @@
+<?php
+
+namespace common\tests\unit\models;
+
+use Yii;
+use frontend\tests\unit\TestCase;
+use common\models\User;
+use yii\helpers\Security;
+
+class LoginFormTest extends TestCase
+{
+	
+	use \Codeception\Specify;
+
+	protected function tearDown()
+	{
+		Yii::$app->user->logout();
+		parent::tearDown();
+	}
+
+	public function testLoginNoUser()
+	{
+		$model = $this->mockUser(null);
+
+		$model->username = 'some_username';
+		$model->password = 'some_password';
+
+		$this->specify('user should not be able to login, when there is no identity' , function () use ($model) {
+			expect('model should not login user', $model->login())->false();
+			expect('user should not be logged in', Yii::$app->user->isGuest)->true();
+		});
+	}
+
+	public function testLoginWrongPassword()
+	{
+		$model = $this->mockUser(new User(['password_hash' => Security::generatePasswordHash('will-not-match')]));
+
+		$model->username = 'demo';
+		$model->password = 'wrong-password';
+
+		$this->specify('user should not be able to login with wrong password', function () use ($model) {
+			expect('model should not login user', $model->login())->false();
+			expect('error message should be set', $model->errors)->hasKey('password');
+			expect('user should not be logged in', Yii::$app->user->isGuest)->true();
+		});
+	}
+
+	public function testLoginCorrect()
+	{
+		$model = $this->mockUser(new User(['password_hash' => Security::generatePasswordHash('demo')]));
+
+		$model->username = 'demo';
+		$model->password = 'demo';
+
+		$this->specify('user should be able to login with correct credentials', function() use ($model) {
+			expect('model should login user', $model->login())->true();
+			expect('error message should not be set', $model->errors)->hasntKey('password');
+			expect('user should be logged in', Yii::$app->user->isGuest)->false();
+		});
+	}
+
+	private function mockUser($user)
+	{
+		$loginForm = $this->getMock('common\models\LoginForm',['getUser']);
+		$loginForm->expects($this->any())->method('getUser')->will($this->returnValue($user));
+		return $loginForm;
+	}
+
+}
diff --git a/apps/advanced/common/tests/unit/yii b/apps/advanced/common/tests/unit/yii
new file mode 100644
index 0000000..a348a9a
--- /dev/null
+++ b/apps/advanced/common/tests/unit/yii
@@ -0,0 +1,21 @@
+#!/usr/bin/env php
+<?php
+/**
+ * Yii console bootstrap file.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+// fcgi doesn't have STDIN and STDOUT defined by default
+defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
+defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
+
+require_once(__DIR__ . '/../_console.php');
+
+$config = require(__DIR__ . '/_console.php');
+
+$application = new yii\console\Application($config);
+$exitCode = $application->run();
+exit($exitCode);
diff --git a/apps/advanced/common/tests/unit/yii.bat b/apps/advanced/common/tests/unit/yii.bat
new file mode 100644
index 0000000..5e21e2e
--- /dev/null
+++ b/apps/advanced/common/tests/unit/yii.bat
@@ -0,0 +1,20 @@
+@echo off
+
+rem -------------------------------------------------------------
+rem  Yii command line bootstrap script for Windows.
+rem
+rem  @author Qiang Xue <qiang.xue@gmail.com>
+rem  @link http://www.yiiframework.com/
+rem  @copyright Copyright &copy; 2012 Yii Software LLC
+rem  @license http://www.yiiframework.com/license/
+rem -------------------------------------------------------------
+
+@setlocal
+
+set YII_PATH=%~dp0
+
+if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
+
+"%PHP_COMMAND%" "%YII_PATH%yii" %*
+
+@endlocal
diff --git a/apps/advanced/composer.json b/apps/advanced/composer.json
index b44cd7f..53e926f 100644
--- a/apps/advanced/composer.json
+++ b/apps/advanced/composer.json
@@ -20,9 +20,16 @@
 		"yiisoft/yii2-swiftmailer": "*"
 	},
 	"require-dev": {
+		"yiisoft/yii2-codeception": "*",
 		"yiisoft/yii2-debug": "*",
 		"yiisoft/yii2-gii": "*"
 	},
+	"suggest": {
+		"codeception/codeception": "Codeception, 1.8.*@dev is currently works well with Yii.",
+		"codeception/specify": "BDD style code blocks for PHPUnit and Codeception",
+		"codeception/verify": "BDD Assertions for PHPUnit and Codeception",
+		"yiisoft/yii2-faker": "Fixtures generator for Yii2 based on Faker lib"
+	},
 	"scripts": {
 		"post-create-project-cmd": [
 			"yii\\composer\\Installer::setPermission"
diff --git a/apps/advanced/console/codeception.yml b/apps/advanced/console/codeception.yml
new file mode 100644
index 0000000..5b1f441
--- /dev/null
+++ b/apps/advanced/console/codeception.yml
@@ -0,0 +1,18 @@
+paths:
+    tests: tests
+    log: tests/_log
+    data: tests/_data
+    helpers: tests/_helpers
+settings:
+    bootstrap: _bootstrap.php
+    suite_class: \PHPUnit_Framework_TestSuite
+    colors: true
+    memory_limit: 1024M
+    log: true
+modules:
+    config:
+        Db:
+            dsn: ''
+            user: ''
+            password: ''
+            dump: tests/_data/dump.sql
diff --git a/apps/advanced/console/tests/_bootstrap.php b/apps/advanced/console/tests/_bootstrap.php
new file mode 100644
index 0000000..c64ad3b
--- /dev/null
+++ b/apps/advanced/console/tests/_bootstrap.php
@@ -0,0 +1,23 @@
+<?php
+
+// the entry script URL (without host info) for functional and acceptance tests
+// PLEASE ADJUST IT TO THE ACTUAL ENTRY SCRIPT URL
+defined('TEST_ENTRY_URL') or define('TEST_ENTRY_URL', '/index-test.php');
+
+// the entry script file path for functional and acceptance tests
+defined('TEST_ENTRY_FILE') or define('TEST_ENTRY_FILE', dirname(__DIR__) . '/index-test.php');
+
+defined('YII_DEBUG') or define('YII_DEBUG', true);
+
+defined('YII_ENV') or define('YII_ENV', 'test');
+
+require_once(__DIR__ . '/../../vendor/autoload.php');
+
+require_once(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
+
+require(__DIR__ . '/../../common/config/aliases.php');
+
+// set correct script paths
+$_SERVER['SCRIPT_FILENAME'] = TEST_ENTRY_FILE;
+$_SERVER['SCRIPT_NAME'] = TEST_ENTRY_URL;
+$_SERVER['SERVER_NAME'] = 'localhost';
diff --git a/apps/advanced/console/tests/_config.php b/apps/advanced/console/tests/_config.php
new file mode 100644
index 0000000..d9cc356
--- /dev/null
+++ b/apps/advanced/console/tests/_config.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * application configurations shared by all test types
+ */
+return [
+	'components' => [
+		'mail' => [
+			'useFileTransport' => true,
+		],
+		'urlManager' => [
+			'showScriptName' => true,
+		],
+	],
+];
diff --git a/apps/advanced/console/tests/_console.php b/apps/advanced/console/tests/_console.php
new file mode 100644
index 0000000..b38c84e
--- /dev/null
+++ b/apps/advanced/console/tests/_console.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * Yii console bootstrap file.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+// fcgi doesn't have STDIN and STDOUT defined by default
+defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
+defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
+
+require_once(__DIR__ . '/../../vendor/autoload.php');
+require_once(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
+require_once(__DIR__ . '/../../common/config/aliases.php');
+
+defined('YII_DEBUG') or define('YII_DEBUG', true);
+defined('YII_ENV') or define('YII_ENV', 'test');
diff --git a/apps/advanced/console/tests/_data/dump.sql b/apps/advanced/console/tests/_data/dump.sql
new file mode 100644
index 0000000..4bc742c
--- /dev/null
+++ b/apps/advanced/console/tests/_data/dump.sql
@@ -0,0 +1 @@
+/* Replace this file with actual dump of your database */
\ No newline at end of file
diff --git a/apps/advanced/console/tests/_helpers/CodeHelper.php b/apps/advanced/console/tests/_helpers/CodeHelper.php
new file mode 100644
index 0000000..972c8f3
--- /dev/null
+++ b/apps/advanced/console/tests/_helpers/CodeHelper.php
@@ -0,0 +1,8 @@
+<?php
+namespace Codeception\Module;
+
+// here you can define custom functions for CodeGuy 
+
+class CodeHelper extends \Codeception\Module
+{
+}
diff --git a/apps/advanced/console/tests/_helpers/TestHelper.php b/apps/advanced/console/tests/_helpers/TestHelper.php
new file mode 100644
index 0000000..37737cd
--- /dev/null
+++ b/apps/advanced/console/tests/_helpers/TestHelper.php
@@ -0,0 +1,8 @@
+<?php
+namespace Codeception\Module;
+
+// here you can define custom functions for TestGuy 
+
+class TestHelper extends \Codeception\Module
+{
+}
diff --git a/apps/advanced/console/tests/_helpers/WebHelper.php b/apps/advanced/console/tests/_helpers/WebHelper.php
new file mode 100644
index 0000000..66b070e
--- /dev/null
+++ b/apps/advanced/console/tests/_helpers/WebHelper.php
@@ -0,0 +1,8 @@
+<?php
+namespace Codeception\Module;
+
+// here you can define custom functions for WebGuy 
+
+class WebHelper extends \Codeception\Module
+{
+}
diff --git a/apps/advanced/console/tests/_log/.gitignore b/apps/advanced/console/tests/_log/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/apps/advanced/console/tests/_log/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/apps/advanced/console/tests/unit.suite.yml b/apps/advanced/console/tests/unit.suite.yml
new file mode 100644
index 0000000..04873a8
--- /dev/null
+++ b/apps/advanced/console/tests/unit.suite.yml
@@ -0,0 +1,8 @@
+# Codeception Test Suite Configuration
+
+# suite for unit (internal) tests.
+# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
+
+class_name: CodeGuy
+modules:
+    enabled: [CodeHelper]
diff --git a/apps/advanced/console/tests/unit/DbTestCase.php b/apps/advanced/console/tests/unit/DbTestCase.php
new file mode 100644
index 0000000..cc157ff
--- /dev/null
+++ b/apps/advanced/console/tests/unit/DbTestCase.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace console\tests\unit;
+
+class DbTestCase extends \yii\codeception\DbTestCase
+{
+	public $appConfig = '@console/tests/unit/_config.php';
+}
diff --git a/apps/advanced/console/tests/unit/TestCase.php b/apps/advanced/console/tests/unit/TestCase.php
new file mode 100644
index 0000000..43ce144
--- /dev/null
+++ b/apps/advanced/console/tests/unit/TestCase.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace console\tests\unit;
+
+class TestCase extends \yii\codeception\TestCase
+{
+	public $appConfig = '@console/tests/unit/_config.php';
+}
diff --git a/apps/advanced/console/tests/unit/_bootstrap.php b/apps/advanced/console/tests/unit/_bootstrap.php
new file mode 100644
index 0000000..7dfa7c3
--- /dev/null
+++ b/apps/advanced/console/tests/unit/_bootstrap.php
@@ -0,0 +1,2 @@
+<?php
+// Here you can initialize variables that will for your tests
diff --git a/apps/advanced/console/tests/unit/_config.php b/apps/advanced/console/tests/unit/_config.php
new file mode 100644
index 0000000..e68d549
--- /dev/null
+++ b/apps/advanced/console/tests/unit/_config.php
@@ -0,0 +1,14 @@
+<?php
+
+return yii\helpers\ArrayHelper::merge(
+	require(__DIR__ . '/../../config/main.php'),
+	require(__DIR__ . '/../../config/main-local.php'),
+	require(__DIR__ . '/../_config.php'),
+	[
+		'components' => [
+			'db' => [
+				'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_unit',
+			],
+		],
+	]
+);
diff --git a/apps/advanced/console/tests/unit/_console.php b/apps/advanced/console/tests/unit/_console.php
new file mode 100644
index 0000000..280dfc0
--- /dev/null
+++ b/apps/advanced/console/tests/unit/_console.php
@@ -0,0 +1,15 @@
+<?php
+
+return yii\helpers\ArrayHelper::merge(
+	require(__DIR__ . '/../../../common/config/main.php'),
+	require(__DIR__ . '/../../../common/config/main-local.php'),
+	require(__DIR__ . '/../../config/main.php'),
+	require(__DIR__ . '/../../config/main-local.php'),
+	[
+		'components' => [
+			'db' => [
+				'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_unit',
+			],
+		],
+	]
+);
diff --git a/apps/advanced/console/tests/unit/yii b/apps/advanced/console/tests/unit/yii
new file mode 100644
index 0000000..a348a9a
--- /dev/null
+++ b/apps/advanced/console/tests/unit/yii
@@ -0,0 +1,21 @@
+#!/usr/bin/env php
+<?php
+/**
+ * Yii console bootstrap file.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+// fcgi doesn't have STDIN and STDOUT defined by default
+defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
+defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
+
+require_once(__DIR__ . '/../_console.php');
+
+$config = require(__DIR__ . '/_console.php');
+
+$application = new yii\console\Application($config);
+$exitCode = $application->run();
+exit($exitCode);
diff --git a/apps/advanced/console/tests/unit/yii.bat b/apps/advanced/console/tests/unit/yii.bat
new file mode 100644
index 0000000..5e21e2e
--- /dev/null
+++ b/apps/advanced/console/tests/unit/yii.bat
@@ -0,0 +1,20 @@
+@echo off
+
+rem -------------------------------------------------------------
+rem  Yii command line bootstrap script for Windows.
+rem
+rem  @author Qiang Xue <qiang.xue@gmail.com>
+rem  @link http://www.yiiframework.com/
+rem  @copyright Copyright &copy; 2012 Yii Software LLC
+rem  @license http://www.yiiframework.com/license/
+rem -------------------------------------------------------------
+
+@setlocal
+
+set YII_PATH=%~dp0
+
+if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
+
+"%PHP_COMMAND%" "%YII_PATH%yii" %*
+
+@endlocal
diff --git a/apps/advanced/environments/dev/backend/config/main-local.php b/apps/advanced/environments/dev/backend/config/main-local.php
index b2aaa3a..6823fd9 100644
--- a/apps/advanced/environments/dev/backend/config/main-local.php
+++ b/apps/advanced/environments/dev/backend/config/main-local.php
@@ -1,10 +1,12 @@
 <?php
-return [
-	'preload' => [
-		'debug',
-	],
-	'modules' => [
-		'debug' => 'yii\debug\Module',
-		'gii' => 'yii\gii\Module',
-	],
-];
+
+$config = [];
+
+if (!YII_ENV_TEST) {
+	// configuration adjustments for 'dev' environment
+	$config['preload'][] = 'debug';
+	$config['modules']['debug'] = 'yii\debug\Module';
+	$config['modules']['gii'] = 'yii\gii\Module';
+}
+
+return $config;
diff --git a/apps/advanced/environments/dev/backend/web/index-test.php b/apps/advanced/environments/dev/backend/web/index-test.php
new file mode 100644
index 0000000..1cd0794
--- /dev/null
+++ b/apps/advanced/environments/dev/backend/web/index-test.php
@@ -0,0 +1,17 @@
+<?php
+
+// NOTE: Make sure this file is not accessible when deployed to production
+if (!in_array(@$_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1'])) {
+	die('You are not allowed to access this file.');
+}
+
+defined('YII_DEBUG') or define('YII_DEBUG', true);
+defined('YII_ENV') or define('YII_ENV', 'test');
+
+require(__DIR__ . '/../../vendor/autoload.php');
+require(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
+require(__DIR__ . '/../../common/config/aliases.php');
+
+$config = require(__DIR__ . '/../tests/acceptance/_config.php');
+
+(new yii\web\Application($config))->run();
diff --git a/apps/advanced/environments/dev/frontend/config/main-local.php b/apps/advanced/environments/dev/frontend/config/main-local.php
index b2aaa3a..6823fd9 100644
--- a/apps/advanced/environments/dev/frontend/config/main-local.php
+++ b/apps/advanced/environments/dev/frontend/config/main-local.php
@@ -1,10 +1,12 @@
 <?php
-return [
-	'preload' => [
-		'debug',
-	],
-	'modules' => [
-		'debug' => 'yii\debug\Module',
-		'gii' => 'yii\gii\Module',
-	],
-];
+
+$config = [];
+
+if (!YII_ENV_TEST) {
+	// configuration adjustments for 'dev' environment
+	$config['preload'][] = 'debug';
+	$config['modules']['debug'] = 'yii\debug\Module';
+	$config['modules']['gii'] = 'yii\gii\Module';
+}
+
+return $config;
diff --git a/apps/advanced/environments/dev/frontend/web/index-test.php b/apps/advanced/environments/dev/frontend/web/index-test.php
new file mode 100644
index 0000000..1cd0794
--- /dev/null
+++ b/apps/advanced/environments/dev/frontend/web/index-test.php
@@ -0,0 +1,17 @@
+<?php
+
+// NOTE: Make sure this file is not accessible when deployed to production
+if (!in_array(@$_SERVER['REMOTE_ADDR'], ['127.0.0.1', '::1'])) {
+	die('You are not allowed to access this file.');
+}
+
+defined('YII_DEBUG') or define('YII_DEBUG', true);
+defined('YII_ENV') or define('YII_ENV', 'test');
+
+require(__DIR__ . '/../../vendor/autoload.php');
+require(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
+require(__DIR__ . '/../../common/config/aliases.php');
+
+$config = require(__DIR__ . '/../tests/acceptance/_config.php');
+
+(new yii\web\Application($config))->run();
diff --git a/apps/advanced/frontend/codeception.yml b/apps/advanced/frontend/codeception.yml
new file mode 100644
index 0000000..5b1f441
--- /dev/null
+++ b/apps/advanced/frontend/codeception.yml
@@ -0,0 +1,18 @@
+paths:
+    tests: tests
+    log: tests/_log
+    data: tests/_data
+    helpers: tests/_helpers
+settings:
+    bootstrap: _bootstrap.php
+    suite_class: \PHPUnit_Framework_TestSuite
+    colors: true
+    memory_limit: 1024M
+    log: true
+modules:
+    config:
+        Db:
+            dsn: ''
+            user: ''
+            password: ''
+            dump: tests/_data/dump.sql
diff --git a/apps/advanced/frontend/controllers/SiteController.php b/apps/advanced/frontend/controllers/SiteController.php
index a360d11..6816daf 100644
--- a/apps/advanced/frontend/controllers/SiteController.php
+++ b/apps/advanced/frontend/controllers/SiteController.php
@@ -87,8 +87,12 @@ class SiteController extends Controller
 	public function actionContact()
 	{
 		$model = new ContactForm();
-		if ($model->load(Yii::$app->request->post()) && $model->contact(Yii::$app->params['adminEmail'])) {
-			Yii::$app->session->setFlash('success', 'Thank you for contacting us. We will respond to you as soon as possible.');
+		if ($model->load(Yii::$app->request->post()) && $model->validate()) {
+			if ($model->sendEmail(Yii::$app->params['adminEmail'])) {
+				Yii::$app->session->setFlash('success', 'Thank you for contacting us. We will respond to you as soon as possible.');
+			} else {
+				Yii::$app->session->setFlash('error', 'There was an error sending email.');
+			}
 			return $this->refresh();
 		} else {
 			return $this->render('contact', [
@@ -122,7 +126,7 @@ class SiteController extends Controller
 	public function actionRequestPasswordReset()
 	{
 		$model = new PasswordResetRequestForm();
-		if ($model->load(Yii::$app->request->post())) {
+		if ($model->load(Yii::$app->request->post()) && $model->validate()) {
 			if ($model->sendEmail()) {
 				Yii::$app->getSession()->setFlash('success', 'Check your email for further instructions.');
 				return $this->goHome();
@@ -144,7 +148,7 @@ class SiteController extends Controller
 			throw new BadRequestHttpException($e->getMessage());
 		}
 
-		if ($model->load(Yii::$app->request->post()) && $model->resetPassword()) {
+		if ($model->load(Yii::$app->request->post()) && $model->validate() && $model->resetPassword()) {
 			Yii::$app->getSession()->setFlash('success', 'New password was saved.');
 			return $this->goHome();
 		}
diff --git a/apps/advanced/frontend/models/ContactForm.php b/apps/advanced/frontend/models/ContactForm.php
index 367096d..7d99ae8 100644
--- a/apps/advanced/frontend/models/ContactForm.php
+++ b/apps/advanced/frontend/models/ContactForm.php
@@ -45,20 +45,15 @@ class ContactForm extends Model
 	 * Sends an email to the specified email address using the information collected by this model.
 	 *
 	 * @param string $email the target email address
-	 * @return boolean whether the model passes validation
+	 * @return boolean whether the email was sent
 	 */
-	public function contact($email)
+	public function sendEmail($email)
 	{
-		if ($this->validate()) {
-			Yii::$app->mail->compose()
-				->setTo($email)
-				->setFrom([$this->email => $this->name])
-				->setSubject($this->subject)
-				->setTextBody($this->body)
-				->send();
-			return true;
-		} else {
-			return false;
-		}
+		return Yii::$app->mail->compose()
+			->setTo($email)
+			->setFrom([$this->email => $this->name])
+			->setSubject($this->subject)
+			->setTextBody($this->body)
+			->send();
 	}
 }
diff --git a/apps/advanced/frontend/models/PasswordResetRequestForm.php b/apps/advanced/frontend/models/PasswordResetRequestForm.php
index a82b647..3b74be4 100644
--- a/apps/advanced/frontend/models/PasswordResetRequestForm.php
+++ b/apps/advanced/frontend/models/PasswordResetRequestForm.php
@@ -20,7 +20,11 @@ class PasswordResetRequestForm extends Model
 			['email', 'filter', 'filter' => 'trim'],
 			['email', 'required'],
 			['email', 'email'],
-			['email', 'exist', 'targetClass' => '\common\models\User', 'message' => 'There is no user with such email.'],
+			['email', 'exist',
+				'targetClass' => '\common\models\User',
+				'filter' => ['status' => User::STATUS_ACTIVE],
+				'message' => 'There is no user with such email.'
+			],
 		];
 	}
 
@@ -37,17 +41,15 @@ class PasswordResetRequestForm extends Model
 			'email' => $this->email,
 		]);
 
-		if (!$user) {
-			return false;
-		}
-
-		$user->generatePasswordResetToken();
-		if ($user->save()) {
-			return \Yii::$app->mail->compose('passwordResetToken', ['user' => $user])
-				->setFrom([\Yii::$app->params['supportEmail'] => \Yii::$app->name . ' robot'])
-				->setTo($this->email)
-				->setSubject('Password reset for ' . \Yii::$app->name)
-				->send();
+		if ($user) {
+			$user->generatePasswordResetToken();
+			if ($user->save()) {
+				return \Yii::$app->mail->compose('passwordResetToken', ['user' => $user])
+					->setFrom([\Yii::$app->params['supportEmail'] => \Yii::$app->name . ' robot'])
+					->setTo($this->email)
+					->setSubject('Password reset for ' . \Yii::$app->name)
+					->send();
+			}
 		}
 
 		return false;
diff --git a/apps/advanced/frontend/tests/_bootstrap.php b/apps/advanced/frontend/tests/_bootstrap.php
new file mode 100644
index 0000000..09f11dd
--- /dev/null
+++ b/apps/advanced/frontend/tests/_bootstrap.php
@@ -0,0 +1,23 @@
+<?php
+
+// the entry script URL (without host info) for functional and acceptance tests
+// PLEASE ADJUST IT TO THE ACTUAL ENTRY SCRIPT URL
+defined('TEST_ENTRY_URL') or define('TEST_ENTRY_URL', '/frontend/web/index-test.php');
+
+// the entry script file path for functional and acceptance tests
+defined('TEST_ENTRY_FILE') or define('TEST_ENTRY_FILE', dirname(__DIR__) . '/web/index-test.php');
+
+defined('YII_DEBUG') or define('YII_DEBUG', true);
+
+defined('YII_ENV') or define('YII_ENV', 'test');
+
+require_once(__DIR__ . '/../../vendor/autoload.php');
+
+require_once(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
+
+require(__DIR__ . '/../../common/config/aliases.php');
+
+// set correct script paths
+$_SERVER['SCRIPT_FILENAME'] = TEST_ENTRY_FILE;
+$_SERVER['SCRIPT_NAME'] = TEST_ENTRY_URL;
+$_SERVER['SERVER_NAME'] = 'localhost';
diff --git a/apps/advanced/frontend/tests/_config.php b/apps/advanced/frontend/tests/_config.php
new file mode 100644
index 0000000..d9cc356
--- /dev/null
+++ b/apps/advanced/frontend/tests/_config.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * application configurations shared by all test types
+ */
+return [
+	'components' => [
+		'mail' => [
+			'useFileTransport' => true,
+		],
+		'urlManager' => [
+			'showScriptName' => true,
+		],
+	],
+];
diff --git a/apps/advanced/frontend/tests/_console.php b/apps/advanced/frontend/tests/_console.php
new file mode 100644
index 0000000..b38c84e
--- /dev/null
+++ b/apps/advanced/frontend/tests/_console.php
@@ -0,0 +1,19 @@
+<?php
+/**
+ * Yii console bootstrap file.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+// fcgi doesn't have STDIN and STDOUT defined by default
+defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
+defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
+
+require_once(__DIR__ . '/../../vendor/autoload.php');
+require_once(__DIR__ . '/../../vendor/yiisoft/yii2/Yii.php');
+require_once(__DIR__ . '/../../common/config/aliases.php');
+
+defined('YII_DEBUG') or define('YII_DEBUG', true);
+defined('YII_ENV') or define('YII_ENV', 'test');
diff --git a/apps/advanced/frontend/tests/_data/dump.sql b/apps/advanced/frontend/tests/_data/dump.sql
new file mode 100644
index 0000000..4bc742c
--- /dev/null
+++ b/apps/advanced/frontend/tests/_data/dump.sql
@@ -0,0 +1 @@
+/* Replace this file with actual dump of your database */
\ No newline at end of file
diff --git a/apps/advanced/frontend/tests/_helpers/CodeHelper.php b/apps/advanced/frontend/tests/_helpers/CodeHelper.php
new file mode 100644
index 0000000..972c8f3
--- /dev/null
+++ b/apps/advanced/frontend/tests/_helpers/CodeHelper.php
@@ -0,0 +1,8 @@
+<?php
+namespace Codeception\Module;
+
+// here you can define custom functions for CodeGuy 
+
+class CodeHelper extends \Codeception\Module
+{
+}
diff --git a/apps/advanced/frontend/tests/_helpers/TestHelper.php b/apps/advanced/frontend/tests/_helpers/TestHelper.php
new file mode 100644
index 0000000..37737cd
--- /dev/null
+++ b/apps/advanced/frontend/tests/_helpers/TestHelper.php
@@ -0,0 +1,8 @@
+<?php
+namespace Codeception\Module;
+
+// here you can define custom functions for TestGuy 
+
+class TestHelper extends \Codeception\Module
+{
+}
diff --git a/apps/advanced/frontend/tests/_helpers/WebHelper.php b/apps/advanced/frontend/tests/_helpers/WebHelper.php
new file mode 100644
index 0000000..66b070e
--- /dev/null
+++ b/apps/advanced/frontend/tests/_helpers/WebHelper.php
@@ -0,0 +1,8 @@
+<?php
+namespace Codeception\Module;
+
+// here you can define custom functions for WebGuy 
+
+class WebHelper extends \Codeception\Module
+{
+}
diff --git a/apps/advanced/frontend/tests/_log/.gitignore b/apps/advanced/frontend/tests/_log/.gitignore
new file mode 100644
index 0000000..d6b7ef3
--- /dev/null
+++ b/apps/advanced/frontend/tests/_log/.gitignore
@@ -0,0 +1,2 @@
+*
+!.gitignore
diff --git a/apps/advanced/frontend/tests/_pages/AboutPage.php b/apps/advanced/frontend/tests/_pages/AboutPage.php
new file mode 100644
index 0000000..8ad0134
--- /dev/null
+++ b/apps/advanced/frontend/tests/_pages/AboutPage.php
@@ -0,0 +1,10 @@
+<?php
+
+namespace frontend\tests\_pages;
+
+use yii\codeception\BasePage;
+
+class AboutPage extends BasePage
+{
+	public $route = 'site/about';
+}
diff --git a/apps/advanced/frontend/tests/_pages/ContactPage.php b/apps/advanced/frontend/tests/_pages/ContactPage.php
new file mode 100644
index 0000000..c3e291c
--- /dev/null
+++ b/apps/advanced/frontend/tests/_pages/ContactPage.php
@@ -0,0 +1,22 @@
+<?php
+
+namespace frontend\tests\_pages;
+
+use yii\codeception\BasePage;
+
+class ContactPage extends BasePage
+{
+	public $route = 'site/contact';
+
+	/**
+	 * @param array $contactData
+	 */
+	public function submit(array $contactData)
+	{
+		foreach ($contactData as $field => $value) {
+			$inputType = $field === 'body' ? 'textarea' : 'input';
+			$this->guy->fillField($inputType . '[name="ContactForm[' . $field . ']"]', $value);
+		}
+		$this->guy->click('contact-button');
+	}
+}
diff --git a/apps/advanced/frontend/tests/_pages/SignupPage.php b/apps/advanced/frontend/tests/_pages/SignupPage.php
new file mode 100644
index 0000000..ba4a9cb
--- /dev/null
+++ b/apps/advanced/frontend/tests/_pages/SignupPage.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace frontend\tests\_pages;
+
+use \yii\codeception\BasePage;
+
+class SignupPage extends BasePage
+{
+
+	public $route = 'site/signup';
+
+	/**
+	 * @param array $signupData
+	 */
+	public function submit(array $signupData)
+	{
+		foreach ($signupData as $field => $value) {
+			$inputType = $field === 'body' ? 'textarea' : 'input';
+			$this->guy->fillField($inputType . '[name="SignupForm[' . $field . ']"]', $value);
+		}
+		$this->guy->click('signup-button');
+	}
+
+}
diff --git a/apps/advanced/frontend/tests/acceptance.suite.yml b/apps/advanced/frontend/tests/acceptance.suite.yml
new file mode 100644
index 0000000..e6c64d1
--- /dev/null
+++ b/apps/advanced/frontend/tests/acceptance.suite.yml
@@ -0,0 +1,28 @@
+# Codeception Test Suite Configuration
+
+# suite for acceptance tests.
+# perform tests in browser using the Selenium-like tools.
+# powered by Mink (http://mink.behat.org).
+# (tip: that's what your customer will see).
+# (tip: test your ajax and javascript by one of Mink drivers).
+
+# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
+
+class_name: WebGuy
+modules:
+    enabled:
+        - WebHelper
+        - PhpBrowser
+        - common\tests\_helpers\FixtureHelper
+# you can use WebDriver instead of PhpBrowser to test javascript and ajax.
+# This will require you to install selenium. See http://codeception.com/docs/04-AcceptanceTests#Selenium
+# "restart" option is used by the WebDriver to start each time per test-file new session and cookies, 
+# it is useful if you want to login in your app in each test.
+#        - WebDriver
+    config:
+        PhpBrowser:
+            url: 'http://localhost:8080'
+#        WebDriver:
+#            url: 'http://localhost'
+#            browser: firefox
+#            restart: true
diff --git a/apps/advanced/frontend/tests/acceptance/AboutCept.php b/apps/advanced/frontend/tests/acceptance/AboutCept.php
new file mode 100644
index 0000000..71e387c
--- /dev/null
+++ b/apps/advanced/frontend/tests/acceptance/AboutCept.php
@@ -0,0 +1,8 @@
+<?php
+
+use frontend\tests\_pages\AboutPage;
+
+$I = new WebGuy($scenario);
+$I->wantTo('ensure that about works');
+AboutPage::openBy($I);
+$I->see('About', 'h1');
diff --git a/apps/advanced/frontend/tests/acceptance/ContactCept.php b/apps/advanced/frontend/tests/acceptance/ContactCept.php
new file mode 100644
index 0000000..84c2911
--- /dev/null
+++ b/apps/advanced/frontend/tests/acceptance/ContactCept.php
@@ -0,0 +1,45 @@
+<?php
+
+use frontend\tests\_pages\ContactPage;
+
+$I = new WebGuy($scenario);
+$I->wantTo('ensure that contact works');
+
+$contactPage = ContactPage::openBy($I);
+
+$I->see('Contact', 'h1');
+
+$I->amGoingTo('submit contact form with no data');
+$contactPage->submit([]);
+$I->expectTo('see validations errors');
+$I->see('Contact', 'h1');
+$I->see('Name cannot be blank');
+$I->see('Email cannot be blank');
+$I->see('Subject cannot be blank');
+$I->see('Body cannot be blank');
+$I->see('The verification code is incorrect');
+
+$I->amGoingTo('submit contact form with not correct email');
+$contactPage->submit([
+	'name'			=>	'tester',
+	'email'			=>	'tester.email',
+	'subject'		=>	'test subject',
+	'body'			=>	'test content',
+	'verifyCode'	=>	'testme',
+]);
+$I->expectTo('see that email adress is wrong');
+$I->dontSee('Name cannot be blank', '.help-inline');
+$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->amGoingTo('submit contact form with correct data');
+$contactPage->submit([
+	'name'			=>	'tester',
+	'email'			=>	'tester@example.com',
+	'subject'		=>	'test subject',
+	'body'			=>	'test content',
+	'verifyCode'	=>	'testme',
+]);
+$I->see('Thank you for contacting us. We will respond to you as soon as possible.');
diff --git a/apps/advanced/frontend/tests/acceptance/HomeCept.php b/apps/advanced/frontend/tests/acceptance/HomeCept.php
new file mode 100644
index 0000000..62456f9
--- /dev/null
+++ b/apps/advanced/frontend/tests/acceptance/HomeCept.php
@@ -0,0 +1,9 @@
+<?php
+
+$I = new WebGuy($scenario);
+$I->wantTo('ensure that home page works');
+$I->amOnPage(Yii::$app->homeUrl);
+$I->see('My Company');
+$I->seeLink('About');
+$I->click('About');
+$I->see('This is the About page.');
diff --git a/apps/advanced/frontend/tests/acceptance/LoginCept.php b/apps/advanced/frontend/tests/acceptance/LoginCept.php
new file mode 100644
index 0000000..62ebc0e
--- /dev/null
+++ b/apps/advanced/frontend/tests/acceptance/LoginCept.php
@@ -0,0 +1,30 @@
+<?php
+
+use common\tests\_pages\LoginPage;
+
+$I = new WebGuy($scenario);
+$I->wantTo('ensure login page works');
+
+$loginPage = LoginPage::openBy($I);
+
+$I->amGoingTo('submit login form with no data');
+$loginPage->login('', '');
+$I->expectTo('see validations errors');
+$I->see('Username cannot be blank.');
+$I->see('Password cannot be blank.');
+
+$I->amGoingTo('try to login with wrong credentials');
+$I->expectTo('see validations errors');
+$loginPage->login('admin', 'wrong');
+$I->expectTo('see validations errors');
+$I->see('Incorrect username or password.');
+
+$I->amGoingTo('try to login with correct credentials');
+$loginPage->login('erau', 'password_0');
+$I->expectTo('see that user is logged');
+$I->see('Logout (erau)');
+$I->dontSee('Login');
+$I->dontSee('Signup');
+$I->click('Logout (erau)');
+$I->dontSee('Logout (erau)');
+$I->see('Login');
diff --git a/apps/advanced/frontend/tests/acceptance/SignupCest.php b/apps/advanced/frontend/tests/acceptance/SignupCest.php
new file mode 100644
index 0000000..a166f34
--- /dev/null
+++ b/apps/advanced/frontend/tests/acceptance/SignupCest.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace frontend\tests\acceptance;
+
+use frontend\tests\_pages\SignupPage;
+use common\models\User;
+
+class SignupCest
+{
+
+	/**
+	 * This method is called after each cest class test method
+	 * @param \Codeception\Event\Test $event
+	 */
+	public function _before($event)
+	{
+	}
+
+	/**
+	 * This method is called after each cest class test method, even if test failed.
+	 * @param \Codeception\Event\Test $event
+	 */
+	public function _after($event)
+	{
+		User::deleteAll([
+			'email' => 'tester.email@example.com',
+			'username' => 'tester',
+		]);
+	}
+
+	/**
+	 * This method is called when test fails.
+	 * @param \Codeception\Event\Fail $event
+	 */
+	public function _fail($event)
+	{
+	}
+
+	/**
+	 * 
+	 * @param \WebGuy $I
+	 * @param \Codeception\Scenario $scenario
+	 */
+	public function testUserSignup($I, $scenario)
+	{
+		$I->wantTo('ensure that signup works');
+
+		$signupPage = SignupPage::openBy($I);
+		$I->see('Please fill out the following fields to signup:');
+
+		$I->amGoingTo('submit signup form with no data');
+
+		$signupPage->submit([]);
+
+		$I->expectTo('see validation errors');
+		$I->see('Username cannot be blank.');
+		$I->see('Email cannot be blank.');
+		$I->see('Password cannot be blank.');
+
+		$I->amGoingTo('submit signup form with not correct email');
+		$signupPage->submit([
+			'username'		=>	'tester',
+			'email'			=>	'tester.email',
+			'password'		=>	'tester_password',
+		]);
+
+		$I->expectTo('see that email adress is wrong');
+		$I->dontSee('Username cannot be blank.', '.help-inline');
+		$I->dontSee('Password cannot be blank.', '.help-inline');
+		$I->see('Email is not a valid email address.', '.help-block');
+
+		$I->amGoingTo('submit signup form with correct email');
+		$signupPage->submit([
+			'username'		=>	'tester',
+			'email'			=>	'tester.email@example.com',
+			'password'		=>	'tester_password',
+		]);
+
+		$I->expectTo('see that user logged in');
+		$I->see('Logout (tester)');
+	}
+
+}
diff --git a/apps/advanced/frontend/tests/acceptance/_bootstrap.php b/apps/advanced/frontend/tests/acceptance/_bootstrap.php
new file mode 100644
index 0000000..6ce3d17
--- /dev/null
+++ b/apps/advanced/frontend/tests/acceptance/_bootstrap.php
@@ -0,0 +1,3 @@
+<?php
+
+new yii\web\Application(require(__DIR__ . '/_config.php'));
diff --git a/apps/advanced/frontend/tests/acceptance/_config.php b/apps/advanced/frontend/tests/acceptance/_config.php
new file mode 100644
index 0000000..4a70bb9
--- /dev/null
+++ b/apps/advanced/frontend/tests/acceptance/_config.php
@@ -0,0 +1,16 @@
+<?php
+
+return yii\helpers\ArrayHelper::merge(
+	require(__DIR__ . '/../../config/main.php'),
+	require(__DIR__ . '/../../config/main-local.php'),
+	require(__DIR__ . '/../../../common/config/main.php'),
+	require(__DIR__ . '/../../../common/config/main-local.php'),
+	require(__DIR__ . '/../_config.php'),
+	[
+		'components' => [
+			'db' => [
+				'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_acceptance',
+			],
+		],
+	]
+);
diff --git a/apps/advanced/frontend/tests/acceptance/_console.php b/apps/advanced/frontend/tests/acceptance/_console.php
new file mode 100644
index 0000000..1e1ec56
--- /dev/null
+++ b/apps/advanced/frontend/tests/acceptance/_console.php
@@ -0,0 +1,15 @@
+<?php
+
+return yii\helpers\ArrayHelper::merge(
+	require(__DIR__ . '/../../../common/config/main.php'),
+	require(__DIR__ . '/../../../common/config/main-local.php'),
+	require(__DIR__ . '/../../../console/config/main.php'),
+	require(__DIR__ . '/../../../console/config/main-local.php'),
+	[
+		'components' => [
+			'db' => [
+				'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_acceptance',
+			],
+		],
+	]
+);
diff --git a/apps/advanced/frontend/tests/acceptance/yii b/apps/advanced/frontend/tests/acceptance/yii
new file mode 100644
index 0000000..a348a9a
--- /dev/null
+++ b/apps/advanced/frontend/tests/acceptance/yii
@@ -0,0 +1,21 @@
+#!/usr/bin/env php
+<?php
+/**
+ * Yii console bootstrap file.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+// fcgi doesn't have STDIN and STDOUT defined by default
+defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
+defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
+
+require_once(__DIR__ . '/../_console.php');
+
+$config = require(__DIR__ . '/_console.php');
+
+$application = new yii\console\Application($config);
+$exitCode = $application->run();
+exit($exitCode);
diff --git a/apps/advanced/frontend/tests/acceptance/yii.bat b/apps/advanced/frontend/tests/acceptance/yii.bat
new file mode 100644
index 0000000..5e21e2e
--- /dev/null
+++ b/apps/advanced/frontend/tests/acceptance/yii.bat
@@ -0,0 +1,20 @@
+@echo off
+
+rem -------------------------------------------------------------
+rem  Yii command line bootstrap script for Windows.
+rem
+rem  @author Qiang Xue <qiang.xue@gmail.com>
+rem  @link http://www.yiiframework.com/
+rem  @copyright Copyright &copy; 2012 Yii Software LLC
+rem  @license http://www.yiiframework.com/license/
+rem -------------------------------------------------------------
+
+@setlocal
+
+set YII_PATH=%~dp0
+
+if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
+
+"%PHP_COMMAND%" "%YII_PATH%yii" %*
+
+@endlocal
diff --git a/apps/advanced/frontend/tests/functional.suite.yml b/apps/advanced/frontend/tests/functional.suite.yml
new file mode 100644
index 0000000..5242b9b
--- /dev/null
+++ b/apps/advanced/frontend/tests/functional.suite.yml
@@ -0,0 +1,18 @@
+# Codeception Test Suite Configuration
+
+# suite for functional (integration) tests.
+# emulate web requests and make application process them.
+# (tip: better to use with frameworks).
+
+# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
+#basic/web/index.php
+class_name: TestGuy
+modules:
+    enabled:
+      - Filesystem
+      - TestHelper
+      - Yii2
+      - common\tests\_helpers\FixtureHelper
+    config:
+        Yii2:
+            configFile: 'tests/functional/_config.php'
diff --git a/apps/advanced/frontend/tests/functional/AboutCept.php b/apps/advanced/frontend/tests/functional/AboutCept.php
new file mode 100644
index 0000000..b2153b3
--- /dev/null
+++ b/apps/advanced/frontend/tests/functional/AboutCept.php
@@ -0,0 +1,8 @@
+<?php
+
+use frontend\tests\_pages\AboutPage;
+
+$I = new TestGuy($scenario);
+$I->wantTo('ensure that about works');
+AboutPage::openBy($I);
+$I->see('About', 'h1');
diff --git a/apps/advanced/frontend/tests/functional/ContactCept.php b/apps/advanced/frontend/tests/functional/ContactCept.php
new file mode 100644
index 0000000..7fc1ddb
--- /dev/null
+++ b/apps/advanced/frontend/tests/functional/ContactCept.php
@@ -0,0 +1,45 @@
+<?php
+
+use frontend\tests\_pages\ContactPage;
+
+$I = new TestGuy($scenario);
+$I->wantTo('ensure that contact works');
+
+$contactPage = ContactPage::openBy($I);
+
+$I->see('Contact', 'h1');
+
+$I->amGoingTo('submit contact form with no data');
+$contactPage->submit([]);
+$I->expectTo('see validations errors');
+$I->see('Contact', 'h1');
+$I->see('Name cannot be blank');
+$I->see('Email cannot be blank');
+$I->see('Subject cannot be blank');
+$I->see('Body cannot be blank');
+$I->see('The verification code is incorrect');
+
+$I->amGoingTo('submit contact form with not correct email');
+$contactPage->submit([
+	'name'			=>	'tester',
+	'email'			=>	'tester.email',
+	'subject'		=>	'test subject',
+	'body'			=>	'test content',
+	'verifyCode'	=>	'testme',
+]);
+$I->expectTo('see that email adress is wrong');
+$I->dontSee('Name cannot be blank', '.help-inline');
+$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->amGoingTo('submit contact form with correct data');
+$contactPage->submit([
+	'name'			=>	'tester',
+	'email'			=>	'tester@example.com',
+	'subject'		=>	'test subject',
+	'body'			=>	'test content',
+	'verifyCode'	=>	'testme',
+]);
+$I->see('Thank you for contacting us. We will respond to you as soon as possible.');
diff --git a/apps/advanced/frontend/tests/functional/HomeCept.php b/apps/advanced/frontend/tests/functional/HomeCept.php
new file mode 100644
index 0000000..3258ba3
--- /dev/null
+++ b/apps/advanced/frontend/tests/functional/HomeCept.php
@@ -0,0 +1,9 @@
+<?php
+
+$I = new TestGuy($scenario);
+$I->wantTo('ensure that home page works');
+$I->amOnPage(Yii::$app->homeUrl);
+$I->see('My Company');
+$I->seeLink('About');
+$I->click('About');
+$I->see('This is the About page.');
diff --git a/apps/advanced/frontend/tests/functional/LoginCept.php b/apps/advanced/frontend/tests/functional/LoginCept.php
new file mode 100644
index 0000000..1d2dc14
--- /dev/null
+++ b/apps/advanced/frontend/tests/functional/LoginCept.php
@@ -0,0 +1,30 @@
+<?php
+
+use common\tests\_pages\LoginPage;
+
+$I = new TestGuy($scenario);
+$I->wantTo('ensure login page works');
+
+$loginPage = LoginPage::openBy($I);
+
+$I->amGoingTo('submit login form with no data');
+$loginPage->login('', '');
+$I->expectTo('see validations errors');
+$I->see('Username cannot be blank.');
+$I->see('Password cannot be blank.');
+
+$I->amGoingTo('try to login with wrong credentials');
+$I->expectTo('see validations errors');
+$loginPage->login('admin', 'wrong');
+$I->expectTo('see validations errors');
+$I->see('Incorrect username or password.');
+
+$I->amGoingTo('try to login with correct credentials');
+$loginPage->login('erau', 'password_0');
+$I->expectTo('see that user is logged');
+$I->see('Logout (erau)');
+$I->dontSee('Login');
+$I->dontSee('Signup');
+$I->click('Logout (erau)');
+$I->dontSee('Logout (erau)');
+$I->see('Login');
diff --git a/apps/advanced/frontend/tests/functional/SignupCest.php b/apps/advanced/frontend/tests/functional/SignupCest.php
new file mode 100644
index 0000000..cfef787
--- /dev/null
+++ b/apps/advanced/frontend/tests/functional/SignupCest.php
@@ -0,0 +1,83 @@
+<?php
+
+namespace frontend\tests\functional;
+
+use frontend\tests\_pages\SignupPage;
+use common\models\User;
+
+class SignupCest
+{
+
+	/**
+	 * This method is called after each cest class test method
+	 * @param \Codeception\Event\Test $event
+	 */
+	public function _before($event)
+	{
+	}
+
+	/**
+	 * This method is called after each cest class test method, even if test failed.
+	 * @param \Codeception\Event\Test $event
+	 */
+	public function _after($event)
+	{
+		User::deleteAll([
+			'email' => 'tester.email@example.com',
+			'username' => 'tester',
+		]);
+	}
+
+	/**
+	 * This method is called when test fails.
+	 * @param \Codeception\Event\Fail $event
+	 */
+	public function _fail($event)
+	{
+	}
+
+	/**
+	 * 
+	 * @param \TestGuy $I
+	 * @param \Codeception\Scenario $scenario
+	 */
+	public function testUserSignup($I, $scenario)
+	{
+		$I->wantTo('ensure that signup works');
+
+		$signupPage = SignupPage::openBy($I);
+		$I->see('Please fill out the following fields to signup:');
+
+		$I->amGoingTo('submit signup form with no data');
+
+		$signupPage->submit([]);
+
+		$I->expectTo('see validation errors');
+		$I->see('Username cannot be blank.');
+		$I->see('Email cannot be blank.');
+		$I->see('Password cannot be blank.');
+
+		$I->amGoingTo('submit signup form with not correct email');
+		$signupPage->submit([
+			'username'		=>	'tester',
+			'email'			=>	'tester.email',
+			'password'		=>	'tester_password',
+		]);
+
+		$I->expectTo('see that email adress is wrong');
+		$I->dontSee('Username cannot be blank.', '.help-inline');
+		$I->dontSee('Password cannot be blank.', '.help-inline');
+		$I->see('Email is not a valid email address.', '.help-block');
+
+		$I->amGoingTo('submit signup form with correct email');
+		$signupPage->submit([
+			'username'		=>	'tester',
+			'email'			=>	'tester.email@example.com',
+			'password'		=>	'tester_password',
+		]);
+
+		$I->expectTo('see that user logged in');
+		$I->see('Logout (tester)');
+	}
+
+}
diff --git a/apps/advanced/frontend/tests/functional/_bootstrap.php b/apps/advanced/frontend/tests/functional/_bootstrap.php
new file mode 100644
index 0000000..6ce3d17
--- /dev/null
+++ b/apps/advanced/frontend/tests/functional/_bootstrap.php
@@ -0,0 +1,3 @@
+<?php
+
+new yii\web\Application(require(__DIR__ . '/_config.php'));
diff --git a/apps/advanced/frontend/tests/functional/_config.php b/apps/advanced/frontend/tests/functional/_config.php
new file mode 100644
index 0000000..c1bc080
--- /dev/null
+++ b/apps/advanced/frontend/tests/functional/_config.php
@@ -0,0 +1,20 @@
+<?php
+
+// set correct script paths
+$_SERVER['SCRIPT_FILENAME'] = TEST_ENTRY_FILE;
+$_SERVER['SCRIPT_NAME'] = TEST_ENTRY_URL;
+
+return yii\helpers\ArrayHelper::merge(
+	require(__DIR__ . '/../../config/main.php'),
+	require(__DIR__ . '/../../config/main-local.php'),
+	require(__DIR__ . '/../../../common/config/main.php'),
+	require(__DIR__ . '/../../../common/config/main-local.php'),
+	require(__DIR__ . '/../_config.php'),
+	[
+		'components' => [
+			'db' => [
+				'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_functional',
+			],
+		],
+	]
+);
diff --git a/apps/advanced/frontend/tests/functional/_console.php b/apps/advanced/frontend/tests/functional/_console.php
new file mode 100644
index 0000000..39434f7
--- /dev/null
+++ b/apps/advanced/frontend/tests/functional/_console.php
@@ -0,0 +1,15 @@
+<?php
+
+return yii\helpers\ArrayHelper::merge(
+	require(__DIR__ . '/../../../common/config/main.php'),
+	require(__DIR__ . '/../../../common/config/main-local.php'),
+	require(__DIR__ . '/../../../console/config/main.php'),
+	require(__DIR__ . '/../../../console/config/main-local.php'),
+	[
+		'components' => [
+			'db' => [
+				'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_functional',
+			],
+		],
+	]
+);
diff --git a/apps/advanced/frontend/tests/functional/yii b/apps/advanced/frontend/tests/functional/yii
new file mode 100644
index 0000000..a348a9a
--- /dev/null
+++ b/apps/advanced/frontend/tests/functional/yii
@@ -0,0 +1,21 @@
+#!/usr/bin/env php
+<?php
+/**
+ * Yii console bootstrap file.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+// fcgi doesn't have STDIN and STDOUT defined by default
+defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
+defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
+
+require_once(__DIR__ . '/../_console.php');
+
+$config = require(__DIR__ . '/_console.php');
+
+$application = new yii\console\Application($config);
+$exitCode = $application->run();
+exit($exitCode);
diff --git a/apps/advanced/frontend/tests/functional/yii.bat b/apps/advanced/frontend/tests/functional/yii.bat
new file mode 100644
index 0000000..5e21e2e
--- /dev/null
+++ b/apps/advanced/frontend/tests/functional/yii.bat
@@ -0,0 +1,20 @@
+@echo off
+
+rem -------------------------------------------------------------
+rem  Yii command line bootstrap script for Windows.
+rem
+rem  @author Qiang Xue <qiang.xue@gmail.com>
+rem  @link http://www.yiiframework.com/
+rem  @copyright Copyright &copy; 2012 Yii Software LLC
+rem  @license http://www.yiiframework.com/license/
+rem -------------------------------------------------------------
+
+@setlocal
+
+set YII_PATH=%~dp0
+
+if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
+
+"%PHP_COMMAND%" "%YII_PATH%yii" %*
+
+@endlocal
diff --git a/apps/advanced/frontend/tests/unit.suite.yml b/apps/advanced/frontend/tests/unit.suite.yml
new file mode 100644
index 0000000..04873a8
--- /dev/null
+++ b/apps/advanced/frontend/tests/unit.suite.yml
@@ -0,0 +1,8 @@
+# Codeception Test Suite Configuration
+
+# suite for unit (internal) tests.
+# RUN `build` COMMAND AFTER ADDING/REMOVING MODULES.
+
+class_name: CodeGuy
+modules:
+    enabled: [CodeHelper]
diff --git a/apps/advanced/frontend/tests/unit/DbTestCase.php b/apps/advanced/frontend/tests/unit/DbTestCase.php
new file mode 100644
index 0000000..998d5b8
--- /dev/null
+++ b/apps/advanced/frontend/tests/unit/DbTestCase.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace frontend\tests\unit;
+
+class DbTestCase extends \yii\codeception\DbTestCase
+{
+	public $appConfig = '@frontend/tests/unit/_config.php';
+}
diff --git a/apps/advanced/frontend/tests/unit/TestCase.php b/apps/advanced/frontend/tests/unit/TestCase.php
new file mode 100644
index 0000000..721408c
--- /dev/null
+++ b/apps/advanced/frontend/tests/unit/TestCase.php
@@ -0,0 +1,8 @@
+<?php
+
+namespace frontend\tests\unit;
+
+class TestCase extends \yii\codeception\TestCase
+{
+	public $appConfig = '@frontend/tests/unit/_config.php';
+}
diff --git a/apps/advanced/frontend/tests/unit/_bootstrap.php b/apps/advanced/frontend/tests/unit/_bootstrap.php
new file mode 100644
index 0000000..7dfa7c3
--- /dev/null
+++ b/apps/advanced/frontend/tests/unit/_bootstrap.php
@@ -0,0 +1,2 @@
+<?php
+// Here you can initialize variables that will for your tests
diff --git a/apps/advanced/frontend/tests/unit/_config.php b/apps/advanced/frontend/tests/unit/_config.php
new file mode 100644
index 0000000..22e5c62
--- /dev/null
+++ b/apps/advanced/frontend/tests/unit/_config.php
@@ -0,0 +1,16 @@
+<?php
+
+return yii\helpers\ArrayHelper::merge(
+	require(__DIR__ . '/../../../common/config/main.php'),
+	require(__DIR__ . '/../../../common/config/main-local.php'),
+	require(__DIR__ . '/../../config/main.php'),
+	require(__DIR__ . '/../../config/main-local.php'),
+	require(__DIR__ . '/../_config.php'),
+	[
+		'components' => [
+			'db' => [
+				'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_unit',
+			],
+		],
+	]
+);
diff --git a/apps/advanced/frontend/tests/unit/_console.php b/apps/advanced/frontend/tests/unit/_console.php
new file mode 100644
index 0000000..a0d8d02
--- /dev/null
+++ b/apps/advanced/frontend/tests/unit/_console.php
@@ -0,0 +1,15 @@
+<?php
+
+return yii\helpers\ArrayHelper::merge(
+	require(__DIR__ . '/../../../common/config/main.php'),
+	require(__DIR__ . '/../../../common/config/main-local.php'),
+	require(__DIR__ . '/../../../console/config/main.php'),
+	require(__DIR__ . '/../../../console/config/main-local.php'),
+	[
+		'components' => [
+			'db' => [
+				'dsn' => 'mysql:host=localhost;dbname=yii2_advanced_unit',
+			],
+		],
+	]
+);
diff --git a/apps/advanced/frontend/tests/unit/fixtures/data/tbl_user.php b/apps/advanced/frontend/tests/unit/fixtures/data/tbl_user.php
new file mode 100644
index 0000000..69552c2
--- /dev/null
+++ b/apps/advanced/frontend/tests/unit/fixtures/data/tbl_user.php
@@ -0,0 +1,23 @@
+<?php
+
+return [
+	[
+		'username' => 'okirlin',
+		'auth_key' => 'iwTNae9t34OmnK6l4vT4IeaTk-YWI2Rv',
+		'password_hash' => '$2y$13$CXT0Rkle1EMJ/c1l5bylL.EylfmQ39O5JlHJVFpNn618OUS1HwaIi',
+		'password_reset_token' => 't5GU9NwpuGYSfb7FEZMAxqtuz2PkEvv_1391885313',
+		'created_at' => '1391885313',
+		'updated_at' => '1391885313',
+		'email' => 'brady.renner@rutherford.com',
+	],
+	[
+		'username' => 'troy.becker',
+		'auth_key' => 'EdKfXrx88weFMV0vIxuTMWKgfK2tS3Lp',
+		'password_hash' => '$2y$13$g5nv41Px7VBqhS3hVsVN2.MKfgT3jFdkXEsMC4rQJLfaMa7VaJqL2',
+		'password_reset_token' => '4BSNyiZNAuxjs5Mty990c47sVrgllIi_1391885313',
+		'created_at' => '1391885313',
+		'updated_at' => '1391885313',
+		'email' => 'nicolas.dianna@hotmail.com',
+		'status' => '0',
+	],
+];
diff --git a/apps/advanced/frontend/tests/unit/models/ContactFormTest.php b/apps/advanced/frontend/tests/unit/models/ContactFormTest.php
new file mode 100644
index 0000000..23e6288
--- /dev/null
+++ b/apps/advanced/frontend/tests/unit/models/ContactFormTest.php
@@ -0,0 +1,60 @@
+<?php
+
+namespace frontend\tests\unit\models;
+
+use Yii;
+use frontend\tests\unit\TestCase;
+
+class ContactFormTest extends TestCase
+{
+
+	use \Codeception\Specify;
+
+	protected function setUp()
+	{
+		parent::setUp();
+		Yii::$app->mail->fileTransportCallback = function ($mailer, $message) {
+			return 'testing_message.eml';
+		};
+	}
+
+	protected function tearDown()
+	{
+		unlink($this->getMessageFile());
+		parent::tearDown();
+	}
+
+	public function testContact()
+	{
+		$model = $this->getMock('frontend\models\ContactForm', ['validate']);
+		$model->expects($this->once())->method('validate')->will($this->returnValue(true));
+
+		$model->attributes = [
+			'name' => 'Tester',
+			'email' => 'tester@example.com',
+			'subject' => 'very important letter subject',
+			'body' => 'body of current message',
+		];
+
+		$model->contact('admin@example.com');
+
+		$this->specify('email should be send', function () {
+			expect('email file should exist', file_exists($this->getMessageFile()))->true();
+		});
+
+		$this->specify('message should contain correct data', function () use($model) {
+			$emailMessage = file_get_contents($this->getMessageFile());
+
+			expect('email should contain user name', $emailMessage)->contains($model->name);
+			expect('email should contain sender email', $emailMessage)->contains($model->email);
+			expect('email should contain subject', $emailMessage)->contains($model->subject);
+			expect('email should contain body', $emailMessage)->contains($model->body);
+		});
+	}
+
+	private function getMessageFile()
+	{
+		return Yii::getAlias(Yii::$app->mail->fileTransportPath) . '/testing_message.eml';
+	}
+
+}
diff --git a/apps/advanced/frontend/tests/unit/models/PasswordResetRequestFormTest.php b/apps/advanced/frontend/tests/unit/models/PasswordResetRequestFormTest.php
new file mode 100644
index 0000000..b736039
--- /dev/null
+++ b/apps/advanced/frontend/tests/unit/models/PasswordResetRequestFormTest.php
@@ -0,0 +1,79 @@
+<?php
+
+namespace frontend\tests\unit\models;
+
+use Yii;
+use frontend\tests\unit\DbTestCase;
+use frontend\models\PasswordResetRequestForm;
+use common\tests\fixtures\UserFixture;
+use common\models\User;
+
+class PasswordResetRequestFormTest extends DbTestCase
+{
+	use \Codeception\Specify;
+
+	protected function setUp()
+	{
+		parent::setUp();
+		Yii::$app->mail->fileTransportCallback = function ($mailer, $message) {
+			return 'testing_message.eml';
+		};
+	}
+
+	protected function tearDown()
+	{
+		@unlink($this->getMessageFile());
+		parent::tearDown();
+	}
+
+	public function testSendEmailWrongUser()
+	{
+		$this->specify('no user with such email, message should not be send', function() {
+			$model = new PasswordResetRequestForm();
+			$model->email = 'not-existing-email@example.com';
+			
+			expect('email not send', $model->sendEmail())->false();
+		});
+
+		$this->specify('user is not active, message should not be send', function() {
+			$model = new PasswordResetRequestForm();
+			$model->email = $this->user[1]['email'];
+			
+			expect('email not send', $model->sendEmail())->false();
+		});
+	}
+	
+	public function testSendEmailCorrectUser()
+	{
+		$model = new PasswordResetRequestForm();
+		$model->email = $this->user[0]['email'];
+		$user = User::find(['password_reset_token' => $this->user[0]['password_reset_token']]);
+
+		expect('email sent', $model->sendEmail())->true();
+		expect('user has valid token', $user->password_reset_token)->notNull();
+			
+		$this->specify('message has correct format', function() use ($model) {
+			expect('message file exists',  file_exists($this->getMessageFile()))->true();
+
+			$message = file_get_contents($this->getMessageFile());
+			expect('message "from" is correct', $message)->contains(Yii::$app->params['supportEmail']);
+			expect('message "to" is correct', $message)->contains($model->email);
+		});
+	}
+
+	public function fixtures()
+	{
+		return [
+			'user' => [
+				'class' => UserFixture::className(),
+				'dataFile' => '@frontend/tests/unit/fixtures/data/tbl_user.php'
+			],
+		];
+	}
+
+	private function getMessageFile()
+	{
+		return Yii::getAlias(Yii::$app->mail->fileTransportPath) . '/testing_message.eml';
+	}
+
+}
diff --git a/apps/advanced/frontend/tests/unit/models/ResetPasswordFormTest.php b/apps/advanced/frontend/tests/unit/models/ResetPasswordFormTest.php
new file mode 100644
index 0000000..763683f
--- /dev/null
+++ b/apps/advanced/frontend/tests/unit/models/ResetPasswordFormTest.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace frontend\tests\unit\models;
+
+use frontend\tests\unit\DbTestCase;
+use common\tests\fixtures\UserFixture;
+use frontend\models\ResetPasswordForm;
+
+class ResetPasswordFormTest extends DbTestCase
+{
+
+	use \Codeception\Specify;
+
+	public function testResetPassword()
+	{
+		$this->specify('wrong reset token', function() {
+			$this->setExpectedException('\Exception','Wrong password reset token.');
+			new ResetPasswordForm('notexistingtoken_1391882543');
+		});
+
+		$this->specify('not correct token', function() {
+			$this->setExpectedException('yii\base\InvalidParamException','Password reset token cannot be blank.');
+			new ResetPasswordForm('');
+		});
+	}
+
+	public function fixtures()
+	{
+		return [
+			'user' => [
+				'class' => UserFixture::className(),
+				'dataFile' => '@frontend/tests/unit/fixtures/data/tbl_user.php'
+			],
+		];
+	}
+
+}
diff --git a/apps/advanced/frontend/tests/unit/models/SignupFormTest.php b/apps/advanced/frontend/tests/unit/models/SignupFormTest.php
new file mode 100644
index 0000000..5176baa
--- /dev/null
+++ b/apps/advanced/frontend/tests/unit/models/SignupFormTest.php
@@ -0,0 +1,47 @@
+<?php
+
+namespace frontend\tests\unit\models;
+
+use frontend\tests\unit\DbTestCase;
+use common\tests\fixtures\UserFixture;
+
+class SignupFormTest extends DbTestCase
+{
+
+	use \Codeception\Specify;
+
+	public function testCorrectSignup()
+	{
+		$model = $this->getMock('frontend\models\SignupForm',['validate']);
+		$model->expects($this->once())->method('validate')->will($this->returnValue(true));
+
+		$model->username = 'some_username';
+		$model->email = 'some_email@example.com';
+		$model->password = 'some_password';
+
+		$user = $model->signup();
+		$this->assertInstanceOf('common\models\User', $user);
+		expect('username should be correct', $user->username)->equals('some_username');
+		expect('email should be correct', $user->email)->equals('some_email@example.com');
+		expect('password should be correct', $user->validatePassword('some_password'))->true();
+	}
+
+	public function testNotCorrectSignup()
+	{
+		$model = $this->getMock('frontend\models\SignupForm',['validate']);
+		$model->expects($this->once())->method('validate')->will($this->returnValue(false));
+
+		expect('user should not be created', $model->signup())->null();
+	}
+
+	public function fixtures()
+	{
+		return [
+			'user' => [
+				'class' => UserFixture::className(),
+				'dataFile' => false, //do not load test data, only table cleanup
+			],
+		];
+	}
+
+}
diff --git a/apps/advanced/frontend/tests/unit/yii b/apps/advanced/frontend/tests/unit/yii
new file mode 100644
index 0000000..a348a9a
--- /dev/null
+++ b/apps/advanced/frontend/tests/unit/yii
@@ -0,0 +1,21 @@
+#!/usr/bin/env php
+<?php
+/**
+ * Yii console bootstrap file.
+ *
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+// fcgi doesn't have STDIN and STDOUT defined by default
+defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
+defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
+
+require_once(__DIR__ . '/../_console.php');
+
+$config = require(__DIR__ . '/_console.php');
+
+$application = new yii\console\Application($config);
+$exitCode = $application->run();
+exit($exitCode);
diff --git a/apps/advanced/frontend/tests/unit/yii.bat b/apps/advanced/frontend/tests/unit/yii.bat
new file mode 100644
index 0000000..5e21e2e
--- /dev/null
+++ b/apps/advanced/frontend/tests/unit/yii.bat
@@ -0,0 +1,20 @@
+@echo off
+
+rem -------------------------------------------------------------
+rem  Yii command line bootstrap script for Windows.
+rem
+rem  @author Qiang Xue <qiang.xue@gmail.com>
+rem  @link http://www.yiiframework.com/
+rem  @copyright Copyright &copy; 2012 Yii Software LLC
+rem  @license http://www.yiiframework.com/license/
+rem -------------------------------------------------------------
+
+@setlocal
+
+set YII_PATH=%~dp0
+
+if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe
+
+"%PHP_COMMAND%" "%YII_PATH%yii" %*
+
+@endlocal
diff --git a/apps/advanced/frontend/views/site/contact.php b/apps/advanced/frontend/views/site/contact.php
index f826cb4..8248f3f 100644
--- a/apps/advanced/frontend/views/site/contact.php
+++ b/apps/advanced/frontend/views/site/contact.php
@@ -29,7 +29,7 @@ $this->params['breadcrumbs'][] = $this->title;
 					'template' => '<div class="row"><div class="col-lg-3">{image}</div><div class="col-lg-6">{input}</div></div>',
 				]) ?>
 				<div class="form-group">
-					<?= Html::submitButton('Submit', ['class' => 'btn btn-primary']) ?>
+					<?= Html::submitButton('Submit', ['class' => 'btn btn-primary', 'name' => 'contact-button']) ?>
 				</div>
 			<?php ActiveForm::end(); ?>
 		</div>
diff --git a/apps/advanced/frontend/views/site/login.php b/apps/advanced/frontend/views/site/login.php
index 60f8ed0..ffe70c1 100644
--- a/apps/advanced/frontend/views/site/login.php
+++ b/apps/advanced/frontend/views/site/login.php
@@ -25,7 +25,7 @@ $this->params['breadcrumbs'][] = $this->title;
 					If you forgot your password you can <?= Html::a('reset it', ['site/request-password-reset']) ?>.
 				</div>
 				<div class="form-group">
-					<?= Html::submitButton('Login', ['class' => 'btn btn-primary']) ?>
+					<?= Html::submitButton('Login', ['class' => 'btn btn-primary', 'name' => 'login-button']) ?>
 				</div>
 			<?php ActiveForm::end(); ?>
 		</div>
diff --git a/apps/advanced/frontend/views/site/signup.php b/apps/advanced/frontend/views/site/signup.php
index 27bd3dd..795d03d 100644
--- a/apps/advanced/frontend/views/site/signup.php
+++ b/apps/advanced/frontend/views/site/signup.php
@@ -22,7 +22,7 @@ $this->params['breadcrumbs'][] = $this->title;
 				<?= $form->field($model, 'email') ?>
 				<?= $form->field($model, 'password')->passwordInput() ?>
 				<div class="form-group">
-					<?= Html::submitButton('Signup', ['class' => 'btn btn-primary']) ?>
+					<?= Html::submitButton('Signup', ['class' => 'btn btn-primary', 'name' => 'signup-button']) ?>
 				</div>
 			<?php ActiveForm::end(); ?>
 		</div>
diff --git a/apps/basic/models/LoginForm.php b/apps/basic/models/LoginForm.php
index d536443..76cf1de 100644
--- a/apps/basic/models/LoginForm.php
+++ b/apps/basic/models/LoginForm.php
@@ -24,10 +24,10 @@ class LoginForm extends Model
 		return [
 			// username and password are both required
 			[['username', 'password'], 'required'],
-			// password is validated by validatePassword()
-			['password', 'validatePassword'],
 			// rememberMe must be a boolean value
 			['rememberMe', 'boolean'],
+			// password is validated by validatePassword()
+			['password', 'validatePassword'],
 		];
 	}
 
@@ -37,10 +37,12 @@ class LoginForm extends Model
 	 */
 	public function validatePassword()
 	{
-		$user = $this->getUser();
+		if (!$this->hasErrors()) {
+			$user = $this->getUser();
 
-		if (!$user || !$user->validatePassword($this->password)) {
-			$this->addError('password', 'Incorrect username or password.');
+			if (!$user || !$user->validatePassword($this->password)) {
+				$this->addError('password', 'Incorrect username or password.');
+			}
 		}
 	}
 
diff --git a/apps/basic/tests/acceptance/_console.php b/apps/basic/tests/acceptance/_console.php
index 58b5f0f..f89eecf 100644
--- a/apps/basic/tests/acceptance/_console.php
+++ b/apps/basic/tests/acceptance/_console.php
@@ -10,4 +10,4 @@ return yii\helpers\ArrayHelper::merge(
 			],
 		],
 	]
-);
\ No newline at end of file
+);
diff --git a/apps/basic/tests/functional/_console.php b/apps/basic/tests/functional/_console.php
index 7137b4c..2e674c4 100644
--- a/apps/basic/tests/functional/_console.php
+++ b/apps/basic/tests/functional/_console.php
@@ -10,4 +10,4 @@ return yii\helpers\ArrayHelper::merge(
 			],
 		],
 	]
-);
\ No newline at end of file
+);
diff --git a/apps/basic/tests/unit/_console.php b/apps/basic/tests/unit/_console.php
index 78fc118..d2a233a 100644
--- a/apps/basic/tests/unit/_console.php
+++ b/apps/basic/tests/unit/_console.php
@@ -10,4 +10,4 @@ return yii\helpers\ArrayHelper::merge(
 			],
 		],
 	]
-);
\ No newline at end of file
+);
diff --git a/apps/basic/tests/unit/models/ContactFormTest.php b/apps/basic/tests/unit/models/ContactFormTest.php
index d215ef6..c679717 100644
--- a/apps/basic/tests/unit/models/ContactFormTest.php
+++ b/apps/basic/tests/unit/models/ContactFormTest.php
@@ -7,7 +7,6 @@ use yii\codeception\TestCase;
 
 class ContactFormTest extends TestCase
 {
-
 	use \Codeception\Specify;
 
 	protected function setUp()
@@ -42,7 +41,7 @@ class ContactFormTest extends TestCase
 			expect('email file should exist', file_exists($this->getMessageFile()))->true();
 		});
 
-		$this->specify('message should contain correct data', function () use($model) {
+		$this->specify('message should contain correct data', function () use ($model) {
 			$emailMessage = file_get_contents($this->getMessageFile());
 
 			expect('email should contain user name', $emailMessage)->contains($model->name);
@@ -56,5 +55,4 @@ class ContactFormTest extends TestCase
 	{
 		return Yii::getAlias(Yii::$app->mail->fileTransportPath) . '/testing_message.eml';
 	}
-
 }
diff --git a/apps/basic/tests/unit/models/LoginFormTest.php b/apps/basic/tests/unit/models/LoginFormTest.php
index dbcc372..d1f6043 100644
--- a/apps/basic/tests/unit/models/LoginFormTest.php
+++ b/apps/basic/tests/unit/models/LoginFormTest.php
@@ -8,7 +8,6 @@ use app\models\User;
 
 class LoginFormTest extends TestCase
 {
-	
 	use \Codeception\Specify;
 
 	protected function tearDown()
@@ -24,7 +23,7 @@ class LoginFormTest extends TestCase
 		$model->username = 'some_username';
 		$model->password = 'some_password';
 
-		$this->specify('user should not be able to login, when there is no identity' , function () use ($model) {
+		$this->specify('user should not be able to login, when there is no identity', function () use ($model) {
 			expect('model should not login user', $model->login())->false();
 			expect('user should not be logged in', Yii::$app->user->isGuest)->true();
 		});
@@ -51,7 +50,7 @@ class LoginFormTest extends TestCase
 		$model->username = 'demo';
 		$model->password = 'demo';
 
-		$this->specify('user should be able to login with correct credentials', function() use ($model) {
+		$this->specify('user should be able to login with correct credentials', function () use ($model) {
 			expect('model should login user', $model->login())->true();
 			expect('error message should not be set', $model->errors)->hasntKey('password');
 			expect('user should be logged in', Yii::$app->user->isGuest)->false();
@@ -60,9 +59,8 @@ class LoginFormTest extends TestCase
 
 	private function mockUser($user)
 	{
-		$loginForm = $this->getMock('app\models\LoginForm',['getUser']);
+		$loginForm = $this->getMock('app\models\LoginForm', ['getUser']);
 		$loginForm->expects($this->any())->method('getUser')->will($this->returnValue($user));
 		return $loginForm;
 	}
-
 }
diff --git a/apps/basic/views/layouts/main.php b/apps/basic/views/layouts/main.php
index cfe6401..f32b53f 100644
--- a/apps/basic/views/layouts/main.php
+++ b/apps/basic/views/layouts/main.php
@@ -40,7 +40,7 @@ AppAsset::register($this);
 					['label' => 'Contact', 'url' => ['/site/contact']],
 					Yii::$app->user->isGuest ?
 						['label' => 'Login', 'url' => ['/site/login']] :
-						['label' => 'Logout (' . Yii::$app->user->identity->username . ')' ,
+						['label' => 'Logout (' . Yii::$app->user->identity->username . ')',
 							'url' => ['/site/logout'],
 							'linkOptions' => ['data-method' => 'post']],
 				],
diff --git a/build/build b/build/build
index a64a5f9..51949a9 100755
--- a/build/build
+++ b/build/build
@@ -14,6 +14,13 @@ defined('STDOUT') or define('STDOUT', fopen('php://stdout', 'w'));
 
 define('YII_DEBUG', true);
 
+$vendor = __DIR__ . '/../vendor/autoload.php';
+if (file_exists($vendor)) {
+	require($vendor);
+} else {
+	echo "composer autoloader could not be found.\nYou should run `composer install` in repo root directory.\n";
+	exit(1);
+}
 require(__DIR__ . '/../framework/Yii.php');
 
 $application = new yii\console\Application([
diff --git a/composer.json b/composer.json
index d159bb0..d01de32 100644
--- a/composer.json
+++ b/composer.json
@@ -75,12 +75,14 @@
 		"yiisoft/yii2-composer": "*",
 		"yiisoft/jquery": "~2.0 | ~1.10",
 		"ezyang/htmlpurifier": "4.6.*",
-		"michelf/php-markdown": "1.3.*",
-		"phpspec/php-diff": ">=1.0.2"
+		"cebe/markdown": "0.9.*"
 	},
 	"require-dev": {
 		"phpunit/phpunit": "3.7.*",
-		"twig/twig": "*"
+		"twig/twig": "*",
+		"smarty/smarty": "*",
+		"imagine/imagine": "v0.5.0",
+		"swiftmailer/swiftmailer": "*"
 	},
 	"suggest": {
 		"phpdocumentor/reflection": "required by yii2-apidoc extension",
@@ -93,6 +95,7 @@
 		"imagine/imagine": "required by yii2-imagine extension",
 		"smarty/smarty": "required by yii2-smarty extension",
 		"swiftmailer/swiftmailer": "required by yii2-swiftmailer extension",
+		"twig/twig": "required by yii2-twig extension",
 		"yiisoft/yii2-coding-standards": "you can use this package to check for code style issues when contributing to yii"
 	},
 	"autoload": {
diff --git a/docs/api/base/Component.md b/docs/api/base/Component.md
deleted file mode 100644
index f198798..0000000
--- a/docs/api/base/Component.md
+++ /dev/null
@@ -1,80 +0,0 @@
-Component is the base class that implements the *property*, *event* and *behavior* features.
-
-Component provides the *event* and *behavior* features, in addition to the *property* feature which is implemented in
-its parent class [[Object]].
-
-Event is a way to "inject" custom code into existing code at certain places. For example, a comment object can trigger
-an "add" event when the user adds a comment. We can write custom code and attach it to this event so that when the event
-is triggered (i.e. comment will be added), our custom code will be executed.
-
-An event is identified by a name that should be unique within the class it is defined at. Event names are *case-sensitive*.
-
-One or multiple PHP callbacks, called *event handlers*, can be attached to an event. You can call [[trigger()]] to
-raise an event. When an event is raised, the event handlers will be invoked automatically in the order they were
-attached.
-
-To attach an event handler to an event, call [[on()]]:
-
-~~~
-$post->on('update', function($event) {
-    // send email notification
-});
-~~~
-
-In the above, an anonymous function is attached to the "update" event of the post. You may attach
-the following types of event handlers:
-
-- anonymous function: `function($event) { ... }`
-- object method: `[$object, 'handleAdd']`
-- static class method: `['Page', 'handleAdd']`
-- global function: `'handleAdd'`
-
-The signature of an event handler should be like the following:
-
-~~~
-function foo($event)
-~~~
-
-where `$event` is an [[Event]] object which includes parameters associated with the event.
-
-You can also attach a handler to an event when configuring a component with a configuration array.
-The syntax is like the following:
-
-~~~
-[
-    'on add' => function($event) { ... }
-]
-~~~
-
-where `on add` stands for attaching an event to the `add` event.
-
-Sometimes, you may want to associate extra data with an event handler when you attach it to an event
-and then access it when the handler is invoked. You may do so by
-
-~~~
-$post->on('update', function($event) {
-    // the data can be accessed via $event->data
-}, $data);
-~~~
-
-
-A behavior is an instance of [[Behavior]] or its child class. A component can be attached with one or multiple
-behaviors. When a behavior is attached to a component, its public properties and methods can be accessed via the
-component directly, as if the component owns those properties and methods.
-
-To attach a behavior to a component, declare it in [[behaviors()]], or explicitly call [[attachBehavior]]. Behaviors
-declared in [[behaviors()]] are automatically attached to the corresponding component.
-
-One can also attach a behavior to a component when configuring it with a configuration array. The syntax is like the
-following:
-
-~~~
-[
-    'as tree' => [
-        'class' => 'Tree',
-    ],
-]
-~~~
-
-where `as tree` stands for attaching a behavior named `tree`, and the array will be passed to [[\Yii::createObject()]]
-to create the behavior object.
\ No newline at end of file
diff --git a/docs/api/base/Object.md b/docs/api/base/Object.md
deleted file mode 100644
index a6ab2c1..0000000
--- a/docs/api/base/Object.md
+++ /dev/null
@@ -1,61 +0,0 @@
-Object is the base class that implements the *property* feature.
-
-A property is defined by a getter method (e.g. `getLabel`), and/or a setter method (e.g. `setLabel`). For example,
-the following getter and setter methods define a property named `label`:
-
-~~~
-private $_label;
-
-public function getLabel()
-{
-    return $this->_label;
-}
-
-public function setLabel($value)
-{
-    $this->_label = $value;
-}
-~~~
-
-Property names are *case-insensitive*.
-
-A property can be accessed like a member variable of an object. Reading or writing a property will cause the invocation
-of the corresponding getter or setter method. For example,
-
-~~~
-// equivalent to $label = $object->getLabel();
-$label = $object->label;
-// equivalent to $object->setLabel('abc');
-$object->label = 'abc';
-~~~
-
-If a property has only a getter method and has no setter method, it is considered as *read-only*. In this case, trying
-to modify the property value will cause an exception.
-
-One can call [[hasProperty()]], [[canGetProperty()]] and/or [[canSetProperty()]] to check the existence of a property.
-
-
-Besides the property feature, Object also introduces an important object initialization life cycle. In particular,
-creating an new instance of Object or its derived class will involve the following life cycles sequentially:
-
-1. the class constructor is invoked;
-2. object properties are initialized according to the given configuration;
-3. the `init()` method is invoked.
-
-In the above, both Step 2 and 3 occur at the end of the class constructor. It is recommended that
-you perform object initialization in the `init()` method because at that stage, the object configuration
-is already applied.
-
-In order to ensure the above life cycles, if a child class of Object needs to override the constructor,
-it should be done like the following:
-
-~~~
-public function __construct($param1, $param2, ..., $config = [])
-{
-    ...
-    parent::__construct($config);
-}
-~~~
-
-That is, a `$config` parameter (defaults to `[]`) should be declared as the last parameter
-of the constructor, and the parent implementation should be called at the end of the constructor.
diff --git a/docs/api/db/ActiveRecord-find.md b/docs/api/db/ActiveRecord-find.md
deleted file mode 100644
index 8653853..0000000
--- a/docs/api/db/ActiveRecord-find.md
+++ /dev/null
@@ -1,26 +0,0 @@
-The returned [[ActiveQuery]] instance can be further customized by calling
-methods defined in [[ActiveQuery]] before `one()`, `all()` or `value()` is
-called to return the populated active records:
-
-~~~
-// find all customers
-$customers = Customer::find()->all();
-
-// find all active customers and order them by their age:
-$customers = Customer::find()
-    ->where(['status' => 1])
-    ->orderBy('age')
-    ->all();
-
-// find a single customer whose primary key value is 10
-$customer = Customer::find(10);
-
-// the above is equivalent to:
-$customer = Customer::find()->where(['id' => 10])->one();
-
-// find a single customer whose age is 30 and whose status is 1
-$customer = Customer::find(['age' => 30, 'status' => 1]);
-
-// the above is equivalent to:
-$customer = Customer::find()->where(['age' => 30, 'status' => 1])->one();
-~~~
\ No newline at end of file
diff --git a/docs/api/db/ActiveRecord.md b/docs/api/db/ActiveRecord.md
deleted file mode 100644
index ef050d0..0000000
--- a/docs/api/db/ActiveRecord.md
+++ /dev/null
@@ -1,451 +0,0 @@
-ActiveRecord implements the [Active Record design pattern](http://en.wikipedia.org/wiki/Active_record).
-The idea is that an ActiveRecord object is associated with a row in a database table
-so object properties are mapped to columns of the corresponding database row.
-For example, a `Customer` object is associated with a row in the `tbl_customer`
-table. Instead of writing raw SQL statements to access the data in the table,
-you can call intuitive methods available in the corresponding ActiveRecord class
-to achieve the same goals. For example, calling [[save()]] would insert or update a row
-in the underlying table:
-
-~~~
-$customer = new Customer();
-$customer->name = 'Qiang';
-$customer->save();
-~~~
-
-
-### Declaring ActiveRecord Classes
-
-To declare an ActiveRecord class you need to extend [[\yii\db\ActiveRecord]] and
-implement `tableName` method like the following:
-
-~~~
-class Customer extends \yii\db\ActiveRecord
-{
-	/**
-	 * @return string the name of the table associated with this ActiveRecord class.
-	 */
-	public static function tableName()
-	{
-		return 'tbl_customer';
-	}
-}
-~~~
-
-### Connecting to Database
-
-ActiveRecord relies on a [[Connection|DB connection]]. By default, it assumes that
-there is an application component named `db` that gives the needed [[Connection]]
-instance which serves as the DB connection. Usually this component is configured
-via application configuration like the following:
-
-~~~
-return [
-	'components' => [
-		'db' => [
-			'class' => 'yii\db\Connection',
-			'dsn' => 'mysql:host=localhost;dbname=testdb',
-			'username' => 'demo',
-			'password' => 'demo',
-			// turn on schema caching to improve performance
-			// 'schemaCacheDuration' => 3600,
-		],
-	],
-];
-~~~
-
-
-### Getting Data from Database
-
-There are two ActiveRecord methods for getting data:
-
-- [[find()]]
-- [[findBySql()]]
-
-They both return an [[ActiveQuery]] instance. Coupled with the various customization and query methods
-provided by [[ActiveQuery]], ActiveRecord supports very flexible and powerful data retrieval approaches.
-
-The followings are some examples,
-
-~~~
-// to retrieve all *active* customers and order them by their ID:
-$customers = Customer::find()
-	->where(['status' => $active])
-	->orderBy('id')
-	->all();
-
-// to return a single customer whose ID is 1:
-$customer = Customer::find()
-	->where(['id' => 1])
-	->one();
-
-// or use the following shortcut approach:
-$customer = Customer::find(1);
-
-// to retrieve customers using a raw SQL statement:
-$sql = 'SELECT * FROM tbl_customer';
-$customers = Customer::findBySql($sql)->all();
-
-// to return the number of *active* customers:
-$count = Customer::find()
-	->where(['status' => $active])
-	->count();
-
-// to return customers in terms of arrays rather than `Customer` objects:
-$customers = Customer::find()->asArray()->all();
-// each $customers element is an array of name-value pairs
-
-// to index the result by customer IDs:
-$customers = Customer::find()->indexBy('id')->all();
-// $customers array is indexed by customer IDs
-~~~
-
-
-### Accessing Column Data
-
-ActiveRecord maps each column of the corresponding database table row to an *attribute* in the ActiveRecord
-object. An attribute is like a regular object property whose name is the same as the corresponding column
-name and is case sensitive.
-
-To read the value of a column, we can use the following expression:
-
-~~~
-// "id" is the name of a column in the table associated with $customer ActiveRecord object
-$id = $customer->id;
-// or alternatively,
-$id = $customer->getAttribute('id');
-~~~
-
-We can get all column values through the [[attributes]] property:
-
-~~~
-$values = $customer->attributes;
-~~~
-
-
-### Persisting Data to Database
-
-ActiveRecord provides the following methods to insert, update and delete data:
-
-- [[save()]]
-- [[insert()]]
-- [[update()]]
-- [[delete()]]
-- [[updateCounters()]]
-- [[updateAll()]]
-- [[updateAllCounters()]]
-- [[deleteAll()]]
-
-Note that [[updateAll()]], [[updateAllCounters()]] and [[deleteAll()]] apply to the whole database
-table, while the rest of the methods only apply to the row associated with the ActiveRecord object.
-
-The followings are some examples:
-
-~~~
-// to insert a new customer record
-$customer = new Customer;
-$customer->name = 'James';
-$customer->email = 'james@example.com';
-$customer->save();  // equivalent to $customer->insert();
-
-// to update an existing customer record
-$customer = Customer::find($id);
-$customer->email = 'james@example.com';
-$customer->save();  // equivalent to $customer->update();
-
-// to delete an existing customer record
-$customer = Customer::find($id);
-$customer->delete();
-
-// to increment the age of all customers by 1
-Customer::updateAllCounters(['age' => 1]);
-~~~
-
-
-### Getting Relational Data
-
-Using ActiveRecord you can expose relationships as properties. For example,
-with an appropriate declaration, `$customer->orders` can return an array of `Order` objects
-which represent the orders placed by the specified customer.
-
-To declare a relationship, define a getter method which returns an [[ActiveRelation]] object. For example,
-
-~~~
-class Customer extends \yii\db\ActiveRecord
-{
-	public function getOrders()
-	{
-		return $this->hasMany(Order::className(), ['customer_id' => 'id']);
-	}
-}
-
-class Order extends \yii\db\ActiveRecord
-{
-	public function getCustomer()
-	{
-		return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
-	}
-}
-~~~
-
-Within the getter methods above, we call [[hasMany()]] or [[hasOne()]] methods to
-create a new [[ActiveRelation]] object. The [[hasMany()]] method declares
-a one-many relationship. For example, a customer has many orders. And the [[hasOne()]]
-method declares a many-one or one-one relationship. For example, an order has one customer.
-Both methods take two parameters:
-
-- `$class`: the name of the class that the related models should use.
-- `$link`: the association between columns from two tables. This should be given as an array.
-  The keys of the array are the names of the columns from the table associated with `$class`,
-  while the values of the array are the names of the columns from the declaring class.
-  It is a good practice to define relationships based on table foreign keys.
-
-After declaring relationships getting relational data is as easy as accessing
-a component property that is defined by the getter method:
-
-~~~
-// the orders of a customer
-$customer = Customer::find($id);
-$orders = $customer->orders;  // $orders is an array of Order objects
-
-// the customer of the first order
-$customer2 = $orders[0]->customer;  // $customer == $customer2
-~~~
-
-Because [[ActiveRelation]] extends from [[ActiveQuery]], it has the same query building methods,
-which allows us to customize the query for retrieving the related objects.
-For example, we may declare a `bigOrders` relationship which returns orders whose
-subtotal exceeds certain amount:
-
-~~~
-class Customer extends \yii\db\ActiveRecord
-{
-	public function getBigOrders($threshold = 100)
-	{
-		return $this->hasMany(Order::className(), ['customer_id' => 'id'])
-			->where('subtotal > :threshold', [':threshold' => $threshold])
-			->orderBy('id');
-	}
-}
-~~~
-
-
-Sometimes, two tables are related together via an intermediary table called
-[pivot table](http://en.wikipedia.org/wiki/Pivot_table). To declare such relationships, we can customize
-the [[ActiveRelation]] object by calling its [[ActiveRelation::via()]] or [[ActiveRelation::viaTable()]]
-method.
-
-For example, if table `tbl_order` and table `tbl_item` are related via pivot table `tbl_order_item`,
-we can declare the `items` relation in the `Order` class like the following:
-
-~~~
-class Order extends \yii\db\ActiveRecord
-{
-	public function getItems()
-	{
-		return $this->hasMany(Item::className(), ['id' => 'item_id'])
-			->viaTable('tbl_order_item', ['order_id' => 'id']);
-	}
-}
-~~~
-
-[[ActiveRelation::via()]] method is similar to [[ActiveRelation::viaTable()]] except that
-the first parameter of [[ActiveRelation::via()]] takes a relation name declared in the ActiveRecord class.
-For example, the above `items` relation can be equivalently declared as follows:
-
-~~~
-class Order extends \yii\db\ActiveRecord
-{
-	public function getOrderItems()
-	{
-		return $this->hasMany(OrderItem::className(), ['order_id' => 'id']);
-	}
-
-	public function getItems()
-	{
-		return $this->hasMany(Item::className(), ['id' => 'item_id'])
-			->via('orderItems');
-	}
-}
-~~~
-
-
-When you access the related objects the first time, behind the scene ActiveRecord performs a DB query
-to retrieve the corresponding data and populate it into the related objects. No query will be performed
-if you access the same related objects again. We call this *lazy loading*. For example,
-
-~~~
-// SQL executed: SELECT * FROM tbl_customer WHERE id=1
-$customer = Customer::find(1);
-// SQL executed: SELECT * FROM tbl_order WHERE customer_id=1
-$orders = $customer->orders;
-// no SQL executed
-$orders2 = $customer->orders;
-~~~
-
-
-Lazy loading is very convenient to use. However, it may suffer from performance
-issue in the following scenario:
-
-~~~
-// SQL executed: SELECT * FROM tbl_customer LIMIT 100
-$customers = Customer::find()->limit(100)->all();
-
-foreach ($customers as $customer) {
-	// SQL executed: SELECT * FROM tbl_order WHERE customer_id=...
-	$orders = $customer->orders;
-	// ...handle $orders...
-}
-~~~
-
-How many SQL queries will be performed in the above code, assuming there are more than 100 customers in
-the database? 101! The first SQL query brings back 100 customers. Then for each customer, a SQL query
-is performed to bring back the customer's orders.
-
-To solve the above performance problem, you can use the so-called *eager loading* by calling [[ActiveQuery::with()]]:
-
-~~~
-// SQL executed: SELECT * FROM tbl_customer LIMIT 100
-//               SELECT * FROM tbl_orders WHERE customer_id IN (1,2,...)
-$customers = Customer::find()->limit(100)
-	->with('orders')->all();
-
-foreach ($customers as $customer) {
-	// no SQL executed
-	$orders = $customer->orders;
-	// ...handle $orders...
-}
-~~~
-
-As you can see, only two SQL queries are needed for the same task.
-
-
-Sometimes, you may want to customize the relational queries on the fly. It can be
-done for both lazy loading and eager loading. For example,
-
-~~~
-$customer = Customer::find(1);
-// lazy loading: SELECT * FROM tbl_order WHERE customer_id=1 AND subtotal>100
-$orders = $customer->getOrders()->where('subtotal>100')->all();
-
-// eager loading: SELECT * FROM tbl_customer LIMIT 10
-                  SELECT * FROM tbl_order WHERE customer_id IN (1,2,...) AND subtotal>100
-$customers = Customer::find()->limit(100)->with([
-	'orders' => function($query) {
-		$query->andWhere('subtotal>100');
-	},
-])->all();
-~~~
-
-
-### Working with Relationships
-
-ActiveRecord provides the following two methods for establishing and breaking a
-relationship between two ActiveRecord objects:
-
-- [[link()]]
-- [[unlink()]]
-
-For example, given a customer and a new order, we can use the following code to make the
-order owned by the customer:
-
-~~~
-$customer = Customer::find(1);
-$order = new Order;
-$order->subtotal = 100;
-$customer->link('orders', $order);
-~~~
-
-The [[link()]] call above will set the `customer_id` of the order to be the primary key
-value of `$customer` and then call [[save()]] to save the order into database.
-
-
-### Data Input and Validation
-
-TBD
-
-
-### Life Cycles of an ActiveRecord Object
-
-An ActiveRecord object undergoes different life cycles when it is used in different cases.
-Subclasses or ActiveRecord behaviors may "inject" custom code in these life cycles through
-method overriding and event handling mechanisms.
-
-When instantiating a new ActiveRecord instance, we will have the following life cycles:
-
-1. constructor
-2. [[init()]]: will trigger an [[EVENT_INIT]] event
-
-When getting an ActiveRecord instance through the [[find()]] method, we will have the following life cycles:
-
-1. constructor
-2. [[init()]]: will trigger an [[EVENT_INIT]] event
-3. [[afterFind()]]: will trigger an [[EVENT_AFTER_FIND]] event
-
-When calling [[save()]] to insert or update an ActiveRecord, we will have the following life cycles:
-
-1. [[beforeValidate()]]: will trigger an [[EVENT_BEFORE_VALIDATE]] event
-2. [[afterValidate()]]: will trigger an [[EVENT_AFTER_VALIDATE]] event
-3. [[beforeSave()]]: will trigger an [[EVENT_BEFORE_INSERT]] or [[EVENT_BEFORE_UPDATE]] event
-4. perform the actual data insertion or updating
-5. [[afterSave()]]: will trigger an [[EVENT_AFTER_INSERT]] or [[EVENT_AFTER_UPDATE]] event
-
-Finally when calling [[delete()]] to delete an ActiveRecord, we will have the following life cycles:
-
-1. [[beforeDelete()]]: will trigger an [[EVENT_BEFORE_DELETE]] event
-2. perform the actual data deletion
-3. [[afterDelete()]]: will trigger an [[EVENT_AFTER_DELETE]] event
-
-
-### Scopes
-
-A scope is a method that customizes a given [[ActiveQuery]] object. Scope methods are defined
-in the ActiveRecord classes. They can be invoked through the [[ActiveQuery]] object that is created
-via [[find()]] or [[findBySql()]]. The following is an example:
-
-~~~
-class Customer extends \yii\db\ActiveRecord
-{
-	// ...
-
-	/**
-	 * @param ActiveQuery $query
-	 */
-	public static function active($query)
-	{
-		$query->andWhere('status = 1');
-	}
-}
-
-$customers = Customer::find()->active()->all();
-~~~
-
-In the above, the `active()` method is defined in `Customer` while we are calling it
-through `ActiveQuery` returned by `Customer::find()`.
-
-Scopes can be parameterized. For example, we can define and use the following `olderThan` scope:
-
-~~~
-class Customer extends \yii\db\ActiveRecord
-{
-	// ...
-
-	/**
-	 * @param ActiveQuery $query
-	 * @param integer $age
-	 */
-	public static function olderThan($query, $age = 30)
-	{
-		$query->andWhere('age > :age', [':age' => $age]);
-	}
-}
-
-$customers = Customer::find()->olderThan(50)->all();
-~~~
-
-The parameters should follow after the `$query` parameter when defining the scope method, and they
-can take default values like shown above.
-
-### Atomic operations and scenarios
-
-TBD
diff --git a/docs/guide/active-record.md b/docs/guide/active-record.md
index bd36f47..c54bb08 100644
--- a/docs/guide/active-record.md
+++ b/docs/guide/active-record.md
@@ -2,7 +2,7 @@ Active Record
 =============
 
 Active Record implements the [Active Record design pattern](http://en.wikipedia.org/wiki/Active_record).
-The premise behind Active Record is that an individual [[yii\db\ActiveRecord]] object is associated with a specific row in a database table. The object's attributes are mapped to the columns of the corresponding table. Referencing an Active Record attribute is equivalent to accessing
+The premise behind Active Record is that an individual [[yii\db\ActiveRecord|ActiveRecord]] object is associated with a specific row in a database table. The object's attributes are mapped to the columns of the corresponding table. Referencing an Active Record attribute is equivalent to accessing
 the corresponding table column for that record.
 
 As an example, say that the `Customer` ActiveRecord class is associated with the
@@ -21,7 +21,7 @@ $customer->save();  // a new row is inserted into tbl_customer
 Declaring ActiveRecord Classes
 ------------------------------
 
-To declare an ActiveRecord class you need to extend [[\yii\db\ActiveRecord]] and
+To declare an ActiveRecord class you need to extend [[yii\db\ActiveRecord]] and
 implement the `tableName` method:
 
 ```php
@@ -197,7 +197,7 @@ Customer::updateAllCounters(['age' => 1]);
 Data Input and Validation
 -------------------------
 
-ActiveRecord inherits data validation and data input features from [[\yii\base\Model]]. Data validation is called
+ActiveRecord inherits data validation and data input features from [[yii\base\Model]]. Data validation is called
 automatically when `save()` is performed. If data validation fails, the saving operation will be cancelled.
 
 For more details refer to the [Model](model.md) section of this guide.
@@ -205,12 +205,15 @@ For more details refer to the [Model](model.md) section of this guide.
 Querying Relational Data
 ------------------------
 
-You can use ActiveRecord to also query a table's relational data (i.e., selection of data from Table A can also pull in related data from Table B). Thanks to ActiveRecord, the relational data returned can be accessed like a property of the ActiveRecord object associated with the primary table.
+You can use ActiveRecord to also query a table's relational data (i.e., selection of data from Table A can also pull
+in related data from Table B). Thanks to ActiveRecord, the relational data returned can be accessed like a property
+of the ActiveRecord object associated with the primary table.
 
 For example, with an appropriate relation declaration, by accessing `$customer->orders` you may obtain
 an array of `Order` objects which represent the orders placed by the specified customer.
 
-To declare a relation, define a getter method which returns an [[yii\db\ActiveRelation]] object. For example,
+To declare a relation, define a getter method which returns an [[yii\db\ActiveQuery]] object that has relation
+information about the relation context and thus will only query for related records. For example,
 
 ```php
 class Customer extends \yii\db\ActiveRecord
@@ -235,7 +238,7 @@ class Order extends \yii\db\ActiveRecord
 The methods [[yii\db\ActiveRecord::hasMany()]] and [[yii\db\ActiveRecord::hasOne()]] used in the above
 are used to model the many-one relationship and one-one relationship in a relational database.
 For example, a customer has many orders, and an order has one customer.
-Both methods take two parameters and return an [[yii\db\ActiveRelation]] object:
+Both methods take two parameters and return an [[yii\db\ActiveQuery]] object:
 
  - `$class`: the name of the class of the related model(s). This should be a fully qualified class name.
  - `$link`: the association between columns from the two tables. This should be given as an array.
@@ -259,8 +262,8 @@ SELECT * FROM tbl_customer WHERE id=1;
 SELECT * FROM tbl_order WHERE customer_id=1;
 ```
 
-> Tip: If you access the expression `$customer->orders` again, will it perform the second SQL query again?
-Nope. The SQL query is only performed the first time when this expression is accessed. Any further
+> Tip: If you access the expression `$customer->orders` again, it will not perform the second SQL query again.
+The SQL query is only performed the first time when this expression is accessed. Any further
 accesses will only return the previously fetched results that are cached internally. If you want to re-query
 the relational data, simply unset the existing one first: `unset($customer->orders);`.
 
@@ -280,8 +283,8 @@ class Customer extends \yii\db\ActiveRecord
 }
 ```
 
-Remember that `hasMany()` returns an [[yii\db\ActiveRelation]] object which extends from [[yii\db\ActiveQuery]]
-and thus supports the same set of querying methods as [[yii\db\ActiveQuery]].
+Remember that `hasMany()` returns an [[yii\db\ActiveQuery]] object which allows you to customize the query by
+calling the methods of [[yii\db\ActiveQuery]].
 
 With the above declaration, if you access `$customer->bigOrders`, it will only return the orders
 whose subtotal is greater than 100. To specify a different threshold value, use the following code:
@@ -290,20 +293,19 @@ whose subtotal is greater than 100. To specify a different threshold value, use 
 $orders = $customer->getBigOrders(200)->all();
 ```
 
-> Note: A relation method returns an instance of [[yii\db\ActiveRelation]]. If you access the relation like
-an attribute, the return value will be the query result of the relation, which could be an instance of `ActiveRecord`,
+> Note: A relation method returns an instance of [[yii\db\ActiveQuery]]. If you access the relation like
+an attribute (i.e. a class property), the return value will be the query result of the relation, which could be an instance of [[yii\db\ActiveRecord]],
 an array of that, or null, depending the multiplicity of the relation. For example, `$customer->getOrders()` returns
-an `ActiveRelation` instance, while `$customer->orders` returns an array of `Order` objects (or an empty array if
+an `ActiveQuery` instance, while `$customer->orders` returns an array of `Order` objects (or an empty array if
 the query results in nothing).
 
 
 Relations with Pivot Table
 --------------------------
 
-Sometimes, two tables are related together via an intermediary table called
-[pivot table](http://en.wikipedia.org/wiki/Pivot_table). To declare such relations, we can customize
-the [[yii\db\ActiveRelation]] object by calling its [[yii\db\ActiveRelation::via()]] or [[yii\db\ActiveRelation::viaTable()]]
-method.
+Sometimes, two tables are related together via an intermediary table called [pivot table][]. To declare such relations,
+we can customize the [[yii\db\ActiveQuery]] object by calling its [[yii\db\ActiveQuery::via()|via()]] or
+[[yii\db\ActiveQuery::viaTable()|viaTable()]] method.
 
 For example, if table `tbl_order` and table `tbl_item` are related via pivot table `tbl_order_item`,
 we can declare the `items` relation in the `Order` class like the following:
@@ -319,8 +321,8 @@ class Order extends \yii\db\ActiveRecord
 }
 ```
 
-[[yii\db\ActiveRelation::via()]] method is similar to [[yii\db\ActiveRelation::viaTable()]] except that
-the first parameter of [[yii\db\ActiveRelation::via()]] takes a relation name declared in the ActiveRecord class
+The [[yii\db\ActiveQuery::via()|via()]] method is similar to [[yii\db\ActiveQuery::viaTable()|viaTable()]] except that
+the first parameter of [[yii\db\ActiveQuery::via()|via()]] takes a relation name declared in the ActiveRecord class
 instead of the pivot table name. For example, the above `items` relation can be equivalently declared as follows:
 
 ```php
@@ -339,6 +341,8 @@ class Order extends \yii\db\ActiveRecord
 }
 ```
 
+[pivot table]: http://en.wikipedia.org/wiki/Pivot_table "Pivot table on Wikipedia"
+
 
 Lazy and Eager Loading
 ----------------------
@@ -394,6 +398,14 @@ As you can see, only two SQL queries are needed for the same task!
 > a total number of `1+M+N` SQL queries will be performed: one query to bring back the rows for the primary table, one for
 > each of the `M` pivot tables corresponding to the `via()` or `viaTable()` calls, and one for each of the `N` related tables.
 
+> Note: When you are customizing `select()` with eager loading, make sure you include the columns that link
+> the related models. Otherwise, the related models will not be loaded. For example,
+
+```php
+$orders = Order::find()->select(['id', 'amount'])->with('customer')->all();
+// $orders[0]->customer is always null. To fix the problem, you should do the following:
+$orders = Order::find()->select(['id', 'amount', 'customer_id'])->with('customer')->all();
+```
 
 Sometimes, you may want to customize the relational queries on the fly. This can be
 done for both lazy loading and eager loading. For example,
@@ -413,6 +425,97 @@ $customers = Customer::find()->limit(100)->with([
 ```
 
 
+Inverse Relations
+-----------------
+
+Relations can often be defined in pairs. For example, `Customer` may have a relation named `orders` while `Order` may have a relation
+named `customer`:
+
+```php
+class Customer extends ActiveRecord
+{
+	....
+	public function getOrders()
+	{
+		return $this->hasMany(Order::className, ['customer_id' => 'id']);
+	}
+}
+
+class Order extends ActiveRecord
+{
+	....
+	public function getCustomer()
+	{
+		return $this->hasOne(Customer::className, ['id' => 'customer_id']);
+	}
+}
+```
+
+If we perform the following query, we would find that the `customer` of an order is not the same customer object
+that finds those orders, and accessing `customer->orders` will trigger one SQL execution while accessing
+the `customer` of an order will trigger another SQL execution:
+
+```php
+// SELECT * FROM tbl_customer WHERE id=1
+$customer = Customer::find(1);
+// echoes "not equal"
+// SELECT * FROM tbl_order WHERE customer_id=1
+// SELECT * FROM tbl_customer WHERE id=1
+if ($customer->orders[0]->customer === $customer) {
+	echo 'equal';
+} else {
+	echo 'not equal';
+}
+```
+
+To avoid the redundant execution of the last SQL statement, we could declare the inverse relations for the `customer`
+and the `orders` relations by calling the [[yii\db\ActiveQuery::inverseOf()|inverseOf()]] method, like the following:
+
+```php
+class Customer extends ActiveRecord
+{
+	....
+	public function getOrders()
+	{
+		return $this->hasMany(Order::className, ['customer_id' => 'id'])->inverseOf('customer');
+	}
+}
+```
+
+Now if we execute the same query as shown above, we would get:
+
+```php
+// SELECT * FROM tbl_customer WHERE id=1
+$customer = Customer::find(1);
+// echoes "equal"
+// SELECT * FROM tbl_order WHERE customer_id=1
+if ($customer->orders[0]->customer === $customer) {
+	echo 'equal';
+} else {
+	echo 'not equal';
+}
+```
+
+In the above, we have shown how to use inverse relations in lazy loading. Inverse relations also apply in
+eager loading:
+
+```php
+// SELECT * FROM tbl_customer
+// SELECT * FROM tbl_order WHERE customer_id IN (1, 2, ...)
+$customers = Customer::find()->with('orders')->all();
+// echoes "equal"
+if ($customers[0]->orders[0]->customer === $customers[0]) {
+	echo 'equal';
+} else {
+	echo 'not equal';
+}
+```
+
+> Note: Inverse relation cannot be defined with a relation that involves pivoting tables.
+> That is, if your relation is defined with [[yii\db\ActiveQuery::via()|via()]] or [[yii\db\ActiveQuery::viaTable()|viaTable()]],
+> you cannot call [[yii\db\ActiveQuery::inverseOf()]] further.
+
+
 Joining with Relations
 ----------------------
 
@@ -481,7 +584,7 @@ $orders = Order::find()->joinWith('books', false, 'INNER JOIN')->all();
 ```
 
 Sometimes when joining two tables, you may need to specify some extra condition in the ON part of the JOIN query.
-This can be done by calling the [[\yii\db\ActiveRelation::onCondition()]] method like the following:
+This can be done by calling the [[yii\db\ActiveQuery::onCondition()]] method like the following:
 
 ```php
 class User extends ActiveRecord
@@ -493,7 +596,8 @@ class User extends ActiveRecord
 }
 ```
 
-In the above, the `hasMany()` method returns an `ActiveRelation` instance, upon which `onCondition()` is called
+In the above, the [[yii\db\ActiveRecord::hasMany()|hasMany()]] method returns an [[yii\db\ActiveQuery]] instance,
+upon which [[yii\db\ActiveQuery::onCondition()|onCondition()]] is called
 to specify that only items whose `category_id` is 1 should be returned.
 
 When you perform query using [[yii\db\ActiveQuery::joinWith()|joinWith()]], the on-condition will be put in the ON part
@@ -575,8 +679,10 @@ Finally when calling [[yii\db\ActiveRecord::delete()|delete()]] to delete an Act
 Scopes
 ------
 
-When [[yii\db\ActiveRecord::find()|find()]] or [[yii\db\ActiveRecord::findBySql()|findBySql()]], it returns an [[yii\db\ActiveRecord::yii\db\ActiveQuery|yii\db\ActiveQuery]]
-instance. You may call additional query methods, such as `where()`, `orderBy()`, to further specify the query conditions, etc.
+When you call [[yii\db\ActiveRecord::find()|find()]] or [[yii\db\ActiveRecord::findBySql()|findBySql()]], it returns an
+[[yii\db\ActiveQuery|ActiveQuery]] instance.
+You may call additional query methods, such as [[yii\db\ActiveQuery::where()|where()]], [[yii\db\ActiveQuery::orderBy()|orderBy()]],
+to further specify the query conditions.
 
 It is possible that you may want to call the same set of query methods in different places. If this is the case,
 you should consider defining the so-called *scopes*. A scope is essentially a method defined in a custom query class that
@@ -605,21 +711,22 @@ Important points are:
 
 1. Class should extend from `yii\db\ActiveQuery` (or another `ActiveQuery` such as `yii\mongodb\ActiveQuery`).
 2. A method should be `public` and should return `$this` in order to allow method chaining. It may accept parameters.
-3. Check `ActiveQuery` methods that are very useful for modifying query conditions.
+3. Check [[yii\db\ActiveQuery]] methods that are very useful for modifying query conditions.
 
-Second, override `ActiveRecord::createQuery()` to use the custom query class instead of the regular `ActiveQuery`.
+Second, override [[yii\db\ActiveRecord::createQuery()]] to use the custom query class instead of the regular [[yii\db\ActiveQuery|ActiveQuery]].
 For the example above, you need to write the following code:
 
-```
+```php
 namespace app\models;
 
 use yii\db\ActiveRecord;
 
 class Comment extends ActiveRecord
 {
-	public static function createQuery()
+	public static function createQuery($config = [])
 	{
-		return new CommentQuery(['modelClass' => get_called_class()]);
+		$config['modelClass'] = get_called_class();
+		return new CommentQuery($config);
 	}
 }
 ```
@@ -636,7 +743,7 @@ You can also use scopes when defining relations. For example,
 ```php
 class Post extends \yii\db\ActiveRecord
 {
-	public function getComments()
+	public function getActiveComments()
 	{
 		return $this->hasMany(Comment::className(), ['post_id' => 'id'])->active();
 
@@ -654,20 +761,6 @@ $posts = Post::find()->with([
 ])->all();
 ```
 
-### Default Scope
-
-If you used Yii 1.1 before, you may know a concept called *default scope*. A default scope is a scope that
-applies to ALL queries. You can define a default scope easily by overriding `ActiveRecord::createQuery()`. For example,
-
-```php
-public static function createQuery()
-{
-	$query = new CommentQuery(['modelClass' => get_called_class()]);
-	$query->where(['deleted' => false]);
-	return $query;
-}
-```
-
 
 ### Making it IDE-friendly
 
@@ -696,10 +789,27 @@ class CommentQuery extends ActiveQuery
 }
 ```
 
+### Default Scope
+
+If you used Yii 1.1 before, you may know a concept called *default scope*. A default scope is a scope that
+applies to ALL queries. You can define a default scope easily by overriding [[yii\db\ActiveRecord::createQuery()]]. For example,
+
+```php
+public static function createQuery($config = [])
+{
+	$config['modelClass'] = get_called_class();
+	return (new ActiveQuery($config))->where(['deleted' => false]);
+}
+```
+
+Note that all your queries should then not use [[yii\db\ActiveQuery::where()|where()]] but
+[[yii\db\ActiveQuery::andWhere()|andWhere()]] and [[yii\db\ActiveQuery::orWhere()|orWhere()]]
+to not override the default condition.
+
+
 Transactional operations
 ------------------------
 
-
 When a few DB operations are related and are executed
 
 TODO: FIXME: WIP, TBD, https://github.com/yiisoft/yii2/issues/226
@@ -843,4 +953,4 @@ See also
 --------
 
 - [Model](model.md)
-- [[\yii\db\ActiveRecord]]
+- [[yii\db\ActiveRecord]]
diff --git a/docs/guide/apps-advanced.md b/docs/guide/apps-advanced.md
index 1325346..559a5e1 100644
--- a/docs/guide/apps-advanced.md
+++ b/docs/guide/apps-advanced.md
@@ -175,3 +175,29 @@ All these packages are coming from [packagist.org](https://packagist.org/) so fe
 
 After your `composer.json` is changed you can run `php composer.phar update --prefer-dist`, wait till packages are downloaded and
 installed and then just use them. Autoloading of classes will be handled automatically.
+
+Creating links from backend to frontend
+---------------------------------------
+
+Often it's required to create links from backend application to frontend application. Since frontend application may
+contain its own URL manager rules you need to duplicate that for backend application naming it differently:
+
+```php
+return [
+	'components' => [
+		'urlManager' => [
+			// here is your normal backend url manager config
+		],
+		'urlManagerFrontend' => [
+			// here is your frontend URL manager config
+		],
+
+	],
+];
+```
+
+After it is done you can get URL poiting to frontend like the following:
+
+```php
+echo Yii::$app->urlManagerFrontend->createUrl(...);
+```
diff --git a/docs/guide/apps-basic.md b/docs/guide/apps-basic.md
index 873125c..f7e4061 100644
--- a/docs/guide/apps-basic.md
+++ b/docs/guide/apps-basic.md
@@ -1,23 +1,22 @@
 Basic application template
 ==========================
 
-This template is a perfect fit for small projects or learning Yii2.
+The basic Yii application template is a perfect fit for small projects or when you're just learning the framework.
 
-The application has four pages: the homepage, the about page, the contact page and the login page.
-The contact page displays a contact form that users can fill in to submit their inquiries to the webmaster,
-and the login page allows users to be authenticated before accessing privileged contents.
+The basic application template includes four pages: a homepage, an about page, a contact page, and a login page.
+The contact page displays a contact form that users can fill in to submit their inquiries to the webmaster. Assuming the site has access to a mail server and that the administrator's email address is entered in the configuration file, the contact form will work. The same goes for the login page, which allows users to be authenticated before accessing privileged content.
 
 Installation
 ------------
 
-If you do not have [Composer](http://getcomposer.org/), you may download it from
-[http://getcomposer.org/](http://getcomposer.org/) or run the following command on Linux/Unix/MacOS:
+Installation of the framework requires [Composer](http://getcomposer.org/). If you do not have Composer on your system yet, you may download it from
+[http://getcomposer.org/](http://getcomposer.org/), or run the following command on Linux/Unix/MacOS:
 
 ~~~
 curl -s http://getcomposer.org/installer | php
 ~~~
 
-You can then install the Bootstrap Application using the following command:
+You can then create a basic Yii application using the following :
 
 ~~~
 php composer.phar create-project --prefer-dist --stability=dev yiisoft/yii2-app-basic /path/to/yii-application
diff --git a/docs/guide/assets.md b/docs/guide/assets.md
index e4667bf..4e370b6 100644
--- a/docs/guide/assets.md
+++ b/docs/guide/assets.md
@@ -59,9 +59,29 @@ Asset bundles are regular classes so if you need to define another one, just cre
 class can be placed anywhere but the convention for it is to be under `assets` directory of the application.
 
 Additionally you may specify `$jsOptions`, `$cssOptions` and `$publishOptions` that will be passed to
-[[\yii\web\View::registerJsFile()]], [[\yii\web\View::registerCssFile()]] and [[\yii\web\AssetManager::publish()]]
+[[yii\web\View::registerJsFile()]], [[yii\web\View::registerCssFile()]] and [[yii\web\AssetManager::publish()]]
 respectively during registering and publising an asset.
 
+### Language-specific asset bundle
+
+If you need to define an asset bundle that includes JavaScript file depending on the language you can do it the
+following way:
+
+```php
+class LanguageAsset extends AssetBundle
+{
+    public $sourcePath = '@app/assets/language';
+    public $js = [
+    ];
+
+    public function init()
+    {
+        $this->js[] = 'language-' . Yii::$app->language . '.js';
+        parent::init();
+    }
+}
+```
+
 Registering asset bundle
 ------------------------
 
diff --git a/docs/guide/authentication.md b/docs/guide/authentication.md
index c0c0271..e7c1d94 100644
--- a/docs/guide/authentication.md
+++ b/docs/guide/authentication.md
@@ -3,7 +3,7 @@ Authentication
 
 Authentication is the act of verifying who a user is, and is the basis of the login process. Typically, authentication uses the combination of an identifier--a username or email address--and a password. The user submits these values  through a form, and the application then compares the submitted information against that previously stored (e.g., upon registration).
 
-In Yii, this entire process is performed semi-automatically, leaving the developer to merely implement [[\yii\web\IdentityInterface]], the most important class in the authentication system. Typically, implementation of `IdentityInterface` is accomplished using the `User` model. 
+In Yii, this entire process is performed semi-automatically, leaving the developer to merely implement [[yii\web\IdentityInterface]], the most important class in the authentication system. Typically, implementation of `IdentityInterface` is accomplished using the `User` model.
 
 You can find a full featured example of authentication in the
 [advanced application template](installation.md). Below, only the interface methods are listed:
diff --git a/docs/guide/authorization.md b/docs/guide/authorization.md
index 7877647..864afdd 100644
--- a/docs/guide/authorization.md
+++ b/docs/guide/authorization.md
@@ -7,7 +7,7 @@ of controlling it.
 Access control basics
 ---------------------
 
-Basic access control is very simple to implement using [[\yii\web\AccessControl]]:
+Basic access control is very simple to implement using [[yii\web\AccessControl]]:
 
 ```php
 class SiteController extends Controller
@@ -38,7 +38,7 @@ class SiteController extends Controller
 
 In the code above we're attaching access control behavior to a controller. Since there's `only` option specified, it
 will be applied to 'login', 'logout' and 'signup' actions only. A set of rules that are basically options for
-[[\yii\web\AccessRule]] reads as follows:
+[[yii\web\AccessRule]] reads as follows:
 
 - Allow all guest (not yet authenticated) users to access 'login' and 'signup' actions.
 - Allow authenticated users to access 'logout' action.
@@ -46,7 +46,7 @@ will be applied to 'login', 'logout' and 'signup' actions only. A set of rules t
 Rules are checked one by one from top to bottom. If rule matches, action takes place immediately. If not, next rule is
 checked. If no rules matched access is denied.
 
-[[\yii\web\AccessRule]] is quite flexible and allows additionally to what was demonstrated checking IPs and request method
+[[yii\web\AccessRule]] is quite flexible and allows additionally to what was demonstrated checking IPs and request method
 (i.e. POST, GET). If it's not enough you can specify your own check via anonymous function:
 
 ```php
diff --git a/docs/guide/behaviors.md b/docs/guide/behaviors.md
index 2e7bda4..6afeddc 100644
--- a/docs/guide/behaviors.md
+++ b/docs/guide/behaviors.md
@@ -2,7 +2,7 @@ Behaviors
 =========
 
 A behavior (also knows as *mixin*) can be used to enhance the functionality of an existing component without modifying the component's
-code. In particular, a behavior can "inject" its own methods and properties into the component, making them directly accessible
+code. In particular, a behavior can "inject" its public methods and properties into the component, making them directly accessible
 via the component itself. A behavior can also respond to  events triggered in the component, thus intercepting the normal
 code execution. Unlike [PHP's traits](http://www.php.net/traits), behaviors can be attached to classes at runtime.
 
@@ -11,10 +11,12 @@ Using behaviors
 
 A behavior can be attached to any class that extends from [[yii\base\Component]]. In order to attach a behavior to a class,
 the component class must implement the `behaviors`
-method. As an example, Yii provides the [[yii\behaviors\AutoTimestamp|AutoTimestamp]] behavior for automatically updating timestamp
+method. As an example, Yii provides the [[yii\behaviors\TimestampBehavior]] behavior for automatically updating timestamp
 fields when saving an [[yii\db\ActiveRecord|Active Record]] model:
 
 ```php
+use yii\behaviors\TimestampBehavior;
+
 class User extends ActiveRecord
 {
 	// ...
@@ -23,7 +25,7 @@ class User extends ActiveRecord
 	{
 		return [
 			'timestamp' => [
-				'class' => 'yii\behaviors\AutoTimestamp',
+				'class' => TimestampBehavior::className(),
 				'attributes' => [
 					ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
 					ActiveRecord::EVENT_BEFORE_UPDATE => 'updated_at',
@@ -34,9 +36,38 @@ class User extends ActiveRecord
 }
 ```
 
-In the above, the `class` value is a string representing the fully qualified behavior class name.
-All of the other key-value pairs represent corresponding public properties of the [[yii\behaviors\AutoTimestamp|AutoTimestamp]]
-class, thereby customizing how the behavior functions.
+In the above, the name `timestamp` can be used to reference the behavior through the component. For example, `$user->timestamp`
+gives the attached timestamp behavior instance. The corresponding array is the configuration used to create the
+[[yii\behaviors\TimestampBehavior|TimestampBehavior]] object.
+
+Besides responding to the insertion and update events of ActiveRecord, `TimestampBehavior` also provides a method `touch()`
+that can assign the current timestamp to a specified attribute. As aforementioned, you can access this method directly
+through the component, like the following:
+
+```php
+$user->touch('login_time');
+```
+
+If you do not need to access a behavior object, or the behavior does not need customization, you can also
+use the following simplified format when specifying the behavior,
+
+```php
+use yii\behaviors\TimestampBehavior;
+
+class User extends ActiveRecord
+{
+	// ...
+
+	public function behaviors()
+	{
+		return [
+			TimestampBehavior::className(),
+			// or the following if you want to access the behavior object
+			// 'timestamp' => TimestampBehavior::className(),
+		];
+	}
+}
+```
 
 
 Creating your own behaviors
@@ -54,7 +85,7 @@ class MyBehavior extends Behavior
 }
 ```
 
-To make it customizable, like [[yii\behaviors\AutoTimestamp]], add public properties:
+To make it customizable, like [[yii\behaviors\TimestampBehavior]], add public properties:
 
 ```php
 namespace app\components;
diff --git a/docs/guide/caching.md b/docs/guide/caching.md
index 1e4119d..b7ee784 100644
--- a/docs/guide/caching.md
+++ b/docs/guide/caching.md
@@ -38,38 +38,38 @@ When the application is running, the cache component can be accessed through `Yi
 Yii provides various cache components that can store cached data in different media. The following
 is a summary of the available cache components:
 
-* [[\yii\caching\ApcCache]]: uses PHP [APC](http://php.net/manual/en/book.apc.php) extension. This option can be
+* [[yii\caching\ApcCache]]: uses PHP [APC](http://php.net/manual/en/book.apc.php) extension. This option can be
   considered as the fastest one when dealing with cache for a centralized thick application (e.g. one
   server, no dedicated load balancers, etc.).
 
-* [[\yii\caching\DbCache]]: uses a database table to store cached data. By default, it will create and use a
+* [[yii\caching\DbCache]]: uses a database table to store cached data. By default, it will create and use a
   [SQLite3](http://sqlite.org/) database under the runtime directory. You can explicitly specify a database for
   it to use by setting its `db` property.
 
-* [[\yii\caching\DummyCache]]: presents dummy cache that does no caching at all. The purpose of this component
+* [[yii\caching\DummyCache]]: presents dummy cache that does no caching at all. The purpose of this component
   is to simplify the code that needs to check the availability of cache. For example, during development or if
   the server doesn't have actual cache support, we can use this cache component. When an actual cache support
   is enabled, we can switch to use the corresponding cache component. In both cases, we can use the same
   code `Yii::$app->cache->get($key)` to attempt retrieving a piece of data without worrying that
   `Yii::$app->cache` might be `null`.
 
-* [[\yii\caching\FileCache]]: uses standard files to store cached data. This is particular suitable
+* [[yii\caching\FileCache]]: uses standard files to store cached data. This is particular suitable
   to cache large chunk of data (such as pages).
 
-* [[\yii\caching\MemCache]]: uses PHP [memcache](http://php.net/manual/en/book.memcache.php)
+* [[yii\caching\MemCache]]: uses PHP [memcache](http://php.net/manual/en/book.memcache.php)
   and [memcached](http://php.net/manual/en/book.memcached.php) extensions. This option can be considered as
   the fastest one when dealing with cache in a distributed applications (e.g. with several servers, load
   balancers, etc.)
 
-* [[\yii\caching\RedisCache]]: implements a cache component based on [Redis](http://redis.io/) key-value store
+* [[yii\redis\Cache]]: implements a cache component based on [Redis](http://redis.io/) key-value store
   (redis version 2.6.12 or higher is required).
 
-* [[\yii\caching\WinCache]]: uses PHP [WinCache](http://iis.net/downloads/microsoft/wincache-extension)
+* [[yii\caching\WinCache]]: uses PHP [WinCache](http://iis.net/downloads/microsoft/wincache-extension)
   ([see also](http://php.net/manual/en/book.wincache.php)) extension.
 
-* [[\yii\caching\XCache]]: uses PHP [XCache](http://xcache.lighttpd.net/) extension.
+* [[yii\caching\XCache]]: uses PHP [XCache](http://xcache.lighttpd.net/) extension.
 
-* [[\yii\caching\ZendDataCache]]: uses
+* [[yii\caching\ZendDataCache]]: uses
   [Zend Data Cache](http://files.zend.com/help/Zend-Server-6/zend-server.htm#data_cache_component.htm)
   as the underlying caching medium.
 
@@ -91,7 +91,7 @@ Data Caching
 ------------
 
 Data caching is about storing some PHP variable in cache and retrieving it later from cache. For this purpose,
-the cache component base class [[\yii\caching\Cache]] provides two methods that are used most of the time:
+the cache component base class [[yii\caching\Cache]] provides two methods that are used most of the time:
 [[yii\caching\Cache::set()|set()]] and [[yii\caching\Cache::get()|get()]]. Note, only serializable variables and objects could be cached successfully.
 
 To store a variable `$value` in cache, we choose a unique `$key` and call [[yii\caching\Cache::set()|set()]] to store it:
@@ -158,7 +158,7 @@ Besides expiration setting, cached data may also be invalidated according to som
 are caching the content of some file and the file is changed, we should invalidate the cached copy and read the latest
 content from the file instead of the cache.
 
-We represent a dependency as an instance of [[\yii\caching\Dependency]] or its child class. We pass the dependency
+We represent a dependency as an instance of [[yii\caching\Dependency]] or its child class. We pass the dependency
 instance along with the data to be cached when calling [[yii\caching\Cache::set()|set()]].
 
 ```php
@@ -174,12 +174,12 @@ get a false value, indicating the data needs to be regenerated.
 
 Below is a summary of the available cache dependencies:
 
-- [[\yii\caching\FileDependency]]: the dependency is changed if the file's last modification time is changed.
-- [[\yii\caching\GroupDependency]]: marks a cached data item with a group name. You may invalidate the cached data items
-  with the same group name all at once by calling [[\yii\caching\GroupDependency::invalidate()]].
-- [[\yii\caching\DbDependency]]: the dependency is changed if the query result of the specified SQL statement is changed.
-- [[\yii\caching\ChainedDependency]]: the dependency is changed if any of the dependencies on the chain is changed.
-- [[\yii\caching\ExpressionDependency]]: the dependency is changed if the result of the specified PHP expression is
+- [[yii\caching\FileDependency]]: the dependency is changed if the file's last modification time is changed.
+- [[yii\caching\GroupDependency]]: marks a cached data item with a group name. You may invalidate the cached data items
+  with the same group name all at once by calling [[yii\caching\GroupDependency::invalidate()]].
+- [[yii\caching\DbDependency]]: the dependency is changed if the query result of the specified SQL statement is changed.
+- [[yii\caching\ChainedDependency]]: the dependency is changed if any of the dependencies on the chain is changed.
+- [[yii\caching\ExpressionDependency]]: the dependency is changed if the result of the specified PHP expression is
   changed.
 
 ### Query Caching
diff --git a/docs/guide/console-migrate.md b/docs/guide/console-migrate.md
index f08e036..cf24b9b 100644
--- a/docs/guide/console-migrate.md
+++ b/docs/guide/console-migrate.md
@@ -104,7 +104,7 @@ You can use them to write database independent migrations.
 For example `pk` will be replaced by `int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY`
 for MySQL and `integer PRIMARY KEY AUTOINCREMENT NOT NULL` for sqlite.
 See documentation of [[yii\db\QueryBuilder::getColumnType()]] for more details and a list
-of available types. You may also use the constants defined in [[\yii\db\Schema]] to
+of available types. You may also use the constants defined in [[yii\db\Schema]] to
 define column types.
 
 
diff --git a/docs/guide/console.md b/docs/guide/console.md
index 2ebc638..91f2616 100644
--- a/docs/guide/console.md
+++ b/docs/guide/console.md
@@ -2,7 +2,7 @@ Console applications
 ====================
 
 Yii has full featured support of console. Console application structure in Yii is very similar to web application. It
-consists of one or more [[\yii\console\Controller]] (often referred to as commands). Each has one or more actions.
+consists of one or more [[yii\console\Controller]] (often referred to as commands). Each has one or more actions.
 
 Usage
 -----
diff --git a/docs/guide/controller.md b/docs/guide/controller.md
index 1e10ee0..208b0d1 100644
--- a/docs/guide/controller.md
+++ b/docs/guide/controller.md
@@ -11,7 +11,7 @@ Basics
 Controller resides in application's `controllers` directory and is named like `SiteController.php`,
 where the `Site` part could be anything describing a set of actions it contains.
 
-The basic web controller is a class that extends [[\yii\web\Controller]] and could be very simple:
+The basic web controller is a class that extends [[yii\web\Controller]] and could be very simple:
 
 ```php
 namespace app\controllers;
@@ -97,12 +97,12 @@ In case module, controller or action specified isn't found Yii will return "not 
 ### Defaults
 
 If user isn't specifying any route i.e. using URL like `http://example.com/`, Yii assumes that default route should be
-used. It is determined by [[\yii\web\Application::defaultRoute]] method and is `site` by default meaning that `SiteController`
+used. It is determined by [[yii\web\Application::defaultRoute]] method and is `site` by default meaning that `SiteController`
 will be loaded.
 
 A controller has a default action. When the user request does not specify which action to execute by using an URL such as
 `http://example.com/?r=site`, the default action will be executed. By default, the default action is named as `index`.
-It can be changed by setting the [[\yii\base\Controller::defaultAction]] property.
+It can be changed by setting the [[yii\base\Controller::defaultAction]] property.
 
 Action parameters
 -----------------
@@ -164,9 +164,9 @@ class BlogController extends Controller
 		}
 
 		if (\Yii::$app->request->isPost) {
-			$post->load($_POST);
+			$post->load(Yii::$app->request->post());
 			if ($post->save()) {
-				$this->redirect(['view', 'id' => $post->id]);
+				return $this->redirect(['view', 'id' => $post->id]);
 			}
 		}
 
diff --git a/docs/guide/database-basics.md b/docs/guide/database-basics.md
index 0dcba6f..9e0659e 100644
--- a/docs/guide/database-basics.md
+++ b/docs/guide/database-basics.md
@@ -53,7 +53,7 @@ After the connection component is configured you can access it using the followi
 $connection = \Yii::$app->db;
 ```
 
-You can refer to [[\yii\db\Connection]] for a list of properties you can configure. Also note that you can define more
+You can refer to [[yii\db\Connection]] for a list of properties you can configure. Also note that you can define more
 than one connection component and use both at the same time if needed:
 
 ```php
@@ -96,7 +96,7 @@ return [
 Basic SQL queries
 -----------------
 
-Once you have a connection instance you can execute SQL queries using [[\yii\db\Command]].
+Once you have a connection instance you can execute SQL queries using [[yii\db\Command]].
 
 ### SELECT
 
@@ -173,8 +173,8 @@ $rowCount = $connection->createCommand($sql)->queryScalar();
 In the code above `[[X]]` will be converted to properly quoted column name while `{{Y}}` will be converted to properly
 quoted table name.
 
-The alternative is to quote table and column names manually using [[\yii\db\Connection::quoteTableName()]] and
-[[\yii\db\Connection::quoteColumnName()]]:
+The alternative is to quote table and column names manually using [[yii\db\Connection::quoteTableName()]] and
+[[yii\db\Connection::quoteColumnName()]]:
 
 ```php
 $column = $connection->quoteColumnName($column);
@@ -253,7 +253,7 @@ Working with database schema
 
 ### Getting schema information
 
-You can get a [[\yii\db\Schema]] instance like the following:
+You can get a [[yii\db\Schema]] instance like the following:
 
 ```php
 $schema = $connection->getSchema();
@@ -265,11 +265,11 @@ It contains a set of methods allowing you to retrieve various information about 
 $tables = $schema->getTableNames();
 ```
 
-For the full reference check [[\yii\db\Schema]].
+For the full reference check [[yii\db\Schema]].
 
 ### Modifying schema
 
-Aside from basic SQL queries [[\yii\db\Command]] contains a set of methods allowing to modify database schema:
+Aside from basic SQL queries [[yii\db\Command]] contains a set of methods allowing to modify database schema:
 
 - createTable, renameTable, dropTable, truncateTable
 - addColumn, renameColumn, dropColumn, alterColumn
@@ -288,4 +288,4 @@ $connection->createCommand()->createTable('tbl_post', [
 ]);
 ```
 
-For the full reference check [[\yii\db\Command]].
+For the full reference check [[yii\db\Command]].
diff --git a/docs/guide/events.md b/docs/guide/events.md
index eb31670..4288090 100644
--- a/docs/guide/events.md
+++ b/docs/guide/events.md
@@ -40,7 +40,7 @@ $component->on($eventName, function($event) {
 ```
 
 As shown in the anonymous function example, the event handling function must be defined so that it takes one argument.
-This will be an [[\yii\base\Event]] object.
+This will be an [[yii\base\Event]] object.
 
 
 Removing Event Handlers
diff --git a/docs/guide/extensions.md b/docs/guide/extensions.md
index e3c27c1..b50d64f 100644
--- a/docs/guide/extensions.md
+++ b/docs/guide/extensions.md
@@ -1,21 +1,26 @@
 Extending Yii
 =============
+The Yii framework was designed to be easily extendable. Additional features can be added to your project and then reused, either by yourself on other projects or by sharing your work as a formal Yii extension.
 
 Code style
 ----------
 
-- Use [core framework code style](https://github.com/yiisoft/yii2/wiki/Core-framework-code-style).
-- Document classes, methods and properties using phpdoc. Note that you can use markdown and link to properties and methods
-  using the following syntax: e.g. `[[name()]]`, `[[name\space\MyClass::name()]]`.
-- Extension classes should not be prefixed. No `TbNavBar`, `EMyWidget`, etc.
+To be consistent with core Yii conventions, your extensions ought to adhere to certain coding styles:
+
+- Use the [core framework code style](https://github.com/yiisoft/yii2/wiki/Core-framework-code-style).
+- Document classes, methods and properties using [phpdoc](http://www.phpdoc.org/). - Extension classes should *not* be prefixed. Do not use the format `TbNavBar`, `EMyWidget`, etc.
+
+> Note that you can use Markdown within your code for documentation purposes. With Markdown, you can link to properties and methods using the following syntax: `[[name()]]`, `[[namespace\MyClass::name()]]`.
 
 ### Namespace
 
-- Do not use `yiisoft` in the namespaces.
-- Do not use `\yii`, `\yii2` or `\yiisoft` as root namespace.
-- Namespace should be `vendorName\uniqueName`.
+Yii 2 relies upon namespaces to organize code. (Namespace support was added to PHP in version 5.3.) If you want to use namespaces within your extension,
+
+- Do not use `yiisoft` anywhere in your namespaces.
+- Do not use `\yii`, `\yii2` or `\yiisoft` as root namespaces.
+- Namespaces should use the syntax `vendorName\uniqueName`.
 
-Choosing unique namespace is important to prevent name collisions and autoload faster. Examples:
+Choosing a unique namespace is important to prevent name collisions, and also results in faster autoloading of classes. Examples of unique, consistent namepacing are:
 
 - `samdark\wiki`
 - `samdark\debugger`
@@ -24,15 +29,22 @@ Choosing unique namespace is important to prevent name collisions and autoload f
 Distribution
 ------------
 
-- There should be a `readme.md` file clearly describing what extension does in English, its requirements, how to install
-  and use it. It should be written using markdown. If you want to provide translated readme, name it as `readme_ru.md`
-  where `ru` is your language code. If extension provides a widget it is a good idea to include some screenshots.
-- It is recommended to host your extensions at [Github](github.com).
-- Extension should be registered at [Packagist](https://packagist.org) in order to be installable via Composer.
-  Choose package name wisely since changing it leads to losing stats and inability to install package by the old name.
+Beyond the code itself, the entire extension distribution ought to have certain things.
+
+There should be a `readme.md` file, written in English. This file should clearly describe what the extension does, its requirements, how to install it, 
+  and to use it. The README should be written using Markdown. If you want to provide translated README files, name them as `readme_ru.md`
+  where `ru` is your language code (in this case, Russian). 
+  
+  It is a good idea to include some screenshots as part of the documentation, especially if your extension provides a widget. 
+  
+It is recommended to host your extensions at [Github](github.com).
+
+Extensions should also be registered at [Packagist](https://packagist.org) in order to be installable via Composer. 
 
 ### Composer package name
 
+Choose your extension's package name wisely, as you shouldn't change the package name later on. (Changing the name leads to losing the Composer stats, and makes it impossible for people  to install the package by the old name.) 
+
 If your extension was made specifically for Yii2 (i.e. cannot be used as a standalone PHP library) it is recommended to
 name it like the following:
 
@@ -40,32 +52,35 @@ name it like the following:
 yii2-my-extension-name-type
 ```
 
-In the above:
+Where: 
 
-- `yii2-` prefix.
-- Extension name lowecase, words separated by `-`.
-- `-type` postfix where type may be `widget`, `behavior`, `module` etc.
+- `yii2-` is a prefix.
+- The extension name is in all lowercase letters, with words separated by `-`.
+- The `-type` postfix may be `widget`, `behavior`, `module` etc.
 
 ### Dependencies
 
-- Additional code, eg. libraries, should be required in your `composer.json` file.
-- When extension is released in a stable version, its requirements should not include `dev` packages that do not
-  have a `stable` release.
-- Use appropriate version constraints, eg. `1.*`, `@stable` for requirements.
+Some extensions you develop may have their own dependencies, such as relying upon other extensions or third-party libraries. When dependencies exist, you should require them in your extension's `composer.json` file. Be certain to also use appropriate version constraints, eg. `1.*`, `@stable` for requirements.
+
+Finally, when your extension is released in a stable version, double-check that its requirements do not include `dev` packages that do not have a `stable` release. In other words, the stable release of your extension should only rely upon stable dependencies.
 
 ### Versioning
 
+As you maintain and upgrading your extension, 
+
 - Use the rules of [semantic versioning](http://semver.org).
 - Use a consistent format for your repository tags, as they are treated as version strings by composer, eg. `0.2.4`,
   `0.2.5`,`0.3.0`,`1.0.0`.
 
 ### composer.json
 
+Yii2 uses Composer for installation, and extensions for Yii2 should as well. Towards that end, 
+
 - Use the type `yii2-extension` in `composer.json` file if your extension is Yii-specific.
-- Do not use `yii` or `yii2` as composer vendor name.
-- Do not use `yiisoft` in the composer package name or the composer vendor name.
+- Do not use `yii` or `yii2` as the Composer vendor name.
+- Do not use `yiisoft` in the Composer package name or the Composer vendor name.
 
-If your extension classes reside directly in repository root use PSR-4 the following way in your `composer.json`:
+If your extension classes reside directly in the repository root directory, you can use the PSR-4 autoloader in the following way in your `composer.json` file:
 
 ```
 {
@@ -92,19 +107,21 @@ If your extension classes reside directly in repository root use PSR-4 the follo
 }
 ```
 
-In the above `myname/mywidget` is the package name that will be registered
-at [Packagist](https://packagist.org). It is common for it to match your github repository.
+In the above, `myname/mywidget` is the package name that will be registered
+at [Packagist](https://packagist.org). It is common for the package name to match your Github repository name.
 
-We're using `psr-4` autoloader and mapping `myname\mywidget` namespace to the root directory where our classes reside.
+In the above, the `psr-4` autoloader is specified, mapping the `myname\mywidget` namespace to the root directory where the classes reside.
 
-More details can be found in the [composer documentation](http://getcomposer.org/doc/04-schema.md#autoload).
+More details on this syntax can be found in the [Composer documentation](http://getcomposer.org/doc/04-schema.md#autoload).
 
 Working with database
 ---------------------
 
-- If extension creates or modifies database schema always use Yii migrations instead of SQL files or custom scripts.
-- Migrations should be appliable to as many data storages as possible.
-- Do not use active record models in your migrations.
+Extensions sometimes have to use their own database tables. In such situations, 
+
+- If the extension creates or modifies the database schema, always use Yii migrations instead of SQL files or custom scripts.
+- Migrations should be applicable to as many data storages as possible.
+- Do not use Active Record models in your migrations.
 
 Assets
 ------
diff --git a/docs/guide/form.md b/docs/guide/form.md
index dbfb39c..05e28b8 100644
--- a/docs/guide/form.md
+++ b/docs/guide/form.md
@@ -2,7 +2,7 @@ Working with forms
 ==================
 
 The primary way of using forms in Yii is through [[yii\widgets\ActiveForm]]. This approach should be preferred when
-the form is based upon  a model. Additionally, there are some useful methods in [[\yii\helpers\Html]] that are typically
+the form is based upon  a model. Additionally, there are some useful methods in [[yii\helpers\Html]] that are typically
 used for adding buttons and help text to any form.
 
 When creating model-based forms, the first step is to define the model itself. The model can be either based upon the
diff --git a/docs/guide/i18n.md b/docs/guide/i18n.md
index 65b11b8..d0b23ff 100644
--- a/docs/guide/i18n.md
+++ b/docs/guide/i18n.md
@@ -8,8 +8,8 @@ because the potential users may be worldwide.
 Locale and Language
 -------------------
 
-There are two languages defined in Yii application: [[\yii\base\Application::$sourceLanguage|source language]] and
-[[\yii\base\Application::$language|target language]].
+There are two languages defined in Yii application: [[yii\base\Application::$sourceLanguage|source language]] and
+[[yii\base\Application::$language|target language]].
 
 Source language is the language original application messages are written in such as:
 
diff --git a/docs/guide/installation.md b/docs/guide/installation.md
index 928ad4a..cc8e7f5 100644
--- a/docs/guide/installation.md
+++ b/docs/guide/installation.md
@@ -3,8 +3,8 @@ Installation
 
 There are two ways you can install the Yii framework:
 
-* Installation via [Composer](http://getcomposer.org/) (recommended)
-* Download an application template packed with all requirements including the Yii Framework
+* Via [Composer](http://getcomposer.org/) (recommended)
+* Download an application template containing all site requirements, including the Yii framework itself
 
 
 Installing via Composer
diff --git a/docs/guide/logging.md b/docs/guide/logging.md
index 9be8d5d..975d791 100644
--- a/docs/guide/logging.md
+++ b/docs/guide/logging.md
@@ -62,7 +62,7 @@ You may configure the targets in application configuration, like the following:
 ]
 ```
 
-In the config above we are defining two log targets: [[\yii\log\FileTarget|file]] and [[\yii\log\EmailTarget|email]].
+In the config above we are defining two log targets: [[yii\log\FileTarget|file]] and [[yii\log\EmailTarget|email]].
 In both cases we are filtering messages handles by these targets by severity. In case of file target we're
 additionally filter by category. `yii\*` means all categories starting with `yii\`.
 
diff --git a/docs/guide/model.md b/docs/guide/model.md
index 9697e00..f840457 100644
--- a/docs/guide/model.md
+++ b/docs/guide/model.md
@@ -9,7 +9,7 @@ Yii models have the following basic features:
 - Massive attribute assignment: the ability to populate multiple model attributes in one step.
 - Scenario-based data validation.
 
-Models in Yii extend from the [[\yii\base\Model]] class. Models are typically used to both hold data and define
+Models in Yii extend from the [[yii\base\Model]] class. Models are typically used to both hold data and define
 the validation rules for that data (aka, the business logic). The business logic greatly simplifies the generation
 of models from complex web forms by providing validation and error reporting.
 
@@ -31,7 +31,7 @@ echo $post->title;
 echo $post->content;
 ```
 
-Since [[\yii\base\Model|Model]] implements the [ArrayAccess](http://php.net/manual/en/class.arrayaccess.php) interface,
+Since [[yii\base\Model|Model]] implements the [ArrayAccess](http://php.net/manual/en/class.arrayaccess.php) interface,
 you can also access the attributes as if they were array elements:
 
 ```php
@@ -42,7 +42,7 @@ echo $post['title'];
 echo $post['content'];
 ```
 
-By default, [[\yii\base\Model|Model]] requires that attributes be declared as *public* and *non-static*
+By default, [[yii\base\Model|Model]] requires that attributes be declared as *public* and *non-static*
 class member variables. In the following example, the `LoginForm` model class declares two attributes:
 `username` and `password`.
 
@@ -55,8 +55,8 @@ class LoginForm extends \yii\base\Model
 }
 ```
 
-Derived model classes may declare attributes in different ways, by overriding the [[\yii\base\Model::attributes()|attributes()]]
-method. For example, [[\yii\db\ActiveRecord]] defines attributes using the column names of the database table
+Derived model classes may declare attributes in different ways, by overriding the [[yii\base\Model::attributes()|attributes()]]
+method. For example, [[yii\db\ActiveRecord]] defines attributes using the column names of the database table
 that is associated with the class.
 
 
@@ -65,12 +65,12 @@ Attribute Labels
 
 Attribute labels are mainly used for display purpose. For example, given an attribute `firstName`, we can declare
 a label `First Name` that is more user-friendly when displayed to end users in places such as form labels and
-error messages. Given an attribute name, you can obtain its label by calling [[\yii\base\Model::getAttributeLabel()]].
+error messages. Given an attribute name, you can obtain its label by calling [[yii\base\Model::getAttributeLabel()]].
 
-To declare attribute labels, override the [[\yii\base\Model::attributeLabels()]] method. The overridden method returns
+To declare attribute labels, override the [[yii\base\Model::attributeLabels()]] method. The overridden method returns
 a mapping of attribute names to attribute labels, as shown in the example below. If an attribute is not found
-in this mapping, its label will be generated using the [[\yii\base\Model::generateAttributeLabel()]] method.
-In many cases, [[\yii\base\Model::generateAttributeLabel()]] will generate reasonable labels (e.g. `username` to `Username`,
+in this mapping, its label will be generated using the [[yii\base\Model::generateAttributeLabel()]] method.
+In many cases, [[yii\base\Model::generateAttributeLabel()]] will generate reasonable labels (e.g. `username` to `Username`,
 `orderNumber` to `Order Number`).
 
 ```php
@@ -200,7 +200,7 @@ if ($model->validate()) {
 
 The possible validation rules for a model should be listed in its `rules()` method. Each validation rule applies to one
 or several attributes and is effective in one or several scenarios. A rule can be specified using a validator object - an
-instance of a [[\yii\validators\Validator]] child class, or an array with the following format:
+instance of a [[yii\validators\Validator]] child class, or an array with the following format:
 
 ```php
 [
@@ -449,4 +449,4 @@ See also
 --------
 
 - [Model validation reference](validation.md)
-- [[\yii\base\Model]]
+- [[yii\base\Model]]
diff --git a/docs/guide/performance.md b/docs/guide/performance.md
index 07c3eb1..9eb8ccf 100644
--- a/docs/guide/performance.md
+++ b/docs/guide/performance.md
@@ -137,7 +137,7 @@ sending either `ETag` or `Last-Modified` header in your application response. If
 HTTP specification (most browsers are), content will be fetched only if it is different from what it was prevously.
 
 Forming proper headers is time consuming task so Yii provides a shortcut in form of controller filter
-[[\yii\web\HttpCache]]. Using it is very easy. In a controller you need to implement `behaviors` method like
+[[yii\web\HttpCache]]. Using it is very easy. In a controller you need to implement `behaviors` method like
 the following:
 
 ```php
diff --git a/docs/guide/test-fixture.md b/docs/guide/test-fixture.md
index 17f3ab6..c301c78 100644
--- a/docs/guide/test-fixture.md
+++ b/docs/guide/test-fixture.md
@@ -205,8 +205,8 @@ In this way you will avoid collision of fixture data files between tests and use
 
 > Note: In the example above fixture files are named only for example purpose. In real life you should name them
 > according to which fixture class your fixture classes are extending from. For example, if you are extending
-> from [[\yii\test\ActiveFixture]] for DB fixtures, you should use DB table names as the fixture data file names;
-> If you are extending for [[\yii\mongodb\ActiveFixture]] for MongoDB fixtures, you should use collection names as the file names.
+> from [[yii\test\ActiveFixture]] for DB fixtures, you should use DB table names as the fixture data file names;
+> If you are extending for [[yii\mongodb\ActiveFixture]] for MongoDB fixtures, you should use collection names as the file names.
 
 The similar hierarchy can be used to organize fixture class files. Instead of using `data` as the root directory, you may
 want to use `fixtures` as the root directory to avoid conflict with the data files.
diff --git a/docs/guide/title.md b/docs/guide/title.md
index 8c7b25d..4653208 100644
--- a/docs/guide/title.md
+++ b/docs/guide/title.md
@@ -5,4 +5,4 @@ This tutorial is released under [the Terms of Yii Documentation](http://www.yiif
 
 All Rights Reserved.
 
-2014 (c) Yii Software LLC.
+&copy; 2014 Yii Software LLC.
diff --git a/docs/guide/url.md b/docs/guide/url.md
index dee59ef..b02f363 100644
--- a/docs/guide/url.md
+++ b/docs/guide/url.md
@@ -21,8 +21,8 @@ Creating URLs
 The most important rule for creating URLs in your site is to always do so using the URL manager. The URL manager is a built-in application component named `urlManager`. This component is accessible from both web and console applications via
 `\Yii::$app->urlManager`. The component makes availabe the two following URL creation methods:
 
-- `createUrl($route, $params = [])`
-- `createAbsoluteUrl($route, $params = [])`
+- `createUrl($params)`
+- `createAbsoluteUrl($params, $schema = null)`
 
 The `createUrl` method creates an URL relative to the application root, such as `/index.php/site/index/`.
 The `createAbsoluteUrl` method creates an URL prefixed with the proper protocol and hostname:
@@ -33,9 +33,9 @@ generating RSS feeds etc.
 Some examples:
 
 ```php
-echo \Yii::$app->urlManager->createUrl('site/page', ['id' => 'about']);
+echo \Yii::$app->urlManager->createUrl(['site/page', 'id' => 'about']);
 // /index.php/site/page/id/about/
-echo \Yii::$app->urlManager->createUrl('date-time/fast-forward', ['id' => 105])
+echo \Yii::$app->urlManager->createUrl(['date-time/fast-forward', 'id' => 105])
 // /index.php?r=date-time/fast-forward&id=105
 echo \Yii::$app->urlManager->createAbsoluteUrl('blog/post/index');
 // http://www.example.com/index.php/blog/post/index/
@@ -56,15 +56,15 @@ Inside a web application controller, you can use the controller's `createUrl` sh
 
 ```php
 echo $this->createUrl(''); // currently active route
-echo $this->createUrl('view', ['id' => 'contact']); // same controller, different action
+echo $this->createUrl(['view', 'id' => 'contact']); // same controller, different action
 echo $this->createUrl('post/index'); // same module, different controller and action
 echo $this->createUrl('/site/index'); // absolute route no matter what controller is making this call
 echo $this->createurl('hi-tech'); // url for the case sensitive action `actionHiTech` of the current controller
-echo $this->createurl('/date-time/fast-forward', ['id' => 105]); // url for action the case sensitive controller, `DateTimeController::actionFastForward`
+echo $this->createurl(['/date-time/fast-forward', 'id' => 105]); // url for action the case sensitive controller, `DateTimeController::actionFastForward`
 ```
 
 > **Tip**: In order to generate URL with a hashtag, for example `/index.php?r=site/page&id=100#title`, you need to
-  specify the parameter named `#` using `$this->createUrl('post/read', ['id' => 100, '#' => 'title'])`.
+  specify the parameter named `#` using `$this->createUrl(['post/read', 'id' => 100, '#' => 'title'])`.
 
 Customizing URLs
 ----------------
@@ -115,8 +115,8 @@ Let's use some examples to explain how URL rules work. We assume that our rule s
 ```
 
 - Calling `$this->createUrl('post/list')` generates `/index.php/posts`. The first rule is applied.
-- Calling `$this->createUrl('post/read', ['id' => 100])` generates `/index.php/post/100`. The second rule is applied.
-- Calling `$this->createUrl('post/read', ['year' => 2008, 'title' => 'a sample post'])` generates
+- Calling `$this->createUrl(['post/read', 'id' => 100])` generates `/index.php/post/100`. The second rule is applied.
+- Calling `$this->createUrl(['post/read', 'year' => 2008, 'title' => 'a sample post'])` generates
   `/index.php/post/2008/a%20sample%20post`. The third rule is applied.
 - Calling `$this->createUrl('post/read')` generates `/index.php/post/read`. None of the rules is applied, convention is used
   instead.
@@ -192,10 +192,10 @@ return [
 ### Handling REST requests
 
 TBD:
-- RESTful routing: [[\yii\web\VerbFilter]], [[\yii\web\UrlManager::$rules]]
+- RESTful routing: [[yii\web\VerbFilter]], [[yii\web\UrlManager::$rules]]
 - Json API:
-  - response: [[\yii\web\Response::format]]
-  - request: [[yii\web\Request::$parsers]], [[\yii\web\JsonParser]]
+  - response: [[yii\web\Response::format]]
+  - request: [[yii\web\Request::$parsers]], [[yii\web\JsonParser]]
 
 
 URL parsing
@@ -222,13 +222,13 @@ return [
 Creating your own rule classes
 ------------------------------
 
-[[\yii\web\UrlRule]] class is used for both parsing URL into parameters and creating URL based on parameters. Despite
+[[yii\web\UrlRule]] class is used for both parsing URL into parameters and creating URL based on parameters. Despite
 the fact that default implementation is flexible enough for the majority of projects, there are situations when using
 your own rule class is the best choice. For example, in a car dealer website, we may want to support the URL format like
 `/Manufacturer/Model`, where `Manufacturer` and `Model` must both match some data in a database table. The default rule
 class will not work because it mostly relies on statically declared regular expressions which have no database knowledge.
 
-We can write a new URL rule class by extending from [[\yii\web\UrlRule]] and use it in one or multiple URL rules. Using
+We can write a new URL rule class by extending from [[yii\web\UrlRule]] and use it in one or multiple URL rules. Using
 the above car dealer website as an example, we may declare the following URL rules in application config:
 
 ```php
@@ -276,8 +276,8 @@ class CarUrlRule extends UrlRule
 		if (preg_match('%^(\w+)(/(\w+))?$%', $pathInfo, $matches)) {
 			// check $matches[1] and $matches[3] to see
 			// if they match a manufacturer and a model in the database
-			// If so, set $_GET['manufacturer'] and/or $_GET['model']
-			// and return 'car/index'
+			// If so, set $params['manufacturer'] and/or $params['model']
+			// and return ['car/index', $params]
 		}
 		return false;  // this rule does not apply
 	}
diff --git a/docs/guide/validation.md b/docs/guide/validation.md
index 3680e9a..fe0ddfb 100644
--- a/docs/guide/validation.md
+++ b/docs/guide/validation.md
@@ -166,7 +166,7 @@ Validates that the attribute value is of certain length.
 - `length` specifies the length limit of the value to be validated. Can be `exactly X`, `[min X]`, `[min X, max Y]`.
 - `max`  maximum length. If not set, it means no maximum length limit.
 - `min` minimum length. If not set, it means no minimum length limit.
-- `encoding` the encoding of the string value to be validated. _([[\yii\base\Application::charset]])_
+- `encoding` the encoding of the string value to be validated. _([[yii\base\Application::charset]])_
 
 ### `unique`: [[yii\validators\UniqueValidator|UniqueValidator]]
 
diff --git a/extensions/apidoc/commands/RenderController.php b/extensions/apidoc/commands/RenderController.php
index 02030fe..6cf6a8c 100644
--- a/extensions/apidoc/commands/RenderController.php
+++ b/extensions/apidoc/commands/RenderController.php
@@ -53,6 +53,7 @@ class RenderController extends Controller
 		$renderer->targetDir = $targetDir;
 		if ($this->guide !== null && $renderer->hasProperty('guideUrl')) {
 			$renderer->guideUrl = './';
+			$renderer->markDownFiles = $this->findMarkdownFiles($this->guide, ['README.md']);
 		}
 
 		$this->stdout('Searching files to process... ');
@@ -65,6 +66,11 @@ class RenderController extends Controller
 
 		$this->stdout('done.' . PHP_EOL, Console::FG_GREEN);
 
+		if (empty($files)) {
+			$this->stderr('Error: No php files found to process.' . PHP_EOL);
+			return 1;
+		}
+
 		$context = new Context();
 
 		$cacheFile = $targetDir . '/cache/' . md5(serialize($files)) . '.tmp';
@@ -111,7 +117,7 @@ class RenderController extends Controller
 
 		// render guide if specified
 		if ($this->guide !== null) {
-			$renderer->renderMarkdownFiles($this->findMarkdownFiles($this->guide, ['README.md']), $this);
+			$renderer->renderMarkdownFiles($this);
 
 			$this->stdout('Publishing images...');
 			FileHelper::copyDirectory(rtrim($this->guide, '/\\') . '/images', $targetDir . '/images');
diff --git a/extensions/apidoc/composer.json b/extensions/apidoc/composer.json
index f2a3771..69f9c4b 100644
--- a/extensions/apidoc/composer.json
+++ b/extensions/apidoc/composer.json
@@ -22,7 +22,7 @@
 		"yiisoft/yii2": "*",
 		"yiisoft/yii2-bootstrap": "*",
 		"phpdocumentor/reflection": ">=1.0.3",
-		"erusev/parsedown": "0.9.*"
+		"nikic/php-parser": "0.9.*"
 	},
 	"autoload": {
 		"psr-4": { "yii\\apidoc\\": "" }
diff --git a/extensions/apidoc/helpers/ApiMarkdown.php b/extensions/apidoc/helpers/ApiMarkdown.php
index 9d0c523..4ac7d5e 100644
--- a/extensions/apidoc/helpers/ApiMarkdown.php
+++ b/extensions/apidoc/helpers/ApiMarkdown.php
@@ -7,10 +7,12 @@
 
 namespace yii\apidoc\helpers;
 
+use cebe\markdown\GithubMarkdown;
 use phpDocumentor\Reflection\DocBlock\Type\Collection;
 use yii\apidoc\models\MethodDoc;
 use yii\apidoc\models\TypeDoc;
 use yii\apidoc\templates\BaseRenderer;
+use yii\helpers\Markdown;
 
 /**
  * A Markdown helper with support for class reference links.
@@ -18,26 +20,89 @@ use yii\apidoc\templates\BaseRenderer;
  * @author Carsten Brandt <mail@cebe.cc>
  * @since 2.0
  */
-class ApiMarkdown extends Markdown
+class ApiMarkdown extends GithubMarkdown
 {
 	/**
 	 * @var BaseRenderer
 	 */
 	public static $renderer;
+
+	protected $context;
+
+	public function prepare()
+	{
+		parent::prepare();
+
+		// add references to guide pages
+		$this->references = array_merge($this->references, static::$renderer->getGuideReferences());
+	}
+
 	/**
-	 * @var ApiMarkdown
+	 * @inheritDoc
 	 */
-	private static $_instance;
+	protected function identifyLine($lines, $current)
+	{
+		if (strncmp($lines[$current], '~~~', 3) === 0) {
+			return 'fencedCode';
+		}
+		return parent::identifyLine($lines, $current);
+	}
 
-	private $context;
+	/**
+	 * Consume lines for a fenced code block
+	 */
+	protected function consumeFencedCode($lines, $current)
+	{
+		// consume until ```
+		$block = [
+			'type' => 'code',
+			'content' => [],
+		];
+		$line = rtrim($lines[$current]);
+		if (strncmp($lines[$current], '~~~', 3) === 0) {
+			$fence = '~~~';
+			$language = 'php';
+		} else {
+			$fence = substr($line, 0, $pos = strrpos($line, '`') + 1);
+			$language = substr($line, $pos);
+		}
+		if (!empty($language)) {
+			$block['language'] = $language;
+		}
+		for($i = $current + 1, $count = count($lines); $i < $count; $i++) {
+			if (rtrim($line = $lines[$i]) !== $fence) {
+				$block['content'][] = $line;
+			} else {
+				break;
+			}
+		}
+		return [$block, $i];
+	}
 
-	public function highlight(&$block, &$markup)
+	/**
+	 * Renders a code block
+	 */
+	protected function renderCode($block)
 	{
+		if (isset($block['language'])) {
+			$class = isset($block['language']) ? ' class="language-' . $block['language'] . '"' : '';
+			return "<pre><code$class>" . $this->highlight(implode("\n", $block['content']) . "\n", $block['language']) . '</code></pre>';
+		} else {
+			return parent::renderCode($block);
+		}
+	}
+
+	protected function highlight($code, $language)
+	{
+		if ($language !== 'php') {
+			return htmlspecialchars($code, ENT_NOQUOTES, 'UTF-8');
+		}
+
 		// TODO improve code highlighting
-		if (strncmp($block['text'], '<?php', 5) === 0) {
-			$text = highlight_string(trim($block['text']), true);
+		if (strncmp($code, '<?php', 5) === 0) {
+			$text = highlight_string(trim($code), true);
 		} else {
-			$text = highlight_string("<?php ".trim($block['text']), true);
+			$text = highlight_string("<?php ".trim($code), true);
 			$text = str_replace('&lt;?php', '', $text);
 			if (($pos = strpos($text, '&nbsp;')) !== false) {
 				$text = substr($text, 0, $pos) . substr($text, $pos + 6);
@@ -46,93 +111,93 @@ class ApiMarkdown extends Markdown
 		// remove <code><span style="color: #000000">\n and </span>tags added by php
 		$text = substr(trim($text), 36, -16);
 
-		$code = '<pre><code';
-		if (isset($block['language']))
-		{
-			if ($block['language'] !== 'php') {
-				return false;
-			}
-			$code .= ' class="language-'.$block['language'].'"';
-		}
-		$code .= '>'.$text.'</code></pre>'."\n";
-
-		$markup .= $code;
-		return true;
+		return $text;
 	}
 
-	public function init()
+	protected function inlineMarkers()
 	{
-		$this->registerBlockHander('code', [$this, 'highlight']);
-		$this->registerBlockHander('fenced', [$this, 'highlight']);
+		return array_merge(parent::inlineMarkers(), [
+			'[[' => 'parseApiLinks',
+		]);
+	}
 
-		$context = &$this->context;
-		// register marker for code links
-		$this->unregisterInlineMarkerHandler('[');
-		$this->registerInlineMarkerHandler('[[', function($text, &$markup) use (&$context) {
+	protected function parseApiLinks($text)
+	{
+		$context = $this->context;
 
-			if (preg_match('/^\[\[([\w\d\\\\\(\):$]+)(\|[^\]]*)?\]\]/', $text, $matches)) {
+		if (preg_match('/^\[\[([\w\d\\\\\(\):$]+)(\|[^\]]*)?\]\]/', $text, $matches)) {
 
-				$offset = strlen($matches[0]);
+			$offset = strlen($matches[0]);
 
-				$object = $matches[1];
-				$title = (empty($matches[2]) || $matches[2] == '|') ? null : substr($matches[2], 1);
+			$object = $matches[1];
+			$title = (empty($matches[2]) || $matches[2] == '|') ? null : substr($matches[2], 1);
 
-				if (($pos = strpos($object, '::')) !== false) {
-					$typeName = substr($object, 0, $pos);
-					$subjectName = substr($object, $pos + 2);
-					if ($context !== null) {
-						// Collection resolves relative types
-						$typeName = (new Collection([$typeName], $context->phpDocContext))->__toString();
-					}
-					$type = static::$renderer->context->getType($typeName);
-					if ($type === null) {
+			if (($pos = strpos($object, '::')) !== false) {
+				$typeName = substr($object, 0, $pos);
+				$subjectName = substr($object, $pos + 2);
+				if ($context !== null) {
+					// Collection resolves relative types
+					$typeName = (new Collection([$typeName], $context->phpDocContext))->__toString();
+				}
+				$type = static::$renderer->context->getType($typeName);
+				if ($type === null) {
+					static::$renderer->context->errors[] = [
+						'file' => ($context !== null) ? $context->sourceFile : null,
+						'message' => 'broken link to ' . $typeName . '::' . $subjectName . (($context !== null) ? ' in ' . $context->name : ''),
+					];
+					return [
+						'<span style="background: #f00;">' . $typeName . '::' . $subjectName . '</span>',
+						$offset
+					];
+				} else {
+					if (($subject = $type->findSubject($subjectName)) !== null) {
+						if ($title === null) {
+							$title = $type->name . '::' . $subject->name;
+							if ($subject instanceof MethodDoc) {
+								$title .= '()';
+							}
+						}
+						return [
+							static::$renderer->subjectLink($subject, $title),
+							$offset
+						];
+					} else {
 						static::$renderer->context->errors[] = [
 							'file' => ($context !== null) ? $context->sourceFile : null,
-							'message' => 'broken link to ' . $typeName . '::' . $subjectName . (($context !== null) ? ' in ' . $context->name : ''),
+							'message' => 'broken link to ' . $type->name . '::' . $subjectName . (($context !== null) ? ' in ' . $context->name : ''),
+						];
+						return [
+							'<span style="background: #ff0;">' . $type->name . '</span><span style="background: #f00;">::' . $subjectName . '</span>',
+							$offset
 						];
-						$markup .= '<span style="background: #f00;">' . $typeName . '::' . $subjectName . '</span>';
-					} else {
-						if (($subject = $type->findSubject($subjectName)) !== null) {
-							if ($title === null) {
-								$title = $type->name . '::' . $subject->name;
-								if ($subject instanceof MethodDoc) {
-									$title .= '()';
-								}
-							}
-							$markup .= static::$renderer->subjectLink($subject, $title);
-						} else {
-							static::$renderer->context->errors[] = [
-								'file' => ($context !== null) ? $context->sourceFile : null,
-								'message' => 'broken link to ' . $type->name . '::' . $subjectName . (($context !== null) ? ' in ' . $context->name : ''),
-							];
-							$markup .= '<span style="background: #ff0;">' . $type->name . '</span><span style="background: #f00;">::' . $subjectName . '</span>';
-						}
 					}
-					return $offset;
-				} elseif ($context !== null && ($subject = $context->findSubject($object)) !== null) {
-					$markup .= static::$renderer->subjectLink($subject, $title);
-					return $offset;
-				}
-				if ($context !== null) {
-					// Collection resolves relative types
-					$object = (new Collection([$object], $context->phpDocContext))->__toString();
 				}
-				if (($type = static::$renderer->context->getType($object)) !== null) {
-					$markup .= static::$renderer->typeLink($type, $title);
-					return $offset;
-				}
-				static::$renderer->context->errors[] = [
-					'file' => ($context !== null) ? $context->sourceFile : null,
-					'message' => 'broken link to ' . $object . (($context !== null) ? ' in ' . $context->name : ''),
+			} elseif ($context !== null && ($subject = $context->findSubject($object)) !== null) {
+				return [
+					static::$renderer->subjectLink($subject, $title),
+					$offset
 				];
-				$markup .= '<span style="background: #f00;">' . $object . '</span>';
-				return $offset;
-			} else {
-				$markup .= '[[';
-				return 2;
 			}
-		});
-		$this->registerInlineMarkerHandler('[', null);
+			if ($context !== null) {
+				// Collection resolves relative types
+				$object = (new Collection([$object], $context->phpDocContext))->__toString();
+			}
+			if (($type = static::$renderer->context->getType($object)) !== null) {
+				return [
+					static::$renderer->typeLink($type, $title),
+					$offset
+				];
+			}
+			static::$renderer->context->errors[] = [
+				'file' => ($context !== null) ? $context->sourceFile : null,
+				'message' => 'broken link to ' . $object . (($context !== null) ? ' in ' . $context->name : ''),
+			];
+			return [
+				'<span style="background: #f00;">' . $object . '</span>',
+				$offset
+			];
+		}
+		return ['[[', 2];
 	}
 
 	/**
@@ -140,23 +205,24 @@ class ApiMarkdown extends Markdown
 	 *
 	 * @param string $content
 	 * @param TypeDoc $context
+	 * @param bool $paragraph
 	 * @return string
 	 */
-	public static function process($content, $context = null, $line = false)
+	public static function process($content, $context = null, $paragraph = false)
 	{
-		if (static::$_instance === null) {
-			static::$_instance = new static;
+		if (!isset(Markdown::$flavors['api'])) {
+			Markdown::$flavors['api'] = new static;
 		}
 
 		if (is_string($context)) {
 			$context = static::$renderer->context->getType($context);
 		}
-		static::$_instance->context = $context;
+		Markdown::$flavors['api']->context = $context;
 
-		if ($line) {
-			return static::$_instance->parseLine($content);
+		if ($paragraph) {
+			return Markdown::processParagraph($content, 'api');
 		} else {
-			return static::$_instance->parse($content);
+			return Markdown::process($content, 'api');
 		}
 	}
 }
diff --git a/extensions/apidoc/helpers/Markdown.php b/extensions/apidoc/helpers/Markdown.php
deleted file mode 100644
index 7bd6b3c..0000000
--- a/extensions/apidoc/helpers/Markdown.php
+++ /dev/null
@@ -1,60 +0,0 @@
-<?php
-/**
- * @link http://www.yiiframework.com/
- * @copyright Copyright (c) 2008 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
-
-namespace yii\apidoc\helpers;
-
-use Parsedown;
-use yii\base\Component;
-
-/**
- * A Markdown helper with support for class reference links.
- *
- * @author Carsten Brandt <mail@cebe.cc>
- * @since 2.0
- */
-class Markdown extends Component
-{
-	private $_parseDown;
-
-	protected function getParseDown()
-	{
-		if ($this->_parseDown === null) {
-			$this->_parseDown = new ParseDown();
-		}
-		return $this->_parseDown;
-	}
-
-	public function parse($markdown)
-	{
-		return $this->getParseDown()->parse($markdown);
-	}
-
-	public function parseLine($markdown)
-	{
-		return $this->getParseDown()->parseLine($markdown);
-	}
-
-	public function registerBlockHander($blockName, $callback)
-	{
-		$this->getParseDown()->register_block_handler($blockName, $callback);
-	}
-
-	public function unregisterBlockHander($blockName)
-	{
-		$this->getParseDown()->remove_block_handler($blockName);
-	}
-
-	public function registerInlineMarkerHandler($marker, $callback)
-	{
-		$this->getParseDown()->add_span_marker($marker, $callback);
-	}
-
-	public function unregisterInlineMarkerHandler($marker)
-	{
-		$this->getParseDown()->remove_span_marker($marker);
-	}
-}
diff --git a/extensions/apidoc/templates/BaseRenderer.php b/extensions/apidoc/templates/BaseRenderer.php
index 960a33c..3bd3b98 100644
--- a/extensions/apidoc/templates/BaseRenderer.php
+++ b/extensions/apidoc/templates/BaseRenderer.php
@@ -31,7 +31,10 @@ abstract class BaseRenderer extends Component
 	 * @var Context the [[Context]] currently being rendered.
 	 */
 	public $context;
-
+	/**
+	 * @var array files for guide pages
+	 */
+	public $markDownFiles = [];
 
 	/**
 	 * Renders a given [[Context]].
@@ -49,7 +52,7 @@ abstract class BaseRenderer extends Component
 	 * @param Controller $controller the apidoc controller instance. Can be used to control output.
 	 * @return
 	 */
-	public abstract function renderMarkdownFiles($files, $controller);
+	public abstract function renderMarkdownFiles($controller);
 
 	/**
 	 * creates a link to a type (class, interface or trait)
diff --git a/extensions/apidoc/templates/bootstrap/Renderer.php b/extensions/apidoc/templates/bootstrap/Renderer.php
index b40f53a..488ae3a 100644
--- a/extensions/apidoc/templates/bootstrap/Renderer.php
+++ b/extensions/apidoc/templates/bootstrap/Renderer.php
@@ -134,11 +134,11 @@ class Renderer extends \yii\apidoc\templates\html\Renderer
 	/**
 	 * Renders a given [[Context]].
 	 *
-	 * @param Context $context the api documentation context to render.
 	 * @param Controller $controller the apidoc controller instance. Can be used to control output.
 	 */
-	public function renderMarkdownFiles($files, $controller)
+	public function renderMarkdownFiles($controller)
 	{
+		$files = $this->markDownFiles;
 		$dir = Yii::getAlias($this->targetDir);
 		if (!is_dir($dir)) {
 			mkdir($dir, 0777, true);
@@ -174,7 +174,7 @@ class Renderer extends \yii\apidoc\templates\html\Renderer
 				];
 				$output = $this->getView()->renderFile($this->guideLayout, $params, $this);
 			}
-			$fileName = 'guide_' . str_replace('.md', '.html', basename($file));
+			$fileName = $this->generateGuideFileName($file);
 			file_put_contents($dir . '/' . $fileName, $output);
 			Console::updateProgress(++$done, $fileCount);
 		}
@@ -183,6 +183,21 @@ class Renderer extends \yii\apidoc\templates\html\Renderer
 		$controller->stdout('done.' . PHP_EOL, Console::FG_GREEN);
 	}
 
+	protected function generateGuideFileName($file)
+	{
+		return 'guide_' . basename($file, '.md') . '.html';
+	}
+
+	public function getGuideReferences()
+	{
+		$refs = [];
+		foreach($this->markDownFiles as $file) {
+			$refName = 'guide-' . basename($file, '.md');
+			$refs[$refName] = ['url' => $this->generateGuideFileName($file)];
+		}
+		return $refs;
+	}
+
 	protected function fixMarkdownLinks($content)
 	{
 		$content = preg_replace('/href\s*=\s*"([^"\/]+)\.md(#.*)?"/i', 'href="guide_\1.html\2"', $content);
diff --git a/extensions/apidoc/templates/bootstrap/layouts/main.php b/extensions/apidoc/templates/bootstrap/layouts/main.php
index 1e953a6..801ee65 100644
--- a/extensions/apidoc/templates/bootstrap/layouts/main.php
+++ b/extensions/apidoc/templates/bootstrap/layouts/main.php
@@ -31,7 +31,7 @@ $this->beginPage();
 		'options' => [
 			'class' => 'navbar-inverse navbar-fixed-top',
 		],
-		'padded' => false,
+		'renderInnerContainer' => false,
 		'view' => $this,
 	]);
 	$extItems = [];
diff --git a/extensions/apidoc/templates/html/views/methodDetails.php b/extensions/apidoc/templates/html/views/methodDetails.php
index 3adde72..a3f9ff9 100644
--- a/extensions/apidoc/templates/html/views/methodDetails.php
+++ b/extensions/apidoc/templates/html/views/methodDetails.php
@@ -23,6 +23,7 @@ ArrayHelper::multisort($methods, 'name');
 	<div class="detailHeader h3" id="<?= $method->name . '()-detail' ?>">
 		<?= $method->name ?>()
 		<span class="detailHeaderTag small">
+			<?= $method->visibility ?>
 			method
 			<?php if (!empty($method->since)): ?>
 				(available since version <?php echo $method->since; ?>)
diff --git a/extensions/apidoc/templates/html/views/propertyDetails.php b/extensions/apidoc/templates/html/views/propertyDetails.php
index 15ab7a9..4b45351 100644
--- a/extensions/apidoc/templates/html/views/propertyDetails.php
+++ b/extensions/apidoc/templates/html/views/propertyDetails.php
@@ -23,9 +23,10 @@ ArrayHelper::multisort($properties, 'name');
 	<div class="detailHeader h3" id="<?= $property->name.'-detail' ?>">
 		<?php echo $property->name; ?>
 		<span class="detailHeaderTag small">
-			property
+			<?= $property->visibility ?>
 			<?php if($property->getIsReadOnly()) echo ' <em>read-only</em> '; ?>
 			<?php if($property->getIsWriteOnly()) echo ' <em>write-only</em> '; ?>
+			property
 			<?php if(!empty($property->since)): ?>
 				(available since version <?php echo $property->since; ?>)
 			<?php endif; ?>
diff --git a/extensions/authclient/OAuth1.php b/extensions/authclient/OAuth1.php
index 35bb74c..b797927 100644
--- a/extensions/authclient/OAuth1.php
+++ b/extensions/authclient/OAuth1.php
@@ -236,7 +236,8 @@ class OAuth1 extends BaseOAuth
 	{
 		$params = $_GET;
 		unset($params['oauth_token']);
-		return Yii::$app->getUrlManager()->createAbsoluteUrl(Yii::$app->controller->getRoute(), $params);
+		$params[0] = Yii::$app->controller->getRoute();
+		return Yii::$app->getUrlManager()->createAbsoluteUrl($params);
 	}
 
 	/**
diff --git a/extensions/authclient/OAuth2.php b/extensions/authclient/OAuth2.php
index 09146b8..7933eb7 100644
--- a/extensions/authclient/OAuth2.php
+++ b/extensions/authclient/OAuth2.php
@@ -168,7 +168,8 @@ class OAuth2 extends BaseOAuth
 	{
 		$params = $_GET;
 		unset($params['code']);
-		return Yii::$app->getUrlManager()->createAbsoluteUrl(Yii::$app->controller->getRoute(), $params);
+		$params[0] = Yii::$app->controller->getRoute();
+		return Yii::$app->getUrlManager()->createAbsoluteUrl($params);
 	}
 
 	/**
diff --git a/extensions/authclient/OpenId.php b/extensions/authclient/OpenId.php
index e410d4c..3aba0fd 100644
--- a/extensions/authclient/OpenId.php
+++ b/extensions/authclient/OpenId.php
@@ -199,7 +199,8 @@ class OpenId extends BaseClient implements ClientInterface
 				unset($params[$name]);
 			}
 		}
-		$url = Yii::$app->getUrlManager()->createUrl(Yii::$app->requestedRoute, $params);
+		$params[0] = Yii::$app->requestedRoute;
+		$url = Yii::$app->getUrlManager()->createUrl($params);
 		return $this->getTrustRoot() . $url;
 	}
 
diff --git a/extensions/authclient/README.md b/extensions/authclient/README.md
index 3484028..8413f2d 100644
--- a/extensions/authclient/README.md
+++ b/extensions/authclient/README.md
@@ -1,7 +1,7 @@
 AuthClient Extension for Yii 2
 ==============================
 
-This extension adds [OpenID](http://openid.net/), [OAuth](http://oauth.net/) and [OAuth2](http://oauth.net/2/) consumers for the Yii 2 framework.
+This extension adds [OpenID](http://openid.net/), [OAuth](http://oauth.net/) and [OAuth2](http://oauth.net/2/) consumers for the Yii framework 2.0.
 
 
 Installation
diff --git a/extensions/authclient/clients/LinkedIn.php b/extensions/authclient/clients/LinkedIn.php
index b4e8fdc..3752457 100644
--- a/extensions/authclient/clients/LinkedIn.php
+++ b/extensions/authclient/clients/LinkedIn.php
@@ -139,7 +139,8 @@ class LinkedIn extends OAuth2
 		$params = $_GET;
 		unset($params['code']);
 		unset($params['state']);
-		return Yii::$app->getUrlManager()->createAbsoluteUrl(Yii::$app->controller->getRoute(), $params);
+		$params[0] = Yii::$app->controller->getRoute();
+		return Yii::$app->getUrlManager()->createAbsoluteUrl($params);
 	}
 
 	/**
diff --git a/extensions/bootstrap/CHANGELOG.md b/extensions/bootstrap/CHANGELOG.md
index 7eef113..5fced15 100644
--- a/extensions/bootstrap/CHANGELOG.md
+++ b/extensions/bootstrap/CHANGELOG.md
@@ -11,6 +11,7 @@ Yii Framework 2 bootstrap extension Change Log
 - Enh #1562: Added `yii\bootstrap\Tabs::linkOptions` (kartik-v)
 - Enh #1601: Added support for tagName and encodeLabel parameters in ButtonDropdown (omnilight)
 - Enh #1881: Improved `yii\bootstrap\NavBar` with `containerOptions`, `innerContainerOptions` and `renderInnerContainer` (creocoder)
+- Enh #2425: Tabs widget now selects first tab if no active tab is specified (samdark)
 - Chg #1459: Update Collapse to use bootstrap 3 classes (tonydspaniard)
 - Chg #1820: Update Progress to use bootstrap 3 markup (samdark)
 
diff --git a/extensions/bootstrap/Nav.php b/extensions/bootstrap/Nav.php
index 5ccca05..9d4afc0 100644
--- a/extensions/bootstrap/Nav.php
+++ b/extensions/bootstrap/Nav.php
@@ -148,7 +148,7 @@ class Nav extends Widget
 		$label = $this->encodeLabels ? Html::encode($item['label']) : $item['label'];
 		$options = ArrayHelper::getValue($item, 'options', []);
 		$items = ArrayHelper::getValue($item, 'items');
-		$url = Html::url(ArrayHelper::getValue($item, 'url', '#'));
+		$url = ArrayHelper::getValue($item, 'url', '#');
 		$linkOptions = ArrayHelper::getValue($item, 'linkOptions', []);
 
 		if (isset($item['active'])) {
diff --git a/extensions/bootstrap/Tabs.php b/extensions/bootstrap/Tabs.php
index a001edd..2192746 100644
--- a/extensions/bootstrap/Tabs.php
+++ b/extensions/bootstrap/Tabs.php
@@ -124,6 +124,11 @@ class Tabs extends Widget
 	{
 		$headers = [];
 		$panes = [];
+
+		if (!$this->hasActiveTab() && !empty($this->items)) {
+			$this->items[0]['active'] = true;
+		}
+
 		foreach ($this->items as $n => $item) {
 			if (!isset($item['label'])) {
 				throw new InvalidConfigException("The 'label' option is required.");
@@ -168,6 +173,19 @@ class Tabs extends Widget
 	}
 
 	/**
+	 * @return boolean if there's active tab defined
+	 */
+	protected function hasActiveTab()
+	{
+		foreach ($this->items as $item) {
+			if (isset($item['active']) && $item['active']===true) {
+				return true;
+			}
+		}
+		return false;
+	}
+
+	/**
 	 * Normalizes dropdown item options by removing tab specific keys `content` and `contentOptions`, and also
 	 * configure `panes` accordingly.
 	 * @param array $items the dropdown items configuration.
diff --git a/extensions/codeception/BasePage.php b/extensions/codeception/BasePage.php
index 12fd7ad..145e0c3 100644
--- a/extensions/codeception/BasePage.php
+++ b/extensions/codeception/BasePage.php
@@ -47,12 +47,10 @@ abstract class BasePage extends Component
 	public function getUrl($params = [])
 	{
 		if (is_string($this->route)) {
-			return Yii::$app->getUrlManager()->createUrl($this->route, $params);
+			$params[0] = $this->route;
+			return Yii::$app->getUrlManager()->createUrl($params);
 		} elseif (is_array($this->route) && isset($this->route[0])) {
-			$route = $this->route[0];
-			$ps = $this->route;
-			unset($this->route[0]);
-			return Yii::$app->getUrlManager()->createUrl($route, array_merge($ps, $params));
+			return Yii::$app->getUrlManager()->createUrl(array_merge($this->route, $params));
 		} else {
 			throw new InvalidConfigException('The "route" property must be set.');
 		}
diff --git a/extensions/debug/Module.php b/extensions/debug/Module.php
index c475387..b6d6230 100644
--- a/extensions/debug/Module.php
+++ b/extensions/debug/Module.php
@@ -124,7 +124,7 @@ class Module extends \yii\base\Module
 		if (!$this->checkAccess() || Yii::$app->getRequest()->getIsAjax()) {
 			return;
 		}
-		$url = Yii::$app->getUrlManager()->createUrl($this->id . '/default/toolbar', [
+		$url = Yii::$app->getUrlManager()->createUrl([$this->id . '/default/toolbar',
 			'tag' => $this->logTarget->tag,
 		]);
 		echo '<div id="yii-debug-toolbar" data-url="' . $url . '" style="display:none"></div>';
diff --git a/extensions/debug/Panel.php b/extensions/debug/Panel.php
index 48efec0..0f9e673 100644
--- a/extensions/debug/Panel.php
+++ b/extensions/debug/Panel.php
@@ -88,7 +88,7 @@ class Panel extends Component
 	 */
 	public function getUrl()
 	{
-		return Yii::$app->getUrlManager()->createUrl($this->module->id . '/default/view', [
+		return Yii::$app->getUrlManager()->createUrl([$this->module->id . '/default/view',
 			'panel' => $this->id,
 			'tag' => $this->tag,
 		]);
diff --git a/extensions/debug/assets/main.css b/extensions/debug/assets/main.css
index c881ca8..332b130 100644
--- a/extensions/debug/assets/main.css
+++ b/extensions/debug/assets/main.css
@@ -34,6 +34,10 @@ td, th {
 	word-break: break-all;
 }
 
+.config-php-info-table td.v {
+	word-break: break-all;
+}
+
 .detail-grid-view th {
     white-space: nowrap;
 }
diff --git a/extensions/debug/models/search/Mail.php b/extensions/debug/models/search/Mail.php
index 59d310c..464436a 100644
--- a/extensions/debug/models/search/Mail.php
+++ b/extensions/debug/models/search/Mail.php
@@ -7,6 +7,9 @@ use yii\debug\components\search\Filter;
 
 /**
  * Mail represents the model behind the search form about current send emails.
+ *
+ * @author Mark Jebri <mark.github@yandex.ru>
+ * @since 2.0
  */
 class Mail extends Base
 {
diff --git a/extensions/debug/panels/ConfigPanel.php b/extensions/debug/panels/ConfigPanel.php
index a527d9b..4ac76b4 100644
--- a/extensions/debug/panels/ConfigPanel.php
+++ b/extensions/debug/panels/ConfigPanel.php
@@ -13,6 +13,8 @@ use yii\debug\Panel;
 /**
  * Debugger panel that collects and displays application configuration and environment.
  *
+ * @property array $extensions This property is read-only.
+ *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
  */
@@ -68,7 +70,7 @@ class ConfigPanel extends Panel
 		$pinfo = ob_get_contents();
 		ob_end_clean();
 		$phpinfo = preg_replace('%^.*<body>(.*)</body>.*$%ms', '$1', $pinfo);
-		$phpinfo = str_replace('<table ', '<table class="table table-condensed table-bordered table-striped table-hover"', $phpinfo);
+		$phpinfo = str_replace('<table ', '<table class="table table-condensed table-bordered table-striped table-hover config-php-info-table"', $phpinfo);
 
 		return $phpinfo;
 	}
diff --git a/extensions/debug/panels/DbPanel.php b/extensions/debug/panels/DbPanel.php
index 95de124..48ca756 100644
--- a/extensions/debug/panels/DbPanel.php
+++ b/extensions/debug/panels/DbPanel.php
@@ -15,6 +15,8 @@ use yii\debug\models\search\Db;
 /**
  * Debugger panel that collects and displays database queries performed.
  *
+ * @property array $profileLogs This property is read-only.
+ *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
  */
diff --git a/extensions/debug/panels/MailPanel.php b/extensions/debug/panels/MailPanel.php
index 55bb3f3..19a85ca 100644
--- a/extensions/debug/panels/MailPanel.php
+++ b/extensions/debug/panels/MailPanel.php
@@ -11,6 +11,11 @@ use yii\helpers\FileHelper;
 
 /**
  * Debugger panel that collects and displays the generated emails.
+ *
+ * @property array $messages Messages. This property is read-only.
+ *
+ * @author Mark Jebri <mark.github@yandex.ru>
+ * @since 2.0
  */
 class MailPanel extends Panel
 {
diff --git a/extensions/elasticsearch/ActiveQuery.php b/extensions/elasticsearch/ActiveQuery.php
index d2f7e74..c7d23ad 100644
--- a/extensions/elasticsearch/ActiveQuery.php
+++ b/extensions/elasticsearch/ActiveQuery.php
@@ -9,11 +9,18 @@ namespace yii\elasticsearch;
 
 use yii\db\ActiveQueryInterface;
 use yii\db\ActiveQueryTrait;
+use yii\db\ActiveRelationTrait;
 
 /**
  * ActiveQuery represents a [[Query]] associated with an [[ActiveRecord]] class.
  *
+ * An ActiveQuery can be a normal query or be used in a relational context.
+ *
  * ActiveQuery instances are usually created by [[ActiveRecord::find()]].
+ * Relational queries are created by [[ActiveRecord::hasOne()]] and [[ActiveRecord::hasMany()]].
+ *
+ * Normal Query
+ * ------------
  *
  * ActiveQuery mainly provides the following methods to retrieve the query results:
  *
@@ -35,12 +42,32 @@ use yii\db\ActiveQueryTrait;
  *
  * These options can be configured using methods of the same name. For example:
  *
- * ~~~
+ * ```php
  * $customers = Customer::find()->with('orders')->asArray()->all();
- * ~~~
+ * ```
+ * > NOTE: elasticsearch limits the number of records returned to 10 records by default.
+ * > If you expect to get more records you should specify limit explicitly.
+ *
+ * Relational query
+ * ----------------
+ *
+ * In relational context ActiveQuery represents a relation between two Active Record classes.
+ *
+ * Relational ActiveQuery instances are usually created by calling [[ActiveRecord::hasOne()]] and
+ * [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
+ * a getter method which calls one of the above methods and returns the created ActiveQuery object.
+ *
+ * A relation is specified by [[link]] which represents the association between columns
+ * of different tables; and the multiplicity of the relation is indicated by [[multiple]].
  *
- * NOTE: elasticsearch limits the number of records returned to 10 records by default.
- * If you expect to get more records you should specify limit explicitly.
+ * If a relation involves a pivot table, it may be specified by [[via()]].
+ * This methods may only be called in a relational context. Same is true for [[inverseOf()]], which
+ * marks a relation as inverse of another relation.
+ *
+ * > NOTE: elasticsearch limits the number of records returned by any query to 10 records by default.
+ * > If you expect to get more records you should specify limit explicitly in relation definition.
+ * > This is also important for relations that use [[via()]] so that if via records are limited to 10
+ * > the relations records can also not be more than 10.
  *
  * @author Carsten Brandt <mail@cebe.cc>
  * @since 2.0
@@ -48,6 +75,7 @@ use yii\db\ActiveQueryTrait;
 class ActiveQuery extends Query implements ActiveQueryInterface
 {
 	use ActiveQueryTrait;
+	use ActiveRelationTrait;
 
 	/**
 	 * Creates a DB command that can be used to execute this query.
@@ -57,6 +85,26 @@ class ActiveQuery extends Query implements ActiveQueryInterface
 	 */
 	public function createCommand($db = null)
 	{
+		if ($this->primaryModel !== null) {
+			// lazy loading
+			if (is_array($this->via)) {
+				// via relation
+				/** @var ActiveQuery $viaQuery */
+				list($viaName, $viaQuery) = $this->via;
+				if ($viaQuery->multiple) {
+					$viaModels = $viaQuery->all();
+					$this->primaryModel->populateRelation($viaName, $viaModels);
+				} else {
+					$model = $viaQuery->one();
+					$this->primaryModel->populateRelation($viaName, $model);
+					$viaModels = $model === null ? [] : [$model];
+				}
+				$this->filterByModels($viaModels);
+			} else {
+				$this->filterByModels([$this->primaryModel]);
+			}
+		}
+
 		/** @var ActiveRecord $modelClass */
 		$modelClass = $this->modelClass;
 		if ($db === null) {
diff --git a/extensions/elasticsearch/ActiveRecord.php b/extensions/elasticsearch/ActiveRecord.php
index abbca0d..4fdff54 100644
--- a/extensions/elasticsearch/ActiveRecord.php
+++ b/extensions/elasticsearch/ActiveRecord.php
@@ -138,19 +138,32 @@ class ActiveRecord extends BaseActiveRecord
 	// TODO add percolate functionality http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/search-percolate.html
 
 	/**
-	 * @inheritdoc
-	 */
-	public static function createQuery()
-	{
-		return new ActiveQuery(['modelClass' => get_called_class()]);
-	}
-
-	/**
-	 * @inheritdoc
+	 * Creates an [[ActiveQuery]] instance.
+	 *
+	 * This method is called by [[find()]], [[findBySql()]] to start a SELECT query but also
+	 * by [[hasOne()]] and [[hasMany()]] to create a relational query.
+	 * You may override this method to return a customized query (e.g. `CustomerQuery` specified
+	 * written for querying `Customer` purpose.)
+	 *
+	 * You may also define default conditions that should apply to all queries unless overridden:
+	 *
+	 * ```php
+	 * public static function createQuery($config = [])
+	 * {
+	 *     return parent::createQuery($config)->where(['deleted' => false]);
+	 * }
+	 * ```
+	 *
+	 * Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
+	 * default condition. Using [[Query::where()]] will override the default condition.
+	 *
+	 * @param array $config the configuration passed to the ActiveQuery class.
+	 * @return ActiveQuery the newly created [[ActiveQuery]] instance.
 	 */
-	public static function createRelation($config = [])
+	public static function createQuery($config = [])
 	{
-		return new ActiveRelation($config);
+		$config['modelClass'] = get_called_class();
+		return new ActiveQuery($config);
 	}
 
 	// TODO implement copy and move as pk change is not possible
diff --git a/extensions/elasticsearch/ActiveRelation.php b/extensions/elasticsearch/ActiveRelation.php
deleted file mode 100644
index 5b97f69..0000000
--- a/extensions/elasticsearch/ActiveRelation.php
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-/**
- * @link http://www.yiiframework.com/
- * @copyright Copyright (c) 2008 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
-
-namespace yii\elasticsearch;
-
-use yii\db\ActiveRelationInterface;
-use yii\db\ActiveRelationTrait;
-
-/**
- * ActiveRelation represents a relation between two Active Record classes.
- *
- * ActiveRelation instances are usually created by calling [[ActiveRecord::hasOne()]] and
- * [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
- * a getter method which calls one of the above methods and returns the created ActiveRelation object.
- *
- * A relation is specified by [[link]] which represents the association between columns
- * of different tables; and the multiplicity of the relation is indicated by [[multiple]].
- *
- * If a relation involves a pivot table, it may be specified by [[via()]] method.
- *
- * NOTE: elasticsearch limits the number of records returned by any query to 10 records by default.
- * If you expect to get more records you should specify limit explicitly in relation definition.
- * This is also important for relations that use [[via()]] so that if via records are limited to 10
- * the relations records can also not be more than 10.
- *
- * @author Carsten Brandt <mail@cebe.cc>
- * @since 2.0
- */
-class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
-{
-	use ActiveRelationTrait;
-
-	/**
-	 * Creates a DB command that can be used to execute this query.
-	 * @param Connection $db the DB connection used to create the DB command.
-	 * If null, the DB connection returned by [[modelClass]] will be used.
-	 * @return Command the created DB command instance.
-	 */
-	public function createCommand($db = null)
-	{
-		if ($this->primaryModel !== null) {
-			// lazy loading
-			if (is_array($this->via)) {
-				// via relation
-				/** @var ActiveRelation $viaQuery */
-				list($viaName, $viaQuery) = $this->via;
-				if ($viaQuery->multiple) {
-					$viaModels = $viaQuery->all();
-					$this->primaryModel->populateRelation($viaName, $viaModels);
-				} else {
-					$model = $viaQuery->one();
-					$this->primaryModel->populateRelation($viaName, $model);
-					$viaModels = $model === null ? [] : [$model];
-				}
-				$this->filterByModels($viaModels);
-			} else {
-				$this->filterByModels([$this->primaryModel]);
-			}
-		}
-		return parent::createCommand($db);
-	}
-}
diff --git a/extensions/elasticsearch/CHANGELOG.md b/extensions/elasticsearch/CHANGELOG.md
index f95acae..0ee84eb 100644
--- a/extensions/elasticsearch/CHANGELOG.md
+++ b/extensions/elasticsearch/CHANGELOG.md
@@ -11,6 +11,9 @@ Yii Framework 2 elasticsearch extension Change Log
 - Enh #1765: Added support for primary key path mapping, pk can now be part of the attributes when mapping is defined (cebe)
 - Chg #1765: Changed handling of ActiveRecord primary keys, removed getId(), use getPrimaryKey() instead (cebe)
 - Chg #2281: Renamed `ActiveRecord::create()` to `populateRecord()` and changed signature. This method will not call instantiate() anymore (cebe)
+- Chg #2146: Removed `ActiveRelation` class and moved the functionality to `ActiveQuery`.
+             All relational queries are now directly served by `ActiveQuery` allowing to use
+             custom scopes in relations (cebe)
 
 2.0.0 alpha, December 1, 2013
 -----------------------------
diff --git a/extensions/elasticsearch/DebugAction.php b/extensions/elasticsearch/DebugAction.php
index d4ddd41..503a54f 100644
--- a/extensions/elasticsearch/DebugAction.php
+++ b/extensions/elasticsearch/DebugAction.php
@@ -1,19 +1,26 @@
 <?php
 /**
- * @author Carsten Brandt <mail@cebe.cc>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
  */
 
 namespace yii\elasticsearch;
 
-
 use yii\base\Action;
 use yii\base\NotSupportedException;
 use yii\debug\Panel;
 use yii\helpers\ArrayHelper;
 use yii\web\HttpException;
-use Yii;
 use yii\web\Response;
+use Yii;
 
+/**
+ * Debug Action is used by [[DebugPanel]] to perform elasticsearch queries using ajax.
+ *
+ * @author Carsten Brandt <mail@cebe.cc>
+ * @since 2.0
+ */
 class DebugAction extends Action
 {
 	/**
@@ -73,4 +80,4 @@ class DebugAction extends Action
 			'result' => $result,
 		];
 	}
-} 
\ No newline at end of file
+}
diff --git a/extensions/elasticsearch/README.md b/extensions/elasticsearch/README.md
index c95d74c..63df0eb 100644
--- a/extensions/elasticsearch/README.md
+++ b/extensions/elasticsearch/README.md
@@ -84,7 +84,7 @@ class Customer extends \yii\elasticsearch\ActiveRecord
     }
 
     /**
-     * @return ActiveRelation defines a relation to the Order record (can be in other database, e.g. redis or sql)
+     * @return ActiveQuery defines a relation to the Order record (can be in other database, e.g. redis or sql)
      */
     public function getOrders()
     {
diff --git a/extensions/gii/CHANGELOG.md b/extensions/gii/CHANGELOG.md
index dde6f17..8a7632d 100644
--- a/extensions/gii/CHANGELOG.md
+++ b/extensions/gii/CHANGELOG.md
@@ -12,6 +12,7 @@ Yii Framework 2 gii extension Change Log
 - Enh #1818: Do not display checkbox column if all rows are empty (johonunu)
 - Enh #1897: diff markup is now copy paste friendly (samdark)
 - Enh #2327: better visual representation of changed files, added header and refresh button to diff modal (thiagotalma)
+- Enh #2491: Added support for using the same base class name of search model and data model in Gii (qiangxue)
 
 2.0.0 alpha, December 1, 2013
 -----------------------------
diff --git a/extensions/gii/controllers/DefaultController.php b/extensions/gii/controllers/DefaultController.php
index b2bd1d6..169bc18 100644
--- a/extensions/gii/controllers/DefaultController.php
+++ b/extensions/gii/controllers/DefaultController.php
@@ -110,8 +110,9 @@ class DefaultController extends Controller
 	/**
 	 * @inheritdoc
 	 */
-	public function createUrl($route, $params = [])
+	public function createUrl($params)
 	{
+		$params = (array)$params;
 		if (!isset($params['id']) && $this->generator !== null) {
 			foreach ($this->module->generators as $id => $generator) {
 				if ($generator === $this->generator) {
@@ -120,7 +121,7 @@ class DefaultController extends Controller
 				}
 			}
 		}
-		return parent::createUrl($route, $params);
+		return parent::createUrl($params);
 	}
 
 	/**
@@ -139,7 +140,8 @@ class DefaultController extends Controller
 			}
 		}
 		$params['name'] = $name;
-		return parent::createUrl('action', $params);
+		$params[0] = 'action';
+		return parent::createUrl($params);
 	}
 
 	/**
diff --git a/extensions/gii/generators/crud/Generator.php b/extensions/gii/generators/crud/Generator.php
index b5741c6..8a8cb1c 100644
--- a/extensions/gii/generators/crud/Generator.php
+++ b/extensions/gii/generators/crud/Generator.php
@@ -17,8 +17,11 @@ use yii\web\Controller;
 
 /**
  *
+ * @property array $columnNames Model column names. This property is read-only.
  * @property string $controllerID The controller ID (without the module ID prefix). This property is
  * read-only.
+ * @property array $searchAttributes Searchable attributes. This property is read-only.
+ * @property boolean|\yii\db\TableSchema $tableSchema This property is read-only.
  * @property string $viewPath The action view file path. This property is read-only.
  *
  * @author Qiang Xue <qiang.xue@gmail.com>
diff --git a/extensions/gii/generators/crud/templates/controller.php b/extensions/gii/generators/crud/templates/controller.php
index 97de222..3b8c10c 100644
--- a/extensions/gii/generators/crud/templates/controller.php
+++ b/extensions/gii/generators/crud/templates/controller.php
@@ -14,7 +14,7 @@ $controllerClass = StringHelper::basename($generator->controllerClass);
 $modelClass = StringHelper::basename($generator->modelClass);
 $searchModelClass = StringHelper::basename($generator->searchModelClass);
 if ($modelClass === $searchModelClass) {
-	$searchModelAlias = $searchModelClass.'Search';
+	$searchModelAlias = $searchModelClass . 'Search';
 }
 
 /** @var ActiveRecordInterface $class */
@@ -31,7 +31,7 @@ namespace <?= StringHelper::dirname(ltrim($generator->controllerClass, '\\')) ?>
 
 use Yii;
 use <?= ltrim($generator->modelClass, '\\') ?>;
-use <?= ltrim($generator->searchModelClass, '\\') ?><?php if (isset($searchModelAlias)):?> as <?= $searchModelAlias ?><?php endif ?>;
+use <?= ltrim($generator->searchModelClass, '\\') . (isset($searchModelAlias) ? " as $searchModelAlias" : "") ?>;
 use <?= ltrim($generator->baseControllerClass, '\\') ?>;
 use yii\web\NotFoundHttpException;
 use yii\web\VerbFilter;
diff --git a/extensions/gii/generators/crud/templates/search.php b/extensions/gii/generators/crud/templates/search.php
index bc55c60..986250e 100644
--- a/extensions/gii/generators/crud/templates/search.php
+++ b/extensions/gii/generators/crud/templates/search.php
@@ -11,6 +11,9 @@ use yii\helpers\StringHelper;
 
 $modelClass = StringHelper::basename($generator->modelClass);
 $searchModelClass = StringHelper::basename($generator->searchModelClass);
+if ($modelClass === $searchModelClass) {
+	$modelAlias = $modelClass . 'Model';
+}
 $rules = $generator->generateSearchRules();
 $labels = $generator->generateSearchLabels();
 $searchAttributes = $generator->getSearchAttributes();
@@ -23,10 +26,10 @@ namespace <?= StringHelper::dirname(ltrim($generator->searchModelClass, '\\')) ?
 
 use yii\base\Model;
 use yii\data\ActiveDataProvider;
-use <?= ltrim($generator->modelClass, '\\') ?>;
+use <?= ltrim($generator->modelClass, '\\') . (isset($modelAlias) ? " as $modelAlias" : "") ?>;
 
 /**
- * <?= $searchModelClass ?> represents the model behind the search form about <?= $modelClass ?>.
+ * <?= $searchModelClass ?> represents the model behind the search form about `<?= $generator->modelClass ?>`.
  */
 class <?= $searchModelClass ?> extends Model
 {
@@ -53,7 +56,7 @@ class <?= $searchModelClass ?> extends Model
 
 	public function search($params)
 	{
-		$query = <?= $modelClass ?>::find();
+		$query = <?= isset($modelAlias) ? $modelAlias : $modelClass ?>::find();
 		$dataProvider = new ActiveDataProvider([
 			'query' => $query,
 		]);
@@ -69,7 +72,13 @@ class <?= $searchModelClass ?> extends Model
 
 	protected function addCondition($query, $attribute, $partialMatch = false)
 	{
-		$value = $this->$attribute;
+		if (($pos = strrpos($attribute, '.')) !== false) {
+			$modelAttribute = substr($attribute, $pos + 1);
+		} else {
+			$modelAttribute = $attribute;
+		}
+
+		$value = $this->$modelAttribute;
 		if (trim($value) === '') {
 			return;
 		}
diff --git a/extensions/gii/generators/crud/templates/views/view.php b/extensions/gii/generators/crud/templates/views/view.php
index 9e74aff..989dab5 100644
--- a/extensions/gii/generators/crud/templates/views/view.php
+++ b/extensions/gii/generators/crud/templates/views/view.php
@@ -33,8 +33,10 @@ $this->params['breadcrumbs'][] = $this->title;
 		<?= "<?= " ?>Html::a('Update', ['update', <?= $urlParams ?>], ['class' => 'btn btn-primary']) ?>
 		<?= "<?php " ?>echo Html::a('Delete', ['delete', <?= $urlParams ?>], [
 			'class' => 'btn btn-danger',
-			'data-confirm' => Yii::t('app', 'Are you sure to delete this item?'),
-			'data-method' => 'post',
+			'data' => [
+				'confirm' => Yii::t('app', 'Are you sure to delete this item?'),
+				'method' => 'post',
+			],
 		]); ?>
 	</p>
 
diff --git a/extensions/gii/generators/model/templates/model.php b/extensions/gii/generators/model/templates/model.php
index dcd1461..49dfcde 100644
--- a/extensions/gii/generators/model/templates/model.php
+++ b/extensions/gii/generators/model/templates/model.php
@@ -62,7 +62,7 @@ class <?= $className ?> extends <?= '\\' . ltrim($generator->baseClass, '\\') . 
 <?php foreach ($relations as $name => $relation): ?>
 
 	/**
-	 * @return \yii\db\ActiveRelation
+	 * @return \yii\db\ActiveQuery
 	 */
 	public function get<?= $name ?>()
 	{
diff --git a/extensions/jui/CHANGELOG.md b/extensions/jui/CHANGELOG.md
index b31c34e..55c5e33 100644
--- a/extensions/jui/CHANGELOG.md
+++ b/extensions/jui/CHANGELOG.md
@@ -4,7 +4,8 @@ Yii Framework 2 jui extension Change Log
 2.0.0 beta under development
 ----------------------------
 
-- Bug #1550: fixed the issue that JUI input widgets did not property input IDs.
+- Bug #1550: fixed the issue that JUI input widgets did not property input IDs. (qiangxue)
+- Bug #2514: Jui sortable clientEvents were not working because of wrong naming assumptions. (cebe)
 
 2.0.0 alpha, December 1, 2013
 -----------------------------
diff --git a/extensions/jui/Slider.php b/extensions/jui/Slider.php
index b1efcce..5d80894 100644
--- a/extensions/jui/Slider.php
+++ b/extensions/jui/Slider.php
@@ -26,6 +26,9 @@ use yii\helpers\Html;
  */
 class Slider extends Widget
 {
+	/**
+	 * @inheritDoc
+	 */
 	protected $clientEventMap = [
 		'change' => 'slidechange',
 		'create' => 'slidecreate',
diff --git a/extensions/jui/SliderInput.php b/extensions/jui/SliderInput.php
index 20599cf..d5153e2 100644
--- a/extensions/jui/SliderInput.php
+++ b/extensions/jui/SliderInput.php
@@ -43,6 +43,9 @@ use yii\helpers\Html;
  */
 class SliderInput extends InputWidget
 {
+	/**
+	 * @inheritDoc
+	 */
 	protected $clientEventMap = [
 		'change' => 'slidechange',
 		'create' => 'slidecreate',
diff --git a/extensions/jui/Sortable.php b/extensions/jui/Sortable.php
index 6209cb6..3945b8c 100644
--- a/extensions/jui/Sortable.php
+++ b/extensions/jui/Sortable.php
@@ -29,7 +29,7 @@ use yii\helpers\Html;
  *     'options' => ['tag' => 'ul'],
  *     'itemOptions' => ['tag' => 'li'],
  *     'clientOptions' => ['cursor' => 'move'],
- * ));
+ * ]);
  * ```
  *
  * @see http://api.jqueryui.com/sortable/
@@ -65,6 +65,25 @@ class Sortable extends Widget
 	 */
 	public $itemOptions = [];
 
+	/**
+	 * @inheritDoc
+	 */
+	protected $clientEventMap = [
+		'activate' => 'sortactivate',
+		'beforeStop' => 'sortbeforestop',
+		'change' => 'sortchange',
+		'create' => 'sortcreate',
+		'deactivate' => 'sortdeactivate',
+		'out' => 'sortout',
+		'over' => 'sortover',
+		'receive' => 'sortreceive',
+		'remove' => 'sortremove',
+		'sort' => 'sort',
+		'start' => 'sortstart',
+		'stop' => 'sortstop',
+		'update' => 'sortupdate',
+	];
+
 
 	/**
 	 * Renders the widget.
diff --git a/extensions/jui/Spinner.php b/extensions/jui/Spinner.php
index b88c70b..c57c59a 100644
--- a/extensions/jui/Spinner.php
+++ b/extensions/jui/Spinner.php
@@ -37,6 +37,9 @@ use yii\helpers\Html;
  */
 class Spinner extends InputWidget
 {
+	/**
+	 * @inheritDoc
+	 */
 	protected $clientEventMap = [
 		'spin' => 'spin',
 	];
diff --git a/extensions/jui/Widget.php b/extensions/jui/Widget.php
index 62bbd16..9bcabc7 100644
--- a/extensions/jui/Widget.php
+++ b/extensions/jui/Widget.php
@@ -38,6 +38,16 @@ class Widget extends \yii\base\Widget
 	 * Please refer to the corresponding jQuery UI widget Web page for possible events.
 	 * For example, [this page](http://api.jqueryui.com/accordion/) shows
 	 * how to use the "Accordion" widget and the supported events (e.g. "create").
+	 * Keys are the event names and values are javascript code that is passed to the `.on()` function
+	 * as the event handler.
+	 *
+	 * For example you could write the following in your widget configuration:
+	 *
+	 * ```php
+	 * 'clientEvents' => [
+	 *     'change' => 'function() { alert('event "change" occured.'); }'
+	 * ],
+	 * ```
 	 */
 	public $clientEvents = [];
 
@@ -47,6 +57,7 @@ class Widget extends \yii\base\Widget
 	 */
 	protected $clientEventMap = [];
 
+
 	/**
 	 * Initializes the widget.
 	 * If you override this method, make sure you call the parent implementation first.
@@ -99,7 +110,7 @@ class Widget extends \yii\base\Widget
 				if (isset($this->clientEventMap[$event])) {
 					$eventName = $this->clientEventMap[$event];
 				} else {
-					$eventName = $name.$event;
+					$eventName = strtolower($name . $event);
 				}
 				$js[] = "jQuery('#$id').on('$eventName', $handler);";
 			}
diff --git a/extensions/mongodb/ActiveQuery.php b/extensions/mongodb/ActiveQuery.php
index cf7db82..351f942 100644
--- a/extensions/mongodb/ActiveQuery.php
+++ b/extensions/mongodb/ActiveQuery.php
@@ -9,10 +9,19 @@ namespace yii\mongodb;
 
 use yii\db\ActiveQueryInterface;
 use yii\db\ActiveQueryTrait;
+use yii\db\ActiveRelationTrait;
 
 /**
  * ActiveQuery represents a Mongo query associated with an Active Record class.
  *
+ * An ActiveQuery can be a normal query or be used in a relational context.
+ *
+ * ActiveQuery instances are usually created by [[ActiveRecord::find()]].
+ * Relational queries are created by [[ActiveRecord::hasOne()]] and [[ActiveRecord::hasMany()]].
+ *
+ * Normal Query
+ * ------------
+ *
  * ActiveQuery instances are usually created by [[ActiveRecord::find()]].
  *
  * Because ActiveQuery extends from [[Query]], one can use query methods, such as [[where()]],
@@ -25,9 +34,25 @@ use yii\db\ActiveQueryTrait;
  *
  * These options can be configured using methods of the same name. For example:
  *
- * ~~~
+ * ```php
  * $customers = Customer::find()->with('orders')->asArray()->all();
- * ~~~
+ * ```
+ *
+ * Relational query
+ * ----------------
+ *
+ * In relational context ActiveQuery represents a relation between two Active Record classes.
+ *
+ * Relational ActiveQuery instances are usually created by calling [[ActiveRecord::hasOne()]] and
+ * [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
+ * a getter method which calls one of the above methods and returns the created ActiveQuery object.
+ *
+ * A relation is specified by [[link]] which represents the association between columns
+ * of different tables; and the multiplicity of the relation is indicated by [[multiple]].
+ *
+ * If a relation involves a pivot table, it may be specified by [[via()]].
+ * This methods may only be called in a relational context. Same is true for [[inverseOf()]], which
+ * marks a relation as inverse of another relation.
  *
  * @property Collection $collection Collection instance. This property is read-only.
  *
@@ -37,6 +62,38 @@ use yii\db\ActiveQueryTrait;
 class ActiveQuery extends Query implements ActiveQueryInterface
 {
 	use ActiveQueryTrait;
+	use ActiveRelationTrait;
+
+	/**
+	 * @inheritdoc
+	 */
+	protected function buildCursor($db = null)
+	{
+		if ($this->primaryModel !== null) {
+			// lazy loading
+			if ($this->via instanceof self) {
+				// via pivot collection
+				$viaModels = $this->via->findPivotRows([$this->primaryModel]);
+				$this->filterByModels($viaModels);
+			} elseif (is_array($this->via)) {
+				// via relation
+				/** @var ActiveQuery $viaQuery */
+				list($viaName, $viaQuery) = $this->via;
+				if ($viaQuery->multiple) {
+					$viaModels = $viaQuery->all();
+					$this->primaryModel->populateRelation($viaName, $viaModels);
+				} else {
+					$model = $viaQuery->one();
+					$this->primaryModel->populateRelation($viaName, $model);
+					$viaModels = $model === null ? [] : [$model];
+				}
+				$this->filterByModels($viaModels);
+			} else {
+				$this->filterByModels([$this->primaryModel]);
+			}
+		}
+		return parent::buildCursor($db);
+	}
 
 	/**
 	 * Executes query and returns all results as an array.
diff --git a/extensions/mongodb/ActiveRecord.php b/extensions/mongodb/ActiveRecord.php
index ce8ac81..fd48d0e 100644
--- a/extensions/mongodb/ActiveRecord.php
+++ b/extensions/mongodb/ActiveRecord.php
@@ -37,7 +37,7 @@ abstract class ActiveRecord extends BaseActiveRecord
 	 * For example, to change the status to be 1 for all customers whose status is 2:
 	 *
 	 * ~~~
-	 * Customer::updateAll(['status' => 1], ['status' = 2]);
+	 * Customer::updateAll(['status' => 1], ['status' => 2]);
 	 * ~~~
 	 *
 	 * @param array $attributes attribute values (name-value pairs) to be saved into the collection
@@ -78,7 +78,7 @@ abstract class ActiveRecord extends BaseActiveRecord
 	 * For example, to delete all customers whose status is 3:
 	 *
 	 * ~~~
-	 * Customer::deleteAll('status = 3');
+	 * Customer::deleteAll(['status' => 3]);
 	 * ~~~
 	 *
 	 * @param array $condition description of the objects to delete.
@@ -88,23 +88,36 @@ abstract class ActiveRecord extends BaseActiveRecord
 	 */
 	public static function deleteAll($condition = [], $options = [])
 	{
-		$options['w'] = 1;
-		if (!array_key_exists('multiple', $options)) {
-			$options['multiple'] = true;
-		}
 		return static::getCollection()->remove($condition, $options);
 	}
 
 	/**
 	 * Creates an [[ActiveQuery]] instance.
-	 * This method is called by [[find()]] to start a "find" command.
+	 *
+	 * This method is called by [[find()]], [[findBySql()]] to start a SELECT query but also
+	 * by [[hasOne()]] and [[hasMany()]] to create a relational query.
 	 * You may override this method to return a customized query (e.g. `CustomerQuery` specified
 	 * written for querying `Customer` purpose.)
+	 *
+	 * You may also define default conditions that should apply to all queries unless overridden:
+	 *
+	 * ```php
+	 * public static function createQuery($config = [])
+	 * {
+	 *     return parent::createQuery($config)->where(['deleted' => false]);
+	 * }
+	 * ```
+	 *
+	 * Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
+	 * default condition. Using [[Query::where()]] will override the default condition.
+	 *
+	 * @param array $config the configuration passed to the ActiveQuery class.
 	 * @return ActiveQuery the newly created [[ActiveQuery]] instance.
 	 */
-	public static function createQuery()
+	public static function createQuery($config = [])
 	{
-		return new ActiveQuery(['modelClass' => get_called_class()]);
+		$config['modelClass'] = get_called_class();
+		return new ActiveQuery($config);
 	}
 
 	/**
@@ -146,18 +159,6 @@ abstract class ActiveRecord extends BaseActiveRecord
 	}
 
 	/**
-	 * Creates an [[ActiveRelation]] instance.
-	 * This method is called by [[hasOne()]] and [[hasMany()]] to create a relation instance.
-	 * You may override this method to return a customized relation.
-	 * @param array $config the configuration passed to the ActiveRelation class.
-	 * @return ActiveRelation the newly created [[ActiveRelation]] instance.
-	 */
-	public static function createRelation($config = [])
-	{
-		return new ActiveRelation($config);
-	}
-
-	/**
 	 * Returns the list of all attribute names of the model.
 	 * This method must be overridden by child classes to define available attributes.
 	 * Note: primary key attribute "_id" should be always present in returned array.
@@ -237,8 +238,7 @@ abstract class ActiveRecord extends BaseActiveRecord
 				$values[$key] = isset($currentAttributes[$key]) ? $currentAttributes[$key] : null;
 			}
 		}
-		$collection = static::getCollection();
-		$newId = $collection->insert($values);
+		$newId = static::getCollection()->insert($values);
 		$this->setAttribute('_id', $newId);
 		foreach ($values as $name => $value) {
 			$this->setOldAttribute($name, $value);
@@ -348,4 +348,4 @@ abstract class ActiveRecord extends BaseActiveRecord
 		}
 		return $this->collectionName() === $record->collectionName() && (string)$this->getPrimaryKey() === (string)$record->getPrimaryKey();
 	}
-}
+}
\ No newline at end of file
diff --git a/extensions/mongodb/ActiveRelation.php b/extensions/mongodb/ActiveRelation.php
deleted file mode 100644
index 785b701..0000000
--- a/extensions/mongodb/ActiveRelation.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-/**
- * @link http://www.yiiframework.com/
- * @copyright Copyright (c) 2008 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
-
-namespace yii\mongodb;
-
-use yii\db\ActiveRelationInterface;
-use yii\db\ActiveRelationTrait;
-
-/**
- * ActiveRelation represents a relation to Mongo Active Record class.
- *
- * @author Paul Klimov <klimov.paul@gmail.com>
- * @since 2.0
- */
-class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
-{
-	use ActiveRelationTrait;
-
-	/**
-	 * @inheritdoc
-	 */
-	protected function buildCursor($db = null)
-	{
-		if ($this->primaryModel !== null) {
-			// lazy loading
-			if ($this->via instanceof self) {
-				// via pivot collection
-				$viaModels = $this->via->findPivotRows([$this->primaryModel]);
-				$this->filterByModels($viaModels);
-			} elseif (is_array($this->via)) {
-				// via relation
-				/** @var ActiveRelation $viaQuery */
-				list($viaName, $viaQuery) = $this->via;
-				if ($viaQuery->multiple) {
-					$viaModels = $viaQuery->all();
-					$this->primaryModel->populateRelation($viaName, $viaModels);
-				} else {
-					$model = $viaQuery->one();
-					$this->primaryModel->populateRelation($viaName, $model);
-					$viaModels = $model === null ? [] : [$model];
-				}
-				$this->filterByModels($viaModels);
-			} else {
-				$this->filterByModels([$this->primaryModel]);
-			}
-		}
-		return parent::buildCursor($db);
-	}
-}
\ No newline at end of file
diff --git a/extensions/mongodb/Collection.php b/extensions/mongodb/Collection.php
index 21411e7..38144bc 100644
--- a/extensions/mongodb/Collection.php
+++ b/extensions/mongodb/Collection.php
@@ -260,6 +260,18 @@ class Collection extends Object
 	}
 
 	/**
+	 * Returns a a single document.
+	 * @param array $condition query condition
+	 * @param array $fields fields to be selected
+	 * @return array|null the single document. Null is returned if the query results in nothing.
+	 * @see http://www.php.net/manual/en/mongocollection.findone.php
+	 */
+	public function findOne($condition = [], $fields = [])
+	{
+		return $this->mongoCollection->findOne($this->buildCondition($condition), $fields);
+	}
+
+	/**
 	 * Inserts new data into collection.
 	 * @param array|object $data data to be inserted.
 	 * @param array $options list of options in format: optionName => optionValue.
@@ -372,11 +384,12 @@ class Collection extends Object
 	 * @param array $options list of options in format: optionName => optionValue.
 	 * @return integer|boolean number of updated documents or whether operation was successful.
 	 * @throws Exception on failure.
+	 * @see http://www.php.net/manual/en/mongocollection.remove.php
 	 */
 	public function remove($condition = [], $options = [])
 	{
 		$condition = $this->buildCondition($condition);
-		$options = array_merge(['w' => 1, 'multiple' => true], $options);
+		$options = array_merge(['w' => 1, 'justOne' => false], $options);
 		$token = $this->composeLogToken('remove', [$condition, $options]);
 		Yii::info($token, __METHOD__);
 		try {
@@ -522,7 +535,7 @@ class Collection extends Object
 	 * Argument will be automatically cast to [[\MongoCode]].
 	 * @param string|array $out output collection name. It could be a string for simple output
 	 * ('outputCollection'), or an array for parametrized output (['merge' => 'outputCollection']).
-     * You can pass ['inline' => true] to fetch the result at once without temporary collection usage.
+	 * You can pass ['inline' => true] to fetch the result at once without temporary collection usage.
 	 * @param array $condition criteria for including a document in the aggregation.
 	 * @param array $options additional optional parameters to the mapReduce command. Valid options include:
 	 *  - sort - array - key to sort the input documents. The sort key must be in an existing index for this collection.
diff --git a/extensions/mongodb/Connection.php b/extensions/mongodb/Connection.php
index ab8e30f..e79bfcb 100644
--- a/extensions/mongodb/Connection.php
+++ b/extensions/mongodb/Connection.php
@@ -73,6 +73,11 @@ use Yii;
 class Connection extends Component
 {
 	/**
+	 * @event Event an event that is triggered after a DB connection is established
+	 */
+	const EVENT_AFTER_OPEN = 'afterOpen';
+
+	/**
 	 * @var string host:port
 	 *
 	 * Correct syntax is:
@@ -233,6 +238,7 @@ class Connection extends Component
 					$options['db'] = $this->defaultDatabaseName;
 				}
 				$this->mongoClient = new \MongoClient($this->dsn, $options);
+				$this->initConnection();
 				Yii::endProfile($token, __METHOD__);
 			} catch (\Exception $e) {
 				Yii::endProfile($token, __METHOD__);
@@ -253,4 +259,14 @@ class Connection extends Component
 			$this->_databases = [];
 		}
 	}
+
+	/**
+	 * Initializes the DB connection.
+	 * This method is invoked right after the DB connection is established.
+	 * The default implementation triggers an [[EVENT_AFTER_OPEN]] event.
+	 */
+	protected function initConnection()
+	{
+		$this->trigger(self::EVENT_AFTER_OPEN);
+	}
 }
\ No newline at end of file
diff --git a/extensions/mongodb/README.md b/extensions/mongodb/README.md
index 705258f..fe33088 100644
--- a/extensions/mongodb/README.md
+++ b/extensions/mongodb/README.md
@@ -90,7 +90,7 @@ In these cases, ensure you have converted [[\MongoId]] into the string:
 
 ```php
 /** @var yii\web\View $this */
-echo $this->createUrl('item/update', ['id' => (string)$row['_id']]);
+echo $this->createUrl(['item/update', 'id' => (string)$row['_id']]);
 ```
 
 While building condition, values for the key '_id' will be automatically cast to [[\MongoId]] instance,
diff --git a/extensions/mongodb/Session.php b/extensions/mongodb/Session.php
index f2ce8b3..9b630cc 100644
--- a/extensions/mongodb/Session.php
+++ b/extensions/mongodb/Session.php
@@ -91,28 +91,22 @@ class Session extends \yii\web\Session
 		parent::regenerateID(false);
 		$newID = session_id();
 
-		$query = new Query;
-		$row = $query->from($this->sessionCollection)
-			->where(['id' => $oldID])
-			->one($this->db);
-		if ($row !== false) {
+		$collection = $this->db->getCollection($this->sessionCollection);
+		$row = $collection->findOne(['id' => $oldID]);
+		if ($row !== null) {
 			if ($deleteOldSession) {
-				$this->db->getCollection($this->sessionCollection)
-					->update(['_id' => $row['_id']], ['id' => $newID]);
+				$collection->update(['id' => $oldID], ['id' => $newID]);
 			} else {
 				unset($row['_id']);
 				$row['id'] = $newID;
-				$this->db->getCollection($this->sessionCollection)
-					->insert($row);
+				$collection->insert($row);
 			}
 		} else {
 			// shouldn't reach here normally
-			$this->db->getCollection($this->sessionCollection)
-				->insert([
-					'id' => $newID,
-					'expire' => time() + $this->getTimeout(),
-					'data' => '',
-				]);
+			$collection->insert([
+				'id' => $newID,
+				'expire' => time() + $this->getTimeout()
+			]);
 		}
 	}
 
@@ -124,15 +118,15 @@ class Session extends \yii\web\Session
 	 */
 	public function readSession($id)
 	{
-		$query = new Query;
-		$row = $query->select(['data'])
-			->from($this->sessionCollection)
-			->where([
+		$collection = $this->db->getCollection($this->sessionCollection);
+		$doc = $collection->findOne(
+			[
+				'id' => $id,
 				'expire' => ['$gt' => time()],
-				'id' => $id
-			])
-			->one($this->db);
-		return $row === false ? '' : $row['data'];
+			],
+			['data' => 1, '_id' => 0]
+		);
+		return isset($doc['data']) ? $doc['data'] : '';
 	}
 
 	/**
@@ -147,23 +141,15 @@ class Session extends \yii\web\Session
 		// exception must be caught in session write handler
 		// http://us.php.net/manual/en/function.session-set-save-handler.php
 		try {
-			$expire = time() + $this->getTimeout();
-			$query = new Query;
-			$exists = $query->select(['id'])
-				->from($this->sessionCollection)
-				->where(['id' => $id])
-				->one($this->db);
-			if ($exists === false) {
-				$this->db->getCollection($this->sessionCollection)
-					->insert([
-						'id' => $id,
-						'data' => $data,
-						'expire' => $expire,
-					]);
-			} else {
-				$this->db->getCollection($this->sessionCollection)
-					->update(['id' => $id], ['data' => $data, 'expire' => $expire]);
-			}
+			$this->db->getCollection($this->sessionCollection)->update(
+				['id' => $id],
+				[
+					'id' => $id,
+					'data' => $data,
+					'expire' => time() + $this->getTimeout(),
+				],
+				['upsert' => true]
+			);
 		} catch (\Exception $e) {
 			if (YII_DEBUG) {
 				echo $e->getMessage();
@@ -182,8 +168,10 @@ class Session extends \yii\web\Session
 	 */
 	public function destroySession($id)
 	{
-		$this->db->getCollection($this->sessionCollection)
-			->remove(['id' => $id]);
+		$this->db->getCollection($this->sessionCollection)->remove(
+			['id' => $id],
+			['justOne' => true]
+		);
 		return true;
 	}
 
@@ -196,9 +184,7 @@ class Session extends \yii\web\Session
 	public function gcSession($maxLifetime)
 	{
 		$this->db->getCollection($this->sessionCollection)
-			->remove([
-				'expire' => ['$lt' => time()]
-			]);
+			->remove(['expire' => ['$lt' => time()]]);
 		return true;
 	}
 }
\ No newline at end of file
diff --git a/extensions/mongodb/file/ActiveQuery.php b/extensions/mongodb/file/ActiveQuery.php
index f89d201..79bcd2f 100644
--- a/extensions/mongodb/file/ActiveQuery.php
+++ b/extensions/mongodb/file/ActiveQuery.php
@@ -9,6 +9,7 @@ namespace yii\mongodb\file;
 
 use yii\db\ActiveQueryInterface;
 use yii\db\ActiveQueryTrait;
+use yii\db\ActiveRelationTrait;
 
 /**
  * ActiveQuery represents a Mongo query associated with an file Active Record class.
@@ -37,6 +38,7 @@ use yii\db\ActiveQueryTrait;
 class ActiveQuery extends Query implements ActiveQueryInterface
 {
 	use ActiveQueryTrait;
+	use ActiveRelationTrait;
 
 	/**
 	 * Executes query and returns all results as an array.
diff --git a/extensions/mongodb/file/ActiveRecord.php b/extensions/mongodb/file/ActiveRecord.php
index 4e7f3dd..887b1c3 100644
--- a/extensions/mongodb/file/ActiveRecord.php
+++ b/extensions/mongodb/file/ActiveRecord.php
@@ -46,14 +46,31 @@ abstract class ActiveRecord extends \yii\mongodb\ActiveRecord
 {
 	/**
 	 * Creates an [[ActiveQuery]] instance.
-	 * This method is called by [[find()]] to start a "find" command.
-	 * You may override this method to return a customized query (e.g. `ImageFileQuery` specified
-	 * written for querying `ImageFile` purpose.)
+	 *
+	 * This method is called by [[find()]], [[findBySql()]] to start a SELECT query but also
+	 * by [[hasOne()]] and [[hasMany()]] to create a relational query.
+	 * You may override this method to return a customized query (e.g. `CustomerQuery` specified
+	 * written for querying `Customer` purpose.)
+	 *
+	 * You may also define default conditions that should apply to all queries unless overridden:
+	 *
+	 * ```php
+	 * public static function createQuery($config = [])
+	 * {
+	 *     return parent::createQuery($config)->where(['deleted' => false]);
+	 * }
+	 * ```
+	 *
+	 * Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
+	 * default condition. Using [[Query::where()]] will override the default condition.
+	 *
+	 * @param array $config the configuration passed to the ActiveQuery class.
 	 * @return ActiveQuery the newly created [[ActiveQuery]] instance.
 	 */
-	public static function createQuery()
+	public static function createQuery($config = [])
 	{
-		return new ActiveQuery(['modelClass' => get_called_class()]);
+		$config['modelClass'] = get_called_class();
+		return new ActiveQuery($config);
 	}
 
 	/**
@@ -66,18 +83,6 @@ abstract class ActiveRecord extends \yii\mongodb\ActiveRecord
 	}
 
 	/**
-	 * Creates an [[ActiveRelation]] instance.
-	 * This method is called by [[hasOne()]] and [[hasMany()]] to create a relation instance.
-	 * You may override this method to return a customized relation.
-	 * @param array $config the configuration passed to the ActiveRelation class.
-	 * @return ActiveRelation the newly created [[ActiveRelation]] instance.
-	 */
-	public static function createRelation($config = [])
-	{
-		return new ActiveRelation($config);
-	}
-
-	/**
 	 * Returns the list of all attribute names of the model.
 	 * This method could be overridden by child classes to define available attributes.
 	 * Note: all attributes defined in base Active Record class should be always present
diff --git a/extensions/mongodb/file/ActiveRelation.php b/extensions/mongodb/file/ActiveRelation.php
deleted file mode 100644
index ea1f7e6..0000000
--- a/extensions/mongodb/file/ActiveRelation.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-/**
- * @link http://www.yiiframework.com/
- * @copyright Copyright (c) 2008 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
-
-namespace yii\mongodb\file;
-
-use yii\db\ActiveRelationInterface;
-use yii\db\ActiveRelationTrait;
-
-/**
- * ActiveRelation represents a relation to Mongo GridFS Active Record class.
- *
- * @author Paul Klimov <klimov.paul@gmail.com>
- * @since 2.0
- */
-class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
-{
-	use ActiveRelationTrait;
-}
\ No newline at end of file
diff --git a/extensions/redis/ActiveQuery.php b/extensions/redis/ActiveQuery.php
index b5f8beb..df69be1 100644
--- a/extensions/redis/ActiveQuery.php
+++ b/extensions/redis/ActiveQuery.php
@@ -6,17 +6,24 @@
  */
 
 namespace yii\redis;
+
 use yii\base\InvalidParamException;
 use yii\base\NotSupportedException;
 use yii\db\ActiveQueryInterface;
 use yii\db\ActiveQueryTrait;
+use yii\db\ActiveRelationTrait;
 use yii\db\QueryTrait;
 
 /**
  * ActiveQuery represents a query associated with an Active Record class.
  *
- * ActiveQuery instances are usually created by [[ActiveRecord::find()]]
- * and [[ActiveRecord::count()]].
+ * An ActiveQuery can be a normal query or be used in a relational context.
+ *
+ * ActiveQuery instances are usually created by [[ActiveRecord::find()]].
+ * Relational queries are created by [[ActiveRecord::hasOne()]] and [[ActiveRecord::hasMany()]].
+ *
+ * Normal Query
+ * ------------
  *
  * ActiveQuery mainly provides the following methods to retrieve the query results:
  *
@@ -40,9 +47,25 @@ use yii\db\QueryTrait;
  *
  * These options can be configured using methods of the same name. For example:
  *
- * ~~~
+ * ```php
  * $customers = Customer::find()->with('orders')->asArray()->all();
- * ~~~
+ * ```
+ *
+ * Relational query
+ * ----------------
+ *
+ * In relational context ActiveQuery represents a relation between two Active Record classes.
+ *
+ * Relational ActiveQuery instances are usually created by calling [[ActiveRecord::hasOne()]] and
+ * [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
+ * a getter method which calls one of the above methods and returns the created ActiveQuery object.
+ *
+ * A relation is specified by [[link]] which represents the association between columns
+ * of different tables; and the multiplicity of the relation is indicated by [[multiple]].
+ *
+ * If a relation involves a pivot table, it may be specified by [[via()]].
+ * This methods may only be called in a relational context. Same is true for [[inverseOf()]], which
+ * marks a relation as inverse of another relation.
  *
  * @author Carsten Brandt <mail@cebe.cc>
  * @since 2.0
@@ -51,6 +74,7 @@ class ActiveQuery extends \yii\base\Component implements ActiveQueryInterface
 {
 	use QueryTrait;
 	use ActiveQueryTrait;
+	use ActiveRelationTrait;
 
 	/**
 	 * Executes the query and returns all results as an array.
@@ -252,6 +276,30 @@ class ActiveQuery extends \yii\base\Component implements ActiveQueryInterface
 	 */
 	protected function executeScript($db, $type, $columnName = null)
 	{
+		if ($this->primaryModel !== null) {
+			// lazy loading
+			if ($this->via instanceof self) {
+				// via pivot table
+				$viaModels = $this->via->findPivotRows([$this->primaryModel]);
+				$this->filterByModels($viaModels);
+			} elseif (is_array($this->via)) {
+				// via relation
+				/** @var ActiveQuery $viaQuery */
+				list($viaName, $viaQuery) = $this->via;
+				if ($viaQuery->multiple) {
+					$viaModels = $viaQuery->all();
+					$this->primaryModel->populateRelation($viaName, $viaModels);
+				} else {
+					$model = $viaQuery->one();
+					$this->primaryModel->populateRelation($viaName, $model);
+					$viaModels = $model === null ? [] : [$model];
+				}
+				$this->filterByModels($viaModels);
+			} else {
+				$this->filterByModels([$this->primaryModel]);
+			}
+		}
+
 		if (!empty($this->orderBy)) {
 			throw new NotSupportedException('orderBy is currently not supported by redis ActiveRecord.');
 		}
diff --git a/extensions/redis/ActiveRecord.php b/extensions/redis/ActiveRecord.php
index 944a80d..34c9c05 100644
--- a/extensions/redis/ActiveRecord.php
+++ b/extensions/redis/ActiveRecord.php
@@ -49,19 +49,32 @@ class ActiveRecord extends BaseActiveRecord
 	}
 
 	/**
-	 * @inheritdoc
-	 */
-	public static function createQuery()
-	{
-		return new ActiveQuery(['modelClass' => get_called_class()]);
-	}
-
-	/**
-	 * @inheritdoc
+	 * Creates an [[ActiveQuery]] instance.
+	 *
+	 * This method is called by [[find()]], [[findBySql()]] to start a SELECT query but also
+	 * by [[hasOne()]] and [[hasMany()]] to create a relational query.
+	 * You may override this method to return a customized query (e.g. `CustomerQuery` specified
+	 * written for querying `Customer` purpose.)
+	 *
+	 * You may also define default conditions that should apply to all queries unless overridden:
+	 *
+	 * ```php
+	 * public static function createQuery($config= [])
+	 * {
+	 *     return parent::createQuery($config)->where(['deleted' => false]);
+	 * }
+	 * ```
+	 *
+	 * Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
+	 * default condition. Using [[Query::where()]] will override the default condition.
+	 *
+	 * @param array $config the configuration passed to the ActiveQuery class.
+	 * @return ActiveQuery the newly created [[ActiveQuery]] instance.
 	 */
-	public static function createRelation($config = [])
+	public static function createQuery($config = [])
 	{
-		return new ActiveRelation($config);
+		$config['modelClass'] = get_called_class();
+		return new ActiveQuery($config);
 	}
 
 	/**
diff --git a/extensions/redis/ActiveRelation.php b/extensions/redis/ActiveRelation.php
deleted file mode 100644
index b2f5cea..0000000
--- a/extensions/redis/ActiveRelation.php
+++ /dev/null
@@ -1,67 +0,0 @@
-<?php
-/**
- * @link http://www.yiiframework.com/
- * @copyright Copyright (c) 2008 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
-
-namespace yii\redis;
-
-use yii\db\ActiveRelationInterface;
-use yii\db\ActiveRelationTrait;
-
-/**
- * ActiveRelation represents a relation between two Active Record classes.
- *
- * ActiveRelation instances are usually created by calling [[ActiveRecord::hasOne()]] and
- * [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
- * a getter method which calls one of the above methods and returns the created ActiveRelation object.
- *
- * A relation is specified by [[link]] which represents the association between columns
- * of different tables; and the multiplicity of the relation is indicated by [[multiple]].
- *
- * If a relation involves a pivot table, it may be specified by [[via()]] or [[viaTable()]] method.
- *
- * @author Carsten Brandt <mail@cebe.cc>
- * @since 2.0
- */
-class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
-{
-	use ActiveRelationTrait;
-
-	/**
-	 * Executes a script created by [[LuaScriptBuilder]]
-	 * @param Connection $db the database connection used to execute the query.
-	 * If this parameter is not given, the `db` application component will be used.
-	 * @param string $type the type of the script to generate
-	 * @param null $column
-	 * @return array|bool|null|string
-	 */
-	protected function executeScript($db, $type, $column=null)
-	{
-		if ($this->primaryModel !== null) {
-			// lazy loading
-			if ($this->via instanceof self) {
-				// via pivot table
-				$viaModels = $this->via->findPivotRows([$this->primaryModel]);
-				$this->filterByModels($viaModels);
-			} elseif (is_array($this->via)) {
-				// via relation
-				/** @var ActiveRelation $viaQuery */
-				list($viaName, $viaQuery) = $this->via;
-				if ($viaQuery->multiple) {
-					$viaModels = $viaQuery->all();
-					$this->primaryModel->populateRelation($viaName, $viaModels);
-				} else {
-					$model = $viaQuery->one();
-					$this->primaryModel->populateRelation($viaName, $model);
-					$viaModels = $model === null ? [] : [$model];
-				}
-				$this->filterByModels($viaModels);
-			} else {
-				$this->filterByModels([$this->primaryModel]);
-			}
-		}
-		return parent::executeScript($db, $type, $column);
-	}
-}
diff --git a/extensions/redis/CHANGELOG.md b/extensions/redis/CHANGELOG.md
index f6741f0..8c88fe5 100644
--- a/extensions/redis/CHANGELOG.md
+++ b/extensions/redis/CHANGELOG.md
@@ -7,6 +7,9 @@ Yii Framework 2 redis extension Change Log
 - Bug #1993: afterFind event in AR is now called after relations have been populated (cebe, creocoder)
 - Enh #1773: keyPrefix property of Session and Cache is not restricted to alnum characters anymore (cebe)
 - Chg #2281: Renamed `ActiveRecord::create()` to `populateRecord()` and changed signature. This method will not call instantiate() anymore (cebe)
+- Chg #2146: Removed `ActiveRelation` class and moved the functionality to `ActiveQuery`.
+             All relational queries are now directly served by `ActiveQuery` allowing to use
+             custom scopes in relations (cebe)
 
 2.0.0 alpha, December 1, 2013
 -----------------------------
diff --git a/extensions/redis/README.md b/extensions/redis/README.md
index 99e8998..eff0a3f 100644
--- a/extensions/redis/README.md
+++ b/extensions/redis/README.md
@@ -148,7 +148,7 @@ class Customer extends \yii\redis\ActiveRecord
 	}
 
 	/**
-	 * @return ActiveRelation defines a relation to the Order record (can be in other database, e.g. elasticsearch or sql)
+	 * @return ActiveQuery defines a relation to the Order record (can be in other database, e.g. elasticsearch or sql)
 	 */
 	public function getOrders()
 	{
diff --git a/extensions/smarty/ViewRenderer.php b/extensions/smarty/ViewRenderer.php
index 2cfb216..ce4e462 100644
--- a/extensions/smarty/ViewRenderer.php
+++ b/extensions/smarty/ViewRenderer.php
@@ -88,7 +88,7 @@ class ViewRenderer extends BaseViewRenderer
 	public function render($view, $file, $params)
 	{
 		/** @var \Smarty_Internal_Template $template */
-		$template = $this->smarty->createTemplate($file, null, null, $params, true);
+		$template = $this->smarty->createTemplate($file, null, null, empty($params) ? null : $params, true);
 
 		$template->assign('app', \Yii::$app);
 		$template->assign('this', $view);
diff --git a/extensions/sphinx/ActiveQuery.php b/extensions/sphinx/ActiveQuery.php
index b74846c..db56090 100644
--- a/extensions/sphinx/ActiveQuery.php
+++ b/extensions/sphinx/ActiveQuery.php
@@ -10,11 +10,18 @@ namespace yii\sphinx;
 use yii\base\InvalidCallException;
 use yii\db\ActiveQueryInterface;
 use yii\db\ActiveQueryTrait;
+use yii\db\ActiveRelationTrait;
 
 /**
  * ActiveQuery represents a Sphinx query associated with an Active Record class.
  *
+ * An ActiveQuery can be a normal query or be used in a relational context.
+ *
  * ActiveQuery instances are usually created by [[ActiveRecord::find()]] and [[ActiveRecord::findBySql()]].
+ * Relational queries are created by [[ActiveRecord::hasOne()]] and [[ActiveRecord::hasMany()]].
+ *
+ * Normal Query
+ * ------------
  *
  * Because ActiveQuery extends from [[Query]], one can use query methods, such as [[where()]],
  * [[orderBy()]] to customize the query options.
@@ -54,12 +61,29 @@ use yii\db\ActiveQueryTrait;
  * $articles = Article::find()->with('source')->snippetByModel()->all();
  * ~~~
  *
+ * Relational query
+ * ----------------
+ *
+ * In relational context ActiveQuery represents a relation between two Active Record classes.
+ *
+ * Relational ActiveQuery instances are usually created by calling [[ActiveRecord::hasOne()]] and
+ * [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
+ * a getter method which calls one of the above methods and returns the created ActiveQuery object.
+ *
+ * A relation is specified by [[link]] which represents the association between columns
+ * of different tables; and the multiplicity of the relation is indicated by [[multiple]].
+ *
+ * If a relation involves a pivot table, it may be specified by [[via()]].
+ * This methods may only be called in a relational context. Same is true for [[inverseOf()]], which
+ * marks a relation as inverse of another relation.
+ *
  * @author Paul Klimov <klimov.paul@gmail.com>
  * @since 2.0
  */
 class ActiveQuery extends Query implements ActiveQueryInterface
 {
 	use ActiveQueryTrait;
+	use ActiveRelationTrait;
 
 	/**
 	 * @var string the SQL statement to be executed for retrieving AR records.
@@ -165,6 +189,30 @@ class ActiveQuery extends Query implements ActiveQueryInterface
 	 */
 	public function createCommand($db = null)
 	{
+		if ($this->primaryModel !== null) {
+			// lazy loading a relational query
+			if ($this->via instanceof self) {
+				// via pivot index
+				$viaModels = $this->via->findPivotRows([$this->primaryModel]);
+				$this->filterByModels($viaModels);
+			} elseif (is_array($this->via)) {
+				// via relation
+				/** @var ActiveQuery $viaQuery */
+				list($viaName, $viaQuery) = $this->via;
+				if ($viaQuery->multiple) {
+					$viaModels = $viaQuery->all();
+					$this->primaryModel->populateRelation($viaName, $viaModels);
+				} else {
+					$model = $viaQuery->one();
+					$this->primaryModel->populateRelation($viaName, $model);
+					$viaModels = $model === null ? [] : [$model];
+				}
+				$this->filterByModels($viaModels);
+			} else {
+				$this->filterByModels([$this->primaryModel]);
+			}
+		}
+
 		$this->setConnection($db);
 		$db = $this->getConnection();
 
diff --git a/extensions/sphinx/ActiveRecord.php b/extensions/sphinx/ActiveRecord.php
index bfe3880..cbfe9e1 100644
--- a/extensions/sphinx/ActiveRecord.php
+++ b/extensions/sphinx/ActiveRecord.php
@@ -9,7 +9,6 @@ namespace yii\sphinx;
 
 use yii\base\InvalidConfigException;
 use yii\base\NotSupportedException;
-use yii\db\ActiveRelationInterface;
 use yii\db\BaseActiveRecord;
 use yii\db\StaleObjectException;
 use yii\helpers\Inflector;
@@ -135,14 +134,31 @@ abstract class ActiveRecord extends BaseActiveRecord
 
 	/**
 	 * Creates an [[ActiveQuery]] instance.
-	 * This method is called by [[find()]], [[findBySql()]] and [[count()]] to start a SELECT query.
-	 * You may override this method to return a customized query (e.g. `ArticleQuery` specified
-	 * written for querying `Article` purpose.)
+	 *
+	 * This method is called by [[find()]], [[findBySql()]] to start a SELECT query but also
+	 * by [[hasOne()]] and [[hasMany()]] to create a relational query.
+	 * You may override this method to return a customized query (e.g. `CustomerQuery` specified
+	 * written for querying `Customer` purpose.)
+	 *
+	 * You may also define default conditions that should apply to all queries unless overridden:
+	 *
+	 * ```php
+	 * public static function createQuery($config = [])
+	 * {
+	 *     return parent::createQuery($config)->where(['deleted' => false]);
+	 * }
+	 * ```
+	 *
+	 * Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
+	 * default condition. Using [[Query::where()]] will override the default condition.
+	 *
+	 * @param array $config the configuration passed to the ActiveQuery class.
 	 * @return ActiveQuery the newly created [[ActiveQuery]] instance.
 	 */
-	public static function createQuery()
+	public static function createQuery($config = [])
 	{
-		return new ActiveQuery(['modelClass' => get_called_class()]);
+		$config['modelClass'] = get_called_class();
+		return new ActiveQuery($config);
 	}
 
 	/**
@@ -304,18 +320,6 @@ abstract class ActiveRecord extends BaseActiveRecord
 	}
 
 	/**
-	 * Creates an [[ActiveRelationInterface]] instance.
-	 * This method is called by [[hasOne()]] and [[hasMany()]] to create a relation instance.
-	 * You may override this method to return a customized relation.
-	 * @param array $config the configuration passed to the ActiveRelation class.
-	 * @return ActiveRelationInterface the newly created [[ActiveRelation]] instance.
-	 */
-	public static function createRelation($config = [])
-	{
-		return new ActiveRelation($config);
-	}
-
-	/**
 	 * Returns the list of all attribute names of the model.
 	 * The default implementation will return all column names of the table associated with this AR class.
 	 * @return array list of attribute names.
diff --git a/extensions/sphinx/ActiveRelation.php b/extensions/sphinx/ActiveRelation.php
deleted file mode 100644
index 71939e0..0000000
--- a/extensions/sphinx/ActiveRelation.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-/**
- * @link http://www.yiiframework.com/
- * @copyright Copyright (c) 2008 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
-
-namespace yii\sphinx;
-
-use yii\db\ActiveRelationInterface;
-use yii\db\ActiveRelationTrait;
-
-/**
- * ActiveRelation represents a relation to Sphinx Active Record class.
- *
- * @author Paul Klimov <klimov.paul@gmail.com>
- * @since 2.0
- */
-class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
-{
-	use ActiveRelationTrait;
-
-	/**
-	 * @inheritdoc
-	 */
-	public function createCommand($db = null)
-	{
-		if ($this->primaryModel !== null) {
-			// lazy loading
-			if ($this->via instanceof self) {
-				// via pivot index
-				$viaModels = $this->via->findPivotRows([$this->primaryModel]);
-				$this->filterByModels($viaModels);
-			} elseif (is_array($this->via)) {
-				// via relation
-				/** @var ActiveRelation $viaQuery */
-				list($viaName, $viaQuery) = $this->via;
-				if ($viaQuery->multiple) {
-					$viaModels = $viaQuery->all();
-					$this->primaryModel->populateRelation($viaName, $viaModels);
-				} else {
-					$model = $viaQuery->one();
-					$this->primaryModel->populateRelation($viaName, $model);
-					$viaModels = $model === null ? [] : [$model];
-				}
-				$this->filterByModels($viaModels);
-			} else {
-				$this->filterByModels([$this->primaryModel]);
-			}
-		}
-		return parent::createCommand($db);
-	}
-}
\ No newline at end of file
diff --git a/extensions/sphinx/CHANGELOG.md b/extensions/sphinx/CHANGELOG.md
index 2f7f0fd..cd24967 100644
--- a/extensions/sphinx/CHANGELOG.md
+++ b/extensions/sphinx/CHANGELOG.md
@@ -5,9 +5,12 @@ Yii Framework 2 sphinx extension Change Log
 ----------------------------
 
 - Bug #1993: afterFind event in AR is now called after relations have been populated (cebe, creocoder)
-- Bug #2160: SphinxQL does not support OFFSET (qiangxue, romeo7)
+- Bug #2160: SphinxQL does not support `OFFSET` (qiangxue, romeo7)
 - Enh #1398: Refactor ActiveRecord to use BaseActiveRecord class of the framework (klimov-paul)
 - Chg #2281: Renamed `ActiveRecord::create()` to `populateRecord()` and changed signature. This method will not call instantiate() anymore (cebe)
+- Chg #2146: Removed `ActiveRelation` class and moved the functionality to `ActiveQuery`.
+             All relational queries are now directly served by `ActiveQuery` allowing to use
+             custom scopes in relations (cebe)
 
 2.0.0 alpha, December 1, 2013
 -----------------------------
diff --git a/extensions/twig/ViewRenderer.php b/extensions/twig/ViewRenderer.php
index 7edfccb..5792806 100644
--- a/extensions/twig/ViewRenderer.php
+++ b/extensions/twig/ViewRenderer.php
@@ -17,6 +17,8 @@ use yii\helpers\Html;
 /**
  * TwigViewRenderer allows you to use Twig templates in views.
  *
+ * @property array $lexerOptions @see self::$lexerOptions. This property is write-only.
+ *
  * @author Alexander Makarov <sam@rmcreative.ru>
  * @since 2.0
  */
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index 3abd9c6..68ab2d8 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -46,6 +46,8 @@ Yii Framework 2 Change Log
 - Bug #2303: Fixed the bug that `yii\base\Theme::pathMap` did not support dynamic update with path aliases (qiangxue)
 - Bug #2324: Fixed QueryBuilder bug when building a query with "query" option (mintao)
 - Bug #2399: Fixed the bug that AssetBundle did not handle relative URLs correctly (qiangxue)
+- Bug #2502: Unclear error message when `$_SERVER['DOCUMENT_ROOT']` is empty (samdark)
+- Bug #2519: MessageSource removed translation messages when event handler was bound to `missingTranslation`-event (cebe)
 - Bug: Fixed `Call to a member function registerAssetFiles() on a non-object` in case of wrong `sourcePath` for an asset bundle (samdark)
 - Bug: Fixed incorrect event name for `yii\jui\Spinner` (samdark)
 - Bug: Json::encode() did not handle objects that implement JsonSerializable interface correctly (cebe)
@@ -113,8 +115,14 @@ Yii Framework 2 Change Log
 - Enh #2325: Adding support for the `X-HTTP-Method-Override` header in `yii\web\Request::getMethod()` (pawzar)
 - Enh #2364: Take into account current error reporting level in error handler (gureedo)
 - Enh #2387: Added support for fetching data from database in batches (nineinchnick, qiangxue)
+- Enh #2392: Added `addCssStyle()`, `removeCssStyle()`, `cssStyleFromArray()` and `cssStyleToArray()` to `Html` (qiangxue, kartik-v, Alex-Code)
 - Enh #2417: Added possibility to set `dataType` for `$.ajax` call in yii.activeForm.js (Borales)
 - Enh #2436: Label of the attribute, which looks like `relatedModel.attribute`, will be received from the related model if it available (djagya)
+- Enh #2415: Added support for inverse relations (qiangxue)
+- Enh #2490: `yii\db\Query::count()` and other query scalar methods now properly handle queries with GROUP BY clause (qiangxue)
+- Enh #2491: Added support for using the same base class name of search model and data model in Gii (qiangxue)
+- Enh #2499: Added ability to downgrade migrations by their absolute apply time (resurtm, gorcer)
+- Enh #2526: Allow for null values in batchInsert (skotos)
 - Enh: Added support for using arrays as option values for console commands (qiangxue)
 - Enh: Added `favicon.ico` and `robots.txt` to default application templates (samdark)
 - Enh: Added `Widget::autoIdPrefix` to support prefixing automatically generated widget IDs (qiangxue)
@@ -150,7 +158,6 @@ Yii Framework 2 Change Log
 	- Removed `yii\web\Request::getPost()`, `getPut()`, `getDelete()`, `getPatch()` in favor of `getBodyParam()` (cebe)
 	- Renamed `yii\web\Request::get()` to `getQueryParams()` and `getRestParams()` to `getBodyParams()` (cebe)
 	- Added `yii\web\Request::get($name = null, $defaultValue = null)` and `yii\web\Request::post($name = null, $defaultValue = null)` (samdark)
-- Chg #2057: AutoTimestamp attributes defaults changed from `create_time` and `update_time` to `created_at` and `updated_at` (creocoder)
 - Chg #2059: Implemented git-flavored file excluding/filtering for `FileHelper` (nineinchnick)
 - Chg #2063: Removed `yii\web\Request::acceptTypes` and renamed `yii\web\Request::acceptedContentTypes` to `acceptableContentTypes` (qiangxue)
 - Chg #2157: The '*' category pattern will match all categories that do not match any other patterns listed in `I18N::translations` (qiangxue, Ragazzo)
@@ -161,6 +168,7 @@ Yii Framework 2 Change Log
 - Chg #2248: Renamed `yii\base\Model::DEFAULT_SCENARIO` to `yii\base\Model::SCENARIO_DEFAULT` (samdark)
 - Chg #2281: Renamed `ActiveRecord::create()` to `populateRecord()` and changed signature. This method will not call instantiate() anymore (cebe)
 - Chg #2405: The CSS class of `MaskedInput` now defaults to `form-control` (qiangxue)
+- Chg #2426: Changed URL creation method signatures to be consistent (samdark)
 - Chg: Renamed `yii\jui\Widget::clientEventsMap` to `clientEventMap` (qiangxue)
 - Chg: Renamed `ActiveRecord::getPopulatedRelations()` to `getRelatedRecords()` (qiangxue)
 - Chg: Renamed `attributeName` and `className` to `targetAttribute` and `targetClass` for `UniqueValidator` and `ExistValidator` (qiangxue)
@@ -173,7 +181,10 @@ Yii Framework 2 Change Log
 - Chg: Advanced app template: moved database connection DSN, login and password to `-local` config not to expose it to VCS (samdark)
 - Chg: Renamed `yii\web\Request::acceptedLanguages` to `acceptableLanguages` (qiangxue)
 - Chg: Removed implementation of `Arrayable` from `yii\Object` (qiangxue)
-- Chg: Renamed `ActiveRecordInterface::createActiveRelation()` to `createRelation()` (qiangxue)
+- Chg #2146: Removed `ActiveRelation` class and `ActiveRelationInterface`, moved the functionality to `ActiveQuery`.
+             All relational queries are now directly served by `ActiveQuery` allowing to use custom scopes in relations
+             and also to declare arbitrary queries as relations.
+			 Also removed `ActiveRecordInterface::createActiveRelation()` (cebe)
 - Chg: The scripts in asset bundles are now registered in `View` at the end of `endBody()`. It was done in `endPage()` previously (qiangxue)
 - Chg: Renamed `csrf-var` to `csrf-param` for CSRF header name (Dilip)
 - Chg: The directory holding email templates is renamed from `mails` to `mail` (qiangxue)
@@ -188,6 +199,7 @@ Yii Framework 2 Change Log
 	- Renamed `yii\web\User::idVar` to `idParam`
 	- Renamed `yii\web\User::authTimeoutVar` to `authTimeoutParam`
 	- Renamed `yii\web\User::returnUrlVar` to `returnUrlParam`
+- Chg: Added `View::viewFile` and removed `ViewEvent::viewFile` (qiangxue)
 
 - New #66: [Auth client library](https://github.com/yiisoft/yii2-authclient) OpenId, OAuth1, OAuth2 clients (klimov-paul)
 - New #706: Added `yii\widgets\Pjax` and enhanced `GridView` to work with `Pjax` to support AJAX-update (qiangxue)
@@ -195,6 +207,7 @@ Yii Framework 2 Change Log
 - New #1438: [MongoDB integration](https://github.com/yiisoft/yii2-mongodb) ActiveRecord and Query (klimov-paul)
 - New #1956: Implemented test fixture framework (qiangxue)
 - New #2149: Added `yii\base\DynamicModel` to support ad-hoc data validation (qiangxue)
+- New #2360: Added `AttributeBehavior` and `BlameableBehavior`, and renamed `AutoTimestamp` to `TimestampBehavior` (lucianobaraglia, qiangxue)
 - New: Yii framework now comes with core messages in multiple languages
 - New: Added yii\codeception\DbTestCase (qiangxue)
 
diff --git a/framework/README.md b/framework/README.md
index 22af4a4..228f5fb 100644
--- a/framework/README.md
+++ b/framework/README.md
@@ -1,7 +1,7 @@
 Yii PHP Framework Version 2
 ===========================
 
-This is the core framework code of [Yii 2](https://github.com/yiisoft/yii2).
+This is the core framework code of [Yii 2](https://github.com/yiisoft/yii2#readme).
 
 
 Installation
diff --git a/framework/base/Component.php b/framework/base/Component.php
index 62d08ee..982ff7f 100644
--- a/framework/base/Component.php
+++ b/framework/base/Component.php
@@ -12,7 +12,84 @@ use Yii;
 /**
  * Component is the base class that implements the *property*, *event* and *behavior* features.
  *
- * @include @yii/base/Component.md
+ * Component provides the *event* and *behavior* features, in addition to the *property* feature which is implemented in
+ * its parent class [[Object]].
+ *
+ * Event is a way to "inject" custom code into existing code at certain places. For example, a comment object can trigger
+ * an "add" event when the user adds a comment. We can write custom code and attach it to this event so that when the event
+ * is triggered (i.e. comment will be added), our custom code will be executed.
+ *
+ * An event is identified by a name that should be unique within the class it is defined at. Event names are *case-sensitive*.
+ *
+ * One or multiple PHP callbacks, called *event handlers*, can be attached to an event. You can call [[trigger()]] to
+ * raise an event. When an event is raised, the event handlers will be invoked automatically in the order they were
+ * attached.
+ *
+ * To attach an event handler to an event, call [[on()]]:
+ *
+ * ~~~
+ * $post->on('update', function($event) {
+ *     // send email notification
+ * });
+ * ~~~
+ *
+ * In the above, an anonymous function is attached to the "update" event of the post. You may attach
+ * the following types of event handlers:
+ *
+ * - anonymous function: `function($event) { ... }`
+ * - object method: `[$object, 'handleAdd']`
+ * - static class method: `['Page', 'handleAdd']`
+ * - global function: `'handleAdd'`
+ *
+ * The signature of an event handler should be like the following:
+ *
+ * ~~~
+ * function foo($event)
+ * ~~~
+ *
+ * where `$event` is an [[Event]] object which includes parameters associated with the event.
+ *
+ * You can also attach a handler to an event when configuring a component with a configuration array.
+ * The syntax is like the following:
+ *
+ * ~~~
+ * [
+ *     'on add' => function($event) { ... }
+ * ]
+ * ~~~
+ *
+ * where `on add` stands for attaching an event to the `add` event.
+ *
+ * Sometimes, you may want to associate extra data with an event handler when you attach it to an event
+ * and then access it when the handler is invoked. You may do so by
+ *
+ * ~~~
+ * $post->on('update', function($event) {
+ *     // the data can be accessed via $event->data
+ * }, $data);
+ * ~~~
+ *
+ *
+ * A behavior is an instance of [[Behavior]] or its child class. A component can be attached with one or multiple
+ * behaviors. When a behavior is attached to a component, its public properties and methods can be accessed via the
+ * component directly, as if the component owns those properties and methods.
+ *
+ * To attach a behavior to a component, declare it in [[behaviors()]], or explicitly call [[attachBehavior]]. Behaviors
+ * declared in [[behaviors()]] are automatically attached to the corresponding component.
+ *
+ * One can also attach a behavior to a component when configuring it with a configuration array. The syntax is like the
+ * following:
+ *
+ * ~~~
+ * [
+ *     'as tree' => [
+ *         'class' => 'Tree',
+ *     ],
+ * ]
+ * ~~~
+ *
+ * where `as tree` stands for attaching a behavior named `tree`, and the array will be passed to [[\Yii::createObject()]]
+ * to create the behavior object.
  *
  * @property Behavior[] $behaviors List of behaviors attached to this component. This property is read-only.
  *
diff --git a/framework/base/Formatter.php b/framework/base/Formatter.php
index d079b5b..7214cb5 100644
--- a/framework/base/Formatter.php
+++ b/framework/base/Formatter.php
@@ -269,7 +269,7 @@ class Formatter extends Component
 			return $this->nullDisplay;
 		}
 		$value = $this->normalizeDatetimeValue($value);
-		return $this->formatTimestamp($value, $format === null ? $this->dateFormat : $format, $value);
+		return $this->formatTimestamp($value, $format === null ? $this->dateFormat : $format);
 	}
 
 	/**
@@ -293,7 +293,7 @@ class Formatter extends Component
 			return $this->nullDisplay;
 		}
 		$value = $this->normalizeDatetimeValue($value);
-		return $this->formatTimestamp($value, $format === null ? $this->timeFormat : $format, $value);
+		return $this->formatTimestamp($value, $format === null ? $this->timeFormat : $format);
 	}
 
 	/**
@@ -317,7 +317,7 @@ class Formatter extends Component
 			return $this->nullDisplay;
 		}
 		$value = $this->normalizeDatetimeValue($value);
-		return $this->formatTimestamp($value, $format === null ? $this->datetimeFormat : $format, $value);
+		return $this->formatTimestamp($value, $format === null ? $this->datetimeFormat : $format);
 	}
 
 	/**
diff --git a/framework/base/Object.php b/framework/base/Object.php
index 4b1b952..8d3814f 100644
--- a/framework/base/Object.php
+++ b/framework/base/Object.php
@@ -12,7 +12,65 @@ use Yii;
 /**
  * Object is the base class that implements the *property* feature.
  *
- * @include @yii/base/Object.md
+ * A property is defined by a getter method (e.g. `getLabel`), and/or a setter method (e.g. `setLabel`). For example,
+ * the following getter and setter methods define a property named `label`:
+ *
+ * ~~~
+ * private $_label;
+ *
+ * public function getLabel()
+ * {
+ *     return $this->_label;
+ * }
+ *
+ * public function setLabel($value)
+ * {
+ *     $this->_label = $value;
+ * }
+ * ~~~
+ *
+ * Property names are *case-insensitive*.
+ *
+ * A property can be accessed like a member variable of an object. Reading or writing a property will cause the invocation
+ * of the corresponding getter or setter method. For example,
+ *
+ * ~~~
+ * // equivalent to $label = $object->getLabel();
+ * $label = $object->label;
+ * // equivalent to $object->setLabel('abc');
+ * $object->label = 'abc';
+ * ~~~
+ *
+ * If a property has only a getter method and has no setter method, it is considered as *read-only*. In this case, trying
+ * to modify the property value will cause an exception.
+ *
+ * One can call [[hasProperty()]], [[canGetProperty()]] and/or [[canSetProperty()]] to check the existence of a property.
+ *
+ *
+ * Besides the property feature, Object also introduces an important object initialization life cycle. In particular,
+ * creating an new instance of Object or its derived class will involve the following life cycles sequentially:
+ *
+ * 1. the class constructor is invoked;
+ * 2. object properties are initialized according to the given configuration;
+ * 3. the `init()` method is invoked.
+ *
+ * In the above, both Step 2 and 3 occur at the end of the class constructor. It is recommended that
+ * you perform object initialization in the `init()` method because at that stage, the object configuration
+ * is already applied.
+ *
+ * In order to ensure the above life cycles, if a child class of Object needs to override the constructor,
+ * it should be done like the following:
+ *
+ * ~~~
+ * public function __construct($param1, $param2, ..., $config = [])
+ * {
+ *     ...
+ *     parent::__construct($config);
+ * }
+ * ~~~
+ *
+ * That is, a `$config` parameter (defaults to `[]`) should be declared as the last parameter
+ * of the constructor, and the parent implementation should be called at the end of the constructor.
  *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
diff --git a/framework/base/View.php b/framework/base/View.php
index 47650f0..b9c61ad 100644
--- a/framework/base/View.php
+++ b/framework/base/View.php
@@ -93,6 +93,12 @@ class View extends Component
 	 */
 	public $dynamicPlaceholders = [];
 
+	/**
+	 * @var array the view files currently being rendered. There may be multiple view files being
+	 * rendered at a moment because one may render a view file within another.
+	 */
+	private $_viewFiles = [];
+
 
 	/**
 	 * Initializes the view component.
@@ -216,9 +222,10 @@ class View extends Component
 		if ($context !== null) {
 			$this->context = $context;
 		}
-
 		$output = '';
-		if ($this->beforeRender($viewFile)) {
+		$this->_viewFiles[] = $viewFile;
+
+		if ($this->beforeRender()) {
 			Yii::trace("Rendering view file: $viewFile", __METHOD__);
 			$ext = pathinfo($viewFile, PATHINFO_EXTENSION);
 			if (isset($this->renderers[$ext])) {
@@ -231,24 +238,32 @@ class View extends Component
 			} else {
 				$output = $this->renderPhpFile($viewFile, $params);
 			}
-			$this->afterRender($viewFile, $output);
+			$this->afterRender($output);
 		}
 
+		array_pop($this->_viewFiles);
 		$this->context = $oldContext;
 
 		return $output;
 	}
 
 	/**
+	 * @return string|boolean the view file currently being rendered. False if no view file is being rendered.
+	 */
+	public function getViewFile()
+	{
+		return end($this->_viewFiles);
+	}
+
+	/**
 	 * This method is invoked right before [[renderFile()]] renders a view file.
 	 * The default implementation will trigger the [[EVENT_BEFORE_RENDER]] event.
 	 * If you override this method, make sure you call the parent implementation first.
-	 * @param string $viewFile the view file to be rendered
 	 * @return boolean whether to continue rendering the view file.
 	 */
-	public function beforeRender($viewFile)
+	public function beforeRender()
 	{
-		$event = new ViewEvent($viewFile);
+		$event = new ViewEvent;
 		$this->trigger(self::EVENT_BEFORE_RENDER, $event);
 		return $event->isValid;
 	}
@@ -257,14 +272,13 @@ class View extends Component
 	 * This method is invoked right after [[renderFile()]] renders a view file.
 	 * The default implementation will trigger the [[EVENT_AFTER_RENDER]] event.
 	 * If you override this method, make sure you call the parent implementation first.
-	 * @param string $viewFile the view file to be rendered
 	 * @param string $output the rendering result of the view file. Updates to this parameter
 	 * will be passed back and returned by [[renderFile()]].
 	 */
-	public function afterRender($viewFile, &$output)
+	public function afterRender(&$output)
 	{
 		if ($this->hasEventHandlers(self::EVENT_AFTER_RENDER)) {
-			$event = new ViewEvent($viewFile);
+			$event = new ViewEvent;
 			$event->output = $output;
 			$this->trigger(self::EVENT_AFTER_RENDER, $event);
 			$output = $event->output;
diff --git a/framework/base/ViewEvent.php b/framework/base/ViewEvent.php
index d02e180..bad7264 100644
--- a/framework/base/ViewEvent.php
+++ b/framework/base/ViewEvent.php
@@ -23,24 +23,9 @@ class ViewEvent extends Event
 	 */
 	public $output;
 	/**
-	 * @var string the view file path that is being rendered by [[View::renderFile()]].
-	 */
-	public $viewFile;
-	/**
 	 * @var boolean whether to continue rendering the view file. Event handlers of
 	 * [[View::EVENT_BEFORE_RENDER]] may set this property to decide whether
 	 * to continue rendering the current view file.
 	 */
 	public $isValid = true;
-
-	/**
-	 * Constructor.
-	 * @param string $viewFile the view file path that is being rendered by [[View::renderFile()]].
-	 * @param array $config name-value pairs that will be used to initialize the object properties
-	 */
-	public function __construct($viewFile, $config = [])
-	{
-		$this->viewFile = $viewFile;
-		parent::__construct($config);
-	}
 }
diff --git a/framework/behaviors/AttributeBehavior.php b/framework/behaviors/AttributeBehavior.php
new file mode 100644
index 0000000..4f6430f
--- /dev/null
+++ b/framework/behaviors/AttributeBehavior.php
@@ -0,0 +1,111 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\behaviors;
+
+use Yii;
+use Closure;
+use yii\base\Behavior;
+use yii\base\Event;
+
+/**
+ * AttributeBehavior automatically assigns a specified value to one or multiple attributes of an ActiveRecord object when certain events happen.
+ *
+ * To use AttributeBehavior, configure the [[attributes]] property which should specify the list of attributes
+ * that need to be updated and the corresponding events that should trigger the update. For example,
+ * Then configure the [[value]] property with a PHP callable whose return value will be used to assign to the current
+ * attribute(s). For example,
+ *
+ * ~~~
+ * use yii\behaviors\AttributeBehavior;
+ *
+ * public function behaviors()
+ * {
+ *     return [
+ *         'attributeStamp' => [
+ *             'class' => AttributeBehavior::className(),
+ *             'attributes' => [
+ *                 ActiveRecord::EVENT_BEFORE_INSERT => 'attribute1',
+ *                 ActiveRecord::EVENT_BEFORE_UPDATE => 'attribute2',
+ *             ],
+ *             'value' => function ($event) {
+ *                 return 'some value';
+ *             },
+ *         ],
+ *     ];
+ * }
+ * ~~~
+ *
+ * @author Luciano Baraglia <luciano.baraglia@gmail.com>
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class AttributeBehavior extends Behavior
+{
+	/**
+	 * @var array list of attributes that are to be automatically filled with the value specified via [[value]].
+	 * The array keys are the ActiveRecord events upon which the attributes are to be updated,
+	 * and the array values are the corresponding attribute(s) to be updated. You can use a string to represent
+	 * a single attribute, or an array to represent a list of attributes. For example,
+	 *
+	 * ```php
+	 * [
+	 *     ActiveRecord::EVENT_BEFORE_INSERT => ['attribute1', 'attribute2'],
+	 *     ActiveRecord::EVENT_BEFORE_UPDATE => 'attribute2',
+	 * ]
+	 * ```
+	 */
+	public $attributes = [];
+	/**
+	 * @var mixed the value that will be assigned to the current attributes. This can be an anonymous function
+	 * or an arbitrary value. If the former, the return value of the function will be assigned to the attributes.
+	 * The signature of the function should be as follows,
+	 *
+	 * ```php
+	 * function ($event) {
+	 *     // return value will be assigned to the attribute
+	 * }
+	 * ```
+	 */
+	public $value;
+
+
+	/**
+	 * @inheritdoc
+	 */
+	public function events()
+	{
+		return array_fill_keys(array_keys($this->attributes), 'evaluateAttributes');
+	}
+
+	/**
+	 * Evaluates the attribute value and assigns it to the current attributes.
+	 * @param $event
+	 */
+	public function evaluateAttributes($event)
+	{
+		if (!empty($this->attributes[$event->name])) {
+			$attributes = (array)$this->attributes[$event->name];
+			$value = $this->getValue($event);
+			foreach ($attributes as $attribute) {
+				$this->owner->$attribute = $value;
+			}
+		}
+	}
+
+	/**
+	 * Returns the value of the current attributes.
+	 * This method is called by [[evaluateAttributes()]]. Its return value will be assigned
+	 * to the attributes corresponding to the triggering event.
+	 * @param Event $event the event that triggers the current attribute updating.
+	 * @return mixed the attribute value
+	 */
+	protected function getValue($event)
+	{
+		return $this->value instanceof Closure ? call_user_func($this->value, $event) : $this->value;
+	}
+}
diff --git a/framework/behaviors/AutoTimestamp.php b/framework/behaviors/AutoTimestamp.php
deleted file mode 100644
index 42e617a..0000000
--- a/framework/behaviors/AutoTimestamp.php
+++ /dev/null
@@ -1,116 +0,0 @@
-<?php
-/**
- * @link http://www.yiiframework.com/
- * @copyright Copyright (c) 2008 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
-
-namespace yii\behaviors;
-
-use yii\base\Behavior;
-use yii\base\Event;
-use yii\db\Expression;
-use yii\db\ActiveRecord;
-
-/**
- * AutoTimestamp will automatically fill the attributes about creation time and updating time.
- *
- * AutoTimestamp fills the attributes when the associated AR model is being inserted or updated.
- * You may specify an AR to use this behavior like the following:
- *
- * ~~~
- * public function behaviors()
- * {
- *     return [
- *         'timestamp' => ['class' => 'yii\behaviors\AutoTimestamp'],
- *     ];
- * }
- * ~~~
- *
- * By default, AutoTimestamp will fill the `created_at` attribute with the current timestamp
- * when the associated AR object is being inserted; it will fill the `updated_at` attribute
- * with the timestamp when the AR object is being updated.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @since 2.0
- */
-class AutoTimestamp extends Behavior
-{
-	/**
-	 * @var array list of attributes that are to be automatically filled with timestamps.
-	 * The array keys are the ActiveRecord events upon which the attributes are to be filled with timestamps,
-	 * and the array values are the corresponding attribute(s) to be updated. You can use a string to represent
-	 * a single attribute, or an array to represent a list of attributes.
-	 * The default setting is to update the `created_at` attribute upon AR insertion,
-	 * and update the `updated_at` attribute upon AR updating.
-	 */
-	public $attributes = [
-		ActiveRecord::EVENT_BEFORE_INSERT => 'created_at',
-		ActiveRecord::EVENT_BEFORE_UPDATE => 'updated_at',
-	];
-	/**
-	 * @var \Closure|Expression The expression that will be used for generating the timestamp.
-	 * This can be either an anonymous function that returns the timestamp value,
-	 * or an [[Expression]] object representing a DB expression (e.g. `new Expression('NOW()')`).
-	 * If not set, it will use the value of `time()` to fill the attributes.
-	 */
-	public $timestamp;
-
-
-	/**
-	 * Declares event handlers for the [[owner]]'s events.
-	 * @return array events (array keys) and the corresponding event handler methods (array values).
-	 */
-	public function events()
-	{
-		$events = $this->attributes;
-		foreach ($events as $i => $event) {
-			$events[$i] = 'updateTimestamp';
-		}
-		return $events;
-	}
-
-	/**
-	 * Updates the attributes with the current timestamp.
-	 * @param Event $event
-	 */
-	public function updateTimestamp($event)
-	{
-		$attributes = isset($this->attributes[$event->name]) ? (array)$this->attributes[$event->name] : [];
-		if (!empty($attributes)) {
-			$timestamp = $this->evaluateTimestamp();
-			foreach ($attributes as $attribute) {
-				$this->owner->$attribute = $timestamp;
-			}
-		}
-	}
-
-	/**
-	 * Gets the current timestamp.
-	 * @return mixed the timestamp value
-	 */
-	protected function evaluateTimestamp()
-	{
-		if ($this->timestamp instanceof Expression) {
-			return $this->timestamp;
-		} elseif ($this->timestamp !== null) {
-			return call_user_func($this->timestamp);
-		} else {
-			return time();
-		}
-	}
-
-	/**
-	 * Updates a timestamp attribute to the current timestamp.
-	 *
-	 * ```php
-	 * $model->touch('lastVisit');
-	 * ```
-	 * @param string $attribute the name of the attribute to update.
-	 */
-	public function touch($attribute)
-	{
-		$timestamp = $this->evaluateTimestamp();
-		$this->owner->updateAttributes([$attribute => $timestamp]);
-	}
-}
diff --git a/framework/behaviors/BlameableBehavior.php b/framework/behaviors/BlameableBehavior.php
new file mode 100644
index 0000000..0a9dba9
--- /dev/null
+++ b/framework/behaviors/BlameableBehavior.php
@@ -0,0 +1,98 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\behaviors;
+
+use Yii;
+use yii\base\Event;
+use yii\db\BaseActiveRecord;
+
+/**
+ * BlameableBehavior automatically fills the specified attributes with the current user ID.
+ *
+ * To use BlameableBehavior, simply insert the following code to your ActiveRecord class:
+ *
+ * ```php
+ * use yii\behaviors\BlameableBehavior;
+ *
+ * public function behaviors()
+ * {
+ *     return [
+ *         BlameableBehavior::className(),
+ *     ];
+ * }
+ * ```
+ *
+ * By default, BlameableBehavior will fill the `created_by` and `updated_by` attributes with the current user ID
+ * when the associated AR object is being inserted; it will fill the `updated_by` attribute
+ * with the current user ID when the AR object is being updated. If your attribute names are different, you may configure
+ * the [[attributes]] property like the following:
+ *
+ * ```php
+ * public function behaviors()
+ * {
+ *     return [
+ *         [
+ *             'class' => BlameableBehavior::className(),
+ *             'attributes' => [
+ *                 ActiveRecord::EVENT_BEFORE_INSERT => 'author_id',
+ *                 ActiveRecord::EVENT_BEFORE_UPDATE => 'updater_id',
+ *             ],
+ *         ],
+ *     ];
+ * }
+ * ```
+ *
+ * @author Luciano Baraglia <luciano.baraglia@gmail.com>
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class BlameableBehavior extends AttributeBehavior
+{
+	/**
+	 * @var array list of attributes that are to be automatically filled with the current user ID.
+	 * The array keys are the ActiveRecord events upon which the attributes are to be filled with the user ID,
+	 * and the array values are the corresponding attribute(s) to be updated. You can use a string to represent
+	 * a single attribute, or an array to represent a list of attributes.
+	 * The default setting is to update both of the `created_by` and `updated_by` attributes upon AR insertion,
+	 * and update the `updated_by` attribute upon AR updating.
+	 */
+	public $attributes = [
+		BaseActiveRecord::EVENT_BEFORE_INSERT => ['created_by', 'updated_by'],
+		BaseActiveRecord::EVENT_BEFORE_UPDATE => 'updated_by',
+	];
+	/**
+	 * @var callable the value that will be assigned to the attributes. This should be a valid
+	 * PHP callable whose return value will be assigned to the current attribute(s).
+	 * The signature of the callable should be:
+	 *
+	 * ```php
+	 * function ($event) {
+	 *     // return value will be assigned to the attribute(s)
+	 * }
+	 * ```
+	 *
+	 * If this property is not set, the value of `Yii::$app->user->id` will be assigned to the attribute(s).
+	 */
+	public $value;
+
+	/**
+	 * Evaluates the value of the user.
+	 * The return result of this method will be assigned to the current attribute(s).
+	 * @param Event $event
+	 * @return mixed the value of the user.
+	 */
+	protected function getValue($event)
+	{
+		if ($this->value === null) {
+			$user = Yii::$app->getUser();
+			return $user && !$user->isGuest ? $user->id : null;
+		} else {
+			return call_user_func($this->value, $event);
+		}
+	}
+}
diff --git a/framework/behaviors/TimestampBehavior.php b/framework/behaviors/TimestampBehavior.php
new file mode 100644
index 0000000..1313488
--- /dev/null
+++ b/framework/behaviors/TimestampBehavior.php
@@ -0,0 +1,111 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\behaviors;
+
+use yii\db\BaseActiveRecord;
+use yii\db\Expression;
+
+/**
+ * TimestampBehavior automatically fills the specified attributes with the current timestamp.
+ *
+ * To use TimestampBehavior, simply insert the following code to your ActiveRecord class:
+ *
+ * ```php
+ * use yii\behaviors\TimestampBehavior;
+ *
+ * public function behaviors()
+ * {
+ *     return [
+ *         TimestampBehavior::className(),
+ *     ];
+ * }
+ * ```
+ *
+ * By default, TimestampBehavior will fill the `created_at` and `updated_at` attributes with the current timestamp
+ * when the associated AR object is being inserted; it will fill the `updated_at` attribute
+ * with the timestamp when the AR object is being updated. The timestamp value is obtained by `time()`.
+ *
+ * If your attribute names are different or you want to use a different way of calculating the timestamp,
+ * you may configure the [[attributes]] and [[value]] properties like the following:
+ *
+ * ```php
+ * use yii\db\Expression;
+ *
+ * public function behaviors()
+ * {
+ *     return [
+ *         'timestamp' => [
+ *             'class' => TimestampBehavior::className(),
+ *             'attributes' => [
+ *                 ActiveRecord::EVENT_BEFORE_INSERT => 'creation_time',
+ *                 ActiveRecord::EVENT_BEFORE_UPDATE => 'update_time',
+ *             ],
+ *             'value' => new Expression('NOW()'),
+ *         ],
+ *     ];
+ * }
+ * ```
+ *
+ * TimestampBehavior also provides a method named [[touch()]] that allows you to assign the current
+ * timestamp to the specified attribute(s) and save them to the database. For example,
+ *
+ * ```php
+ * $this->timestamp->touch('creation_time');
+ * ```
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class TimestampBehavior extends AttributeBehavior
+{
+	/**
+	 * @var array list of attributes that are to be automatically filled with timestamps.
+	 * The array keys are the ActiveRecord events upon which the attributes are to be filled with timestamps,
+	 * and the array values are the corresponding attribute(s) to be updated. You can use a string to represent
+	 * a single attribute, or an array to represent a list of attributes.
+	 * The default setting is to update both of the `created_at` and `updated_at` attributes upon AR insertion,
+	 * and update the `updated_at` attribute upon AR updating.
+	 */
+	public $attributes = [
+		BaseActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
+		BaseActiveRecord::EVENT_BEFORE_UPDATE => 'updated_at',
+	];
+	/**
+	 * @var callable|Expression The expression that will be used for generating the timestamp.
+	 * This can be either an anonymous function that returns the timestamp value,
+	 * or an [[Expression]] object representing a DB expression (e.g. `new Expression('NOW()')`).
+	 * If not set, it will use the value of `time()` to set the attributes.
+	 */
+	public $value;
+
+
+	/**
+	 * @inheritdoc
+	 */
+	protected function getValue($event)
+	{
+		if ($this->value instanceof Expression) {
+			return $this->value;
+		} else {
+			return $this->value !== null ? call_user_func($this->value, $event) : time();
+		}
+	}
+
+	/**
+	 * Updates a timestamp attribute to the current timestamp.
+	 *
+	 * ```php
+	 * $model->touch('lastVisit');
+	 * ```
+	 * @param string $attribute the name of the attribute to update.
+	 */
+	public function touch($attribute)
+	{
+		$this->owner->updateAttributes(array_fill_keys((array)$attribute, $this->getValue(null)));
+	}
+}
diff --git a/framework/captcha/Captcha.php b/framework/captcha/Captcha.php
index 32c5eb9..25361c5 100644
--- a/framework/captcha/Captcha.php
+++ b/framework/captcha/Captcha.php
@@ -78,7 +78,7 @@ class Captcha extends InputWidget
 		} else {
 			$input = Html::textInput($this->name, $this->value, $this->options);
 		}
-		$url = Yii::$app->getUrlManager()->createUrl($this->captchaAction, ['v' => uniqid()]);
+		$url = Yii::$app->getUrlManager()->createUrl([$this->captchaAction, 'v' => uniqid()]);
 		$image = Html::img($url, $this->imageOptions);
 		echo strtr($this->template, [
 			'{input}' => $input,
diff --git a/framework/captcha/CaptchaAction.php b/framework/captcha/CaptchaAction.php
index b4dc67f..85faa99 100644
--- a/framework/captcha/CaptchaAction.php
+++ b/framework/captcha/CaptchaAction.php
@@ -124,7 +124,7 @@ class CaptchaAction extends Action
 				'hash2' => $this->generateValidationHash(strtolower($code)),
 				// we add a random 'v' parameter so that FireFox can refresh the image
 				// when src attribute of image tag is changed
-				'url' => $controller->createUrl($this->id, ['v' => uniqid()]),
+				'url' => $controller->createUrl([$this->id, 'v' => uniqid()]),
 			]);
 		} else {
 			$this->setHttpHeaders();
diff --git a/framework/classes.php b/framework/classes.php
index 72554d0..552b943 100644
--- a/framework/classes.php
+++ b/framework/classes.php
@@ -50,7 +50,9 @@ return [
 	'yii\base\ViewEvent' => YII_PATH . '/base/ViewEvent.php',
 	'yii\base\ViewRenderer' => YII_PATH . '/base/ViewRenderer.php',
 	'yii\base\Widget' => YII_PATH . '/base/Widget.php',
-	'yii\behaviors\AutoTimestamp' => YII_PATH . '/behaviors/AutoTimestamp.php',
+	'yii\behaviors\AttributeBehavior' => YII_PATH . '/behaviors/AttributeBehavior.php',
+	'yii\behaviors\BlameableBehavior' => YII_PATH . '/behaviors/BlameableBehavior.php',
+	'yii\behaviors\TimestampBehavior' => YII_PATH . '/behaviors/TimestampBehavior.php',
 	'yii\caching\ApcCache' => YII_PATH . '/caching/ApcCache.php',
 	'yii\caching\Cache' => YII_PATH . '/caching/Cache.php',
 	'yii\caching\ChainedDependency' => YII_PATH . '/caching/ChainedDependency.php',
@@ -88,6 +90,7 @@ return [
 	'yii\db\ActiveRelationInterface' => YII_PATH . '/db/ActiveRelationInterface.php',
 	'yii\db\ActiveRelationTrait' => YII_PATH . '/db/ActiveRelationTrait.php',
 	'yii\db\BaseActiveRecord' => YII_PATH . '/db/BaseActiveRecord.php',
+	'yii\db\BatchQueryResult' => YII_PATH . '/db/BatchQueryResult.php',
 	'yii\db\ColumnSchema' => YII_PATH . '/db/ColumnSchema.php',
 	'yii\db\Command' => YII_PATH . '/db/Command.php',
 	'yii\db\Connection' => YII_PATH . '/db/Connection.php',
@@ -266,5 +269,7 @@ return [
 	'yii\widgets\MaskedInput' => YII_PATH . '/widgets/MaskedInput.php',
 	'yii\widgets\MaskedInputAsset' => YII_PATH . '/widgets/MaskedInputAsset.php',
 	'yii\widgets\Menu' => YII_PATH . '/widgets/Menu.php',
+	'yii\widgets\Pjax' => YII_PATH . '/widgets/Pjax.php',
+	'yii\widgets\PjaxAsset' => YII_PATH . '/widgets/PjaxAsset.php',
 	'yii\widgets\Spaceless' => YII_PATH . '/widgets/Spaceless.php',
 ];
diff --git a/framework/composer.json b/framework/composer.json
index 0dd71a2..bd11fc7 100644
--- a/framework/composer.json
+++ b/framework/composer.json
@@ -55,7 +55,7 @@
 		"yiisoft/yii2-composer": "*",
 		"yiisoft/jquery": "~2.0 | ~1.10",
 		"ezyang/htmlpurifier": "4.6.*",
-		"michelf/php-markdown": "1.3.*"
+		"cebe/markdown": "0.9.*"
 	},
 	"autoload": {
 		"psr-4": { "yii\\": "" }
diff --git a/framework/console/controllers/MigrateController.php b/framework/console/controllers/MigrateController.php
index b72e9cc..ab521d6 100644
--- a/framework/console/controllers/MigrateController.php
+++ b/framework/console/controllers/MigrateController.php
@@ -286,50 +286,37 @@ class MigrateController extends Controller
 	/**
 	 * Upgrades or downgrades till the specified version.
 	 *
+	 * Can also downgrade versions to the certain apply time in the past by providing
+	 * a UNIX timestamp or a string parseable by the strtotime() function. This means
+	 * that all the versions applied after the specified certain time would be reverted.
+	 *
 	 * This command will first revert the specified migrations, and then apply
 	 * them again. For example,
 	 *
 	 * ~~~
 	 * yii migrate/to 101129_185401                      # using timestamp
 	 * yii migrate/to m101129_185401_create_user_table   # using full name
+	 * yii migrate/to 1392853618                         # using UNIX timestamp
+	 * yii migrate/to "2014-02-15 13:00:50"              # using strtotime() parseable string
 	 * ~~~
 	 *
-	 * @param string $version the version name that the application should be migrated to.
-	 * This can be either the timestamp or the full name of the migration.
-	 * @throws Exception if the version argument is invalid
+	 * @param string $version either the version name or the certain time value in the past
+	 * that the application should be migrated to. This can be either the timestamp,
+	 * the full name of the migration, the UNIX timestamp, or the parseable datetime
+	 * string.
+	 * @throws Exception if the version argument is invalid.
 	 */
 	public function actionTo($version)
 	{
-		$originalVersion = $version;
 		if (preg_match('/^m?(\d{6}_\d{6})(_.*?)?$/', $version, $matches)) {
-			$version = 'm' . $matches[1];
+			$this->migrateToVersion('m' . $matches[1]);
+		} elseif ((string)(int)$version == $version) {
+			$this->migrateToTime($version);
+		} elseif (($time = strtotime($version)) !== false) {
+			$this->migrateToTime($time);
 		} else {
-			throw new Exception("The version argument must be either a timestamp (e.g. 101129_185401)\nor the full name of a migration (e.g. m101129_185401_create_user_table).");
-		}
-
-		// try migrate up
-		$migrations = $this->getNewMigrations();
-		foreach ($migrations as $i => $migration) {
-			if (strpos($migration, $version . '_') === 0) {
-				$this->actionUp($i + 1);
-				return;
-			}
-		}
-
-		// try migrate down
-		$migrations = array_keys($this->getMigrationHistory(-1));
-		foreach ($migrations as $i => $migration) {
-			if (strpos($migration, $version . '_') === 0) {
-				if ($i === 0) {
-					echo "Already at '$originalVersion'. Nothing needs to be done.\n";
-				} else {
-					$this->actionDown($i);
-				}
-				return;
-			}
+			throw new Exception("The version argument must be either a timestamp (e.g. 101129_185401),\n the full name of a migration (e.g. m101129_185401_create_user_table),\n a UNIX timestamp (e.g. 1392853000), or a datetime string parseable\nby the strtotime() function (e.g. 2014-02-15 13:00:50).");
 		}
-
-		throw new Exception("Unable to find the version '$originalVersion'.");
 	}
 
 	/**
@@ -568,6 +555,58 @@ class MigrateController extends Controller
 	}
 
 	/**
+	 * Migrates to the specified apply time in the past.
+	 * @param integer $time UNIX timestamp value.
+	 */
+	protected function migrateToTime($time)
+	{
+		$count = 0;
+		$migrations = array_values($this->getMigrationHistory(-1));
+		while ($count < count($migrations) && $migrations[$count] > $time) {
+			++$count;
+		}
+		if ($count === 0) {
+			echo "Nothing needs to be done.\n";
+		} else {
+			$this->actionDown($count);
+		}
+	}
+
+	/**
+	 * Migrates to the certain version.
+	 * @param string $version name in the full format.
+	 * @throws Exception if the provided version cannot be found.
+	 */
+	protected function migrateToVersion($version)
+	{
+		$originalVersion = $version;
+
+		// try migrate up
+		$migrations = $this->getNewMigrations();
+		foreach ($migrations as $i => $migration) {
+			if (strpos($migration, $version . '_') === 0) {
+				$this->actionUp($i + 1);
+				return;
+			}
+		}
+
+		// try migrate down
+		$migrations = array_keys($this->getMigrationHistory(-1));
+		foreach ($migrations as $i => $migration) {
+			if (strpos($migration, $version . '_') === 0) {
+				if ($i === 0) {
+					echo "Already at '$originalVersion'. Nothing needs to be done.\n";
+				} else {
+					$this->actionDown($i);
+				}
+				return;
+			}
+		}
+
+		throw new Exception("Unable to find the version '$originalVersion'.");
+	}
+
+	/**
 	 * Returns the migration history.
 	 * @param integer $limit the maximum number of records in the history to be returned
 	 * @return array the migration history
diff --git a/framework/data/Pagination.php b/framework/data/Pagination.php
index 5a104b6..9e9359c 100644
--- a/framework/data/Pagination.php
+++ b/framework/data/Pagination.php
@@ -190,12 +190,12 @@ class Pagination extends Object
 		} else {
 			unset($params[$this->pageParam]);
 		}
-		$route = $this->route === null ? Yii::$app->controller->getRoute() : $this->route;
+		$params[0] = $this->route === null ? Yii::$app->controller->getRoute() : $this->route;
 		$urlManager = $this->urlManager === null ? Yii::$app->getUrlManager() : $this->urlManager;
 		if ($absolute) {
-			return $urlManager->createAbsoluteUrl($route, $params);
+			return $urlManager->createAbsoluteUrl($params);
 		} else {
-			return $urlManager->createUrl($route, $params);
+			return $urlManager->createUrl($params);
 		}
 	}
 
diff --git a/framework/data/Sort.php b/framework/data/Sort.php
index e706180..cfcbb82 100644
--- a/framework/data/Sort.php
+++ b/framework/data/Sort.php
@@ -335,12 +335,12 @@ class Sort extends Object
 			$params = $request instanceof Request ? $request->getQueryParams() : [];
 		}
 		$params[$this->sortParam] = $this->createSortParam($attribute);
-		$route = $this->route === null ? Yii::$app->controller->getRoute() : $this->route;
+		$params[0] = $this->route === null ? Yii::$app->controller->getRoute() : $this->route;
 		$urlManager = $this->urlManager === null ? Yii::$app->getUrlManager() : $this->urlManager;
 		if ($absolute) {
-			return $urlManager->createAbsoluteUrl($route, $params);
+			return $urlManager->createAbsoluteUrl($params);
 		} else {
-			return $urlManager->createUrl($route, $params);
+			return $urlManager->createUrl($params);
 		}
 	}
 
diff --git a/framework/db/ActiveQuery.php b/framework/db/ActiveQuery.php
index 39bebb7..7fcb5e0 100644
--- a/framework/db/ActiveQuery.php
+++ b/framework/db/ActiveQuery.php
@@ -11,7 +11,13 @@ namespace yii\db;
 /**
  * ActiveQuery represents a DB query associated with an Active Record class.
  *
+ * An ActiveQuery can be a normal query or be used in a relational context.
+ *
  * ActiveQuery instances are usually created by [[ActiveRecord::find()]] and [[ActiveRecord::findBySql()]].
+ * Relational queries are created by [[ActiveRecord::hasOne()]] and [[ActiveRecord::hasMany()]].
+ *
+ * Normal Query
+ * ------------
  *
  * ActiveQuery mainly provides the following methods to retrieve the query results:
  *
@@ -37,9 +43,26 @@ namespace yii\db;
  *
  * These options can be configured using methods of the same name. For example:
  *
- * ~~~
+ * ```php
  * $customers = Customer::find()->with('orders')->asArray()->all();
- * ~~~
+ * ```
+ *
+ * Relational query
+ * ----------------
+ *
+ * In relational context ActiveQuery represents a relation between two Active Record classes.
+ *
+ * Relational ActiveQuery instances are usually created by calling [[ActiveRecord::hasOne()]] and
+ * [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
+ * a getter method which calls one of the above methods and returns the created ActiveQuery object.
+ *
+ * A relation is specified by [[link]] which represents the association between columns
+ * of different tables; and the multiplicity of the relation is indicated by [[multiple]].
+ *
+ * If a relation involves a pivot table, it may be specified by [[via()]] or [[viaTable()]] method.
+ * These methods may only be called in a relational context. Same is true for [[inverseOf()]], which
+ * marks a relation as inverse of another relation and [[onCondition()]] which adds a condition that
+ * is to be added to relational querys join condition.
  *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @author Carsten Brandt <mail@cebe.cc>
@@ -48,12 +71,21 @@ namespace yii\db;
 class ActiveQuery extends Query implements ActiveQueryInterface
 {
 	use ActiveQueryTrait;
+	use ActiveRelationTrait;
 
 	/**
 	 * @var string the SQL statement to be executed for retrieving AR records.
 	 * This is set by [[ActiveRecord::findBySql()]].
 	 */
 	public $sql;
+	/**
+	 * @var string|array the join condition to be used when this query is used in a relational context.
+	 * The condition will be used in the ON part when [[ActiveQuery::joinWith()]] is called.
+	 * Otherwise, the condition will be used in the WHERE part of a query.
+	 * Please refer to [[Query::where()]] on how to specify this parameter.
+	 * @see onCondition()
+	 */
+	public $on;
 
 
 	/**
@@ -175,6 +207,31 @@ class ActiveQuery extends Query implements ActiveQueryInterface
 	 */
 	public function createCommand($db = null)
 	{
+		if ($this->primaryModel === null) {
+			// not a relational context or eager loading
+			if (!empty($this->on)) {
+				$where = $this->where;
+				$this->andWhere($this->on);
+				$command = $this->createCommandInternal($db);
+				$this->where = $where;
+				return $command;
+			} else {
+				return $this->createCommandInternal($db);
+			}
+		} else {
+			// lazy loading of a relation
+			return $this->createRelationalCommand($db);
+		}
+	}
+
+	/**
+	 * Creates a DB command that can be used to execute this query.
+	 * @param Connection $db the DB connection used to create the DB command.
+	 * If null, the DB connection returned by [[modelClass]] will be used.
+	 * @return Command the created DB command instance.
+	 */
+	protected function createCommandInternal($db)
+	{
 		/** @var ActiveRecord $modelClass */
 		$modelClass = $this->modelClass;
 		if ($db === null) {
@@ -191,6 +248,47 @@ class ActiveQuery extends Query implements ActiveQueryInterface
 	}
 
 	/**
+	 * Creates a command for lazy loading of a relation.
+	 * @param Connection $db the DB connection used to create the DB command.
+	 * @return Command the created DB command instance.
+	 */
+	private function createRelationalCommand($db = null)
+	{
+		$where = $this->where;
+
+		if ($this->via instanceof self) {
+			// via pivot table
+			$viaModels = $this->via->findPivotRows([$this->primaryModel]);
+			$this->filterByModels($viaModels);
+		} elseif (is_array($this->via)) {
+			// via relation
+			/** @var ActiveQuery $viaQuery */
+			list($viaName, $viaQuery) = $this->via;
+			if ($viaQuery->multiple) {
+				$viaModels = $viaQuery->all();
+				$this->primaryModel->populateRelation($viaName, $viaModels);
+			} else {
+				$model = $viaQuery->one();
+				$this->primaryModel->populateRelation($viaName, $model);
+				$viaModels = $model === null ? [] : [$model];
+			}
+			$this->filterByModels($viaModels);
+		} else {
+			$this->filterByModels([$this->primaryModel]);
+		}
+
+		if (!empty($this->on)) {
+			$this->andWhere($this->on);
+		}
+
+		$command = $this->createCommandInternal($db);
+
+		$this->where = $where;
+
+		return $command;
+	}
+
+	/**
 	 * Joins with the specified relations.
 	 *
 	 * This method allows you to reuse existing relation definitions to perform JOIN queries.
@@ -355,14 +453,14 @@ class ActiveQuery extends Query implements ActiveQueryInterface
 	 * Joins a parent query with a child query.
 	 * The current query object will be modified accordingly.
 	 * @param ActiveQuery $parent
-	 * @param ActiveRelation $child
+	 * @param ActiveQuery $child
 	 * @param string $joinType
 	 */
 	private function joinWithRelation($parent, $child, $joinType)
 	{
 		$via = $child->via;
 		$child->via = null;
-		if ($via instanceof ActiveRelation) {
+		if ($via instanceof ActiveQuery) {
 			// via table
 			$this->joinWithRelation($parent, $via, $joinType);
 			$this->joinWithRelation($via, $child, $joinType);
@@ -426,4 +524,67 @@ class ActiveQuery extends Query implements ActiveQueryInterface
 			}
 		}
 	}
+
+	/**
+	 * Sets the ON condition for a relational query.
+	 * The condition will be used in the ON part when [[ActiveQuery::joinWith()]] is called.
+	 * Otherwise, the condition will be used in the WHERE part of a query.
+	 *
+	 * Use this method to specify additional conditions when declaring a relation in the [[ActiveRecord]] class:
+	 *
+	 * ```php
+	 * public function getActiveUsers()
+	 * {
+	 *     return $this->hasMany(User::className(), ['id' => 'user_id'])->onCondition(['active' => true]);
+	 * }
+	 * ```
+	 *
+	 * @param string|array $condition the ON condition. Please refer to [[Query::where()]] on how to specify this parameter.
+	 * @param array $params the parameters (name => value) to be bound to the query.
+	 * @return static the query object itself
+	 */
+	public function onCondition($condition, $params = [])
+	{
+		$this->on = $condition;
+		$this->addParams($params);
+		return $this;
+	}
+
+	/**
+	 * Specifies the pivot table for a relational query.
+	 *
+	 * Use this method to specify a pivot table when declaring a relation in the [[ActiveRecord]] class:
+	 *
+	 * ```php
+	 * public function getItems()
+	 * {
+	 *     return $this->hasMany(Item::className(), ['id' => 'item_id'])
+	 *                 ->viaTable('tbl_order_item', ['order_id' => 'id']);
+	 * }
+	 * ```
+	 *
+	 * @param string $tableName the name of the pivot table.
+	 * @param array $link the link between the pivot table and the table associated with [[primaryModel]].
+	 * The keys of the array represent the columns in the pivot table, and the values represent the columns
+	 * in the [[primaryModel]] table.
+	 * @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
+	 * Its signature should be `function($query)`, where `$query` is the query to be customized.
+	 * @return static
+	 * @see via()
+	 */
+	public function viaTable($tableName, $link, $callable = null)
+	{
+		$relation = new ActiveQuery([
+			'modelClass' => get_class($this->primaryModel),
+			'from' => [$tableName],
+			'link' => $link,
+			'multiple' => true,
+			'asArray' => true,
+		]);
+		$this->via = $relation;
+		if ($callable !== null) {
+			call_user_func($callable, $relation);
+		}
+		return $this;
+	}
 }
diff --git a/framework/db/ActiveQueryInterface.php b/framework/db/ActiveQueryInterface.php
index 8acd574..332d576 100644
--- a/framework/db/ActiveQueryInterface.php
+++ b/framework/db/ActiveQueryInterface.php
@@ -10,7 +10,11 @@ namespace yii\db;
 /**
  * ActiveQueryInterface defines the common interface to be implemented by active record query classes.
  *
- * A class implementing this interface should also use [[ActiveQueryTrait]].
+ * That are methods for either normal queries that return active records but also relational queries
+ * in which the query represents a relation between two active record classes and will return related
+ * records only.
+ *
+ * A class implementing this interface should also use [[ActiveQueryTrait]] and [[ActiveRelationTrait]].
  *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @author Carsten Brandt <mail@cebe.cc>
@@ -74,4 +78,22 @@ interface ActiveQueryInterface extends QueryInterface
 	 * @return static the query object itself
 	 */
 	public function with();
+
+	/**
+	 * Specifies the relation associated with the pivot table for use in relational query.
+	 * @param string $relationName the relation name. This refers to a relation declared in the [[ActiveRelationTrait::primaryModel|primaryModel]] of the relation.
+	 * @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
+	 * Its signature should be `function($query)`, where `$query` is the query to be customized.
+	 * @return static the relation object itself.
+	 */
+	public function via($relationName, $callable = null);
+
+	/**
+	 * Finds the related records for the specified primary record.
+	 * This method is invoked when a relation of an ActiveRecord is being accessed in a lazy fashion.
+	 * @param string $name the relation name
+	 * @param ActiveRecordInterface $model the primary model
+	 * @return mixed the related record(s)
+	 */
+	public function findFor($name, $model);
 }
diff --git a/framework/db/ActiveQueryTrait.php b/framework/db/ActiveQueryTrait.php
index 97eb312..fb2f9f9 100644
--- a/framework/db/ActiveQueryTrait.php
+++ b/framework/db/ActiveQueryTrait.php
@@ -169,7 +169,7 @@ trait ActiveQueryTrait
 	/**
 	 * @param ActiveRecord $model
 	 * @param array $with
-	 * @return ActiveRelationInterface[]
+	 * @return ActiveQueryInterface[]
 	 */
 	private function normalizeRelations($model, $with)
 	{
diff --git a/framework/db/ActiveRecord.php b/framework/db/ActiveRecord.php
index ca630e0..e7461c6 100644
--- a/framework/db/ActiveRecord.php
+++ b/framework/db/ActiveRecord.php
@@ -15,7 +15,61 @@ use yii\helpers\StringHelper;
 /**
  * ActiveRecord is the base class for classes representing relational data in terms of objects.
  *
- * @include @yii/db/ActiveRecord.md
+ * Active Record implements the [Active Record design pattern](http://en.wikipedia.org/wiki/Active_record).
+ * The premise behind Active Record is that an individual [[ActiveRecord]] object is associated with a specific
+ * row in a database table. The object's attributes are mapped to the columns of the corresponding table.
+ * Referencing an Active Record attribute is equivalent to accessing the corresponding table column for that record.
+ *
+ * As an example, say that the `Customer` ActiveRecord class is associated with the `tbl_customer` table.
+ * This would mean that the class's `name` attribute is automatically mapped to the `name` column in `tbl_customer`.
+ * Thanks to Active Record, assuming the variable `$customer` is an object of type `Customer`, to get the value of
+ * the `name` column for the table row, you can use the expression `$customer->name`.
+ * In this example, Active Record is providing an object-oriented interface for accessing data stored in the database.
+ * But Active Record provides much more functionality than this.
+ *
+ * To declare an ActiveRecord class you need to extend [[\yii\db\ActiveRecord]] and
+ * implement the `tableName` method:
+ *
+ * ```php
+ * <?php
+ *
+ * class Customer extends \yii\db\ActiveRecord
+ * {
+ *     /**
+ *      * @return string the name of the table associated with this ActiveRecord class.
+ *      * /
+ *     public static function tableName()
+ *     {
+ *         return 'tbl_customer';
+ *     }
+ * }
+ * ```
+ *
+ * The `tableName` method only has to return the name of the database table associated with the class.
+ *
+ * > Tip: You may also use the [Gii code generator][guide-gii] to generate ActiveRecord classes from your
+ * > database tables.
+ *
+ * Class instances are obtained in one of two ways:
+ *
+ * * Using the `new` operator to create a new, empty object
+ * * Using a method to fetch an existing record (or records) from the database
+ *
+ * Here is a short teaser how working with an ActiveRecord looks like:
+ *
+ * ```php
+ * $user = new User();
+ * $user->name = 'Qiang';
+ * $user->save();  // a new row is inserted into tbl_user
+ *
+ * // the following will retrieve the user 'CeBe' from the database
+ * $user = User::find()->where(['name' => 'CeBe'])->one();
+ *
+ * // this will get related records from table tbl_orders when relation is defined
+ * $orders = $user->orders;
+ * ```
+ *
+ * For more details and usage information on ActiveRecord, see the [guide article on ActiveRecord][guide-active-record].
  *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @author Carsten Brandt <mail@cebe.cc>
@@ -151,27 +205,30 @@ class ActiveRecord extends BaseActiveRecord
 	/**
 	 * Creates an [[ActiveQuery]] instance.
 	 *
-	 * This method is called by [[find()]], [[findBySql()]] to start a SELECT query.
+	 * This method is called by [[find()]], [[findBySql()]] to start a SELECT query but also
+	 * by [[hasOne()]] and [[hasMany()]] to create a relational query.
 	 * You may override this method to return a customized query (e.g. `CustomerQuery` specified
 	 * written for querying `Customer` purpose.)
 	 *
 	 * You may also define default conditions that should apply to all queries unless overridden:
 	 *
 	 * ```php
-	 * public static function createQuery()
+	 * public static function createQuery($config = [])
 	 * {
-	 *     return parent::createQuery()->where(['deleted' => false]);
+	 *     return parent::createQuery($config)->where(['deleted' => false]);
 	 * }
 	 * ```
 	 *
 	 * Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
 	 * default condition. Using [[Query::where()]] will override the default condition.
 	 *
+	 * @param array $config the configuration passed to the ActiveQuery class.
 	 * @return ActiveQuery the newly created [[ActiveQuery]] instance.
 	 */
-	public static function createQuery()
+	public static function createQuery($config = [])
 	{
-		return new ActiveQuery(['modelClass' => get_called_class()]);
+		$config['modelClass'] = get_called_class();
+		return new ActiveQuery($config);
 	}
 
 	/**
@@ -263,18 +320,6 @@ class ActiveRecord extends BaseActiveRecord
 	}
 
 	/**
-	 * Creates an [[ActiveRelation]] instance.
-	 * This method is called by [[hasOne()]] and [[hasMany()]] to create a relation instance.
-	 * You may override this method to return a customized relation.
-	 * @param array $config the configuration passed to the ActiveRelation class.
-	 * @return ActiveRelation the newly created [[ActiveRelation]] instance.
-	 */
-	public static function createRelation($config = [])
-	{
-		return new ActiveRelation($config);
-	}
-
-	/**
 	 * @inheritdoc
 	 */
 	public static function populateRecord($record, $row)
@@ -332,7 +377,7 @@ class ActiveRecord extends BaseActiveRecord
 			return false;
 		}
 		$db = static::getDb();
-		if ($this->isTransactional(self::OP_INSERT) && $db->getTransaction() === null) {
+		if ($this->isTransactional(self::OP_INSERT)) {
 			$transaction = $db->beginTransaction();
 			try {
 				$result = $this->insertInternal($attributes);
@@ -352,9 +397,12 @@ class ActiveRecord extends BaseActiveRecord
 	}
 
 	/**
-	 * @see ActiveRecord::insert()
+	 * Inserts an ActiveRecord into DB without considering transaction.
+	 * @param array $attributes list of attributes that need to be saved. Defaults to null,
+	 * meaning all attributes that are loaded from DB will be saved.
+	 * @return boolean whether the record is inserted successfully.
 	 */
-	private function insertInternal($attributes = null)
+	protected function insertInternal($attributes = null)
 	{
 		if (!$this->beforeSave(true)) {
 			return false;
@@ -444,7 +492,7 @@ class ActiveRecord extends BaseActiveRecord
 			return false;
 		}
 		$db = static::getDb();
-		if ($this->isTransactional(self::OP_UPDATE) && $db->getTransaction() === null) {
+		if ($this->isTransactional(self::OP_UPDATE)) {
 			$transaction = $db->beginTransaction();
 			try {
 				$result = $this->updateInternal($attributes);
@@ -485,36 +533,49 @@ class ActiveRecord extends BaseActiveRecord
 	public function delete()
 	{
 		$db = static::getDb();
-		$transaction = $this->isTransactional(self::OP_DELETE) && $db->getTransaction() === null ? $db->beginTransaction() : null;
-		try {
-			$result = false;
-			if ($this->beforeDelete()) {
-				// we do not check the return value of deleteAll() because it's possible
-				// the record is already deleted in the database and thus the method will return 0
-				$condition = $this->getOldPrimaryKey(true);
-				$lock = $this->optimisticLock();
-				if ($lock !== null) {
-					$condition[$lock] = $this->$lock;
-				}
-				$result = $this->deleteAll($condition);
-				if ($lock !== null && !$result) {
-					throw new StaleObjectException('The object being deleted is outdated.');
-				}
-				$this->setOldAttributes(null);
-				$this->afterDelete();
-			}
-			if ($transaction !== null) {
+		if ($this->isTransactional(self::OP_DELETE)) {
+			$transaction = $db->beginTransaction();
+			try {
+				$result = $this->deleteInternal();
 				if ($result === false) {
 					$transaction->rollBack();
 				} else {
 					$transaction->commit();
 				}
-			}
-		} catch (\Exception $e) {
-			if ($transaction !== null) {
+			} catch (\Exception $e) {
 				$transaction->rollBack();
+				throw $e;
+			}
+		} else {
+			$result = $this->deleteInternal();
+		}
+
+		return $result;
+	}
+
+	/**
+	 * Deletes an ActiveRecord without considering transaction.
+	 * @return integer|boolean the number of rows deleted, or false if the deletion is unsuccessful for some reason.
+	 * Note that it is possible the number of rows deleted is 0, even though the deletion execution is successful.
+	 * @throws StaleObjectException
+	 */
+	protected function deleteInternal()
+	{
+		$result = false;
+		if ($this->beforeDelete()) {
+			// we do not check the return value of deleteAll() because it's possible
+			// the record is already deleted in the database and thus the method will return 0
+			$condition = $this->getOldPrimaryKey(true);
+			$lock = $this->optimisticLock();
+			if ($lock !== null) {
+				$condition[$lock] = $this->$lock;
+			}
+			$result = $this->deleteAll($condition);
+			if ($lock !== null && !$result) {
+				throw new StaleObjectException('The object being deleted is outdated.');
 			}
-			throw $e;
+			$this->setOldAttributes(null);
+			$this->afterDelete();
 		}
 		return $result;
 	}
diff --git a/framework/db/ActiveRecordInterface.php b/framework/db/ActiveRecordInterface.php
index 7c7416b..47cdb75 100644
--- a/framework/db/ActiveRecordInterface.php
+++ b/framework/db/ActiveRecordInterface.php
@@ -112,25 +112,29 @@ interface ActiveRecordInterface
 	/**
 	 * Creates an [[ActiveQueryInterface|ActiveQuery]] instance.
 	 *
-	 * This method is called by [[find()]] to start a SELECT query.
+	 * This method is called by [[find()]] to start a SELECT query but also
+	 * by [[BaseActiveRecord::hasOne()]] and [[BaseActiveRecord::hasMany()]] to
+	 * create a relational query.
+	 *
 	 * You may override this method to return a customized query (e.g. `CustomerQuery` specified
 	 * written for querying `Customer` purpose.)
 	 *
 	 * You may also define default conditions that should apply to all queries unless overridden:
 	 *
 	 * ```php
-	 * public static function createQuery()
+	 * public static function createQuery($config = [])
 	 * {
-	 *     return parent::createQuery()->where(['deleted' => false]);
+	 *     return parent::createQuery($config)->where(['deleted' => false]);
 	 * }
 	 * ```
 	 *
 	 * Note that all queries should use [[Query::andWhere()]] and [[Query::orWhere()]] to keep the
 	 * default condition. Using [[Query::where()]] will override the default condition.
 	 *
+	 * @param array $config the configuration passed to the ActiveQuery class.
 	 * @return ActiveQueryInterface the newly created [[ActiveQueryInterface|ActiveQuery]] instance.
 	 */
-	public static function createQuery();
+	public static function createQuery($config = []);
 
 	/**
 	 * Updates records using the provided attribute values and conditions.
@@ -256,21 +260,12 @@ interface ActiveRecordInterface
 	public function equals($record);
 
 	/**
-	 * Creates an [[ActiveRelationInterface|ActiveRelation]] instance.
-	 * This method is called by [[BaseActiveRecord::hasOne()]] and [[BaseActiveRecord::hasMany()]] to
-	 * create a relation instance.
-	 * You may override this method to return a customized relation.
-	 * @param array $config the configuration passed to the ActiveRelation class.
-	 * @return ActiveRelation the newly created [[ActiveRelation]] instance.
-	 */
-	public static function createRelation($config = []);
-
-	/**
 	 * Returns the relation object with the specified name.
-	 * A relation is defined by a getter method which returns an [[ActiveRelationInterface|ActiveRelation]] object.
+	 * A relation is defined by a getter method which returns an object implementing the [[ActiveQueryInterface]]
+	 * (normally this would be a relational [[ActiveQuery]] object).
 	 * It can be declared in either the ActiveRecord class itself or one of its behaviors.
 	 * @param string $name the relation name
-	 * @return ActiveRelation the relation object
+	 * @return ActiveQueryInterface the relational query object
 	 */
 	public function getRelation($name);
 
@@ -290,7 +285,7 @@ interface ActiveRecordInterface
 	 * @param static $model the record to be linked with the current one.
 	 * @param array $extraColumns additional column values to be saved into the pivot table.
 	 * This parameter is only meaningful for a relationship involving a pivot table
-	 * (i.e., a relation set with `[[ActiveRelationInterface::via()]]`.)
+	 * (i.e., a relation set with `[[ActiveQueryInterface::via()]]`.)
 	 */
 	public function link($name, $model, $extraColumns = []);
 
diff --git a/framework/db/ActiveRelation.php b/framework/db/ActiveRelation.php
deleted file mode 100644
index 3acd211..0000000
--- a/framework/db/ActiveRelation.php
+++ /dev/null
@@ -1,135 +0,0 @@
-<?php
-/**
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @link http://www.yiiframework.com/
- * @copyright Copyright (c) 2008 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
-
-namespace yii\db;
-
-/**
- * ActiveRelation represents a relation between two Active Record classes.
- *
- * ActiveRelation instances are usually created by calling [[ActiveRecord::hasOne()]] and
- * [[ActiveRecord::hasMany()]]. An Active Record class declares a relation by defining
- * a getter method which calls one of the above methods and returns the created ActiveRelation object.
- *
- * A relation is specified by [[link]] which represents the association between columns
- * of different tables; and the multiplicity of the relation is indicated by [[multiple]].
- *
- * If a relation involves a pivot table, it may be specified by [[via()]] or [[viaTable()]] method.
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @author Carsten Brandt <mail@cebe.cc>
- * @since 2.0
- */
-class ActiveRelation extends ActiveQuery implements ActiveRelationInterface
-{
-	use ActiveRelationTrait;
-
-	/**
-	 * @var string|array the join condition. Please refer to [[Query::where()]] on how to specify this parameter.
-	 * The condition will be used in the ON part when [[ActiveQuery::joinWith()]] is called.
-	 * Otherwise, the condition will be used in the WHERE part of a query.
-	 */
-	public $on;
-
-	/**
-	 * Sets the ON condition for the query.
-	 * The condition will be used in the ON part when [[ActiveQuery::joinWith()]] is called.
-	 * Otherwise, the condition will be used in the WHERE part of a query.
-	 * @param string|array $condition the ON condition. Please refer to [[Query::where()]] on how to specify this parameter.
-	 * @param array $params the parameters (name => value) to be bound to the query.
-	 * @return static the query object itself
-	 */
-	public function onCondition($condition, $params = [])
-	{
-		$this->on = $condition;
-		$this->addParams($params);
-		return $this;
-	}
-
-	/**
-	 * Specifies the pivot table.
-	 * @param string $tableName the name of the pivot table.
-	 * @param array $link the link between the pivot table and the table associated with [[primaryModel]].
-	 * The keys of the array represent the columns in the pivot table, and the values represent the columns
-	 * in the [[primaryModel]] table.
-	 * @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
-	 * Its signature should be `function($query)`, where `$query` is the query to be customized.
-	 * @return static
-	 */
-	public function viaTable($tableName, $link, $callable = null)
-	{
-		$relation = new ActiveRelation([
-			'modelClass' => get_class($this->primaryModel),
-			'from' => [$tableName],
-			'link' => $link,
-			'multiple' => true,
-			'asArray' => true,
-		]);
-		$this->via = $relation;
-		if ($callable !== null) {
-			call_user_func($callable, $relation);
-		}
-		return $this;
-	}
-
-	/**
-	 * Creates a DB command that can be used to execute this query.
-	 * @param Connection $db the DB connection used to create the DB command.
-	 * If null, the DB connection returned by [[modelClass]] will be used.
-	 * @return Command the created DB command instance.
-	 */
-	public function createCommand($db = null)
-	{
-		if ($this->primaryModel === null) {
-			// eager loading
-			if (!empty($this->on)) {
-				$where = $this->where;
-				$this->andWhere($this->on);
-				$command = parent::createCommand($db);
-				$this->where = $where;
-				return $command;
-			} else {
-				return parent::createCommand($db);
-			}
-		}
-
-		// lazy loading
-
-		$where = $this->where;
-
-		if ($this->via instanceof self) {
-			// via pivot table
-			$viaModels = $this->via->findPivotRows([$this->primaryModel]);
-			$this->filterByModels($viaModels);
-		} elseif (is_array($this->via)) {
-			// via relation
-			/** @var ActiveRelation $viaQuery */
-			list($viaName, $viaQuery) = $this->via;
-			if ($viaQuery->multiple) {
-				$viaModels = $viaQuery->all();
-				$this->primaryModel->populateRelation($viaName, $viaModels);
-			} else {
-				$model = $viaQuery->one();
-				$this->primaryModel->populateRelation($viaName, $model);
-				$viaModels = $model === null ? [] : [$model];
-			}
-			$this->filterByModels($viaModels);
-		} else {
-			$this->filterByModels([$this->primaryModel]);
-		}
-
-		if (!empty($this->on)) {
-			$this->andWhere($this->on);
-		}
-
-		$command = parent::createCommand($db);
-
-		$this->where = $where;
-
-		return $command;
-	}
-}
diff --git a/framework/db/ActiveRelationInterface.php b/framework/db/ActiveRelationInterface.php
deleted file mode 100644
index 27e4d13..0000000
--- a/framework/db/ActiveRelationInterface.php
+++ /dev/null
@@ -1,29 +0,0 @@
-<?php
-/**
- * @link http://www.yiiframework.com/
- * @copyright Copyright (c) 2008 Yii Software LLC
- * @license http://www.yiiframework.com/license/
- */
-
-namespace yii\db;
-
-/**
- * ActiveRelationInterface defines the common interface to be implemented by active record relation classes.
- *
- * A class implementing this interface should also use [[ActiveRelationTrait]].
- *
- * @author Qiang Xue <qiang.xue@gmail.com>
- * @author Carsten Brandt <mail@cebe.cc>
- * @since 2.0
- */
-interface ActiveRelationInterface extends ActiveQueryInterface
-{
-	/**
-	 * Specifies the relation associated with the pivot table.
-	 * @param string $relationName the relation name. This refers to a relation declared in the [[ActiveRelationTrait::primaryModel|primaryModel]] of the relation.
-	 * @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
-	 * Its signature should be `function($query)`, where `$query` is the query to be customized.
-	 * @return static the relation object itself.
-	 */
-	public function via($relationName, $callable = null);
-}
diff --git a/framework/db/ActiveRelationTrait.php b/framework/db/ActiveRelationTrait.php
index dac3028..5d072bc 100644
--- a/framework/db/ActiveRelationTrait.php
+++ b/framework/db/ActiveRelationTrait.php
@@ -8,9 +8,10 @@
 namespace yii\db;
 
 use yii\base\InvalidConfigException;
+use yii\base\InvalidParamException;
 
 /**
- * ActiveRelationTrait implements the common methods and properties for active record relation classes.
+ * ActiveRelationTrait implements the common methods and properties for active record relational queries.
  *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @author Carsten Brandt <mail@cebe.cc>
@@ -19,27 +20,44 @@ use yii\base\InvalidConfigException;
 trait ActiveRelationTrait
 {
 	/**
-	 * @var boolean whether this relation should populate all query results into AR instances.
-	 * If false, only the first row of the results will be retrieved.
+	 * @var boolean whether this query represents a relation to more than one record.
+	 * This property is only used in relational context. If true, this relation will
+	 * populate all query results into AR instances using [[all()]].
+	 * If false, only the first row of the results will be retrieved using [[one()]].
 	 */
 	public $multiple;
 	/**
-	 * @var ActiveRecord the primary model that this relation is associated with.
+	 * @var ActiveRecord the primary model of a relational query.
 	 * This is used only in lazy loading with dynamic query options.
 	 */
 	public $primaryModel;
 	/**
-	 * @var array the columns of the primary and foreign tables that establish the relation.
+	 * @var array the columns of the primary and foreign tables that establish a relation.
 	 * The array keys must be columns of the table for this relation, and the array values
 	 * must be the corresponding columns from the primary table.
 	 * Do not prefix or quote the column names as this will be done automatically by Yii.
+	 * This property is only used in relational context.
 	 */
 	public $link;
 	/**
 	 * @var array the query associated with the pivot table. Please call [[via()]]
 	 * to set this property instead of directly setting it.
+	 * This property is only used in relational context.
+	 * @see via()
 	 */
 	public $via;
+	/**
+	 * @var string the name of the relation that is the inverse of this relation.
+	 * For example, an order has a customer, which means the inverse of the "customer" relation
+	 * is the "orders", and the inverse of the "orders" relation is the "customer".
+	 * If this property is set, the primary record(s) will be referenced through the specified relation.
+	 * For example, `$customer->orders[0]->customer` and `$customer` will be the same object,
+	 * and accessing the customer of an order will not trigger new DB query.
+	 * This property is only used in relational context.
+	 * @see inverseOf()
+	 */
+	public $inverseOf;
+
 
 	/**
 	 * Clones internal objects.
@@ -56,6 +74,22 @@ trait ActiveRelationTrait
 
 	/**
 	 * Specifies the relation associated with the pivot table.
+	 *
+	 * Use this method to specify a pivot record/table when declaring a relation in the [[ActiveRecord]] class:
+	 *
+	 * ```php
+	 * public function getOrders()
+	 * {
+	 *     return $this->hasOne(Order::className(), ['id' => 'order_id']);
+	 * }
+	 *
+	 * public function getOrderItems()
+	 * {
+	 *     return $this->hasMany(Item::className(), ['id' => 'item_id'])
+	 *                 ->via('orders', ['order_id' => 'id']);
+	 * }
+	 * ```
+	 *
 	 * @param string $relationName the relation name. This refers to a relation declared in [[primaryModel]].
 	 * @param callable $callable a PHP callback for customizing the relation associated with the pivot table.
 	 * Its signature should be `function($query)`, where `$query` is the query to be customized.
@@ -72,6 +106,77 @@ trait ActiveRelationTrait
 	}
 
 	/**
+	 * Sets the name of the relation that is the inverse of this relation.
+	 * For example, an order has a customer, which means the inverse of the "customer" relation
+	 * is the "orders", and the inverse of the "orders" relation is the "customer".
+	 * If this property is set, the primary record(s) will be referenced through the specified relation.
+	 * For example, `$customer->orders[0]->customer` and `$customer` will be the same object,
+	 * and accessing the customer of an order will not trigger a new DB query.
+	 *
+	 * Use this method when declaring a relation in the [[ActiveRecord]] class:
+	 *
+	 * ```php
+	 * public function getOrders()
+	 * {
+	 *     return $this->hasMany(Order::className(), ['customer_id' => 'id'])->inverseOf('customer');
+	 * }
+	 * ```
+	 *
+	 * @param string $relationName the name of the relation that is the inverse of this relation.
+	 * @return static the relation object itself.
+	 */
+	public function inverseOf($relationName)
+	{
+		$this->inverseOf = $relationName;
+		return $this;
+	}
+
+	/**
+	 * Finds the related records for the specified primary record.
+	 * This method is invoked when a relation of an ActiveRecord is being accessed in a lazy fashion.
+	 * @param string $name the relation name
+	 * @param ActiveRecordInterface $model the primary model
+	 * @return mixed the related record(s)
+	 * @throws InvalidParamException if the relation is invalid
+	 */
+	public function findFor($name, $model)
+	{
+		if (method_exists($model, 'get' . $name)) {
+			$method = new \ReflectionMethod($model, 'get' . $name);
+			$realName = lcfirst(substr($method->getName(), 3));
+			if ($realName !== $name) {
+				throw new InvalidParamException('Relation names are case sensitive. ' . get_class($model) . " has a relation named \"$realName\" instead of \"$name\".");
+			}
+		}
+
+		$related = $this->multiple ? $this->all() : $this->one();
+
+		if ($this->inverseOf === null || empty($related)) {
+			return $related;
+		}
+
+		$inverseRelation = (new $this->modelClass)->getRelation($this->inverseOf);
+
+		if ($this->multiple) {
+			foreach ($related as $i => $relatedModel) {
+				if ($relatedModel instanceof ActiveRecordInterface) {
+					$relatedModel->populateRelation($this->inverseOf, $inverseRelation->multiple ? [$model] : $model);
+				} else {
+					$related[$i][$this->inverseOf] = $inverseRelation->multiple ? [$model] : $model;
+				}
+			}
+		} else {
+			if ($related instanceof ActiveRecordInterface) {
+				$related->populateRelation($this->inverseOf, $inverseRelation->multiple ? [$model] : $model);
+			} else {
+				$related[$this->inverseOf] = $inverseRelation->multiple ? [$model] : $model;
+			}
+		}
+
+		return $related;
+	}
+
+	/**
 	 * Finds the related records and populates them into the primary models.
 	 * @param string $name the relation name
 	 * @param array $primaryModels primary models
@@ -109,6 +214,9 @@ trait ActiveRelationTrait
 				} else {
 					$primaryModels[$i][$name] = $model;
 				}
+				if ($this->inverseOf !== null) {
+					$this->populateInverseRelation($primaryModels, [$model], $name, $this->inverseOf);
+				}
 			}
 			return [$model];
 		} else {
@@ -129,18 +237,73 @@ trait ActiveRelationTrait
 					$primaryModels[$i][$name] = $value;
 				}
 			}
+			if ($this->inverseOf !== null) {
+				$this->populateInverseRelation($primaryModels, $models, $name, $this->inverseOf);
+			}
 			return $models;
 		}
 	}
 
+	private function populateInverseRelation(&$primaryModels, $models, $primaryName, $name)
+	{
+		if (empty($models) || empty($primaryModels)) {
+			return;
+		}
+		$model = reset($models);
+		$relation = $model instanceof ActiveRecordInterface ? $model->getRelation($name) : (new $this->modelClass)->getRelation($name);
+
+		if ($relation->multiple) {
+			$buckets = $this->buildBuckets($primaryModels, $relation->link, null, null, false);
+			if ($model instanceof ActiveRecordInterface) {
+				foreach ($models as $model) {
+					$key = $this->getModelKey($model, $relation->link);
+					$model->populateRelation($name, isset($buckets[$key]) ? $buckets[$key] : []);
+				}
+			} else {
+				foreach ($primaryModels as $i => $primaryModel) {
+					if ($this->multiple) {
+						foreach ($primaryModel as $j => $m) {
+							$key = $this->getModelKey($m, $relation->link);
+							$primaryModels[$i][$j][$name] = isset($buckets[$key]) ? $buckets[$key] : [];
+						}
+					} elseif (!empty($primaryModel[$primaryName])) {
+						$key = $this->getModelKey($primaryModel[$primaryName], $relation->link);
+						$primaryModels[$i][$primaryName][$name] = isset($buckets[$key]) ? $buckets[$key] : [];
+					}
+				}
+			}
+		} else {
+			if ($this->multiple) {
+				foreach ($primaryModels as $i => $primaryModel) {
+					foreach ($primaryModel[$primaryName] as $j => $m) {
+						if ($m instanceof ActiveRecordInterface) {
+							$m->populateRelation($name, $primaryModel);
+						} else {
+							$primaryModels[$i][$primaryName][$j][$name] = $primaryModel;
+						}
+					}
+				}
+			} else {
+				foreach ($primaryModels as $i => $primaryModel) {
+					if ($primaryModels[$i][$primaryName] instanceof ActiveRecordInterface) {
+						$primaryModels[$i][$primaryName]->populateRelation($name, $primaryModel);
+					} elseif  (!empty($primaryModels[$i][$primaryName])) {
+						$primaryModels[$i][$primaryName][$name] = $primaryModel;
+					}
+				}
+			}
+		}
+	}
+
 	/**
 	 * @param array $models
 	 * @param array $link
 	 * @param array $viaModels
 	 * @param array $viaLink
+	 * @param boolean $checkMultiple
 	 * @return array
 	 */
-	private function buildBuckets($models, $link, $viaModels = null, $viaLink = null)
+	private function buildBuckets($models, $link, $viaModels = null, $viaLink = null, $checkMultiple = true)
 	{
 		if ($viaModels !== null) {
 			$map = [];
@@ -180,7 +343,7 @@ trait ActiveRelationTrait
 			}
 		}
 
-		if (!$this->multiple) {
+		if ($checkMultiple && !$this->multiple) {
 			foreach ($buckets as $i => $bucket) {
 				$buckets[$i] = reset($bucket);
 			}
diff --git a/framework/db/BaseActiveRecord.php b/framework/db/BaseActiveRecord.php
index 05b0e0e..2f4da5c 100644
--- a/framework/db/BaseActiveRecord.php
+++ b/framework/db/BaseActiveRecord.php
@@ -93,7 +93,32 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 	/**
 	 * Creates an [[ActiveQuery]] instance for query purpose.
 	 *
-	 * @include @yii/db/ActiveRecord-find.md
+	 * The returned [[ActiveQuery]] instance can be further customized by calling
+	 * methods defined in [[ActiveQuery]] before `one()`, `all()` or `value()` is
+	 * called to return the populated active records:
+	 *
+	 * ~~~
+	 * // find all customers
+	 * $customers = Customer::find()->all();
+	 *
+	 * // find all active customers and order them by their age:
+	 * $customers = Customer::find()
+	 * ->where(['status' => 1])
+	 * ->orderBy('age')
+	 * ->all();
+	 *
+	 * // find a single customer whose primary key value is 10
+	 * $customer = Customer::find(10);
+	 *
+	 * // the above is equivalent to:
+	 * $customer = Customer::find()->where(['id' => 10])->one();
+	 *
+	 * // find a single customer whose age is 30 and whose status is 1
+	 * $customer = Customer::find(['age' => 30, 'status' => 1]);
+	 *
+	 * // the above is equivalent to:
+	 * $customer = Customer::find()->where(['age' => 30, 'status' => 1])->one();
+	 * ~~~
 	 *
 	 * @param mixed $q the query parameter. This can be one of the followings:
 	 *
@@ -230,18 +255,11 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 				return $this->_related[$name];
 			}
 			$value = parent::__get($name);
-			if ($value instanceof ActiveRelationInterface) {
-				if (method_exists($this, 'get' . $name)) {
-					$method = new \ReflectionMethod($this, 'get' . $name);
-					$realName = lcfirst(substr($method->getName(), 3));
-					if ($realName !== $name) {
-						throw new InvalidParamException('Relation names are case sensitive. ' . get_class($this) . " has a relation named \"$realName\" instead of \"$name\".");
-					}
-				}
-				$this->populateRelation($name, $value->multiple ? $value->all() : $value->one());
-				return $this->_related[$name];
+			if ($value instanceof ActiveQueryInterface) {
+				return $this->_related[$name] = $value->findFor($name, $this);
+			} else {
+				return $value;
 			}
-			return $value;
 		}
 	}
 
@@ -296,7 +314,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 
 	/**
 	 * Declares a `has-one` relation.
-	 * The declaration is returned in terms of an [[ActiveRelation]] instance
+	 * The declaration is returned in terms of a relational [[ActiveQuery]] instance
 	 * through which the related record can be queried and retrieved back.
 	 *
 	 * A `has-one` relation means that there is at most one related record matching
@@ -316,18 +334,18 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 	 * in the related class `Country`, while the 'country_id' value refers to an attribute name
 	 * in the current AR class.
 	 *
-	 * Call methods declared in [[ActiveRelation]] to further customize the relation.
+	 * Call methods declared in [[ActiveQuery]] to further customize the relation.
 	 *
 	 * @param string $class the class name of the related record
 	 * @param array $link the primary-foreign key constraint. The keys of the array refer to
 	 * the attributes of the record associated with the `$class` model, while the values of the
 	 * array refer to the corresponding attributes in **this** AR class.
-	 * @return ActiveRelationInterface the relation object.
+	 * @return ActiveQueryInterface the relational query object.
 	 */
 	public function hasOne($class, $link)
 	{
 		/** @var ActiveRecord $class */
-		return $class::createRelation([
+		return $class::createQuery([
 			'modelClass' => $class,
 			'primaryModel' => $this,
 			'link' => $link,
@@ -337,7 +355,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 
 	/**
 	 * Declares a `has-many` relation.
-	 * The declaration is returned in terms of an [[ActiveRelation]] instance
+	 * The declaration is returned in terms of a relational [[ActiveQuery]] instance
 	 * through which the related record can be queried and retrieved back.
 	 *
 	 * A `has-many` relation means that there are multiple related records matching
@@ -357,16 +375,18 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 	 * an attribute name in the related class `Order`, while the 'id' value refers to
 	 * an attribute name in the current AR class.
 	 *
+	 * Call methods declared in [[ActiveQuery]] to further customize the relation.
+	 *
 	 * @param string $class the class name of the related record
 	 * @param array $link the primary-foreign key constraint. The keys of the array refer to
 	 * the attributes of the record associated with the `$class` model, while the values of the
 	 * array refer to the corresponding attributes in **this** AR class.
-	 * @return ActiveRelationInterface the relation object.
+	 * @return ActiveQueryInterface the relational query object.
 	 */
 	public function hasMany($class, $link)
 	{
 		/** @var ActiveRecord $class */
-		return $class::createRelation([
+		return $class::createQuery([
 			'modelClass' => $class,
 			'primaryModel' => $this,
 			'link' => $link,
@@ -1042,10 +1062,10 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 
 	/**
 	 * Returns the relation object with the specified name.
-	 * A relation is defined by a getter method which returns an [[ActiveRelation]] object.
+	 * A relation is defined by a getter method which returns an [[ActiveQueryInterface]] object.
 	 * It can be declared in either the Active Record class itself or one of its behaviors.
 	 * @param string $name the relation name
-	 * @return ActiveRelation the relation object
+	 * @return ActiveQueryInterface|ActiveQuery the relational query object
 	 * @throws InvalidParamException if the named relation does not exist.
 	 */
 	public function getRelation($name)
@@ -1057,7 +1077,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 		} catch (UnknownMethodException $e) {
 			throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".', 0, $e);
 		}
-		if (!$relation instanceof ActiveRelationInterface) {
+		if (!$relation instanceof ActiveQueryInterface) {
 			throw new InvalidParamException(get_class($this) . ' has no relation named "' . $name . '".');
 		}
 
@@ -1089,7 +1109,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 	 * @param ActiveRecord $model the model to be linked with the current one.
 	 * @param array $extraColumns additional column values to be saved into the pivot table.
 	 * This parameter is only meaningful for a relationship involving a pivot table
-	 * (i.e., a relation set with `[[ActiveRelation::via()]]` or `[[ActiveRelation::viaTable()]]`.)
+	 * (i.e., a relation set with [[ActiveRelationTrait::via()]] or `[[ActiveQuery::viaTable()]]`.)
 	 * @throws InvalidCallException if the method is unable to link two models.
 	 */
 	public function link($name, $model, $extraColumns = [])
@@ -1101,7 +1121,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 				throw new InvalidCallException('Unable to link models: both models must NOT be newly created.');
 			}
 			if (is_array($relation->via)) {
-				/** @var ActiveRelation $viaRelation */
+				/** @var ActiveQuery $viaRelation */
 				list($viaName, $viaRelation) = $relation->via;
 				$viaClass = $viaRelation->modelClass;
 				// unset $viaName so that it can be reloaded to reflect the change
@@ -1185,7 +1205,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 
 		if ($relation->via !== null) {
 			if (is_array($relation->via)) {
-				/** @var ActiveRelation $viaRelation */
+				/** @var ActiveQuery $viaRelation */
 				list($viaName, $viaRelation) = $relation->via;
 				$viaClass = $viaRelation->modelClass;
 				unset($this->_related[$viaName]);
@@ -1256,8 +1276,8 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 
 	/**
 	 * @param array $link
-	 * @param ActiveRecord $foreignModel
-	 * @param ActiveRecord $primaryModel
+	 * @param BaseActiveRecord $foreignModel
+	 * @param BaseActiveRecord $primaryModel
 	 * @throws InvalidCallException
 	 */
 	private function bindModels($link, $foreignModel, $primaryModel)
diff --git a/framework/db/Migration.php b/framework/db/Migration.php
index 0b51250..26659d1 100644
--- a/framework/db/Migration.php
+++ b/framework/db/Migration.php
@@ -139,7 +139,7 @@ class Migration extends \yii\base\Component
 	{
 		echo "    > execute SQL: $sql ...";
 		$time = microtime(true);
-		$this->db->createCommand($sql)->execute($params);
+		$this->db->createCommand($sql)->bindValues($params)->execute();
 		echo " done (time: " . sprintf('%.3f', microtime(true) - $time) . "s)\n";
 	}
 
diff --git a/framework/db/Query.php b/framework/db/Query.php
index 4e307b2..fa85d0d 100644
--- a/framework/db/Query.php
+++ b/framework/db/Query.php
@@ -356,7 +356,14 @@ class Query extends Component implements QueryInterface
 		$this->limit = $limit;
 		$this->offset = $offset;
 
-		return $command->queryScalar();
+		if (empty($this->groupBy)) {
+			return $command->queryScalar();
+		} else {
+			return (new Query)->select([$selectExpression])
+				->from(['c' => $this])
+				->createCommand($db)
+				->queryScalar();
+		}
 	}
 
 	/**
diff --git a/framework/db/QueryBuilder.php b/framework/db/QueryBuilder.php
index 8e7d7e8..9ca2678 100644
--- a/framework/db/QueryBuilder.php
+++ b/framework/db/QueryBuilder.php
@@ -177,7 +177,7 @@ class QueryBuilder extends \yii\base\Object
 				if (!is_array($value) && isset($columnSchemas[$columns[$i]])) {
 					$value = $columnSchemas[$columns[$i]]->typecast($value);
 				}
-				$vs[] = is_string($value) ? $this->db->quoteValue($value) : $value;
+				$vs[] = is_string($value) ? $this->db->quoteValue($value) : ($value === null ? 'NULL' : $value);
 			}
 			$values[] = '(' . implode(', ', $vs) . ')';
 		}
diff --git a/framework/db/oci/QueryBuilder.php b/framework/db/oci/QueryBuilder.php
index 6d1e9c1..2bec96c 100644
--- a/framework/db/oci/QueryBuilder.php
+++ b/framework/db/oci/QueryBuilder.php
@@ -12,6 +12,8 @@ use yii\base\InvalidParamException;
 /**
  * QueryBuilder is the query builder for Oracle databases.
  *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
  */
 class QueryBuilder extends \yii\db\QueryBuilder
 {
diff --git a/framework/grid/ActionColumn.php b/framework/grid/ActionColumn.php
index dcc305a..ffcfe0a 100644
--- a/framework/grid/ActionColumn.php
+++ b/framework/grid/ActionColumn.php
@@ -124,8 +124,8 @@ class ActionColumn extends Column
 			return call_user_func($this->urlCreator, $action, $model, $key, $index);
 		} else {
 			$params = is_array($key) ? $key : ['id' => (string)$key];
-			$route = $this->controller ? $this->controller . '/' . $action : $action;
-			return Yii::$app->controller->createUrl($route, $params);
+			$params[0] = $this->controller ? $this->controller . '/' . $action : $action;
+			return Yii::$app->controller->createUrl($params);
 		}
 	}
 
diff --git a/framework/grid/DataColumn.php b/framework/grid/DataColumn.php
index 0f53359..559e3ab 100644
--- a/framework/grid/DataColumn.php
+++ b/framework/grid/DataColumn.php
@@ -47,7 +47,7 @@ class DataColumn extends Column
 	 */
 	public $value;
 	/**
-	 * @var string|array in which format should the value of each data model be displayed as (e.g. "text", "html",
+	 * @var string|array in which format should the value of each data model be displayed as (e.g. "raw", "text", "html",
 	 * ['date', 'Y-m-d']). Supported formats are determined by the [[GridView::formatter|formatter]] used by
 	 * the [[GridView]]. Default format is "text" which will format the value as an HTML-encoded plain text when
 	 * [[\yii\base\Formatter::format()]] or [[\yii\i18n\Formatter::format()]] is used.
diff --git a/framework/helpers/BaseHtml.php b/framework/helpers/BaseHtml.php
index fc16ed6..01151ec 100644
--- a/framework/helpers/BaseHtml.php
+++ b/framework/helpers/BaseHtml.php
@@ -119,6 +119,9 @@ class BaseHtml
 	 *
 	 * For example when using `['class' => 'my-class', 'target' => '_blank', 'value' => null]` it will result in the
 	 * html attributes rendered like this: `class="my-class" target="_blank"`.
+	 *
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
+	 *
 	 * @return string the generated HTML tag
 	 * @see beginTag()
 	 * @see endTag()
@@ -135,6 +138,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated start tag
 	 * @see endTag()
 	 * @see tag()
@@ -163,6 +167,7 @@ class BaseHtml
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
 	 * If the options does not contain "type", a "type" attribute with value "text/css" will be used.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated style tag
 	 */
 	public static function style($content, $options = [])
@@ -177,6 +182,7 @@ class BaseHtml
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
 	 * If the options does not contain "type", a "type" attribute with value "text/javascript" will be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated script tag
 	 */
 	public static function script($content, $options = [])
@@ -190,6 +196,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated link tag
 	 * @see url()
 	 */
@@ -208,6 +215,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated script tag
 	 * @see url()
 	 */
@@ -227,6 +235,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated form start tag.
 	 * @see endForm()
 	 */
@@ -295,6 +304,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated hyperlink
 	 * @see url()
 	 */
@@ -316,6 +326,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated mailto link
 	 */
 	public static function mailto($text, $email = null, $options = [])
@@ -330,6 +341,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated image tag
 	 */
 	public static function img($src, $options = [])
@@ -351,6 +363,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated label tag
 	 */
 	public static function label($content, $for = null, $options = [])
@@ -367,6 +380,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated button tag
 	 */
 	public static function button($content = 'Button', $options = [])
@@ -382,6 +396,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated submit button tag
 	 */
 	public static function submitButton($content = 'Submit', $options = [])
@@ -398,6 +413,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated reset button tag
 	 */
 	public static function resetButton($content = 'Reset', $options = [])
@@ -414,6 +430,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated input tag
 	 */
 	public static function input($type, $name = null, $value = null, $options = [])
@@ -430,6 +447,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated button tag
 	 */
 	public static function buttonInput($label = 'Button', $options = [])
@@ -445,6 +463,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated button tag
 	 */
 	public static function submitInput($label = 'Submit', $options = [])
@@ -459,6 +478,7 @@ class BaseHtml
 	 * @param string $label the value attribute. If it is null, the value attribute will not be generated.
 	 * @param array $options the attributes of the button tag. The values will be HTML-encoded using [[encode()]].
 	 * Attributes whose value is null will be ignored and not put in the tag returned.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated button tag
 	 */
 	public static function resetInput($label = 'Reset', $options = [])
@@ -475,6 +495,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated button tag
 	 */
 	public static function textInput($name, $value = null, $options = [])
@@ -489,6 +510,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated button tag
 	 */
 	public static function hiddenInput($name, $value = null, $options = [])
@@ -503,6 +525,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated button tag
 	 */
 	public static function passwordInput($name, $value = null, $options = [])
@@ -520,6 +543,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated button tag
 	 */
 	public static function fileInput($name, $value = null, $options = [])
@@ -534,6 +558,7 @@ class BaseHtml
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
 	 * If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated text area tag
 	 */
 	public static function textarea($name, $value = '', $options = [])
@@ -561,6 +586,7 @@ class BaseHtml
 	 *
 	 * The rest of the options will be rendered as the attributes of the resulting radio button tag. The values will
 	 * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 *
 	 * @return string the generated radio button tag
 	 */
@@ -610,6 +636,7 @@ class BaseHtml
 	 *
 	 * The rest of the options will be rendered as the attributes of the resulting checkbox tag. The values will
 	 * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 *
 	 * @return string the generated checkbox tag
 	 */
@@ -670,6 +697,7 @@ class BaseHtml
 	 *
 	 * The rest of the options will be rendered as the attributes of the resulting tag. The values will
 	 * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 *
 	 * @return string the generated drop-down list tag
 	 */
@@ -716,6 +744,7 @@ class BaseHtml
 	 *
 	 * The rest of the options will be rendered as the attributes of the resulting tag. The values will
 	 * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 *
 	 * @return string the generated list box tag
 	 */
@@ -770,6 +799,9 @@ class BaseHtml
 	 * where $index is the zero-based index of the checkbox in the whole list; $label
 	 * is the label for the checkbox; and $name, $value and $checked represent the name,
 	 * value and the checked status of the checkbox input, respectively.
+	 *
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
+	 *
 	 * @return string the generated checkbox list
 	 */
 	public static function checkboxList($name, $selection = null, $items = [], $options = [])
@@ -838,6 +870,9 @@ class BaseHtml
 	 * where $index is the zero-based index of the radio button in the whole list; $label
 	 * is the label for the radio button; and $name, $value and $checked represent the name,
 	 * value and the checked status of the radio button input, respectively.
+	 *
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
+	 *
 	 * @return string the generated radio button list
 	 */
 	public static function radioList($name, $selection = null, $items = [], $options = [])
@@ -895,6 +930,8 @@ class BaseHtml
 	 * where $index is the array key corresponding to `$item` in `$items`. The callback should return
 	 * the whole list item tag.
 	 *
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
+	 *
 	 * @return string the generated unordered list. An empty string is returned if `$items` is empty.
 	 */
 	public static function ul($items, $options = [])
@@ -937,6 +974,8 @@ class BaseHtml
 	 * where $index is the array key corresponding to `$item` in `$items`. The callback should return
 	 * the whole list item tag.
 	 *
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
+	 *
 	 * @return string the generated ordered list. An empty string is returned if `$items` is empty.
 	 */
 	public static function ol($items, $options = [])
@@ -960,6 +999,8 @@ class BaseHtml
 	 *   If this is not set, [[Model::getAttributeLabel()]] will be called to get the label for display
 	 *   (after encoding).
 	 *
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
+	 *
 	 * @return string the generated label tag
 	 */
 	public static function activeLabel($model, $attribute, $options = [])
@@ -984,6 +1025,8 @@ class BaseHtml
 	 *
 	 * - tag: this specifies the tag name. If not set, "div" will be used.
 	 *
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
+	 *
 	 * @return string the generated label tag
 	 */
 	public static function error($model, $attribute, $options = [])
@@ -1005,6 +1048,7 @@ class BaseHtml
 	 * about attribute expression.
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated input tag
 	 */
 	public static function activeInput($type, $model, $attribute, $options = [])
@@ -1026,6 +1070,7 @@ class BaseHtml
 	 * about attribute expression.
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated input tag
 	 */
 	public static function activeTextInput($model, $attribute, $options = [])
@@ -1042,6 +1087,7 @@ class BaseHtml
 	 * about attribute expression.
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated input tag
 	 */
 	public static function activeHiddenInput($model, $attribute, $options = [])
@@ -1058,6 +1104,7 @@ class BaseHtml
 	 * about attribute expression.
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated input tag
 	 */
 	public static function activePasswordInput($model, $attribute, $options = [])
@@ -1074,6 +1121,7 @@ class BaseHtml
 	 * about attribute expression.
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated input tag
 	 */
 	public static function activeFileInput($model, $attribute, $options = [])
@@ -1092,6 +1140,7 @@ class BaseHtml
 	 * about attribute expression.
 	 * @param array $options the tag options in terms of name-value pairs. These will be rendered as
 	 * the attributes of the resulting tag. The values will be HTML-encoded using [[encode()]].
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 * @return string the generated textarea tag
 	 */
 	public static function activeTextarea($model, $attribute, $options = [])
@@ -1124,6 +1173,8 @@ class BaseHtml
 	 * The rest of the options will be rendered as the attributes of the resulting tag. The values will
 	 * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
 	 *
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
+	 *
 	 * @return string the generated radio button tag
 	 */
 	public static function activeRadio($model, $attribute, $options = [])
@@ -1165,6 +1216,7 @@ class BaseHtml
 	 *
 	 * The rest of the options will be rendered as the attributes of the resulting tag. The values will
 	 * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 *
 	 * @return string the generated checkbox tag
 	 */
@@ -1220,6 +1272,7 @@ class BaseHtml
 	 *
 	 * The rest of the options will be rendered as the attributes of the resulting tag. The values will
 	 * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 *
 	 * @return string the generated drop-down list tag
 	 */
@@ -1271,6 +1324,7 @@ class BaseHtml
 	 *
 	 * The rest of the options will be rendered as the attributes of the resulting tag. The values will
 	 * be HTML-encoded using [[encode()]]. If a value is null, the corresponding attribute will not be rendered.
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
 	 *
 	 * @return string the generated list box tag
 	 */
@@ -1314,6 +1368,9 @@ class BaseHtml
 	 * where $index is the zero-based index of the checkbox in the whole list; $label
 	 * is the label for the checkbox; and $name, $value and $checked represent the name,
 	 * value and the checked status of the checkbox input.
+	 *
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
+	 *
 	 * @return string the generated checkbox list
 	 */
 	public static function activeCheckboxList($model, $attribute, $items, $options = [])
@@ -1355,6 +1412,9 @@ class BaseHtml
 	 * where $index is the zero-based index of the radio button in the whole list; $label
 	 * is the label for the radio button; and $name, $value and $checked represent the name,
 	 * value and the checked status of the radio button input.
+	 *
+	 * See [[renderTagAttributes()]] for details on how these are beeing rendered.
+	 *
 	 * @return string the generated radio button list
 	 */
 	public static function activeRadioList($model, $attribute, $items, $options = [])
@@ -1489,12 +1549,10 @@ class BaseHtml
 	{
 		if (is_array($url)) {
 			if (isset($url[0])) {
-				$route = $url[0];
-				$params = array_splice($url, 1);
 				if (Yii::$app->controller instanceof \yii\web\Controller) {
-					return Yii::$app->controller->createUrl($route, $params);
+					return Yii::$app->controller->createUrl($url);
 				} else {
-					return Yii::$app->getUrlManager()->createUrl($route, $params);
+					return Yii::$app->getUrlManager()->createUrl($url);
 				}
 			} else {
 				throw new InvalidParamException('The array specifying a URL must contain at least one element.');
@@ -1550,6 +1608,122 @@ class BaseHtml
 	}
 
 	/**
+	 * Adds the specified CSS style to the HTML options.
+	 *
+	 * If the options already contain a `style` element, the new style will be merged
+	 * with the existing one. If a CSS property exists in both the new and the old styles,
+	 * the old one may be overwritten if `$overwrite` is true.
+	 *
+	 * For example,
+	 *
+	 * ```php
+	 * Html::addCssStyle($options, 'width: 100px; height: 200px');
+	 * ```
+	 *
+	 * @param array $options the HTML options to be modified.
+	 * @param string|array $style the new style string (e.g. `'width: 100px; height: 200px'`) or
+	 * array (e.g. `['width' => '100px', 'height' => '200px']`).
+	 * @param boolean $overwrite whether to overwrite existing CSS properties if the new style
+	 * contain them too.
+	 * @see removeCssStyle()
+	 * @see cssStyleFromArray()
+	 * @see cssStyleToArray()
+	 */
+	public static function addCssStyle(&$options, $style, $overwrite = true)
+	{
+		if (!empty($options['style'])) {
+			$oldStyle = static::cssStyleToArray($options['style']);
+			$newStyle = is_array($style) ? $style : static::cssStyleToArray($style);
+			if (!$overwrite) {
+				foreach ($newStyle as $property => $value) {
+					if (isset($oldStyle[$property])) {
+						unset($newStyle[$property]);
+					}
+				}
+			}
+			$style = static::cssStyleFromArray(array_merge($oldStyle, $newStyle));
+		}
+		$options['style'] = $style;
+	}
+
+	/**
+	 * Removes the specified CSS style from the HTML options.
+	 *
+	 * For example,
+	 *
+	 * ```php
+	 * Html::removeCssStyle($options, ['width', 'height']);
+	 * ```
+	 *
+	 * @param array $options the HTML options to be modified.
+	 * @param string|array $properties the CSS properties to be removed. You may use a string
+	 * if you are removing a single property.
+	 * @see addCssStyle()
+	 */
+	public static function removeCssStyle(&$options, $properties)
+	{
+		if (!empty($options['style'])) {
+			$style = static::cssStyleToArray($options['style']);
+			foreach ((array)$properties as $property) {
+				unset($style[$property]);
+			}
+			$options['style'] = static::cssStyleFromArray($style);
+		}
+	}
+
+	/**
+	 * Converts a CSS style array into a string representation.
+	 *
+	 * For example,
+	 *
+	 * ```php
+	 * print_r(Html::cssStyleFromArray(['width' => '100px', 'height' => '200px']));
+	 * // will display: 'width: 100px; height: 200px;'
+	 * ```
+	 *
+	 * @param array $style the CSS style array. The array keys are the CSS property names,
+	 * and the array values are the corresponding CSS property values.
+	 * @return string the CSS style string. If the CSS style is empty, a null will be returned.
+	 */
+	public static function cssStyleFromArray(array $style)
+	{
+		$result = '';
+		foreach ($style as $name => $value) {
+			$result .= "$name: $value; ";
+		}
+		// return null if empty to avoid rendering the "style" attribute
+		return $result === '' ? null : rtrim($result);
+	}
+
+	/**
+	 * Converts a CSS style string into an array representation.
+	 *
+	 * The array keys are the CSS property names, and the array values
+	 * are the corresponding CSS property values.
+	 *
+	 * For example,
+	 *
+	 * ```php
+	 * print_r(Html::cssStyleToArray('width: 100px; height: 200px;'));
+	 * // will display: ['width' => '100px', 'height' => '200px']
+	 * ```
+	 *
+	 * @param string $style the CSS style string
+	 * @return array the array representation of the CSS style
+	 */
+	public static function cssStyleToArray($style)
+	{
+		$result = [];
+		foreach (explode(';', $style) as $property) {
+			$property = explode(':', $property);
+			if (count($property) > 1) {
+				$result[trim($property[0])] = trim($property[1]);
+			}
+		}
+		return $result;
+	}
+
+	/**
 	 * Returns the real attribute name from the given attribute expression.
 	 *
 	 * An attribute expression is an attribute name prefixed and/or suffixed with array indexes.
diff --git a/framework/helpers/BaseMarkdown.php b/framework/helpers/BaseMarkdown.php
index 5258534..a22e45e 100644
--- a/framework/helpers/BaseMarkdown.php
+++ b/framework/helpers/BaseMarkdown.php
@@ -7,38 +7,94 @@
 
 namespace yii\helpers;
 
-use Michelf\MarkdownExtra;
+use Yii;
+use yii\base\InvalidParamException;
 
 /**
  * BaseMarkdown provides concrete implementation for [[Markdown]].
  *
  * Do not use BaseMarkdown. Use [[Markdown]] instead.
  *
- * @author Alexander Makarov <sam@rmcreative.ru>
+ * @author Carsten Brandt <mail@cebe.cc>
  * @since 2.0
  */
 class BaseMarkdown
 {
 	/**
-	 * @var MarkdownExtra
+	 * @var array a map of markdown flavor names to corresponding parser class configurations.
 	 */
-	protected static $markdown;
+	public static $flavors = [
+		'original' => [
+			'class' => 'cebe\markdown\Markdown',
+			'html5' => true,
+		],
+		'gfm' => [
+			'class' => 'cebe\markdown\GithubMarkdown',
+			'html5' => true,
+		],
+		'gfm-comment' => [
+			'class' => 'cebe\markdown\Markdown',
+			'html5' => true,
+			'enableNewlines' => true,
+		],
+	];
+	/**
+	 * @var string the markdown flavor to use when none is specified explicitly.
+	 * Defaults to `original`.
+	 * @see $flavors
+	 */
+	public static $defaultFlavor = 'original';
+
 
 	/**
-	 * Converts markdown into HTML
+	 * Converts markdown into HTML.
 	 *
-	 * @param string $content
-	 * @param array $config
-	 * @return string
+	 * @param string $markdown the markdown text to parse
+	 * @param string $flavor the markdown flavor to use. See [[$flavors]] for available values.
+	 * @return string the parsed HTML output
+	 * @throws \yii\base\InvalidParamException when an undefined flavor is given.
 	 */
-	public static function process($content, $config = [])
+	public static function process($markdown, $flavor = 'original')
 	{
-		if (static::$markdown === null) {
-			static::$markdown = new MarkdownExtra();
-		}
-		foreach ($config as $name => $value) {
-			static::$markdown->{$name} = $value;
+		$parser = static::getParser($flavor);
+		return $parser->parse($markdown);
+	}
+
+	/**
+	 * Converts markdown into HTML but only parses inline elements.
+	 *
+	 * This can be useful for parsing small comments or description lines.
+	 *
+	 * @param string $markdown the markdown text to parse
+	 * @param string $flavor the markdown flavor to use. See [[$flavors]] for available values.
+	 * @return string the parsed HTML output
+	 * @throws \yii\base\InvalidParamException when an undefined flavor is given.
+	 */
+	public static function processParagraph($markdown, $flavor = 'original')
+	{
+		$parser = static::getParser($flavor);
+		return $parser->parseParagraph($markdown);
+	}
+
+	/**
+	 * @param string $flavor
+	 * @return \cebe\markdown\Parser
+	 * @throws \yii\base\InvalidParamException when an undefined flavor is given.
+	 */
+	private static function getParser($flavor)
+	{
+		/** @var \cebe\markdown\Markdown $parser */
+		if (!isset(static::$flavors[$flavor])) {
+			throw new InvalidParamException("Markdown flavor '$flavor' is not defined.'");
+		} elseif(!is_object($config = static::$flavors[$flavor])) {
+			$parser = Yii::createObject($config);
+			if (is_array($config)) {
+				foreach ($config as $name => $value) {
+					$parser->{$name} = $value;
+				}
+			}
+			static::$flavors[$flavor] = $parser;
 		}
-		return static::$markdown->transform($content);
+		return static::$flavors[$flavor];
 	}
 }
diff --git a/framework/helpers/Markdown.php b/framework/helpers/Markdown.php
index 326cd34..88debe8 100644
--- a/framework/helpers/Markdown.php
+++ b/framework/helpers/Markdown.php
@@ -13,21 +13,15 @@ namespace yii\helpers;
  * Basic usage is the following:
  *
  * ```php
- * $myHtml = Markdown::process($myText);
+ * $myHtml = Markdown::process($myText); // use original markdown flavor
+ * $myHtml = Markdown::process($myText, 'gfm'); // use github flavored markdown
  * ```
  *
- * If you want to configure the parser:
+ * You can configure multiple flavors using the [[$flavors]] property.
  *
- * ```php
- * $myHtml = Markdown::process($myText, [
- *     'fn_id_prefix' => 'footnote_',
- * ]);
- * ```
- *
- * Note that in order to use this helper you need to install "michelf/php-markdown" Composer package.
+ * For more details please refer to the [Markdown library documentation](https://github.com/cebe/markdown#readme).
  *
- * For more details please refer to [PHP Markdown library documentation](http://michelf.ca/projects/php-markdown/).
- * @author Alexander Makarov <sam@rmcreative.ru>
+ * @author Carsten Brandt <mail@cebe.cc>
  * @since 2.0
  */
 class Markdown extends BaseMarkdown
diff --git a/framework/i18n/MessageSource.php b/framework/i18n/MessageSource.php
index 51948d3..13c580d 100644
--- a/framework/i18n/MessageSource.php
+++ b/framework/i18n/MessageSource.php
@@ -91,12 +91,13 @@ class MessageSource extends Component
 
 	/**
 	 * Translates the specified message.
-	 * If the message is not found, a [[EVENT_MISSING_TRANSLATION|missingTranslation]] event will be triggered
-	 * and the original message will be returned.
-	 * @param string $category the category that the message belongs to
-	 * @param string $message the message to be translated
-	 * @param string $language the target language
-	 * @return string|boolean the translated message or false if translation wasn't found
+	 * If the message is not found, a [[EVENT_MISSING_TRANSLATION|missingTranslation]] event will be triggered.
+	 * If there is an event handler, it may provide a [[MissingTranslationEvent::$translatedMessage|fallback translation]].
+	 * If no fallback translation is provided this method will return `false`.
+	 * @param string $category the category that the message belongs to.
+	 * @param string $message the message to be translated.
+	 * @param string $language the target language.
+	 * @return string|boolean the translated message or false if translation wasn't found.
 	 */
 	protected function translateMessage($category, $message, $language)
 	{
@@ -113,8 +114,10 @@ class MessageSource extends Component
 				'language' => $language,
 			]);
 			$this->trigger(self::EVENT_MISSING_TRANSLATION, $event);
-			$this->_messages[$key] = $event->message;
+			if ($event->translatedMessage !== null) {
+				return $this->_messages[$key][$message] = $event->translatedMessage;
+			}
 		}
-		return false;
+		return $this->_messages[$key][$message] = false;
 	}
 }
diff --git a/framework/i18n/MissingTranslationEvent.php b/framework/i18n/MissingTranslationEvent.php
index 7a22d48..47f1654 100644
--- a/framework/i18n/MissingTranslationEvent.php
+++ b/framework/i18n/MissingTranslationEvent.php
@@ -18,11 +18,16 @@ use yii\base\Event;
 class MissingTranslationEvent extends Event
 {
 	/**
-	 * @var string the message to be translated. An event handler may overwrite this property
-	 * with a translated version if possible.
+	 * @var string the message to be translated. An event handler may use this to provide a fallback translation
+	 * and set [[translatedMessage]] if possible.
 	 */
 	public $message;
 	/**
+	 * @var string the translated message. An event handler may overwrite this property
+	 * with a translated version of [[message]] if possible. If not set (null), it means the message is not translated.
+	 */
+	public $translatedMessage;
+	/**
 	 * @var string the category that the message belongs to
 	 */
 	public $category;
diff --git a/framework/web/AssetManager.php b/framework/web/AssetManager.php
index ab38b47..c600223 100644
--- a/framework/web/AssetManager.php
+++ b/framework/web/AssetManager.php
@@ -156,6 +156,9 @@ class AssetManager extends Component
 		if ($this->_converter === null) {
 			$this->_converter = Yii::createObject(AssetConverter::className());
 		} elseif (is_array($this->_converter) || is_string($this->_converter)) {
+			if (is_array($this->_converter) && !isset($this->_converter['class'])) {
+				$this->_converter['class'] = AssetConverter::className();
+			}
 			$this->_converter = Yii::createObject($this->_converter);
 		}
 		return $this->_converter;
diff --git a/framework/web/Controller.php b/framework/web/Controller.php
index 6923883..85a96c6 100644
--- a/framework/web/Controller.php
+++ b/framework/web/Controller.php
@@ -16,8 +16,6 @@ use yii\helpers\Html;
  *
  * @property string $canonicalUrl The canonical URL of the currently requested page. This property is
  * read-only.
- * @property View $view The view object that can be used to render views or view files. This property is
- * read-only.
  *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
@@ -161,14 +159,14 @@ class Controller extends \yii\base\Controller
 	 *
 	 * After this route conversion, the method calls [[UrlManager::createUrl()]] to create a URL.
 	 *
-	 * @param string $route the route. This can be either an absolute route or a relative route.
-	 * @param array $params the parameters (name-value pairs) to be included in the generated URL
+	 * @param string|array $params route as a string or route and parameters in form of ['route', 'param1' => 'value1', 'param2' => 'value2']
 	 * @return string the created relative URL
 	 */
-	public function createUrl($route, $params = [])
+	public function createUrl($params)
 	{
-		$route = $this->getNormalizedRoute($route);
-		return Yii::$app->getUrlManager()->createUrl($route, $params);
+		$params = (array)$params;
+		$params[0] = $this->getNormalizedRoute($params[0]);
+		return Yii::$app->getUrlManager()->createUrl($params);
 	}
 
 	/**
@@ -185,16 +183,16 @@ class Controller extends \yii\base\Controller
 	 *
 	 * After this route conversion, the method calls [[UrlManager::createUrl()]] to create a URL.
 	 *
-	 * @param string $route the route. This can be either an absolute route or a relative route.
-	 * @param array $params the parameters (name-value pairs) to be included in the generated URL
+	 * @param string|array $params route as a string or route and parameters in form of ['route', 'param1' => 'value1', 'param2' => 'value2']
 	 * @param string $schema the schema to use for the url. e.g. 'http' or 'https'. If not specified
 	 * the schema of the current request will be used.
 	 * @return string the created absolute URL
 	 */
-	public function createAbsoluteUrl($route, $params = [], $schema = null)
+	public function createAbsoluteUrl($params, $schema = null)
 	{
-		$route = $this->getNormalizedRoute($route);
-		return Yii::$app->getUrlManager()->createAbsoluteUrl($route, $params, $schema);
+		$params = (array)$params;
+		$params[0] = $this->getNormalizedRoute($params[0]);
+		return Yii::$app->getUrlManager()->createAbsoluteUrl($params, $schema);
 	}
 
 	/**
@@ -210,7 +208,9 @@ class Controller extends \yii\base\Controller
 	 */
 	public function getCanonicalUrl()
 	{
-		return Yii::$app->getUrlManager()->createAbsoluteUrl($this->getRoute(), $this->actionParams);
+		$params = $this->actionParams;
+		$params[0] = $this->getRoute();
+		return Yii::$app->getUrlManager()->createAbsoluteUrl($params);
 	}
 
 	/**
diff --git a/framework/web/Request.php b/framework/web/Request.php
index bd1cffd..01de665 100644
--- a/framework/web/Request.php
+++ b/framework/web/Request.php
@@ -292,6 +292,15 @@ class Request extends \yii\base\Request
 	}
 
 	/**
+	 * Returns whether this is a PJAX request
+	 * @return boolean whether this is a PJAX request
+	 */
+	public function getIsPjax ()
+	{
+		return $this->getIsAjax() && !empty($_SERVER['HTTP_X_PJAX']);
+	}
+	
+	/**
 	 * Returns whether this is an Adobe Flash or Flex request.
 	 * @return boolean whether this is an Adobe Flash or Adobe Flex request.
 	 */
@@ -547,7 +556,7 @@ class Request extends \yii\base\Request
 				$this->_scriptUrl = $_SERVER['ORIG_SCRIPT_NAME'];
 			} elseif (($pos = strpos($_SERVER['PHP_SELF'], '/' . $scriptName)) !== false) {
 				$this->_scriptUrl = substr($_SERVER['SCRIPT_NAME'], 0, $pos) . '/' . $scriptName;
-			} elseif (isset($_SERVER['DOCUMENT_ROOT']) && strpos($scriptFile, $_SERVER['DOCUMENT_ROOT']) === 0) {
+			} elseif (!empty($_SERVER['DOCUMENT_ROOT']) && strpos($scriptFile, $_SERVER['DOCUMENT_ROOT']) === 0) {
 				$this->_scriptUrl = str_replace('\\', '/', str_replace($_SERVER['DOCUMENT_ROOT'], '', $scriptFile));
 			} else {
 				throw new InvalidConfigException('Unable to determine the entry script URL.');
diff --git a/framework/web/Response.php b/framework/web/Response.php
index 7da1bc5..b3f7548 100644
--- a/framework/web/Response.php
+++ b/framework/web/Response.php
@@ -678,7 +678,9 @@ class Response extends \yii\base\Response
 			$url = Yii::$app->getRequest()->getHostInfo() . $url;
 		}
 
-		if (Yii::$app->getRequest()->getIsAjax()) {
+		if (Yii::$app->getRequest()->getIsPjax()) {
+			$this->getHeaders()->set('X-Pjax-Url', $url);
+		} elseif (Yii::$app->getRequest()->getIsAjax()) {
 			$this->getHeaders()->set('X-Redirect', $url);
 		} else {
 			$this->getHeaders()->set('Location', $url);
diff --git a/framework/web/UrlManager.php b/framework/web/UrlManager.php
index 0dd0dc3..80f2cb6 100644
--- a/framework/web/UrlManager.php
+++ b/framework/web/UrlManager.php
@@ -228,16 +228,17 @@ class UrlManager extends Component
 	/**
 	 * Creates a URL using the given route and parameters.
 	 * The URL created is a relative one. Use [[createAbsoluteUrl()]] to create an absolute URL.
-	 * @param string $route the route
-	 * @param array $params the parameters (name-value pairs)
+	 * @param string|array $params route as a string or route and parameters in form of ['route', 'param1' => 'value1', 'param2' => 'value2']
 	 * @return string the created URL
 	 */
-	public function createUrl($route, $params = [])
+	public function createUrl($params)
 	{
+		$params = (array)$params;
 		$anchor = isset($params['#']) ? '#' . $params['#'] : '';
 		unset($params['#'], $params[$this->routeParam]);
 
-		$route = trim($route, '/');
+		$route = trim($params[0], '/');
+		unset($params[0]);
 		$baseUrl = $this->getBaseUrl();
 
 		if ($this->enablePrettyUrl) {
@@ -275,16 +276,16 @@ class UrlManager extends Component
 	/**
 	 * Creates an absolute URL using the given route and parameters.
 	 * This method prepends the URL created by [[createUrl()]] with the [[hostInfo]].
-	 * @param string $route the route
-	 * @param array $params the parameters (name-value pairs)
+	 * @param string|array $params route as a string or route and parameters in form of ['route', 'param1' => 'value1', 'param2' => 'value2']
 	 * @param string $schema the schema to use for the url. e.g. 'http' or 'https'. If not specified
 	 * the schema of the current request will be used.
 	 * @return string the created URL
 	 * @see createUrl()
 	 */
-	public function createAbsoluteUrl($route, $params = [], $schema = null)
+	public function createAbsoluteUrl($params, $schema = null)
 	{
-		$url = $this->createUrl($route, $params);
+		$params = (array)$params;
+		$url = $this->createUrl($params);
 		if (strpos($url, '://') === false) {
 			$url = $this->getHostInfo($schema) . $url;
 		}
diff --git a/framework/widgets/ActiveField.php b/framework/widgets/ActiveField.php
index 81b1100..55bb9f9 100644
--- a/framework/widgets/ActiveField.php
+++ b/framework/widgets/ActiveField.php
@@ -14,6 +14,8 @@ use yii\base\Model;
 use yii\web\JsExpression;
 
 /**
+ * ActiveField represents a form input field within an [[ActiveForm]].
+ *
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
  */
diff --git a/framework/widgets/BaseListView.php b/framework/widgets/BaseListView.php
index bf3faaf..850152c 100644
--- a/framework/widgets/BaseListView.php
+++ b/framework/widgets/BaseListView.php
@@ -185,6 +185,14 @@ abstract class BaseListView extends Widget
 				]) . '</div>';
 			}
 		}
+		return Yii::$app->getI18n()->format($summaryContent, [
+			'begin' => $begin,
+			'end' => $end,
+			'count' => $count,
+			'totalCount' => $totalCount,
+			'page' => $page,
+			'pageCount' => $pageCount,
+		], Yii::$app->language);
 	}
 
 	/**
diff --git a/framework/widgets/LinkPager.php b/framework/widgets/LinkPager.php
index 807a4b8..06d688a 100644
--- a/framework/widgets/LinkPager.php
+++ b/framework/widgets/LinkPager.php
@@ -38,6 +38,10 @@ class LinkPager extends Widget
 	 */
 	public $options = ['class' => 'pagination'];
 	/**
+	 * @var array HTML attributes for the link in a pager container tag.
+	 */
+	public $linkOptions = [];
+	/**
 	 * @var string the CSS class for the "first" page button.
 	 */
 	public $firstPageCssClass = 'first';
@@ -172,7 +176,9 @@ class LinkPager extends Widget
 			Html::addCssClass($options, $this->disabledPageCssClass);
 			return Html::tag('li', Html::tag('span', $label), $options);
 		}
-		return Html::tag('li', Html::a($label, $this->pagination->createUrl($page), ['data-page' => $page]), $options);
+		$linkOptions = $this->linkOptions;
+		$linkOptions['data-page'] = $page;
+		return Html::tag('li', Html::a($label, $this->pagination->createUrl($page), $linkOptions), $options);
 	}
 
 	/**
diff --git a/tests/unit/data/ar/Customer.php b/tests/unit/data/ar/Customer.php
index 869d905..f8b0b19 100644
--- a/tests/unit/data/ar/Customer.php
+++ b/tests/unit/data/ar/Customer.php
@@ -29,6 +29,11 @@ class Customer extends ActiveRecord
 		return $this->hasMany(Order::className(), ['customer_id' => 'id'])->orderBy('id');
 	}
 
+	public function getOrders2()
+	{
+		return $this->hasMany(Order::className(), ['customer_id' => 'id'])->inverseOf('customer2')->orderBy('id');
+	}
+
 	public function afterSave($insert)
 	{
 		ActiveRecordTest::$afterSaveInsert = $insert;
@@ -36,8 +41,9 @@ class Customer extends ActiveRecord
 		parent::afterSave($insert);
 	}
 
-	public static function createQuery()
+	public static function createQuery($config = [])
 	{
-		return new CustomerQuery(['modelClass' => get_called_class()]);
+		$config['modelClass'] = get_called_class();
+		return new CustomerQuery($config);
 	}
 }
diff --git a/tests/unit/data/ar/Order.php b/tests/unit/data/ar/Order.php
index 22e837d..d46637e 100644
--- a/tests/unit/data/ar/Order.php
+++ b/tests/unit/data/ar/Order.php
@@ -22,6 +22,11 @@ class Order extends ActiveRecord
 		return $this->hasOne(Customer::className(), ['id' => 'customer_id']);
 	}
 
+	public function getCustomer2()
+	{
+		return $this->hasOne(Customer::className(), ['id' => 'customer_id'])->inverseOf('orders2');
+	}
+
 	public function getOrderItems()
 	{
 		return $this->hasMany(OrderItem::className(), ['order_id' => 'id']);
diff --git a/tests/unit/data/ar/elasticsearch/Customer.php b/tests/unit/data/ar/elasticsearch/Customer.php
index 8caf48a..c61e636 100644
--- a/tests/unit/data/ar/elasticsearch/Customer.php
+++ b/tests/unit/data/ar/elasticsearch/Customer.php
@@ -63,8 +63,9 @@ class Customer extends ActiveRecord
 
 	}
 
-	public static function createQuery()
+	public static function createQuery($config = [])
 	{
-		return new CustomerQuery(['modelClass' => get_called_class()]);
+		$config['modelClass'] = get_called_class();
+		return new CustomerQuery($config);
 	}
 }
diff --git a/tests/unit/data/ar/mongodb/Customer.php b/tests/unit/data/ar/mongodb/Customer.php
index 01bcb3d..c32354c 100644
--- a/tests/unit/data/ar/mongodb/Customer.php
+++ b/tests/unit/data/ar/mongodb/Customer.php
@@ -25,8 +25,9 @@ class Customer extends ActiveRecord
 		return $this->hasMany(CustomerOrder::className(), ['customer_id' => '_id']);
 	}
 
-	public static function createQuery()
+	public static function createQuery($config = [])
 	{
-		return new CustomerQuery(['modelClass' => get_called_class()]);
+		$config['modelClass'] = get_called_class();
+		return new CustomerQuery($config);
 	}
 }
\ No newline at end of file
diff --git a/tests/unit/data/ar/mongodb/file/CustomerFile.php b/tests/unit/data/ar/mongodb/file/CustomerFile.php
index 40da8bf..18fabd8 100644
--- a/tests/unit/data/ar/mongodb/file/CustomerFile.php
+++ b/tests/unit/data/ar/mongodb/file/CustomerFile.php
@@ -20,8 +20,9 @@ class CustomerFile extends ActiveRecord
 		);
 	}
 
-	public static function createQuery()
+	public static function createQuery($config = [])
 	{
-		return new CustomerFileQuery(['modelClass' => get_called_class()]);
+		$config['modelClass'] = get_called_class();
+		return new CustomerFileQuery($config);
 	}
 }
\ No newline at end of file
diff --git a/tests/unit/data/ar/redis/Customer.php b/tests/unit/data/ar/redis/Customer.php
index da5d275..4f3a257 100644
--- a/tests/unit/data/ar/redis/Customer.php
+++ b/tests/unit/data/ar/redis/Customer.php
@@ -17,7 +17,7 @@ class Customer extends ActiveRecord
 	}
 
 	/**
-	 * @return \yii\redis\ActiveRelation
+	 * @return \yii\redis\ActiveQuery
 	 */
 	public function getOrders()
 	{
@@ -31,8 +31,9 @@ class Customer extends ActiveRecord
 		parent::afterSave($insert);
 	}
 
-	public static function createQuery()
+	public static function createQuery($config = [])
 	{
-		return new CustomerQuery(['modelClass' => get_called_class()]);
+		$config['modelClass'] = get_called_class();
+		return new CustomerQuery($config);
 	}
 }
\ No newline at end of file
diff --git a/tests/unit/data/ar/sphinx/ArticleDb.php b/tests/unit/data/ar/sphinx/ArticleDb.php
index e81e1f5..9b4813b 100644
--- a/tests/unit/data/ar/sphinx/ArticleDb.php
+++ b/tests/unit/data/ar/sphinx/ArticleDb.php
@@ -2,7 +2,7 @@
 
 namespace yiiunit\data\ar\sphinx;
 
-use yii\sphinx\ActiveRelation;
+use yii\sphinx\ActiveQuery;
 use yiiunit\data\ar\ActiveRecord as ActiveRecordDb;
 
 class ArticleDb extends ActiveRecordDb
@@ -20,6 +20,6 @@ class ArticleDb extends ActiveRecordDb
 			'link' => ['id' => 'id'],
 			'multiple' => false,
 		];
-		return new ActiveRelation($config);
+		return new ActiveQuery($config);
 	}
 }
\ No newline at end of file
diff --git a/tests/unit/data/ar/sphinx/ArticleIndex.php b/tests/unit/data/ar/sphinx/ArticleIndex.php
index fa8939b..27ea274 100644
--- a/tests/unit/data/ar/sphinx/ArticleIndex.php
+++ b/tests/unit/data/ar/sphinx/ArticleIndex.php
@@ -25,8 +25,9 @@ class ArticleIndex extends ActiveRecord
 		return $this->source->content;
 	}
 
-	public static function createQuery()
+	public static function createQuery($config = [])
 	{
-		return new ArticleIndexQuery(['modelClass' => get_called_class()]);
+		$config['modelClass'] = get_called_class();
+		return new ArticleIndexQuery($config);
 	}
 }
\ No newline at end of file
diff --git a/tests/unit/extensions/smarty/ViewRendererTest.php b/tests/unit/extensions/smarty/ViewRendererTest.php
new file mode 100644
index 0000000..3d31de5
--- /dev/null
+++ b/tests/unit/extensions/smarty/ViewRendererTest.php
@@ -0,0 +1,71 @@
+<?php
+/**
+ * 
+ * 
+ * @author Carsten Brandt <mail@cebe.cc>
+ */
+
+namespace yiiunit\extensions\smarty;
+
+use yii\web\AssetManager;
+use yii\web\JqueryAsset;
+use yii\web\View;
+use Yii;
+use yiiunit\TestCase;
+
+/**
+ * @group smarty
+ */
+class ViewRendererTest extends TestCase
+{
+	protected function setUp()
+	{
+		$this->mockApplication();
+	}
+
+	/**
+	 * https://github.com/yiisoft/yii2/issues/2265
+	 */
+	public function testNoParams()
+	{
+		$view = $this->mockView();
+		$content = $view->renderFile('@yiiunit/extensions/smarty/views/simple.tpl');
+
+		$this->assertEquals('simple view without parameters.', $content);
+	}
+
+	public function testRender()
+	{
+		$view = $this->mockView();
+		$content = $view->renderFile('@yiiunit/extensions/smarty/views/view.tpl', ['param' => 'Hello World!']);
+
+		$this->assertEquals('test view Hello World!.', $content);
+	}
+
+	/**
+	 * @return View
+	 */
+	protected function mockView()
+	{
+		return new View([
+			'renderers' => [
+				'tpl' => [
+					'class' => 'yii\smarty\ViewRenderer',
+				],
+			],
+			'assetManager' => $this->mockAssetManager(),
+		]);
+	}
+
+	protected function mockAssetManager()
+	{
+		$assetDir = Yii::getAlias('@runtime/assets');
+		if (!is_dir($assetDir)) {
+			mkdir($assetDir, 0777, true);
+		}
+		return new AssetManager([
+			'basePath' => $assetDir,
+			'baseUrl' => '/assets',
+		]);
+	}
+} 
\ No newline at end of file
diff --git a/tests/unit/extensions/smarty/views/simple.tpl b/tests/unit/extensions/smarty/views/simple.tpl
new file mode 100644
index 0000000..a9f660c
--- /dev/null
+++ b/tests/unit/extensions/smarty/views/simple.tpl
@@ -0,0 +1 @@
+simple view without parameters.
\ No newline at end of file
diff --git a/tests/unit/extensions/smarty/views/view.tpl b/tests/unit/extensions/smarty/views/view.tpl
new file mode 100644
index 0000000..22f58fb
--- /dev/null
+++ b/tests/unit/extensions/smarty/views/view.tpl
@@ -0,0 +1 @@
+test view {$param}.
\ No newline at end of file
diff --git a/tests/unit/framework/ar/ActiveRecordTestTrait.php b/tests/unit/framework/ar/ActiveRecordTestTrait.php
index e915bd7..6bc27d4 100644
--- a/tests/unit/framework/ar/ActiveRecordTestTrait.php
+++ b/tests/unit/framework/ar/ActiveRecordTestTrait.php
@@ -492,7 +492,7 @@ trait ActiveRecordTestTrait
 	}
 
 	/**
-	 * Ensure ActiveRelation does preserve order of items on find via()
+	 * Ensure ActiveRelationTrait does preserve order of items on find via()
 	 * https://github.com/yiisoft/yii2/issues/1310
 	 */
 	public function testFindEagerViaRelationPreserveOrder()
diff --git a/tests/unit/framework/behaviors/AutoTimestampTest.php b/tests/unit/framework/behaviors/TimestampBehaviorTest.php
similarity index 87%
rename from tests/unit/framework/behaviors/AutoTimestampTest.php
rename to tests/unit/framework/behaviors/TimestampBehaviorTest.php
index b988436..8a46215 100644
--- a/tests/unit/framework/behaviors/AutoTimestampTest.php
+++ b/tests/unit/framework/behaviors/TimestampBehaviorTest.php
@@ -6,15 +6,15 @@ use Yii;
 use yiiunit\TestCase;
 use yii\db\Connection;
 use yii\db\ActiveRecord;
-use yii\behaviors\AutoTimestamp;
+use yii\behaviors\TimestampBehavior;
 
 /**
- * Unit test for [[\yii\behaviors\AutoTimestamp]].
- * @see AutoTimestamp
+ * Unit test for [[\yii\behaviors\TimestampBehavior]].
+ * @see TimestampBehavior
  *
  * @group behaviors
  */
-class AutoTimestampTest extends TestCase
+class TimestampBehaviorTest extends TestCase
 {
 	/**
 	 * @var Connection test db connection
@@ -59,7 +59,7 @@ class AutoTimestampTest extends TestCase
 	{
 		$currentTime = time();
 
-		$model = new ActiveRecordAutoTimestamp();
+		$model = new ActiveRecordTimestamp();
 		$model->save(false);
 
 		$this->assertTrue($model->created_at >= $currentTime);
@@ -73,7 +73,7 @@ class AutoTimestampTest extends TestCase
 	{
 		$currentTime = time();
 
-		$model = new ActiveRecordAutoTimestamp();
+		$model = new ActiveRecordTimestamp();
 		$model->save(false);
 
 		$enforcedTime = $currentTime - 100;
@@ -88,24 +88,18 @@ class AutoTimestampTest extends TestCase
 }
 
 /**
- * Test Active Record class with [[AutoTimestamp]] behavior attached.
+ * Test Active Record class with [[TimestampBehavior]] behavior attached.
  *
  * @property integer $id
  * @property integer $created_at
  * @property integer $updated_at
  */
-class ActiveRecordAutoTimestamp extends ActiveRecord
+class ActiveRecordTimestamp extends ActiveRecord
 {
 	public function behaviors()
 	{
 		return [
-			'timestamp' => [
-				'class' => AutoTimestamp::className(),
-				'attributes' => [
-					static::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'],
-					static::EVENT_BEFORE_UPDATE => 'updated_at',
-				],
-			],
+			TimestampBehavior::className(),
 		];
 	}
 
diff --git a/tests/unit/framework/db/ActiveRecordTest.php b/tests/unit/framework/db/ActiveRecordTest.php
index 1eab260..5a2f888 100644
--- a/tests/unit/framework/db/ActiveRecordTest.php
+++ b/tests/unit/framework/db/ActiveRecordTest.php
@@ -324,4 +324,57 @@ class ActiveRecordTest extends DatabaseTestCase
 		$this->assertEquals(0, count($orders[1]->books2));
 		$this->assertEquals(1, count($orders[2]->books2));
 	}
+
+	public function testInverseOf()
+	{
+		// eager loading: find one and all
+		$customer = Customer::find()->with('orders2')->where(['id' => 1])->one();
+		$this->assertTrue($customer->orders2[0]->customer2 === $customer);
+		$customers = Customer::find()->with('orders2')->where(['id' => [1, 3]])->all();
+		$this->assertTrue($customers[0]->orders2[0]->customer2 === $customers[0]);
+		$this->assertTrue(empty($customers[1]->orders2));
+		// lazy loading
+		$customer = Customer::find(2);
+		$orders = $customer->orders2;
+		$this->assertTrue(count($orders) === 2);
+		$this->assertTrue($customer->orders2[0]->customer2 === $customer);
+		$this->assertTrue($customer->orders2[1]->customer2 === $customer);
+		// ad-hoc lazy loading
+		$customer = Customer::find(2);
+		$orders = $customer->getOrders2()->all();
+		$this->assertTrue(count($orders) === 2);
+		$this->assertTrue($customer->orders2[0]->customer2 === $customer);
+		$this->assertTrue($customer->orders2[1]->customer2 === $customer);
+
+		// the other way around
+		$customer = Customer::find()->with('orders2')->where(['id' => 1])->asArray()->one();
+		$this->assertTrue($customer['orders2'][0]['customer2']['id'] === $customer['id']);
+		$customers = Customer::find()->with('orders2')->where(['id' => [1, 3]])->asArray()->all();
+		$this->assertTrue($customer['orders2'][0]['customer2']['id'] === $customers[0]['id']);
+		$this->assertTrue(empty($customers[1]['orders2']));
+
+		$orders = Order::find()->with('customer2')->where(['id' => 1])->all();
+		$this->assertTrue($orders[0]->customer2->orders2 === [$orders[0]]);
+		$order = Order::find()->with('customer2')->where(['id' => 1])->one();
+		$this->assertTrue($order->customer2->orders2 === [$order]);
+
+		$orders = Order::find()->with('customer2')->where(['id' => 1])->asArray()->all();
+		$this->assertTrue($orders[0]['customer2']['orders2'][0]['id'] === $orders[0]['id']);
+		$order = Order::find()->with('customer2')->where(['id' => 1])->asArray()->one();
+		$this->assertTrue($order['customer2']['orders2'][0]['id'] === $orders[0]['id']);
+
+		$orders = Order::find()->with('customer2')->where(['id' => [1, 3]])->all();
+		$this->assertTrue($orders[0]->customer2->orders2 === [$orders[0]]);
+		$this->assertTrue($orders[1]->customer2->orders2 === [$orders[1]]);
+
+		$orders = Order::find()->with('customer2')->where(['id' => [2, 3]])->orderBy('id')->all();
+		$this->assertTrue($orders[0]->customer2->orders2 === $orders);
+		$this->assertTrue($orders[1]->customer2->orders2 === $orders);
+
+		$orders = Order::find()->with('customer2')->where(['id' => [2, 3]])->orderBy('id')->asArray()->all();
+		$this->assertTrue($orders[0]['customer2']['orders2'][0]['id'] === $orders[0]['id']);
+		$this->assertTrue($orders[0]['customer2']['orders2'][1]['id'] === $orders[1]['id']);
+		$this->assertTrue($orders[1]['customer2']['orders2'][0]['id'] === $orders[0]['id']);
+		$this->assertTrue($orders[1]['customer2']['orders2'][1]['id'] === $orders[1]['id']);
+	}
 }
diff --git a/tests/unit/framework/helpers/HtmlTest.php b/tests/unit/framework/helpers/HtmlTest.php
index 7781958..04f3429 100644
--- a/tests/unit/framework/helpers/HtmlTest.php
+++ b/tests/unit/framework/helpers/HtmlTest.php
@@ -536,6 +536,62 @@ EOD;
 		$this->assertEquals([], $options);
 	}
 
+	public function testCssStyleFromArray()
+	{
+		$this->assertEquals('width: 100px; height: 200px;', Html::cssStyleFromArray([
+			'width' => '100px',
+			'height' => '200px',
+		]));
+		$this->assertNull(Html::cssStyleFromArray([]));
+	}
+
+	public function testCssStyleToArray()
+	{
+		$this->assertEquals([
+			'width' => '100px',
+			'height' => '200px',
+		], Html::cssStyleToArray('width: 100px; height: 200px;'));
+		$this->assertEquals([], Html::cssStyleToArray('  '));
+	}
+
+	public function testAddCssStyle()
+	{
+		$options = ['style' => 'width: 100px; height: 200px;'];
+		Html::addCssStyle($options, 'width: 110px; color: red;');
+		$this->assertEquals('width: 110px; height: 200px; color: red;', $options['style']);
+
+		$options = ['style' => 'width: 100px; height: 200px;'];
+		Html::addCssStyle($options, ['width' => '110px', 'color' => 'red']);
+		$this->assertEquals('width: 110px; height: 200px; color: red;', $options['style']);
+
+		$options = ['style' => 'width: 100px; height: 200px;'];
+		Html::addCssStyle($options, 'width: 110px; color: red;', false);
+		$this->assertEquals('width: 100px; height: 200px; color: red;', $options['style']);
+
+		$options = [];
+		Html::addCssStyle($options, 'width: 110px; color: red;');
+		$this->assertEquals('width: 110px; color: red;', $options['style']);
+
+		$options = [];
+		Html::addCssStyle($options, 'width: 110px; color: red;', false);
+		$this->assertEquals('width: 110px; color: red;', $options['style']);
+	}
+
+	public function testRemoveCssStyle()
+	{
+		$options = ['style' => 'width: 110px; height: 200px; color: red;'];
+		Html::removeCssStyle($options, 'width');
+		$this->assertEquals('height: 200px; color: red;', $options['style']);
+		Html::removeCssStyle($options, ['height']);
+		$this->assertEquals('color: red;', $options['style']);
+		Html::removeCssStyle($options, ['color', 'background']);
+		$this->assertNull($options['style']);
+
+		$options = [];
+		Html::removeCssStyle($options, ['color', 'background']);
+		$this->assertTrue(!array_key_exists('style', $options));
+	}
+
 	protected function getDataItems()
 	{
 		return [
diff --git a/tests/unit/framework/i18n/I18NTest.php b/tests/unit/framework/i18n/I18NTest.php
index a444761..b3f19db 100644
--- a/tests/unit/framework/i18n/I18NTest.php
+++ b/tests/unit/framework/i18n/I18NTest.php
@@ -7,6 +7,7 @@
 
 namespace yiiunit\framework\i18n;
 
+use yii\base\Event;
 use yii\base\Model;
 use yii\i18n\I18N;
 use yii\i18n\PhpMessageSource;
@@ -98,6 +99,34 @@ class I18NTest extends TestCase
 	{
 		$this->assertEquals('1 item', $this->i18n->translate('test', '{0, number} {0, plural, one{item} other{items}}', 1, 'hu'));
 	}
+
+	/**
+	 * https://github.com/yiisoft/yii2/issues/2519
+	 */
+	public function testMissingTranslationEvent()
+	{
+		$this->assertEquals('Hallo Welt!', $this->i18n->translate('test', 'Hello world!', [], 'de-DE'));
+		$this->assertEquals('Missing translation message.', $this->i18n->translate('test', 'Missing translation message.', [], 'de-DE'));
+		$this->assertEquals('Hallo Welt!', $this->i18n->translate('test', 'Hello world!', [], 'de-DE'));
+
+		Event::on(PhpMessageSource::className(), PhpMessageSource::EVENT_MISSING_TRANSLATION, function($event) {});
+		$this->assertEquals('Hallo Welt!', $this->i18n->translate('test', 'Hello world!', [], 'de-DE'));
+		$this->assertEquals('Missing translation message.', $this->i18n->translate('test', 'Missing translation message.', [], 'de-DE'));
+		$this->assertEquals('Hallo Welt!', $this->i18n->translate('test', 'Hello world!', [], 'de-DE'));
+		Event::off(PhpMessageSource::className(), PhpMessageSource::EVENT_MISSING_TRANSLATION);
+
+		Event::on(PhpMessageSource::className(), PhpMessageSource::EVENT_MISSING_TRANSLATION, function($event) {
+			if ($event->message == 'New missing translation message.') {
+				$event->translatedMessage = 'TRANSLATION MISSING HERE!';
+			}
+		});
+		$this->assertEquals('Hallo Welt!', $this->i18n->translate('test', 'Hello world!', [], 'de-DE'));
+		$this->assertEquals('Another missing translation message.', $this->i18n->translate('test', 'Another missing translation message.', [], 'de-DE'));
+		$this->assertEquals('Missing translation message.', $this->i18n->translate('test', 'Missing translation message.', [], 'de-DE'));
+		$this->assertEquals('TRANSLATION MISSING HERE!', $this->i18n->translate('test', 'New missing translation message.', [], 'de-DE'));
+		$this->assertEquals('Hallo Welt!', $this->i18n->translate('test', 'Hello world!', [], 'de-DE'));
+		Event::off(PhpMessageSource::className(), PhpMessageSource::EVENT_MISSING_TRANSLATION);
+	}
 }
 
 class ParamModel extends Model
diff --git a/tests/unit/framework/web/UrlManagerTest.php b/tests/unit/framework/web/UrlManagerTest.php
index 3d8e14d..79e4905 100644
--- a/tests/unit/framework/web/UrlManagerTest.php
+++ b/tests/unit/framework/web/UrlManagerTest.php
@@ -23,9 +23,9 @@ class UrlManagerTest extends TestCase
 			'baseUrl' => '/',
 			'cache' => null,
 		]);
-		$url = $manager->createUrl('post/view');
+		$url = $manager->createUrl(['post/view']);
 		$this->assertEquals('?r=post/view', $url);
-		$url = $manager->createUrl('post/view', ['id' => 1, 'title' => 'sample post']);
+		$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
 		$this->assertEquals('?r=post/view&id=1&title=sample+post', $url);
 
 		// default setting with '/test/' as base url
@@ -33,7 +33,7 @@ class UrlManagerTest extends TestCase
 			'baseUrl' => '/test/',
 			'cache' => null,
 		]);
-		$url = $manager->createUrl('post/view', ['id' => 1, 'title' => 'sample post']);
+		$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
 		$this->assertEquals('/test?r=post/view&id=1&title=sample+post', $url);
 
 		// pretty URL without rules
@@ -42,21 +42,21 @@ class UrlManagerTest extends TestCase
 			'baseUrl' => '/',
 			'cache' => null,
 		]);
-		$url = $manager->createUrl('post/view', ['id' => 1, 'title' => 'sample post']);
+		$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
 		$this->assertEquals('/post/view?id=1&title=sample+post', $url);
 		$manager = new UrlManager([
 			'enablePrettyUrl' => true,
 			'baseUrl' => '/test/',
 			'cache' => null,
 		]);
-		$url = $manager->createUrl('post/view', ['id' => 1, 'title' => 'sample post']);
+		$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
 		$this->assertEquals('/test/post/view?id=1&title=sample+post', $url);
 		$manager = new UrlManager([
 			'enablePrettyUrl' => true,
 			'baseUrl' => '/test/index.php',
 			'cache' => null,
 		]);
-		$url = $manager->createUrl('post/view', ['id' => 1, 'title' => 'sample post']);
+		$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
 		$this->assertEquals('/test/index.php/post/view?id=1&title=sample+post', $url);
 
 		// todo: test showScriptName
@@ -73,9 +73,9 @@ class UrlManagerTest extends TestCase
 			],
 			'baseUrl' => '/',
 		]);
-		$url = $manager->createUrl('post/view', ['id' => 1, 'title' => 'sample post']);
+		$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
 		$this->assertEquals('/post/1/sample+post', $url);
-		$url = $manager->createUrl('post/index', ['page' => 1]);
+		$url = $manager->createUrl(['post/index', 'page' => 1]);
 		$this->assertEquals('/post/index?page=1', $url);
 
 		// pretty URL with rules and suffix
@@ -91,9 +91,9 @@ class UrlManagerTest extends TestCase
 			'baseUrl' => '/',
 			'suffix' => '.html',
 		]);
-		$url = $manager->createUrl('post/view', ['id' => 1, 'title' => 'sample post']);
+		$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
 		$this->assertEquals('/post/1/sample+post.html', $url);
-		$url = $manager->createUrl('post/index', ['page' => 1]);
+		$url = $manager->createUrl(['post/index', 'page' => 1]);
 		$this->assertEquals('/post/index.html?page=1', $url);
 
 		// pretty URL with rules that have host info
@@ -109,9 +109,9 @@ class UrlManagerTest extends TestCase
 			],
 			'baseUrl' => '/test',
 		]);
-		$url = $manager->createUrl('post/view', ['id' => 1, 'title' => 'sample post', 'lang' => 'en']);
+		$url = $manager->createUrl(['post/view', 'id' => 1, 'title' => 'sample post', 'lang' => 'en']);
 		$this->assertEquals('http://en.example.com/test/post/1/sample+post', $url);
-		$url = $manager->createUrl('post/index', ['page' => 1]);
+		$url = $manager->createUrl(['post/index', 'page' => 1]);
 		$this->assertEquals('/test/post/index?page=1', $url);
 	}
 
@@ -122,14 +122,14 @@ class UrlManagerTest extends TestCase
 			'hostInfo' => 'http://www.example.com',
 			'cache' => null,
 		]);
-		$url = $manager->createAbsoluteUrl('post/view', ['id' => 1, 'title' => 'sample post']);
+		$url = $manager->createAbsoluteUrl(['post/view', 'id' => 1, 'title' => 'sample post']);
 		$this->assertEquals('http://www.example.com?r=post/view&id=1&title=sample+post', $url);
 
-		$url = $manager->createAbsoluteUrl('post/view', ['id' => 1, 'title' => 'sample post'], 'https');
+		$url = $manager->createAbsoluteUrl(['post/view', 'id' => 1, 'title' => 'sample post'], 'https');
 		$this->assertEquals('https://www.example.com?r=post/view&id=1&title=sample+post', $url);
 
 		$manager->hostInfo = 'https://www.example.com';
-		$url = $manager->createAbsoluteUrl('post/view', ['id' => 1, 'title' => 'sample post'], 'http');
+		$url = $manager->createAbsoluteUrl(['post/view', 'id' => 1, 'title' => 'sample post'], 'http');
 		$this->assertEquals('http://www.example.com?r=post/view&id=1&title=sample+post', $url);
 	}
 
@@ -309,7 +309,7 @@ class UrlManagerTest extends TestCase
 				]
 			]
 		], \yii\web\Application::className());
-		$this->assertEquals('/app/post/delete?id=123', $manager->createUrl('post/delete', ['id' => 123]));
+		$this->assertEquals('/app/post/delete?id=123', $manager->createUrl(['post/delete', 'id' => 123]));
 		$this->destroyApplication();
 
 		unset($_SERVER['REQUEST_METHOD']);