diff --git a/.travis.yml b/.travis.yml
index 1a457a9..5ebd1b9 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -4,7 +4,14 @@ php:
   - 5.4
   - 5.5
   - 5.6
-#  - hhvm # commented until composer or hhvm get fixed: https://github.com/facebook/hhvm/issues/1347
+  - hhvm
+
+# run build against PHP 5.6 and hhvm but allow them to fail
+# http://docs.travis-ci.com/user/build-configuration/#Rows-That-are-Allowed-To-Fail
+matrix:
+  allow_failures:
+    - php: hhvm
+    - php: 5.6
 
 services:
   - redis-server
diff --git a/apps/advanced/backend/controllers/SiteController.php b/apps/advanced/backend/controllers/SiteController.php
index baa8dbe..60d3c1b 100644
--- a/apps/advanced/backend/controllers/SiteController.php
+++ b/apps/advanced/backend/controllers/SiteController.php
@@ -5,6 +5,7 @@ use Yii;
 use yii\web\AccessControl;
 use yii\web\Controller;
 use common\models\LoginForm;
+use yii\web\VerbFilter;
 
 /**
  * Site controller
@@ -31,6 +32,12 @@ class SiteController extends Controller
 					],
 				],
 			],
+			'verbs' => [
+				'class' => VerbFilter::className(),
+				'actions' => [
+					'logout' => ['post'],
+				],
+			],
 		];
 	}
 
diff --git a/apps/advanced/backend/views/layouts/main.php b/apps/advanced/backend/views/layouts/main.php
index cb41c08..0a1bf13 100644
--- a/apps/advanced/backend/views/layouts/main.php
+++ b/apps/advanced/backend/views/layouts/main.php
@@ -37,7 +37,11 @@ AppAsset::register($this);
 			if (Yii::$app->user->isGuest) {
 				$menuItems[] = ['label' => 'Login', 'url' => ['/site/login']];
 			} else {
-				$menuItems[] = ['label' => 'Logout (' . Yii::$app->user->identity->username .')' , 'url' => ['/site/logout']];
+				$menuItems[] = [
+					'label' => 'Logout (' . Yii::$app->user->identity->username . ')',
+					'url' => ['/site/logout'],
+					'linkOptions' => ['data-method' => 'post']
+				];
 			}
 			echo Nav::widget([
 				'options' => ['class' => 'navbar-nav navbar-right'],
diff --git a/apps/advanced/common/mail/layouts/html.php b/apps/advanced/common/mail/layouts/html.php
index 2e6b615..8e2707d 100644
--- a/apps/advanced/common/mail/layouts/html.php
+++ b/apps/advanced/common/mail/layouts/html.php
@@ -1,10 +1,9 @@
 <?php
 use yii\helpers\Html;
-use yii\mail\BaseMessage;
 
 /**
  * @var \yii\web\View $this
- * @var BaseMessage $content
+ * @var \yii\mail\BaseMessage $content
  */
 ?>
 <?php $this->beginPage() ?>
diff --git a/apps/advanced/common/models/User.php b/apps/advanced/common/models/User.php
index fc74532..d2c80c0 100644
--- a/apps/advanced/common/models/User.php
+++ b/apps/advanced/common/models/User.php
@@ -1,6 +1,7 @@
 <?php
 namespace common\models;
 
+use yii\base\NotSupportedException;
 use yii\db\ActiveRecord;
 use yii\helpers\Security;
 use yii\web\IdentityInterface;
@@ -72,6 +73,14 @@ class User extends ActiveRecord implements IdentityInterface
 	}
 
 	/**
+	 * @inheritdoc
+	 */
+	public static function findIdentityByAccessToken($token)
+	{
+		throw new NotSupportedException('"findIdentityByAccessToken" is not implemented.');
+	}
+
+	/**
 	 * Finds user by username
 	 *
 	 * @param string $username
diff --git a/apps/advanced/common/tests/_helpers/FixtureHelper.php b/apps/advanced/common/tests/_helpers/FixtureHelper.php
index 00c3a17..507b60c 100644
--- a/apps/advanced/common/tests/_helpers/FixtureHelper.php
+++ b/apps/advanced/common/tests/_helpers/FixtureHelper.php
@@ -32,7 +32,7 @@ class FixtureHelper extends Module
 	 * to use in acceptance and functional tests.
 	 * @param array $settings
 	 */
-	public function _beforeSuite($settings = array())
+	public function _beforeSuite($settings = [])
 	{
 		$this->loadFixtures();
 	}
@@ -54,5 +54,4 @@ class FixtureHelper extends Module
 			],
 		];
 	}
-
 }
diff --git a/apps/advanced/common/tests/_pages/LoginPage.php b/apps/advanced/common/tests/_pages/LoginPage.php
index af95ea3..5fdf3bc 100644
--- a/apps/advanced/common/tests/_pages/LoginPage.php
+++ b/apps/advanced/common/tests/_pages/LoginPage.php
@@ -18,5 +18,4 @@ class LoginPage extends BasePage
 		$this->guy->fillField('input[name="LoginForm[password]"]', $password);
 		$this->guy->click('login-button');
 	}
-
 }
diff --git a/apps/advanced/common/tests/unit/models/LoginFormTest.php b/apps/advanced/common/tests/unit/models/LoginFormTest.php
index 2ececd6..9ca277d 100644
--- a/apps/advanced/common/tests/unit/models/LoginFormTest.php
+++ b/apps/advanced/common/tests/unit/models/LoginFormTest.php
@@ -25,7 +25,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();
 		});
@@ -52,7 +52,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();
@@ -61,9 +61,8 @@ class LoginFormTest extends TestCase
 
 	private function mockUser($user)
 	{
-		$loginForm = $this->getMock('common\models\LoginForm',['getUser']);
+		$loginForm = $this->getMock('common\models\LoginForm', ['getUser']);
 		$loginForm->expects($this->any())->method('getUser')->will($this->returnValue($user));
 		return $loginForm;
 	}
-
 }
diff --git a/apps/advanced/composer.json b/apps/advanced/composer.json
index 53e926f..7744971 100644
--- a/apps/advanced/composer.json
+++ b/apps/advanced/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-app-advanced",
 	"description": "Yii 2 Advanced Application Template",
-	"keywords": ["yii", "framework", "advanced", "application template"],
+	"keywords": ["yii2", "framework", "advanced", "application template"],
 	"homepage": "http://www.yiiframework.com/",
 	"type": "project",
 	"license": "BSD-3-Clause",
diff --git a/apps/advanced/frontend/controllers/SiteController.php b/apps/advanced/frontend/controllers/SiteController.php
index 6816daf..a45c837 100644
--- a/apps/advanced/frontend/controllers/SiteController.php
+++ b/apps/advanced/frontend/controllers/SiteController.php
@@ -10,6 +10,7 @@ use yii\base\InvalidParamException;
 use yii\web\BadRequestHttpException;
 use yii\web\Controller;
 use Yii;
+use yii\web\VerbFilter;
 
 /**
  * Site controller
@@ -38,6 +39,12 @@ class SiteController extends Controller
 					],
 				],
 			],
+			'verbs' => [
+				'class' => VerbFilter::className(),
+				'actions' => [
+					'logout' => ['post'],
+				],
+			],
 		];
 	}
 
diff --git a/apps/advanced/frontend/tests/_pages/SignupPage.php b/apps/advanced/frontend/tests/_pages/SignupPage.php
index ba4a9cb..0281ac9 100644
--- a/apps/advanced/frontend/tests/_pages/SignupPage.php
+++ b/apps/advanced/frontend/tests/_pages/SignupPage.php
@@ -20,5 +20,4 @@ class SignupPage extends BasePage
 		}
 		$this->guy->click('signup-button');
 	}
-
 }
diff --git a/apps/advanced/frontend/tests/acceptance/SignupCest.php b/apps/advanced/frontend/tests/acceptance/SignupCest.php
index a166f34..268ff6b 100644
--- a/apps/advanced/frontend/tests/acceptance/SignupCest.php
+++ b/apps/advanced/frontend/tests/acceptance/SignupCest.php
@@ -79,5 +79,4 @@ class SignupCest
 		$I->expectTo('see that user logged in');
 		$I->see('Logout (tester)');
 	}
-
 }
diff --git a/apps/advanced/frontend/tests/functional/SignupCest.php b/apps/advanced/frontend/tests/functional/SignupCest.php
index cfef787..0409d23 100644
--- a/apps/advanced/frontend/tests/functional/SignupCest.php
+++ b/apps/advanced/frontend/tests/functional/SignupCest.php
@@ -79,5 +79,4 @@ class SignupCest
 		$I->expectTo('see that user logged in');
 		$I->see('Logout (tester)');
 	}
-
 }
diff --git a/apps/advanced/frontend/tests/unit/models/ContactFormTest.php b/apps/advanced/frontend/tests/unit/models/ContactFormTest.php
index 23e6288..a754672 100644
--- a/apps/advanced/frontend/tests/unit/models/ContactFormTest.php
+++ b/apps/advanced/frontend/tests/unit/models/ContactFormTest.php
@@ -42,7 +42,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 +56,4 @@ class ContactFormTest extends TestCase
 	{
 		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
index b736039..7591ff3 100644
--- a/apps/advanced/frontend/tests/unit/models/PasswordResetRequestFormTest.php
+++ b/apps/advanced/frontend/tests/unit/models/PasswordResetRequestFormTest.php
@@ -28,14 +28,14 @@ class PasswordResetRequestFormTest extends DbTestCase
 
 	public function testSendEmailWrongUser()
 	{
-		$this->specify('no user with such email, message should not be send', function() {
+		$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() {
+		$this->specify('user is not active, message should not be send', function () {
 			$model = new PasswordResetRequestForm();
 			$model->email = $this->user[1]['email'];
 			
@@ -52,8 +52,8 @@ class PasswordResetRequestFormTest extends DbTestCase
 		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();
+		$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']);
@@ -75,5 +75,4 @@ class PasswordResetRequestFormTest extends DbTestCase
 	{
 		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
index 763683f..b00c2ac 100644
--- a/apps/advanced/frontend/tests/unit/models/ResetPasswordFormTest.php
+++ b/apps/advanced/frontend/tests/unit/models/ResetPasswordFormTest.php
@@ -13,13 +13,13 @@ class ResetPasswordFormTest extends DbTestCase
 
 	public function testResetPassword()
 	{
-		$this->specify('wrong reset token', function() {
-			$this->setExpectedException('\Exception','Wrong password reset token.');
+		$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.');
+		$this->specify('not correct token', function () {
+			$this->setExpectedException('yii\base\InvalidParamException', 'Password reset token cannot be blank.');
 			new ResetPasswordForm('');
 		});
 	}
@@ -33,5 +33,4 @@ class ResetPasswordFormTest extends DbTestCase
 			],
 		];
 	}
-
 }
diff --git a/apps/advanced/frontend/tests/unit/models/SignupFormTest.php b/apps/advanced/frontend/tests/unit/models/SignupFormTest.php
index 5176baa..359a02c 100644
--- a/apps/advanced/frontend/tests/unit/models/SignupFormTest.php
+++ b/apps/advanced/frontend/tests/unit/models/SignupFormTest.php
@@ -12,7 +12,7 @@ class SignupFormTest extends DbTestCase
 
 	public function testCorrectSignup()
 	{
-		$model = $this->getMock('frontend\models\SignupForm',['validate']);
+		$model = $this->getMock('frontend\models\SignupForm', ['validate']);
 		$model->expects($this->once())->method('validate')->will($this->returnValue(true));
 
 		$model->username = 'some_username';
@@ -28,7 +28,7 @@ class SignupFormTest extends DbTestCase
 
 	public function testNotCorrectSignup()
 	{
-		$model = $this->getMock('frontend\models\SignupForm',['validate']);
+		$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();
@@ -43,5 +43,4 @@ class SignupFormTest extends DbTestCase
 			],
 		];
 	}
-
 }
diff --git a/apps/advanced/frontend/views/layouts/main.php b/apps/advanced/frontend/views/layouts/main.php
index be8d70d..6e3ae8c 100644
--- a/apps/advanced/frontend/views/layouts/main.php
+++ b/apps/advanced/frontend/views/layouts/main.php
@@ -41,7 +41,11 @@ AppAsset::register($this);
 				$menuItems[] = ['label' => 'Signup', 'url' => ['/site/signup']];
 				$menuItems[] = ['label' => 'Login', 'url' => ['/site/login']];
 			} else {
-				$menuItems[] = ['label' => 'Logout (' . Yii::$app->user->identity->username .')' , 'url' => ['/site/logout']];
+				$menuItems[] = [
+					'label' => 'Logout (' . Yii::$app->user->identity->username . ')',
+					'url' => ['/site/logout'],
+					'linkOptions' => ['data-method' => 'post']
+				];
 			}
 			echo Nav::widget([
 				'options' => ['class' => 'navbar-nav navbar-right'],
diff --git a/apps/basic/composer.json b/apps/basic/composer.json
index 9990ef7..726a7ea 100644
--- a/apps/basic/composer.json
+++ b/apps/basic/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-app-basic",
 	"description": "Yii 2 Basic Application Template",
-	"keywords": ["yii", "framework", "basic", "application template"],
+	"keywords": ["yii2", "framework", "basic", "application template"],
 	"homepage": "http://www.yiiframework.com/",
 	"type": "project",
 	"license": "BSD-3-Clause",
diff --git a/apps/basic/mail/layouts/html.php b/apps/basic/mail/layouts/html.php
index 2e6b615..8e2707d 100644
--- a/apps/basic/mail/layouts/html.php
+++ b/apps/basic/mail/layouts/html.php
@@ -1,10 +1,9 @@
 <?php
 use yii\helpers\Html;
-use yii\mail\BaseMessage;
 
 /**
  * @var \yii\web\View $this
- * @var BaseMessage $content
+ * @var \yii\mail\BaseMessage $content
  */
 ?>
 <?php $this->beginPage() ?>
diff --git a/apps/basic/models/User.php b/apps/basic/models/User.php
index b890e69..7a6fcc0 100644
--- a/apps/basic/models/User.php
+++ b/apps/basic/models/User.php
@@ -8,6 +8,7 @@ class User extends \yii\base\Object implements \yii\web\IdentityInterface
 	public $username;
 	public $password;
 	public $authKey;
+	public $accessToken;
 
 	private static $users = [
 		'100' => [
@@ -15,12 +16,14 @@ class User extends \yii\base\Object implements \yii\web\IdentityInterface
 			'username' => 'admin',
 			'password' => 'admin',
 			'authKey' => 'test100key',
+			'accessToken' => '100-token',
 		],
 		'101' => [
 			'id' => '101',
 			'username' => 'demo',
 			'password' => 'demo',
 			'authKey' => 'test101key',
+			'accessToken' => '101-token',
 		],
 	];
 
@@ -33,6 +36,19 @@ class User extends \yii\base\Object implements \yii\web\IdentityInterface
 	}
 
 	/**
+	 * @inheritdoc
+	 */
+	public static function findIdentityByAccessToken($token)
+	{
+		foreach (self::$users as $user) {
+			if ($user['accessToken'] === $token) {
+				return new static($user);
+			}
+		}
+		return null;
+	}
+
+	/**
 	 * Finds user by username
 	 *
 	 * @param string $username
diff --git a/apps/basic/tests/_bootstrap.php b/apps/basic/tests/_bootstrap.php
index 9340613..4890b3e 100644
--- a/apps/basic/tests/_bootstrap.php
+++ b/apps/basic/tests/_bootstrap.php
@@ -18,5 +18,6 @@ require_once(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
 // set correct script paths
 $_SERVER['SCRIPT_FILENAME'] = TEST_ENTRY_FILE;
 $_SERVER['SCRIPT_NAME'] = TEST_ENTRY_URL;
+$_SERVER['SERVER_NAME'] = 'localhost';
 
 Yii::setAlias('@tests', __DIR__);
diff --git a/apps/benchmark/composer.json b/apps/benchmark/composer.json
index d980f9a..c8ed589 100644
--- a/apps/benchmark/composer.json
+++ b/apps/benchmark/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-app-benchmark",
 	"description": "Yii 2 Benchmark Application",
-	"keywords": ["yii", "framework", "benchmark", "application"],
+	"keywords": ["yii2", "framework", "benchmark", "application"],
 	"homepage": "http://www.yiiframework.com/",
 	"type": "project",
 	"license": "BSD-3-Clause",
diff --git a/build/controllers/ClassmapController.php b/build/controllers/ClassmapController.php
index 706435a..5fd4e5f 100644
--- a/build/controllers/ClassmapController.php
+++ b/build/controllers/ClassmapController.php
@@ -52,7 +52,7 @@ class ClassmapController extends Controller
 		$files = FileHelper::findFiles($root, $options);
 		$map = [];
 		foreach ($files as $file) {
-			if (($pos = strpos($file, $root)) !== 0) {
+			if (strpos($file, $root) !== 0) {
 				die("Something wrong: $file\n");
 			}
 			$path = str_replace('\\', '/', substr($file, strlen($root)));
diff --git a/composer.json b/composer.json
index d01de32..2466074 100644
--- a/composer.json
+++ b/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-dev",
 	"description": "Yii PHP Framework Version 2 - Development Package",
-	"keywords": ["yii", "framework"],
+	"keywords": ["yii2", "framework"],
 	"homepage": "http://www.yiiframework.com/",
 	"type": "yii2-extension",
 	"license": "BSD-3-Clause",
@@ -74,6 +74,7 @@
 		"lib-pcre": "*",
 		"yiisoft/yii2-composer": "*",
 		"yiisoft/jquery": "~2.0 | ~1.10",
+		"yiisoft/jquery-pjax": "*",
 		"ezyang/htmlpurifier": "4.6.*",
 		"cebe/markdown": "0.9.*"
 	},
diff --git a/docs/guide/active-record.md b/docs/guide/active-record.md
index c54bb08..6a3df60 100644
--- a/docs/guide/active-record.md
+++ b/docs/guide/active-record.md
@@ -172,7 +172,7 @@ Note that [[yii\db\ActiveRecord::updateAll()|updateAll()]], [[yii\db\ActiveRecor
 
 ```php
 // to insert a new customer record
-$customer = new Customer;
+$customer = new Customer();
 $customer->name = 'James';
 $customer->email = 'james@example.com';
 $customer->save();  // equivalent to $customer->insert();
@@ -634,7 +634,7 @@ order owned by the customer:
 
 ```php
 $customer = Customer::find(1);
-$order = new Order;
+$order = new Order();
 $order->subtotal = 100;
 $customer->link('orders', $order);
 ```
diff --git a/docs/guide/assets.md b/docs/guide/assets.md
index 17c6797..ca3b07c 100644
--- a/docs/guide/assets.md
+++ b/docs/guide/assets.md
@@ -81,18 +81,26 @@ 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();
-    }
+	public $language;
+	public $sourcePath = '@app/assets/language';
+	public $js = [
+	];
+
+	public function registerAssetFiles($view)
+	{
+		$language = $this->language ? $this->language : Yii::$app->language;
+		$this->js[] = 'language-' . $language . '.js';
+		parent::registerAssetFiles($view);
+	}
 }
 ```
 
+In order to set language use the following code when registering an asset bundle in a view:
+
+```php
+LanguageAsset::register($this)->language = $language;
+```
+
 
 Registering asset bundle
 ------------------------
@@ -114,6 +122,10 @@ To register an asset inside of a widget, the view instance is available as `$thi
 AppAsset::register($this->view);
 ```
 
+> Note: If there is a need to modify third party asset bundles it is recommended to create your own bundles depending
+  on third party ones and use CSS and JavaScript features to modify behavior instead of editing files directly or
+  copying them over.
+
 
 Overriding asset bundles
 ------------------------
diff --git a/docs/guide/authentication.md b/docs/guide/authentication.md
index e7c1d94..f43ff0d 100644
--- a/docs/guide/authentication.md
+++ b/docs/guide/authentication.md
@@ -5,7 +5,7 @@ Authentication is the act of verifying who a user is, and is the basis of the lo
 
 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
+You can find a fully featured example of authentication in the
 [advanced application template](installation.md). Below, only the interface methods are listed:
 
 ```php
@@ -25,6 +25,17 @@ class User extends ActiveRecord implements IdentityInterface
 	}
 
 	/**
+	 * Finds an identity by the given token.
+	 *
+	 * @param string $token the token to be looked for
+	 * @return IdentityInterface|null the identity object that matches the given token.
+	 */
+	public static function findIdentityByAccessToken($token)
+	{
+		return static::find(['access_token' => $token]);
+	}
+
+	/**
 	 * @return int|string current user ID
 	 */
 	public function getId()
diff --git a/docs/guide/controller.md b/docs/guide/controller.md
index 8bf03ad..eec7a89 100644
--- a/docs/guide/controller.md
+++ b/docs/guide/controller.md
@@ -81,13 +81,16 @@ Routes
 ------
 
 Each controller action has a corresponding internal route. In our example above `actionIndex` has `site/index` route
-and `actionTest` has `site/test` route. In this route `site` is referred to as controller ID while `test` is referred to
-as action ID.
+and `actionTest` has `site/test` route. In this route `site` is referred to as controller ID while `test` is action ID.
 
 By default you can access specific controller and action using the `http://example.com/?r=controller/action` URL. This
-behavior is fully customizable. For details refer to [URL Management](url.md).
+behavior is fully customizable. For more details please refer to [URL Management](url.md).
 
-If controller is located inside a module its action internal route will be `module/controller/action`.
+If a controller is located inside a module, the route of its actions will be in the format of `module/controller/action`.
+
+A controller can be located under a subdirectory of the controller directory of an application or module. The route
+will be prefixed with the corresponding directory names. For example, you may have a `UserController` under `controllers/admin`.
+The route of its `actionIndex` would be `admin/user/index`, and `admin/user` would be the controller ID.
 
 In case module, controller or action specified isn't found Yii will return "not found" page and HTTP status code 404.
 
@@ -160,7 +163,7 @@ class BlogController extends Controller
 	{
 		$post = Post::find($id);
 		if (!$post) {
-			throw new NotFoundHttpException;
+			throw new NotFoundHttpException();
 		}
 
 		if (\Yii::$app->request->isPost) {
diff --git a/docs/guide/data-grid.md b/docs/guide/data-grid.md
new file mode 100644
index 0000000..87bfce6
--- /dev/null
+++ b/docs/guide/data-grid.md
@@ -0,0 +1,80 @@
+Data grid
+=========
+
+Data grid or GridView is one of the most powerful Yii widgets. It is extremely useful if you need to quickly build admin
+section of the system. It takes data from [data provider](data-provider.md) and renders each row using a set of columns
+presenting data in a form of a table.
+
+Each row of the table represents the data of a single data item, and a column usually represents an attribute of
+the item (some columns may correspond to complex expression of attributes or static text).
+
+Grid view supports both sorting and pagination of the data items. The sorting and pagination can be done in AJAX mode
+or normal page request. A benefit of using GridView is that when the user disables JavaScript, the sorting and pagination
+automatically degrade to normal page requests and are still functioning as expected.
+
+The minimal code needed to use CGridView is as follows:
+
+```php
+sdf
+```
+
+The above code first creates a data provider and then uses GridView to display every attribute in every row taken from
+data provider. The displayed table is equiped with sorting and pagination functionality.
+
+Grid columns
+------------
+
+Yii grid consists of a number of columns. Depending on column type and settings these are able to present data differently.
+
+These are defined in the columns part of GridView config like the following:
+
+```php
+echo GridView::widget([
+	'dataProvider' => $dataProvider,
+	'columns' => [
+		['class' => 'yii\grid\SerialColumn'],
+		// A simple column defined by the data contained in $dataProvider.
+		// Data from model's column1 will be used.
+		'id',
+		'username',
+		// More complex one.
+		[
+			'class' => 'DataColumn', // can be omitted, default
+			'name' => 'column1',
+			'value' => function ($data) {
+				return $data->name;
+			},
+			'type'=>'raw',
+		],
+	],
+]);
+```
+
+Note: If columns part of config isn't specified, Yii tries to show all possible data provider model columns.
+
+
+### Column classes
+
+
+#### Data column
+
+#### Action column
+
+#### Checkbox column
+
+#### Serial column
+
+TODO: rewrite these:
+
+- https://github.com/samdark/a-guide-to-yii-grids-lists-and-data-providers/blob/master/grid-columns.md
+- https://github.com/samdark/a-guide-to-yii-grids-lists-and-data-providers/pull/1
+
+Sorting data
+------------
+
+- https://github.com/yiisoft/yii2/issues/1576
+
+Filtering data
+--------------
+
+- https://github.com/yiisoft/yii2/issues/1581
\ No newline at end of file
diff --git a/docs/guide/data-overview.md b/docs/guide/data-overview.md
new file mode 100644
index 0000000..6318d18
--- /dev/null
+++ b/docs/guide/data-overview.md
@@ -0,0 +1,14 @@
+Data and widgets for it
+=======================
+
+One of the most powerful aspects of Yii is how it works with data. One may output data directly and that's a good approach
+for website frontend but when it comes to backend data components and widgets may save you weeks.
+
+Typically, you would take the following steps when working with one of these data components:
+
+1. Configure data provider. It may take its data from array, SQL, AR query etc.
+2. Pass data provider to one of the widgets such as list view or grid view.
+3. Customize the widget to reflect the presentational style that you are after.
+
+That's it. After doing these simple steps you can get a full featured data grid supporting pagination, sorting and
+filtering that is ideal for admin part of your project.
\ No newline at end of file
diff --git a/docs/guide/data-providers.md b/docs/guide/data-providers.md
new file mode 100644
index 0000000..9080c55
--- /dev/null
+++ b/docs/guide/data-providers.md
@@ -0,0 +1,130 @@
+Data providers
+==============
+
+Data provider abstracts data set via [[yii\data\DataProviderInterface]] and handles pagination and sorting.
+It can be used by [grids](data-grid.md), [lists and other data widgets](data-widgets.md).
+
+In Yii there are three built-in data providers: [[yii\data\ActiveDataProvider]], [[yii\data\ArrayDataProvider]] and
+[[yii\data\SqlDataProvider]].
+
+Active data provider
+--------------------
+
+`ActiveDataProvider` provides data by performing DB queries using [[\yii\db\Query]] and [[\yii\db\ActiveQuery]].
+
+The following is an example of using it to provide ActiveRecord instances:
+
+```php
+$provider = new ActiveDataProvider([
+	'query' => Post::find(),
+	'pagination' => [
+		'pageSize' => 20,
+	],
+]);
+
+// get the posts in the current page
+$posts = $provider->getModels();
+~~~
+
+And the following example shows how to use ActiveDataProvider without ActiveRecord:
+
+```php
+$query = new Query();
+$provider = new ActiveDataProvider([
+	'query' => $query->from('tbl_post'),
+	'pagination' => [
+		'pageSize' => 20,
+	],
+]);
+
+// get the posts in the current page
+$posts = $provider->getModels();
+```
+
+Array data provider
+-------------------
+
+ArrayDataProvider implements a data provider based on a data array.
+
+The [[allModels]] property contains all data models that may be sorted and/or paginated.
+ArrayDataProvider will provide the data after sorting and/or pagination.
+You may configure the [[sort]] and [[pagination]] properties to
+customize the sorting and pagination behaviors.
+
+Elements in the [[allModels]] array may be either objects (e.g. model objects)
+or associative arrays (e.g. query results of DAO).
+Make sure to set the [[key]] property to the name of the field that uniquely
+identifies a data record or false if you do not have such a field.
+
+Compared to `ActiveDataProvider`, `ArrayDataProvider` could be less efficient
+because it needs to have [[allModels]] ready.
+
+ArrayDataProvider may be used in the following way:
+
+```php
+$query = new Query();
+$provider = new ArrayDataProvider([
+    'allModels' => $query->from('tbl_post')->all(),
+    'sort' => [
+        'attributes' => ['id', 'username', 'email'],
+    ],
+    'pagination' => [
+        'pageSize' => 10,
+    ],
+]);
+// get the posts in the current page
+$posts = $provider->getModels();
+```
+
+> Note: if you want to use the sorting feature, you must configure the [[sort]] property
+so that the provider knows which columns can be sorted.
+
+SQL data provider
+-----------------
+
+SqlDataProvider implements a data provider based on a plain SQL statement. It provides data in terms of arrays, each
+representing a row of query result.
+
+Like other data providers, SqlDataProvider also supports sorting and pagination. It does so by modifying the given
+[[sql]] statement with "ORDER BY" and "LIMIT" clauses. You may configure the [[sort]] and [[pagination]] properties to
+customize sorting and pagination behaviors.
+
+`SqlDataProvider` may be used in the following way:
+
+```php
+$count = Yii::$app->db->createCommand('
+    SELECT COUNT(*) FROM tbl_user WHERE status=:status
+', [':status' => 1])->queryScalar();
+
+$dataProvider = new SqlDataProvider([
+    'sql' => 'SELECTFROM tbl_user WHERE status=:status',
+    'params' => [':status' => 1],
+    'totalCount' => $count,
+    'sort' => [
+        'attributes' => [
+            'age',
+            'name' => [
+                'asc' => ['first_name' => SORT_ASC, 'last_name' => SORT_ASC],
+                'desc' => ['first_name' => SORT_DESC, 'last_name' => SORT_DESC],
+                'default' => SORT_DESC,
+                'label' => 'Name',
+            ],
+        ],
+    ],
+    'pagination' => [
+        'pageSize' => 20,
+    ],
+]);
+
+// get the user records in the current page
+$models = $dataProvider->getModels();
+```
+
+> Note: if you want to use the pagination feature, you must configure the [[totalCount]] property
+to be the total number of rows (without pagination). And if you want to use the sorting feature,
+you must configure the [[sort]] property so that the provider knows which columns can be sorted.
+
+
+Implementing your own custom data provider
+------------------------------------------
+
diff --git a/docs/guide/data-widgets.md b/docs/guide/data-widgets.md
new file mode 100644
index 0000000..8046e8a
--- /dev/null
+++ b/docs/guide/data-widgets.md
@@ -0,0 +1,34 @@
+Data widgets
+============
+
+ListView
+--------
+
+
+
+DetailView
+----------
+
+DetailView displays the detail of a single data [[model]].
+ 
+It is best used for displaying a model in a regular format (e.g. each model attribute is displayed as a row in a table).
+The model can be either an instance of [[Model]] or an associative array.
+ 
+DetailView uses the [[attributes]] property to determines which model attributes should be displayed and how they
+should be formatted.
+ 
+A typical usage of DetailView is as follows:
+ 
+```php
+echo DetailView::widget([
+	'model' => $model,
+	'attributes' => [
+		'title',             // title attribute (in plain text)
+		'description:html',  // description attribute in HTML
+		[                    // the owner name of the model
+			'label' => 'Owner',
+			'value' => $model->owner->name,
+		],
+	],
+]);
+```
diff --git a/docs/guide/index.md b/docs/guide/index.md
index 349caa5..3d5305f 100644
--- a/docs/guide/index.md
+++ b/docs/guide/index.md
@@ -72,10 +72,10 @@ Security and access control
 Data providers, lists and grids
 -------------------------------
 
-- Overview
-- Data providers
-- Grids
-- Lists
+- [Overview](data-overview.md)
+- [Data providers](data-providers.md)
+- [Data widgets](data-widgets.md)
+- [Grid](data-grid.md)
 
 Advanced Topics
 ---------------
diff --git a/docs/guide/model.md b/docs/guide/model.md
index f840457..6585c67 100644
--- a/docs/guide/model.md
+++ b/docs/guide/model.md
@@ -24,7 +24,7 @@ be accessed like the member variables of any object. For example, a `Post` model
 may contain a `title` attribute and a `content` attribute, accessible as follows:
 
 ```php
-$post = new Post;
+$post = new Post();
 $post->title = 'Hello, world';
 $post->content = 'Something interesting is happening.';
 echo $post->title;
@@ -35,7 +35,7 @@ Since [[yii\base\Model|Model]] implements the [ArrayAccess](http://php.net/manua
 you can also access the attributes as if they were array elements:
 
 ```php
-$post = new Post;
+$post = new Post();
 $post['title'] = 'Hello, world';
 $post['content'] = 'Something interesting is happening';
 echo $post['title'];
@@ -160,7 +160,7 @@ class EmployeeController extends \yii\web\Controller
 		$employee = new Employee(['scenario' => 'managementPanel']);
 
 		// second way
-		$employee = new Employee;
+		$employee = new Employee();
 		$employee->scenario = 'managementPanel';
 
 		// third way
@@ -187,7 +187,7 @@ only, etc. If errors are found in validation, they may be presented to the user 
 The following example shows how the validation is performed:
 
 ```php
-$model = new LoginForm;
+$model = new LoginForm();
 $model->username = $_POST['username'];
 $model->password = $_POST['password'];
 if ($model->validate()) {
@@ -331,7 +331,7 @@ For the code above mass assignment will be allowed stsrictly according to `scena
 $user = User::find(42);
 $data = ['password' => '123'];
 $user->attributes = $data;
-print_r($data);
+print_r($user->attributes);
 ```
 
 Will give you empty array because there's no default scenario defined in our `scenarios()`.
@@ -345,7 +345,7 @@ $data = [
 	'hashcode' => 'test',
 ];
 $user->attributes = $data;
-print_r($data);
+print_r($user->attributes);
 ```
 
 Will give you the following:
@@ -386,7 +386,7 @@ $data = [
 	'password' => '123',
 ];
 $user->attributes = $data;
-print_r($data);
+print_r($user->attributes);
 ```
 
 Will give you the following:
diff --git a/docs/guide/module-debug.md b/docs/guide/module-debug.md
index 91da33a..916101c 100644
--- a/docs/guide/module-debug.md
+++ b/docs/guide/module-debug.md
@@ -110,7 +110,7 @@ class ViewsPanel extends Panel
 	{
 		parent::init();
 		Event::on(View::className(), View::EVENT_BEFORE_RENDER, function (ViewEvent $event) {
-			$this->_viewFiles[] = $event->viewFile;
+			$this->_viewFiles[] = $event->sender->getViewFile();
 		});
 	}
 
diff --git a/docs/guide/query-builder.md b/docs/guide/query-builder.md
index c238639..a268311 100644
--- a/docs/guide/query-builder.md
+++ b/docs/guide/query-builder.md
@@ -9,7 +9,7 @@ The Query Builder provides an object-oriented vehicle for generating queries to 
 A typical usage of the query builder looks like the following:
 
 ```php
-$rows = (new \yii\db\Query)
+$rows = (new \yii\db\Query())
 	->select('id, name')
 	->from('tbl_user')
 	->limit(10)
@@ -17,7 +17,7 @@ $rows = (new \yii\db\Query)
 
 // which is equivalent to the following code:
 
-$query = (new \yii\db\Query)
+$query = (new \yii\db\Query())
 	->select('id, name')
 	->from('tbl_user')
 	->limit(10);
@@ -116,7 +116,7 @@ You may specify a sub-query using a `Query` object. In this case, the correspond
 as the alias for the sub-query.
 
 ```php
-$subQuery = (new Query)->select('id')->from('tbl_user')->where('status=1');
+$subQuery = (new Query())->select('id')->from('tbl_user')->where('status=1');
 $query->select('*')->from(['u' => $subQuery]);
 ```
 
@@ -324,10 +324,10 @@ $query->leftJoin(['u' => $subQuery], 'u.id=author_id');
 In Yii in order to build it you can first form two query objects and then use `union` method:
 
 ```php
-$query = new Query;
+$query = new Query();
 $query->select("id, 'post' as type, name")->from('tbl_post')->limit(10);
 
-$anotherQuery = new Query;
+$anotherQuery = new Query();
 $anotherQuery->select('id, 'user' as type, name')->from('tbl_user')->limit(10);
 
 $query->union($anotherQuery);
@@ -347,7 +347,7 @@ Batch query can be used like the following:
 ```php
 use yii\db\Query;
 
-$query = (new Query)
+$query = (new Query())
 	->from('tbl_user')
 	->orderBy('id');
 
@@ -376,7 +376,7 @@ will still keep the proper index. For example,
 ```php
 use yii\db\Query;
 
-$query = (new Query)
+$query = (new Query())
 	->from('tbl_user')
 	->indexBy('username');
 
diff --git a/docs/guide/rest.md b/docs/guide/rest.md
new file mode 100644
index 0000000..6fd215f
--- /dev/null
+++ b/docs/guide/rest.md
@@ -0,0 +1,878 @@
+Implementing RESTful Web Service APIs
+=====================================
+
+Yii provides a whole set of tools to greatly simplify the task of implementing RESTful Web Service APIs.
+In particular, Yii provides support for the following aspects regarding RESTful APIs:
+
+* Quick prototyping with support for common APIs for ActiveRecord;
+* Response format (supporting JSON and XML by default) and API version negotiation;
+* Customizable object serialization with support for selectable output fields;
+* Proper formatting of collection data and validation errors;
+* Efficient routing with proper HTTP verb check;
+* Support `OPTIONS` and `HEAD` verbs;
+* Authentication;
+* Authorization;
+* Support for HATEOAS;
+* Caching via `yii\web\HttpCache`;
+* Rate limiting;
+* Searching and filtering: TBD
+* Testing: TBD
+* Automatic generation of API documentation: TBD
+
+
+A Quick Example
+---------------
+
+Let's use a quick example to show how to build a set of RESTful APIs using Yii.
+Assume you want to expose the user data via RESTful APIs. The user data are stored in the user DB table,
+and you have already created the ActiveRecord class `app\models\User` to access the user data.
+
+First, create a controller class `app\controllers\UserController` as follows,
+
+```php
+namespace app\controllers;
+
+use yii\rest\ActiveController;
+
+class UserController extends ActiveController
+{
+    public $modelClass = 'app\models\User';
+}
+```
+
+Then, modify the configuration about the `urlManager` component in your application configuration:
+
+```php
+'urlManager' => [
+    'enablePrettyUrl' => true,
+    'enableStrictParsing' => true,
+    'showScriptName' => false,
+    'rules' => [
+        ['class' => 'yii\rest\UrlRule', 'controller' => 'user'],
+    ],
+]
+```
+
+With the above minimal amount of effort, you have already finished your task of creating the RESTful APIs
+for accessing the user data. The APIs you have created include:
+
+* `GET /users`: list all users page by page;
+* `HEAD /users`: show the overview information of user listing;
+* `POST /users`: create a new user;
+* `GET /users/123`: return the details of the user 123;
+* `HEAD /users/123`: show the overview information of user 123;
+* `PATCH /users/123` and `PUT /users/123`: update the user 123;
+* `DELETE /users/123`: delete the user 123;
+* `OPTIONS /users`: show the supported verbs regarding endpoint `/users`;
+* `OPTIONS /users/123`: show the supported verbs regarding endpoint `/users/123`.
+
+You may access your APIs with the `curl` command like the following,
+
+```
+curl -i -H "Accept:application/json" "http://localhost/users"
+```
+
+which may give the following output:
+
+```
+HTTP/1.1 200 OK
+Date: Sun, 02 Mar 2014 05:31:43 GMT
+Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
+X-Powered-By: PHP/5.4.20
+X-Pagination-Total-Count: 1000
+X-Pagination-Page-Count: 50
+X-Pagination-Current-Page: 1
+X-Pagination-Per-Page: 20
+Link: <http://localhost/users?page=1>; rel=self, 
+      <http://localhost/users?page=2>; rel=next, 
+      <http://localhost/users?page=50>; rel=last
+Transfer-Encoding: chunked
+Content-Type: application/json; charset=UTF-8
+
+[
+    {
+        "id": 1,
+        ...
+    },
+    {
+        "id": 2,
+        ...
+    },
+    ...
+]
+```
+
+Try changing the acceptable content type to be `application/xml`, and you will see the result
+is returned in XML format:
+
+```
+curl -i -H "Accept:application/xml" "http://localhost/users"
+```
+
+```
+HTTP/1.1 200 OK
+Date: Sun, 02 Mar 2014 05:31:43 GMT
+Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
+X-Powered-By: PHP/5.4.20
+X-Pagination-Total-Count: 1000
+X-Pagination-Page-Count: 50
+X-Pagination-Current-Page: 1
+X-Pagination-Per-Page: 20
+Link: <http://localhost/users?page=1>; rel=self, 
+      <http://localhost/users?page=2>; rel=next, 
+      <http://localhost/users?page=50>; rel=last
+Transfer-Encoding: chunked
+Content-Type: application/xml
+
+<?xml version="1.0" encoding="UTF-8"?>
+<response>
+    <item>
+        <id>1</id>
+        ...
+    </item>
+    <item>
+        <id>2</id>
+        ...
+    </item>
+    ...
+</response>
+```
+
+> Tip: You may also access your APIs via Web browser by entering the URL `http://localhost/users`.
+
+As you can see, in the response headers, there are information about the total count, page count, etc.
+There are also links that allow you to navigate to other pages of data. For example, `http://localhost/users?page=2`
+would give you the next page of the user data.
+
+Using the `fields` and `expand` parameters, you may also request to return a subset of the fields in the result.
+For example, the URL `http://localhost/users?fields=id,email` will only return the `id` and `email` fields in the result:
+
+
+> Info: You may have noticed that the result of `http://localhost/users` includes some sensitive fields,
+> such as `password_hash`, `auth_key`. You certainly do not want these to appear in your API result.
+> You can/should filter out these fields as described in the following sections.
+
+
+In the following sections, we will explain in more details about implementing RESTful APIs.
+
+
+General Architecture
+--------------------
+
+Using the Yii RESTful API framework, you implement an API endpoint in terms of a controller action, and you use
+a controller to organize the actions that implement the endpoints for a single type of resource.
+
+Resources are represented as data models which extend from the [[yii\base\Model]] class.
+If you are working with databases (relational or NoSQL), it is recommended you use ActiveRecord to represent resources.
+
+You may use [[yii\rest\UrlRule]] to simplify the routing to your API endpoints.
+
+While not required, it is recommended that you develop your RESTful APIs as an application, separated from
+your Web front end and back end.
+
+
+Creating Resource Classes
+-------------------------
+
+RESTful APIs are all about accessing and manipulating resources. In Yii, a resource can be an object of any class.
+However, if your resource classes extend from [[yii\base\Model]] or its child classes (e.g. [[yii\db\ActiveRecord]]),
+you may enjoy the following benefits:
+
+* Input data validation;
+* Query, create, update and delete data, if extending from [[yii\db\ActiveRecord]];
+* Customizable data formatting (to be explained in the next section).
+
+
+Formatting Response Data
+------------------------
+
+By default, Yii supports two response formats for RESTful APIs: JSON and XML. If you want to support
+other formats, you should configure [[yii\rest\Controller::supportedFormats]] and also [[yii\web\Response::formatters]].
+
+Formatting response data in general involves two steps:
+
+1. The objects (including embedded objects) in the response data are converted into arrays by [[yii\rest\Serializer]];
+2. The array data are converted into different formats (e.g. JSON, XML) by [[yii\web\ResponseFormatterInterface|response formatters]].
+
+Step 2 is usually a very mechanical data conversion process and can be well handled by the built-in response formatters.
+Step 1 involves some major development effort as explained below.
+
+When the [[yii\rest\Serializer|serializer]] converts an object into an array, it will call the `toArray()` method
+of the object if it implements [[yii\base\ArrayableInterface]]. If an object does not implement this interface,
+its public properties will be returned instead.
+
+For classes extending from [[yii\base\Model]] or [[yii\db\ActiveRecord]], besides directly overriding `toArray()`,
+you may also override the `fields()` method and/or the `extraFields()` method to customize the data being returned.
+
+The method [[yii\base\Model::fields()]] declares a set of *fields* that should be included in the result.
+A field is simply a named data item. In a result array, the array keys are the field names, and the array values
+are the corresponding field values. The default implementation of [[yii\base\Model::fields()]] is to return
+all attributes of a model as the output fields; for [[yii\db\ActiveRecord::fields()]], by default it will return
+the names of the attributes whose values have been populated into the object.
+
+You can override the `fields()` method to add, remove, rename or redefine fields. For example,
+
+```php
+// explicitly list every field, best used when you want to make sure the changes
+// in your DB table or model attributes do not cause your field changes (to keep API backward compatibility).
+public function fields()
+{
+    return [
+        // field name is the same as the attribute name
+        'id',
+        // field name is "email", the corresponding attribute name is "email_address"
+        'email' => 'email_address',
+        // field name is "name", its value is defined by a PHP callback
+        'name' => function () {
+            return $this->first_name . ' ' . $this->last_name;
+        },
+    ];
+}
+
+// filter out some fields, best used when you want to inherit the parent implementation
+// and blacklist some sensitive fields.
+public function fields()
+{
+    $fields = parent::fields();
+
+    // remove fields that contain sensitive information
+    unset($fields['auth_key'], $fields['password_hash'], $fields['password_reset_token']);
+
+    return $fields;
+}
+```
+
+The return value of `fields()` should be an array. The array keys are the field names, and the array values
+are the corresponding field definitions which can be either property/attribute names or anonymous functions
+returning the corresponding field values.
+
+> Warning: Because by default all attributes of a model will be included in the API result, you should
+> examine your data to make sure they do not contain sensitive information. If there is such information,
+> you should override `fields()` or `toArray()` to filter them out. In the above example, we choose
+> to filter out `auth_key`, `password_hash` and `password_reset_token`.
+
+You may use the `fields` query parameter to specify which fields in `fields()` should be included in the result.
+If this parameter is not specified, all fields returned by `fields()` will be returned.
+
+The method [[yii\base\Model::extraFields()]] is very similar to [[yii\base\Model::fields()]].
+The difference between these methods is that the latter declares the fields that should be returned by default,
+while the former declares the fields that should only be returned when the user specifies them in the `expand` query parameter.
+
+For example, `http://localhost/users?fields=id,email&expand=profile` may return the following JSON data:
+
+```php
+[
+    {
+        "id": 100,
+        "email": "100@example.com",
+        "profile": {
+            "id": 100,
+            "age": 30,
+        }
+    },
+    ...
+]
+```
+
+You may wonder who triggers the conversion from objects to arrays when an action returns an object or object collection.
+The answer is that this is done by [[yii\rest\Controller::serializer]] in the [[yii\base\Controller::afterAction()|afterAction()]]
+method. By default, [[yii\rest\Serializer]] is used as the serializer that can recognize resource objects extending from
+[[yii\base\Model]] and collection objects implementing [[yii\data\DataProviderInterface]]. The serializer
+will call the `toArray()` method of these objects and pass the `fields` and `expand` user parameters to the method.
+If there are any embedded objects, they will also be converted into arrays recursively.
+
+If all your resource objects are of [[yii\base\Model]] or its child classes, such as [[yii\db\ActiveRecord]],
+and you only use [[yii\data\DataProviderInterface]] as resource collections, the default data formatting
+implementation should work very well. However, if you want to introduce some new resource classes that do not
+extend from [[yii\base\Model]], or if you want to use some new collection classes, you will need to
+customize the serializer class and configure [[yii\rest\Controller::serializer]] to use it.
+You new resource classes may use the trait [[yii\base\ArrayableTrait]] to support selective field output
+as explained above.
+
+
+### Pagination
+
+For API endpoints about resource collections, pagination is supported out-of-box if you use 
+[[yii\data\DataProviderInterface|data provider]] to serve the response data. In particular,
+through query parameters `page` and `per-page`, an API consumer may specify which page of data
+to return and how many data items should be included in each page. The corresponding response
+will include the pagination information by the following HTTP headers (please also refer to the first example
+in this chapter):
+
+* `X-Pagination-Total-Count`: The total number of data items;
+* `X-Pagination-Page-Count`: The number of pages;
+* `X-Pagination-Current-Page`: The current page (1-based);
+* `X-Pagination-Per-Page`: The number of data items in each page;
+* `Link`: A set of navigational links allowing client to traverse the data page by page.
+
+The response body will contain a list of data items in the requested page.
+
+Sometimes, you may want to help simplify the client development work by including pagination information
+directly in the response body. To do so, configure the [[yii\rest\Serializer::collectionEnvelope]] property
+as follows:
+
+```php
+use yii\rest\ActiveController;
+
+class UserController extends ActiveController
+{
+    public $modelClass = 'app\models\User';
+    public $serializer = [
+        'class' => 'yii\rest\Serializer',
+        'collectionEnvelope' => 'items',
+    ];
+}
+```
+
+You may then get the following response for request `http://localhost/users`:
+
+```
+HTTP/1.1 200 OK
+Date: Sun, 02 Mar 2014 05:31:43 GMT
+Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
+X-Powered-By: PHP/5.4.20
+X-Pagination-Total-Count: 1000
+X-Pagination-Page-Count: 50
+X-Pagination-Current-Page: 1
+X-Pagination-Per-Page: 20
+Link: <http://localhost/users?page=1>; rel=self, 
+      <http://localhost/users?page=2>; rel=next, 
+      <http://localhost/users?page=50>; rel=last
+Transfer-Encoding: chunked
+Content-Type: application/json; charset=UTF-8
+
+{
+    "items": [
+        {
+            "id": 1,
+            ...
+        },
+        {
+            "id": 2,
+            ...
+        },
+        ...
+    ],
+    "_links": {
+        "self": "http://localhost/users?page=1", 
+        "next": "http://localhost/users?page=2", 
+        "last": "http://localhost/users?page=50"
+    },
+    "_meta": {
+        "totalCount": 1000,
+        "pageCount": 50,
+        "currentPage": 1,
+        "perPage": 20
+    }
+}
+```
+
+
+### HATEOAS Support
+
+[HATEOAS](http://en.wikipedia.org/wiki/HATEOAS), an abbreviation for Hypermedia as the Engine of Application State,
+promotes that RESTful APIs should return information that allow clients to discover actions supported for the returned
+resources. The key of HATEOAS is to return a set of hyperlinks with relation information when resource data are served
+by APIs.
+
+You may let your model classes to implement the [[yii\web\Linkable]] interface to support HATEOAS. By implementing
+this interface, a class is required to return a list of [[yii\web\Link|links]]. Typically, you should return at least
+the `self` link, for example:
+
+```php
+use yii\db\ActiveRecord;
+use yii\web\Linkable;
+use yii\helpers\Url;
+
+class User extends ActiveRecord implements Linkable
+{
+    public function getLinks()
+    {
+        return [
+            Link::REL_SELF => Url::action(['user', 'id' => $this->id], true),
+        ];
+    }
+}
+```
+
+When a `User` object is returned in a response, it will contain a `_links` element representing the links related
+to the user, for example,
+
+```
+{
+    "id": 100,
+    "email": "user@example.com",
+    ...,
+    "_links" => [
+        "self": "https://example.com/users/100"
+    ]
+}
+```
+
+
+Creating Controllers and Actions
+--------------------------------
+
+So you have the resource data and you have specified how the resource data should be formatted, the next thing
+to do is to create controller actions to expose the resource data to end users.
+
+Yii provides two base controller classes to simplify your work of creating RESTful actions:
+[[yii\rest\Controller]] and [[yii\rest\ActiveController]]. The difference between these two controllers
+is that the latter provides a default set of actions that are specified designed to deal with
+resources represented as ActiveRecord. So if you are using ActiveRecord and you are comfortable with
+the provided built-in actions, you may consider creating your controller class by extending from
+the latter. Otherwise, extending from [[yii\rest\Controller]] will allow you to develop actions
+from scratch.
+
+Both [[yii\rest\Controller]] and [[yii\rest\ActiveController]] provide the following features which will
+be described in detail in the next few sections:
+
+* Response format negotiation;
+* API version negotiation;
+* HTTP method validation;
+* User authentication;
+* Rate limiting.
+
+[[yii\rest\ActiveController]] in addition provides the following features specifically for working
+with ActiveRecord:
+
+* A set of commonly used actions: `index`, `view`, `create`, `update`, `delete`, `options`;
+* User authorization in regard to the requested action and resource.
+
+When creating a new controller class, a convention in naming the controller class is to use
+the type name of the resource and use singular form. For example, to serve user information,
+the controller may be named as `UserController`.
+
+Creating a new action is similar to creating an action for a Web application. The only difference
+is that instead of rendering the result using a view by calling the `render()` method, for RESTful actions
+you directly return the data. The [[yii\rest\Controller::serializer|serializer]] and the
+[[yii\web\Response|response object]] will handle the conversion from the original data to the requested
+format. For example,
+
+```php
+public function actionSearch($keyword)
+{
+    $result = SolrService::search($keyword);
+    return $result;
+}
+```
+
+If your controller class extends from [[yii\rest\ActiveController]], you should set
+its [[yii\rest\ActiveController::modelClass||modelClass]] property to be the name of the resource class
+that you plan to serve through this controller. The class must implement [[yii\db\ActiveRecordInterface]].
+
+With [[yii\rest\ActiveController]], you may want to disable some of the built-in actions or customize them.
+To do so, override the `actions()` method like the following:
+
+```php
+public function actions()
+{
+    $actions = parent::actions();
+
+    // disable the "delete" and "create" actions
+    unset($actions['delete'], $actions['create']);
+
+    // customize the data provider preparation with the "prepareDataProvider()" method
+    $actions['index']['prepareDataProvider'] = [$this, 'prepareDataProvider'];
+
+    return $actions;
+}
+
+public function prepareDataProvider()
+{
+    // prepare and return a data provider for the "index" action
+}
+```
+
+The following list summarizes the built-in actions supported by [[yii\rest\ActiveController]]:
+
+* [[yii\rest\IndexAction|index]]: list resources page by page;
+* [[yii\rest\ViewAction|view]]: return the details of a specified resource;
+* [[yii\rest\CreateAction|create]]: create a new resource;
+* [[yii\rest\UpdateAction|update]]: update an existing resource;
+* [[yii\rest\DeleteAction|delete]]: delete the specified resource;
+* [[yii\rest\OptionsAction|options]]: return the supported HTTP methods.
+
+
+Routing
+-------
+
+With resource and controller classes ready, you can access the resources using the URL like
+`http://localhost/index.php?r=user/create`. As you can see, the format of the URL is the same as that
+for Web applications.
+
+In practice, you usually want to enable pretty URLs and take advantage of HTTP verbs.
+For example, a request `POST /users` would mean accessing the `user/create` action.
+This can be done easily by configuring the `urlManager` application component in the application
+configuration like the following:
+
+```php
+'urlManager' => [
+    'enablePrettyUrl' => true,
+    'enableStrictParsing' => true,
+    'showScriptName' => false,
+    'rules' => [
+        ['class' => 'yii\rest\UrlRule', 'controller' => 'user'],
+    ],
+]
+```
+
+Compared to the URL management for Web applications, the main new thing above is the use of
+[[yii\rest\UrlRule]] for routing RESTful API requests. This special URL rule class will
+create a whole set of child URL rules to support routing and URL creation for the specified controller(s).
+For example, the above code is roughly equivalent to the following rules:
+
+```php
+[
+    'PUT,PATCH users/<id>' => 'user/update',
+    'DELETE users/<id>' => 'user/delete',
+    'GET,HEAD users/<id>' => 'user/view',
+    'POST users' => 'user/create',
+    'GET,HEAD users' => 'user/index',
+    'users/<id>' => 'user/options',
+    'users' => 'user/options',
+]
+```
+
+And the following API endpoints are supported by this rule:
+
+* `GET /users`: list all users page by page;
+* `HEAD /users`: show the overview information of user listing;
+* `POST /users`: create a new user;
+* `GET /users/123`: return the details of the user 123;
+* `HEAD /users/123`: show the overview information of user 123;
+* `PATCH /users/123` and `PUT /users/123`: update the user 123;
+* `DELETE /users/123`: delete the user 123;
+* `OPTIONS /users`: show the supported verbs regarding endpoint `/users`;
+* `OPTIONS /users/123`: show the supported verbs regarding endpoint `/users/123`.
+
+You may configure the `only` and `except` options to explicitly list which actions to support or which
+actions should be disabled, respectively. For example,
+
+```php
+[
+    'class' => 'yii\rest\UrlRule',
+    'controller' => 'user',
+    'except' => ['delete', 'create', 'update'],
+],
+```
+
+You may also configure `patterns` or `extra` to redefine existing patterns or add new patterns supported by this rule.
+For example, to support a new action `search` by the endpoint `GET /users/search`, configure the `extra` option as follows,
+
+```php
+[
+    'class' => 'yii\rest\UrlRule',
+    'controller' => 'user',
+    'extra' => [
+        'GET search' => 'search',
+    ],
+```
+
+You may have noticed that the controller ID `user` appears in plural form as `users` in the endpoints.
+This is because [[yii\rest\UrlRule]] automatically pluralizes controller IDs for them to use in endpoints.
+You may disable this behavior by setting [[yii\rest\UrlRule::pluralize]] to be false, or if you want
+to use some special names you may configure the [[yii\rest\UrlRule::controller]] property.
+
+
+Authentication
+--------------
+
+Unlike Web applications, RESTful APIs should be stateless, which means sessions or cookies should not
+be used. Therefore, each request should come with some sort of authentication credentials because
+the user authentication status may not be maintained by sessions or cookies. A common practice is
+to send a secret access token with each request to authenticate the user. Since an access token
+can be used to uniquely identify and authenticate a user, **the API requests should always be sent
+via HTTPS to prevent from man-in-the-middle (MitM) attacks**.
+
+There are different ways to send an access token:
+
+* [HTTP Basic Auth](http://en.wikipedia.org/wiki/Basic_access_authentication): the access token
+  is sent as the username. This is should only be used when an access token can be safely stored
+  on the API consumer side. For example, the API consumer is a program running on a server.
+* Query parameter: the access token is sent as a query parameter in the API URL, e.g.,
+  `https://example.com/users?access-token=xxxxxxxx`. Because most Web servers will keep query
+  parameters in server logs, this approach should be mainly used to serve `JSONP` requests which
+  cannot use HTTP headers to send access tokens.
+* [OAuth 2](http://oauth.net/2/): the access token is obtained by the consumer from an authorization
+  server and sent to the API server via [HTTP Bearer Tokens](http://tools.ietf.org/html/rfc6750),
+  according to the OAuth2 protocol.
+
+Yii supports all of the above authentication methods and can be further extended to support other methods.
+
+To enable authentication for your APIs, do the following two steps:
+
+1. Configure [[yii\rest\Controller::authMethods]] with the authentication methods you plan to use.
+2. Implement [[yii\web\IdentityInterface::findIdentityByAccessToken()]] in your [[yii\web\User::identityClass|user identity class]].
+
+For example, to enable all three authentication methods explained above, you would configure `authMethods`
+as follows,
+
+```php
+class UserController extends ActiveController
+{
+    public $authMethods = [
+        'yii\rest\HttpBasicAuth',
+        'yii\rest\QueryParamAuth',
+        'yii\rest\HttpBearerAuth',
+    ];
+}
+```
+
+Each element in `authMethods` should be an auth class name or a configuration array. An auth class
+must implement [[yii\rest\AuthInterface]].
+
+Implementation of `findIdentityByAccessToken()` is application specific. For example, in simple scenarios
+when each user can only have one access token, you may store the access token in an `access_token` column
+in the user table. The method can then be readily implemented in the `User` class as follows,
+
+```php
+use yii\db\ActiveRecord;
+use yii\web\IdentityInterface;
+
+class User extends ActiveRecord implements IdentityInterface
+{
+    public static function findIdentityByAccessToken($token)
+    {
+        return static::find(['access_token' => $token]);
+    }
+}
+```
+
+After authentication is enabled as described above, for every API request, the requested controller
+will try to authenticate the user in its `beforeAction()` step.
+
+If authentication succeeds, the controller will perform other checks (such as rate limiting, authorization)
+and then run the action. The authenticated user identity information can be retrieved via `Yii::$app->user->identity`.
+
+If authentication fails, a response with HTTP status 401 will be sent back together with other appropriate headers
+(such as a `WWW-Authenticate` header for HTTP Basic Auth).
+
+
+Authorization
+-------------
+
+After a user is authenticated, you probably want to check if he has the permission to perform the requested
+action for the requested resource. This process is called *authorization* which is covered in detail in
+the [Authorization chapter](authorization.md).
+
+You may use the [[yii\web\AccessControl]] filter and/or the Role-Based Access Control (RBAC) component
+to implementation authorization.
+
+To simplify the authorization check, you may also override the [[yii\rest\Controller::checkAccess()]] method
+and then call this method in places where authorization is needed. By default, the built-in actions provided
+by [[yii\rest\ActiveController]] will call this method when they are about to run.
+
+```php
+/**
+ * Checks the privilege of the current user.
+ *
+ * This method should be overridden to check whether the current user has the privilege
+ * to run the specified action against the specified data model.
+ * If the user does not have access, a [[ForbiddenHttpException]] should be thrown.
+ *
+ * @param string $action the ID of the action to be executed
+ * @param \yii\base\Model $model the model to be accessed. If null, it means no specific model is being accessed.
+ * @param array $params additional parameters
+ * @throws ForbiddenHttpException if the user does not have access
+ */
+public function checkAccess($action, $model = null, $params = [])
+{
+}
+```
+
+
+Rate Limiting
+-------------
+
+To prevent abuse, you should consider adding rate limiting to your APIs. For example, you may limit the API usage
+of each user to be at most 100 API calls within a period of 10 minutes. If too many requests are received from a user
+within the period of the time, a response with status code 429 (meaning Too Many Requests) should be returned.
+
+To enable rate limiting, the [[yii\web\User::identityClass|user identity class]] should implement [[yii\rest\RateLimitInterface]].
+This interface requires implementation of the following three methods:
+
+* `getRateLimit()`: returns the maximum number of allowed requests and the time period, e.g., `[100, 600]` means
+  at most 100 API calls within 600 seconds.
+* `loadAllowance()`: returns the number of remaining requests allowed and the corresponding UNIX timestamp
+  when the rate limit is checked last time.
+* `saveAllowance()`: saves the number of remaining requests allowed and the current UNIX timestamp.
+
+You may use two columns in the user table to record the allowance and timestamp information.
+And `loadAllowance()` and `saveAllowance()` can then be implementation by reading and saving the values
+of the two columns corresponding to the current authenticated user. To improve performance, you may also
+consider storing these information in cache or some NoSQL storage.
+
+Once the identity class implements the required interface, Yii will automatically use the rate limiter
+as specified by [[yii\rest\Controller::rateLimiter]] to perform rate limiting check. The rate limiter
+will thrown a [[yii\web\TooManyRequestsHttpException]] if rate limit is exceeded.
+
+When rate limiting is enabled, every response will be sent with the following HTTP headers containing
+the current rate limiting information:
+
+* `X-Rate-Limit-Limit`: The maximum number of requests allowed with a time period;
+* `X-Rate-Limit-Remaining`: The number of remaining requests in the current time period;
+* `X-Rate-Limit-Reset`: The number of seconds to wait in order to get the maximum number of allowed requests.
+
+
+Error Handling
+--------------
+
+When handling a RESTful API request, if there is an error in the user request or if something unexpected
+happens on the server, you may simply throw an exception to notify the user something wrong happened. 
+If you can identify the cause of the error (e.g. the requested resource does not exist), you should 
+consider throwing an exception with a proper HTTP status code (e.g. [[yii\web\NotFoundHttpException]] 
+representing a 404 HTTP status code). Yii will send the response with the corresponding HTTP status
+code and text. It will also include in the response body the serialized representation of the 
+exception. For example,
+
+```
+HTTP/1.1 404 Not Found
+Date: Sun, 02 Mar 2014 05:31:43 GMT
+Server: Apache/2.2.26 (Unix) DAV/2 PHP/5.4.20 mod_ssl/2.2.26 OpenSSL/0.9.8y
+Transfer-Encoding: chunked
+Content-Type: application/json; charset=UTF-8
+
+{
+    "type": "yii\\web\\NotFoundHttpException",
+    "name": "Not Found Exception",
+    "message": "The requested resource was not found.",
+    "code": 0,
+    "status": 404
+}
+```
+
+The following list summarizes the HTTP status code that are used by the Yii REST framework:
+
+* `200`: OK. Everything worked as expected.
+* `201`: A resource was successfully created in response to a `POST` request. The `Location` header
+   contains the URL pointing to the newly created resource.
+* `204`: The request is handled successfully and the response contains no body content (like a `DELETE` request).
+* `304`: Resource was not modified. You can use the cached version.
+* `400`: Bad request. This could be caused by various reasons from the user side, such as invalid JSON
+   data in the request body, invalid action parameters, etc.
+* `401`: Authentication failed.
+* `403`: The authenticated user is not allowed to access the specified API endpoint.
+* `404`: The requested resource does not exist.
+* `405`: Method not allowed. Please check the `Allow` header for allowed HTTP methods.
+* `415`: Unsupported media type. The requested content type or version number is invalid.
+* `422`: Data validation failed (in response to a `POST` request, for example). Please check the response body for detailed error messages.
+* `429`: Too many requests. The request is rejected due to rate limiting.
+* `500`: Internal server error. This could be caused by internal program errors.
+
+
+Versioning
+----------
+
+Your APIs should be versioned. Unlike Web applications which you have full control on both client side and server side
+code, for APIs you usually do not have control of the client code that consumes the APIs. Therefore, backward
+compatibility (BC) of the APIs should be maintained whenever possible, and if some BC-breaking changes must be
+introduced to the APIs, you should bump up the version number. You may refer to [Symantic Versioning](http://semver.org/)
+for more information about designing the version numbers of your APIs.
+
+Regarding how to implement API versioning, a common practice is to embed the version number in the API URLs.
+For example, `http://example.com/v1/users` stands for `/users` API of version 1. Another method of API versioning
+which gains momentum recently is to put version numbers in the HTTP request headers, typically through the `Accept` header,
+like the following:
+
+```
+// via a parameter
+Accept: application/json; version=v1
+// via a vendor content type
+Accept: application/vnd.company.myapp-v1+json
+```
+
+Both methods have pros and cons, and there are a lot of debates about them. Below we describe a practical strategy
+of API versioning that is a kind of mix of these two methods:
+
+* Put each major version of API implementation in a separate module whose ID is the major version number (e.g. `v1`, `v2`).
+  Naturally, the API URLs will contain major version numbers.
+* Within each major version (and thus within the corresponding module), use the `Accept` HTTP request header
+  to determine the minor version number and write conditional code to respond to the minor versions accordingly.
+
+For each module serving a major version, it should include the resource classes and the controller classes
+serving for that specific version. To better separate code responsibility, you may keep a common set of
+base resource and controller classes, and subclass them in each individual version module. Within the subclasses,
+implement the concrete code such as `Model::fields()`. As a result, your code may be organized like the following:
+
+```
+api/
+    common/
+        controllers/
+            UserController.php
+            PostController.php
+        models/
+            User.php
+            Post.php
+    modules/
+        v1/
+            controllers/
+                UserController.php
+                PostController.php
+            models/
+                User.php
+                Post.php
+        v2/
+            controllers/
+                UserController.php
+                PostController.php
+            models/
+                User.php
+                Post.php
+```
+
+Your application configuration would look like:
+
+```php
+return [
+    'modules' => [
+        'v1' => [
+            'basePath' => '@app/modules/v1',
+        ],
+        'v2' => [
+            'basePath' => '@app/modules/v2',
+        ],
+    ],
+    'components' => [
+        'urlManager' => [
+            'enablePrettyUrl' => true,
+            'enableStrictParsing' => true,
+            'showScriptName' => false,
+            'rules' => [
+                ['class' => 'yii\rest\UrlRule', 'controller' => ['v1/user', 'v1/post']],
+                ['class' => 'yii\rest\UrlRule', 'controller' => ['v2/user', 'v2/post']],
+            ],
+        ],
+    ],
+];
+```
+
+As a result, `http://example.com/v1/users` will return the list of users in version 1, while
+`http://example.com/v2/users` will return version 2 users.
+
+Using modules, code for different major versions can be well isolated. And it is still possible
+to reuse code across modules via common base classes and other shared classes.
+
+To deal with minor version numbers, you may take advantage of the content type negotiation
+feature provided by [[yii\rest\Controller]]:
+
+* Specify a list of supported minor versions (within the major version of the containing module)
+  via [[yii\rest\Controller::supportedVersions]].
+* Get the version number by reading [[yii\rest\Controller::version]].
+* In relevant code, such as actions, resource classes, serializers, etc., write conditional
+  code according to the requested minor version number.
+
+Since minor versions require maintaining backward compatibility, hopefully there are not much
+version checks in your code. Otherwise, chances are that you may need to create a new major version.
+
+
+Caching
+-------
+
+
+Documentation
+-------------
+
+Testing
+-------
+
diff --git a/docs/guide/theming.md b/docs/guide/theming.md
index e627f7c..037e70c 100644
--- a/docs/guide/theming.md
+++ b/docs/guide/theming.md
@@ -1,7 +1,12 @@
 Theming
 =======
 
-TBD
+A theme is a directory of view and layout files. Each file of the theme overrides corresponding file of an application
+when rendered. A single application may use multiple themes and each may provide totally different experience. At any
+time only one theme can be active.
+
+> Note: Themes usually do not meant to be redistributed since views are too application specific. If you want to
+  redistribute customized look and feel consider CSS and JavaScript files in form of [asset bundles](assets.md) instead.
 
 Configuring current theme
 -------------------------
@@ -18,4 +23,27 @@ be in your application config file:
 		],
 	],
 ],
-```
\ No newline at end of file
+```
+
+In the above `pathMap` defines where to look for view files while `baseUrl` defines base URL for resources referenced
+from these files. For example, if `pathMap` is `['/web/views' => '/web/themes/basic']`,  then the themed version
+for a view file `/web/views/site/index.php` will be `/web/themes/basic/site/index.php`.
+
+Using multiple paths
+--------------------
+
+It is possible to map a single path to multiple paths. For example,
+
+```php
+'pathMap' => [
+	'/web/views' => [
+		'/web/themes/christmas',
+		'/web/themes/basic',
+	],
+]
+```
+
+In this case, the view will be searched in `/web/themes/christmas/site/index.php` then if it's not found it will check
+`/web/themes/basic/site/index.php`. If there's no view there as well application view will be used.
+
+This ability is especially useful if you want to temporary or conditionally override some views.
diff --git a/docs/guide/upgrade-from-v1.md b/docs/guide/upgrade-from-v1.md
index 03a5b21..612eeab 100644
--- a/docs/guide/upgrade-from-v1.md
+++ b/docs/guide/upgrade-from-v1.md
@@ -158,7 +158,7 @@ in controllers or widgets:
 ```php
 $content = Yii::$app->view->renderFile($viewFile, $params);
 // You can also explicitly create a new View instance to do the rendering
-// $view = new View;
+// $view = new View();
 // $view->renderFile($viewFile, $params);
 ```
 
@@ -186,7 +186,7 @@ New methods called [[yii\base\Model::load()|load()] and [[yii\base\Model::loadMu
 introduced to simplify the data population from user inputs to a model. For example,
 
 ```php
-$model = new Post;
+$model = new Post();
 if ($model->load($_POST)) {...}
 // which is equivalent to:
 if (isset($_POST['Post'])) {
@@ -329,9 +329,9 @@ public function behaviors()
             'class' => 'yii\web\AccessControl',
             'rules' => [
                 ['allow' => true, 'actions' => ['admin'], 'roles' => ['@']],
-            ),
-        ),
-    );
+            ],
+        ],
+    ];
 }
 ```
 
@@ -394,7 +394,7 @@ In 1.1, query building is scattered among several classes, including `CDbCommand
 and [[yii\db\QueryBuilder|QueryBuilder]] to generate SQL statements from query objects. For example:
 
 ```php
-$query = new \yii\db\Query;
+$query = new \yii\db\Query();
 $query->select('id, name')
       ->from('tbl_user')
       ->limit(10);
diff --git a/extensions/apidoc/composer.json b/extensions/apidoc/composer.json
index 69f9c4b..6c0c5ce 100644
--- a/extensions/apidoc/composer.json
+++ b/extensions/apidoc/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-apidoc",
 	"description": "API Documentation generator for the Yii framework 2.0",
-	"keywords": ["yii", "phpdoc", "apidoc", "api", "documentation"],
+	"keywords": ["yii2", "phpdoc", "apidoc", "api", "documentation"],
 	"type": "yii2-extension",
 	"license": "BSD-3-Clause",
 	"support": {
@@ -22,6 +22,7 @@
 		"yiisoft/yii2": "*",
 		"yiisoft/yii2-bootstrap": "*",
 		"phpdocumentor/reflection": ">=1.0.3",
+		"phpdocumentor/reflection-docblock": ">2.0.1",
 		"nikic/php-parser": "0.9.*"
 	},
 	"autoload": {
diff --git a/extensions/apidoc/models/PropertyDoc.php b/extensions/apidoc/models/PropertyDoc.php
index 85c482c..fc7d99d 100644
--- a/extensions/apidoc/models/PropertyDoc.php
+++ b/extensions/apidoc/models/PropertyDoc.php
@@ -69,7 +69,7 @@ class PropertyDoc extends BaseDoc
 				$this->types = $tag->getTypes();
 				$this->description = ucfirst($tag->getDescription());
 				if (($pos = strpos($this->description, '.')) !== false) {
-					$this->shortDescription = substr($this->description, 0, $pos);
+					$this->shortDescription = substr($this->description, 0, $pos + 1);
 				} else {
 					$this->shortDescription = $this->description;
 				}
diff --git a/extensions/apidoc/templates/bootstrap/assets/css/style.css b/extensions/apidoc/templates/bootstrap/assets/css/style.css
index 57e3de9..6240146 100644
--- a/extensions/apidoc/templates/bootstrap/assets/css/style.css
+++ b/extensions/apidoc/templates/bootstrap/assets/css/style.css
@@ -50,3 +50,11 @@ body {
 	background: #E6ECFF;
 	border: 1px #BFCFFF solid;
 }
+
+blockquote {
+	font-size: 14px;
+}
+
+td p {
+	margin: 0;
+}
\ No newline at end of file
diff --git a/extensions/apidoc/templates/html/views/constSummary.php b/extensions/apidoc/templates/html/views/constSummary.php
index 6e5aabc..e2b8b02 100644
--- a/extensions/apidoc/templates/html/views/constSummary.php
+++ b/extensions/apidoc/templates/html/views/constSummary.php
@@ -36,7 +36,7 @@ ArrayHelper::multisort($constants, 'name');
 	<tr<?= $constant->definedBy != $type->name ? ' class="inherited"' : '' ?> id="<?= $constant->name ?>">
 	  <td><?= $constant->name ?><a name="<?= $constant->name ?>-detail"></a></td>
 	  <td><?= $constant->value ?></td>
-	  <td><?= APiMarkdown::process($constant->shortDescription . "\n" . $constant->description, $constant->definedBy, true) ?></td>
+	  <td><?= ApiMarkdown::process($constant->shortDescription . "\n" . $constant->description, $constant->definedBy, true) ?></td>
 	  <td><?= $renderer->createTypeLink($constant->definedBy) ?></td>
 	</tr>
 <?php endforeach; ?>
diff --git a/extensions/apidoc/templates/html/views/eventDetails.php b/extensions/apidoc/templates/html/views/eventDetails.php
index dd4293e..46fc5bd 100644
--- a/extensions/apidoc/templates/html/views/eventDetails.php
+++ b/extensions/apidoc/templates/html/views/eventDetails.php
@@ -18,7 +18,7 @@ ArrayHelper::multisort($events, 'name');
 <h2>Event Details</h2>
 <?php foreach($events as $event): ?>
 	<div class="detailHeader h3" id="<?= $event->name.'-detail' ?>">
-		<?php echo $event->name; ?>
+		<?= $event->name ?>
 		<span class="detailHeaderTag small">
 		event
 		<?php if(!empty($event->since)): ?>
@@ -32,7 +32,7 @@ ArrayHelper::multisort($events, 'name');
 		<?php echo $event->trigger->signature; ?>
 	</div>*/ ?>
 
-	<p><?= ApiMarkdown::process($event->description, $type); ?></p>
+	<?= ApiMarkdown::process($event->description, $type); ?>
 
 	<?= $this->render('seeAlso', ['object' => $event]); ?>
 
diff --git a/extensions/apidoc/templates/html/views/eventSummary.php b/extensions/apidoc/templates/html/views/eventSummary.php
index 39e9ab5..c010be4 100644
--- a/extensions/apidoc/templates/html/views/eventSummary.php
+++ b/extensions/apidoc/templates/html/views/eventSummary.php
@@ -39,11 +39,11 @@ ArrayHelper::multisort($events, 'name');
 	<td>
 		<?= ApiMarkdown::process($event->shortDescription, $event->definedBy, true) ?>
 		<?php if(!empty($event->since)): ?>
-			(available since version <?php echo $event->since; ?>)
+			(available since version <?= $event->since ?>)
 		<?php endif; ?>
 	</td>
 	<td><?= $renderer->createTypeLink($event->definedBy) ?></td>
 </tr>
 <?php endforeach; ?>
 </table>
-</div>
\ No newline at end of file
+</div>
diff --git a/extensions/apidoc/templates/html/views/methodDetails.php b/extensions/apidoc/templates/html/views/methodDetails.php
index 61c2d4f..16b043e 100644
--- a/extensions/apidoc/templates/html/views/methodDetails.php
+++ b/extensions/apidoc/templates/html/views/methodDetails.php
@@ -65,8 +65,8 @@ ArrayHelper::multisort($methods, 'name');
 
 <!--	--><?php //$this->renderPartial('sourceCode',array('object'=>$method)); ?>
 
-	<p><?= ApiMarkdown::process($method->shortDescription, $type, true) ?></strong></p>
-	<p><?= ApiMarkdown::process($method->description, $type) ?></p>
+	<p><strong><?= ApiMarkdown::process($method->shortDescription, $type, true) ?></strong></p>
+	<?= ApiMarkdown::process($method->description, $type) ?>
 
 	<?= $this->render('seeAlso', ['object' => $method]); ?>
 
diff --git a/extensions/apidoc/templates/html/views/propertyDetails.php b/extensions/apidoc/templates/html/views/propertyDetails.php
index 685fcb9..6df09fe 100644
--- a/extensions/apidoc/templates/html/views/propertyDetails.php
+++ b/extensions/apidoc/templates/html/views/propertyDetails.php
@@ -24,7 +24,7 @@ ArrayHelper::multisort($properties, 'name');
 <?php foreach($properties as $property): ?>
 
 	<div class="detailHeader h3" id="<?= $property->name.'-detail' ?>">
-		<?php echo $property->name; ?>
+		<?= $property->name ?>
 		<span class="detailHeaderTag small">
 			<?= $property->visibility ?>
 			<?php if($property->getIsReadOnly()) echo ' <em>read-only</em> '; ?>
@@ -38,7 +38,7 @@ ArrayHelper::multisort($properties, 'name');
 
 	<div class="signature"><?php echo $renderer->renderPropertySignature($property); ?></div>
 
-	<p><?= ApiMarkdown::process($property->description, $type) ?></p>
+	<?= ApiMarkdown::process($property->description, $type) ?>
 
 	<?= $this->render('seeAlso', ['object' => $property]); ?>
 
diff --git a/extensions/authclient/composer.json b/extensions/authclient/composer.json
index 60290cb..81c5aa1 100644
--- a/extensions/authclient/composer.json
+++ b/extensions/authclient/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-authclient",
 	"description": "External authentication via OAuth and OpenID for the Yii framework",
-	"keywords": ["yii", "OAuth", "OpenID", "auth"],
+	"keywords": ["yii2", "OAuth", "OpenID", "auth"],
 	"type": "yii2-extension",
 	"license": "BSD-3-Clause",
 	"support": {
diff --git a/extensions/bootstrap/CHANGELOG.md b/extensions/bootstrap/CHANGELOG.md
index 5fced15..464c959 100644
--- a/extensions/bootstrap/CHANGELOG.md
+++ b/extensions/bootstrap/CHANGELOG.md
@@ -12,9 +12,11 @@ Yii Framework 2 bootstrap extension Change Log
 - 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)
+- Enh #2643: Add size attribute to Modal (tof06)
 - Chg #1459: Update Collapse to use bootstrap 3 classes (tonydspaniard)
 - Chg #1820: Update Progress to use bootstrap 3 markup (samdark)
 
+
 2.0.0 alpha, December 1, 2013
 -----------------------------
 
diff --git a/extensions/bootstrap/Modal.php b/extensions/bootstrap/Modal.php
index 276b57c..e498f49 100644
--- a/extensions/bootstrap/Modal.php
+++ b/extensions/bootstrap/Modal.php
@@ -35,6 +35,10 @@ use yii\helpers\Html;
  */
 class Modal extends Widget
 {
+	const SIZE_LARGE = "modal-lg";
+	const SIZE_SMALL = "modal-sm";
+	const SIZE_DEFAULT = "";
+
 	/**
 	 * @var string the header content in the modal window.
 	 */
@@ -44,6 +48,10 @@ class Modal extends Widget
 	 */
 	public $footer;
 	/**
+	 * @var string the modal size. Can be MODAL_LG or MODAL_SM, or empty for default.
+	 */
+	public $size;
+	/**
 	 * @var array the options for rendering the close button tag.
 	 * The close button is displayed in the header of the modal window. Clicking
 	 * on the button will hide the modal window. If this is null, no close button will be rendered.
@@ -86,7 +94,7 @@ class Modal extends Widget
 
 		echo $this->renderToggleButton() . "\n";
 		echo Html::beginTag('div', $this->options) . "\n";
-		echo Html::beginTag('div', ['class' => 'modal-dialog']) . "\n";
+		echo Html::beginTag('div', ['class' => 'modal-dialog ' . $this->size]) . "\n";
 		echo Html::beginTag('div', ['class' => 'modal-content']) . "\n";
 		echo $this->renderHeader() . "\n";
 		echo $this->renderBodyBegin() . "\n";
diff --git a/extensions/bootstrap/Tabs.php b/extensions/bootstrap/Tabs.php
index 2192746..29505f0 100644
--- a/extensions/bootstrap/Tabs.php
+++ b/extensions/bootstrap/Tabs.php
@@ -92,7 +92,7 @@ class Tabs extends Widget
 	 */
 	public $encodeLabels = true;
 	/**
-	 * @var string, specifies the Bootstrap tab styling.
+	 * @var string specifies the Bootstrap tab styling.
 	 */
 	public $navType = 'nav-tabs';
 
diff --git a/extensions/bootstrap/composer.json b/extensions/bootstrap/composer.json
index e2a99df..637a084 100644
--- a/extensions/bootstrap/composer.json
+++ b/extensions/bootstrap/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-bootstrap",
 	"description": "The Twitter Bootstrap extension for the Yii framework",
-	"keywords": ["yii", "bootstrap"],
+	"keywords": ["yii2", "bootstrap"],
 	"type": "yii2-extension",
 	"license": "BSD-3-Clause",
 	"support": {
diff --git a/extensions/codeception/composer.json b/extensions/codeception/composer.json
index 0982d8d..fa99992 100644
--- a/extensions/codeception/composer.json
+++ b/extensions/codeception/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-codeception",
 	"description": "The Codeception integration for the Yii framework",
-	"keywords": ["yii", "codeception"],
+	"keywords": ["yii2", "codeception"],
 	"type": "yii2-extension",
 	"license": "BSD-3-Clause",
 	"support": {
diff --git a/extensions/composer/composer.json b/extensions/composer/composer.json
index 38f5199..f311581 100644
--- a/extensions/composer/composer.json
+++ b/extensions/composer/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-composer",
 	"description": "The composer plugin for Yii extension installer",
-	"keywords": ["yii", "composer", "extension installer"],
+	"keywords": ["yii2", "composer", "extension installer"],
 	"type": "composer-plugin",
 	"license": "BSD-3-Clause",
 	"support": {
diff --git a/extensions/debug/composer.json b/extensions/debug/composer.json
index 84dafd5..3d095b1 100644
--- a/extensions/debug/composer.json
+++ b/extensions/debug/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-debug",
 	"description": "The debugger extension for the Yii framework",
-	"keywords": ["yii", "debug", "debugger"],
+	"keywords": ["yii2", "debug", "debugger"],
 	"type": "yii2-extension",
 	"license": "BSD-3-Clause",
 	"support": {
diff --git a/extensions/debug/models/search/Mail.php b/extensions/debug/models/search/Mail.php
index 464436a..3cd7a27 100644
--- a/extensions/debug/models/search/Mail.php
+++ b/extensions/debug/models/search/Mail.php
@@ -97,7 +97,7 @@ class Mail extends Base
 		$dataProvider = new ArrayDataProvider([
 			'allModels' => $models,
 			'pagination' => [
-				'pageSize' => 5,
+				'pageSize' => 20,
 			],
 			'sort' => [
 				'attributes' => ['from', 'to', 'reply', 'cc', 'bcc', 'subject', 'body', 'charset'],
diff --git a/extensions/debug/views/default/panels/config/detail.php b/extensions/debug/views/default/panels/config/detail.php
index 88e96a1..b2cfde1 100644
--- a/extensions/debug/views/default/panels/config/detail.php
+++ b/extensions/debug/views/default/panels/config/detail.php
@@ -1,6 +1,4 @@
 <?php
-use yii\helpers\Html;
-
 /**
  * @var yii\debug\panels\ConfigPanel $panel
  */
diff --git a/extensions/debug/views/default/panels/config/summary.php b/extensions/debug/views/default/panels/config/summary.php
index af72260..bb3a6dd 100644
--- a/extensions/debug/views/default/panels/config/summary.php
+++ b/extensions/debug/views/default/panels/config/summary.php
@@ -1,7 +1,4 @@
 <?php
-
-use yii\helpers\Html;
-
 /**
  * @var yii\debug\panels\ConfigPanel $panel
  */
diff --git a/extensions/debug/views/default/panels/db/summary.php b/extensions/debug/views/default/panels/db/summary.php
index dcf49d8..022f9a7 100644
--- a/extensions/debug/views/default/panels/db/summary.php
+++ b/extensions/debug/views/default/panels/db/summary.php
@@ -1,6 +1,6 @@
 <?php if ($queryCount): ?>
 <div class="yii-debug-toolbar-block">
-	<a href="<?= $panel->getUrl() ?>" title="Executed <?php echo $queryCount; ?> database queries which took <?= $queryTime ?>.">
+	<a href="<?= $panel->getUrl() ?>" title="Executed <?= $queryCount ?> database queries which took <?= $queryTime ?>.">
 		DB <span class="label label-info"><?= $queryCount ?></span> <span class="label"><?= $queryTime ?></span>
 	</a>
 </div>
diff --git a/extensions/elasticsearch/README.md b/extensions/elasticsearch/README.md
index 63df0eb..ba6c168 100644
--- a/extensions/elasticsearch/README.md
+++ b/extensions/elasticsearch/README.md
@@ -50,9 +50,6 @@ TBD
 
 > **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.
- *
 
 
 Using the ActiveRecord
@@ -60,14 +57,15 @@ Using the ActiveRecord
 
 For general information on how to use yii's ActiveRecord please refer to the [guide](https://github.com/yiisoft/yii2/blob/master/docs/guide/active-record.md).
 
-For defining an elasticsearch ActiveRecord class your record class needs to extend from `yii\elasticsearch\ActiveRecord` and
-implement at least the `attributes()` method to define the attributes of the record.
+For defining an elasticsearch ActiveRecord class your record class needs to extend from [[yii\elasticsearch\ActiveRecord]] and
+implement at least the [[yii\elasticsearch\ActiveRecord::attributes()|attributes()]] method to define the attributes of the record.
 The handling of primary keys is different in elasticsearch as the primary key (the `_id` field in elasticsearch terms)
 is not part of the attributes by default. However it is possible to define a [path mapping](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-id-field.html)
 for the `_id` field to be part of the attributes.
 See [elasticsearch docs](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/mapping-id-field.html) on how to define it.
-The `_id` field of a document/record can be accessed using [[ActiveRecord::getPrimaryKey()]] and [[ActiveRecord::setPrimaryKey()]].
-When path mapping is defined, the attribute name can be defined using the [[primaryKey()]] method.
+The `_id` field of a document/record can be accessed using [[yii\elasticsearch\ActiveRecord::getPrimaryKey()|getPrimaryKey()]] and
+[[yii\elasticsearch\ActiveRecord::setPrimaryKey()|setPrimaryKey()]].
+When path mapping is defined, the attribute name can be defined using the [[yii\elasticsearch\ActiveRecord::primaryKey()|primaryKey()]] method.
 
 The following is an example model called `Customer`:
 
@@ -101,7 +99,8 @@ class Customer extends \yii\elasticsearch\ActiveRecord
 }
 ```
 
-You may override [[index()]] and [[type()]] to define the index and type this record represents.
+You may override [[yii\elasticsearch\ActiveRecord::index()|index()]] and [[yii\elasticsearch\ActiveRecord::type()|type()]]
+to define the index and type this record represents.
 
 The general usage of elasticsearch ActiveRecord is very similar to the database ActiveRecord as described in the
 [guide](https://github.com/yiisoft/yii2/blob/master/docs/guide/active-record.md).
@@ -109,13 +108,18 @@ It supports the same interface and features except the following limitations and
 
 - As elasticsearch does not support SQL, the query API does not support `join()`, `groupBy()`, `having()` and `union()`.
   Sorting, limit, offset and conditional where are all supported.
-- `from()` does not select the tables, but the [index](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/glossary.html#glossary-index)
+- [[yii\elasticsearch\ActiveQuery::from()|from()]] does not select the tables, but the
+  [index](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/glossary.html#glossary-index)
   and [type](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/glossary.html#glossary-type) to query against.
-- `select()` has been replaced with `fields()` which basically does the same but `fields` is more elasticsearch terminology.
+- `select()` has been replaced with [[yii\elasticsearch\ActiveQuery::fields()|fields()]] which basically does the same but
+  `fields` is more elasticsearch terminology.
   It defines the fields to retrieve from a document.
-- `via`-relations can not be defined via a table as there are no tables in elasticsearch. You can only define relations via other records.
-- As elasticsearch is not only a data storage but also a search engine there is of course support added for search your records.
-  There are `query()`, `filter()` and `addFacets()` methods that allows to compose an elasticsearch query.
+- [[yii\elasticsearch\ActiveQuery::via()|via]]-relations can not be defined via a table as there are no tables in elasticsearch. You can only define relations via other records.
+- As elasticsearch is not only a data storage but also a search engine there is of course support added for searching your records.
+  There are
+  [[yii\elasticsearch\ActiveQuery::query()|query()]],
+  [[yii\elasticsearch\ActiveQuery::filter()|filter()]] and
+  [[yii\elasticsearch\ActiveQuery::addFacet()|addFacet()]] methods that allows to compose an elasticsearch query.
   See the usage example below on how they work and check out the [Query DSL](http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/query-dsl.html)
   on how to compose `query` and `filter` parts.
 - It is also possible to define relations from elasticsearch ActiveRecords to normal ActiveRecord classes and vice versa.
diff --git a/extensions/elasticsearch/composer.json b/extensions/elasticsearch/composer.json
index 097b102..d38454f 100644
--- a/extensions/elasticsearch/composer.json
+++ b/extensions/elasticsearch/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-elasticsearch",
 	"description": "Elasticsearch integration and ActiveRecord for the Yii framework",
-	"keywords": ["yii", "elasticsearch", "active-record", "search", "fulltext"],
+	"keywords": ["yii2", "elasticsearch", "active-record", "search", "fulltext"],
 	"type": "yii2-extension",
 	"license": "BSD-3-Clause",
 	"support": {
diff --git a/extensions/faker/composer.json b/extensions/faker/composer.json
index 5af5319..a8a5bc1 100644
--- a/extensions/faker/composer.json
+++ b/extensions/faker/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-faker",
 	"description": "Fixture generator. The Faker integration for the Yii framework.",
-	"keywords": ["yii", "faker", "fixture"],
+	"keywords": ["yii2", "faker", "fixture"],
 	"type": "yii2-extension",
 	"license": "BSD-3-Clause",
 	"support": {
diff --git a/extensions/gii/CHANGELOG.md b/extensions/gii/CHANGELOG.md
index 8a7632d..c67f8f4 100644
--- a/extensions/gii/CHANGELOG.md
+++ b/extensions/gii/CHANGELOG.md
@@ -13,6 +13,8 @@ Yii Framework 2 gii extension Change Log
 - 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)
+- Enh #2595: Browse through all generated files using right and left arrows (thiagotalma)
+- Enh #2633: Keyboard shortcuts to browse through files (thiagotalma)
 
 2.0.0 alpha, December 1, 2013
 -----------------------------
diff --git a/extensions/gii/Module.php b/extensions/gii/Module.php
index 30302b5..0b0f6e4 100644
--- a/extensions/gii/Module.php
+++ b/extensions/gii/Module.php
@@ -141,6 +141,7 @@ class Module extends \yii\base\Module
 			'controller' => ['class' => 'yii\gii\generators\controller\Generator'],
 			'form' => ['class' => 'yii\gii\generators\form\Generator'],
 			'module' => ['class' => 'yii\gii\generators\module\Generator'],
+			'extension' => ['class' => 'yii\gii\generators\extension\Generator'],
 		];
 	}
 }
diff --git a/extensions/gii/assets/gii.js b/extensions/gii/assets/gii.js
index c5a70ab..282ce58 100644
--- a/extensions/gii/assets/gii.js
+++ b/extensions/gii/assets/gii.js
@@ -35,10 +35,13 @@ yii.gii = (function ($) {
 	};
 
 	var initPreviewDiffLinks = function () {
-		$('.preview-code, .diff-code, .modal-refresh').on('click', function () {
+		$('.preview-code, .diff-code, .modal-refresh, .modal-previous, .modal-next').on('click', function () {
 			var $modal = $('#preview-modal');
 			var $link = $(this);
-			$modal.find('.modal-refresh').attr('href', $link.prop('href'));
+			$modal.find('.modal-refresh').attr('href', $link.attr('href'));
+			if ($link.hasClass('preview-code') || $link.hasClass('diff-code')) {
+				$modal.data('action', ($link.hasClass('preview-code') ? 'preview-code' : 'diff-code'))
+			}
 			$modal.find('.modal-title').text($link.data('title'));
 			$modal.find('.modal-body').html('Loading ...');
 			$modal.modal('show');
@@ -48,6 +51,15 @@ yii.gii = (function ($) {
 				url: $link.prop('href'),
 				data: $('.default-view form').serializeArray(),
 				success: function (data) {
+					if (!$link.hasClass('modal-refresh')) {
+						var filesSelector = 'a.' + $modal.data('action');
+						var $files = $(filesSelector);
+						var index = $files.filter('[href="' + $link.attr('href') + '"]').index(filesSelector);
+						var $prev = $files.eq(index-1);
+						var $next = $files.eq((index+1 == $files.length ? 0 : index+1));
+						$modal.find('.modal-previous').attr('href', $prev.attr('href')).data('title', $prev.data('title'));
+						$modal.find('.modal-next').attr('href', $next.attr('href')).data('title', $next.data('title'));
+					}
 					$modal.find('.modal-body').html(data);
 					$modal.find('.content').css('max-height', ($(window).height() - 200) + 'px');
 				},
@@ -57,6 +69,16 @@ yii.gii = (function ($) {
 			});
 			return false;
 		});
+
+		$('#preview-modal').on('keydown', function(e) {
+			if (e.keyCode === 37) {
+				$('.modal-previous').trigger('click');
+			} else if(e.keyCode === 39) {
+				$('.modal-next').trigger('click');
+			} else if(e.keyCode === 82) {
+				$('.modal-refresh').trigger('click');
+			}
+		});
 	};
 
 	var initConfirmationCheckboxes = function () {
diff --git a/extensions/gii/composer.json b/extensions/gii/composer.json
index dad7f87..11fa779 100644
--- a/extensions/gii/composer.json
+++ b/extensions/gii/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-gii",
 	"description": "The Gii extension for the Yii framework",
-	"keywords": ["yii", "gii", "code generator"],
+	"keywords": ["yii2", "gii", "code generator"],
 	"type": "yii2-extension",
 	"license": "BSD-3-Clause",
 	"support": {
diff --git a/extensions/gii/generators/controller/templates/view.php b/extensions/gii/generators/controller/templates/view.php
index 6ef2a87..a458b16 100644
--- a/extensions/gii/generators/controller/templates/view.php
+++ b/extensions/gii/generators/controller/templates/view.php
@@ -18,5 +18,5 @@ echo "<?php\n";
 
 <p>
 	You may change the content of this page by modifying
-	the file <code><?= '<?php' ?> echo __FILE__; ?></code>.
+	the file <code><?= '<?=' ?> __FILE__; ?></code>.
 </p>
diff --git a/extensions/gii/generators/crud/Generator.php b/extensions/gii/generators/crud/Generator.php
index 8a8cb1c..dd0cb08 100644
--- a/extensions/gii/generators/crud/Generator.php
+++ b/extensions/gii/generators/crud/Generator.php
@@ -16,6 +16,7 @@ use yii\helpers\Inflector;
 use yii\web\Controller;
 
 /**
+ * Generates CRUD
  *
  * @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
diff --git a/extensions/gii/generators/crud/templates/views/create.php b/extensions/gii/generators/crud/templates/views/create.php
index 68d08ba..72a1f8d 100644
--- a/extensions/gii/generators/crud/templates/views/create.php
+++ b/extensions/gii/generators/crud/templates/views/create.php
@@ -26,8 +26,8 @@ $this->params['breadcrumbs'][] = $this->title;
 
 	<h1><?= "<?= " ?>Html::encode($this->title) ?></h1>
 
-	<?= "<?php " ?>echo $this->render('_form', [
+	<?= "<?= " ?>$this->render('_form', [
 		'model' => $model,
-	]); ?>
+	]) ?>
 
 </div>
diff --git a/extensions/gii/generators/crud/templates/views/index.php b/extensions/gii/generators/crud/templates/views/index.php
index a4f5f0d..aadc586 100644
--- a/extensions/gii/generators/crud/templates/views/index.php
+++ b/extensions/gii/generators/crud/templates/views/index.php
@@ -37,7 +37,7 @@ $this->params['breadcrumbs'][] = $this->title;
 	</p>
 
 <?php if ($generator->indexWidgetType === 'grid'): ?>
-	<?= "<?php " ?>echo GridView::widget([
+	<?= "<?= " ?>GridView::widget([
 		'dataProvider' => $dataProvider,
 		'filterModel' => $searchModel,
 		'columns' => [
@@ -69,13 +69,13 @@ if (($tableSchema = $generator->getTableSchema()) === false) {
 		],
 	]); ?>
 <?php else: ?>
-	<?= "<?php " ?>echo ListView::widget([
+	<?= "<?= " ?>ListView::widget([
 		'dataProvider' => $dataProvider,
 		'itemOptions' => ['class' => 'item'],
 		'itemView' => function ($model, $key, $index, $widget) {
 			return Html::a(Html::encode($model-><?= $nameAttribute ?>), ['view', <?= $urlParams ?>]);
 		},
-	]); ?>
+	]) ?>
 <?php endif; ?>
 
 </div>
diff --git a/extensions/gii/generators/crud/templates/views/update.php b/extensions/gii/generators/crud/templates/views/update.php
index 2fbbecf..610b5bb 100644
--- a/extensions/gii/generators/crud/templates/views/update.php
+++ b/extensions/gii/generators/crud/templates/views/update.php
@@ -29,8 +29,8 @@ $this->params['breadcrumbs'][] = 'Update';
 
 	<h1><?= "<?= " ?>Html::encode($this->title) ?></h1>
 
-	<?= "<?php " ?>echo $this->render('_form', [
+	<?= "<?= " ?>$this->render('_form', [
 		'model' => $model,
-	]); ?>
+	]) ?>
 
 </div>
diff --git a/extensions/gii/generators/crud/templates/views/view.php b/extensions/gii/generators/crud/templates/views/view.php
index 989dab5..8ffa728 100644
--- a/extensions/gii/generators/crud/templates/views/view.php
+++ b/extensions/gii/generators/crud/templates/views/view.php
@@ -31,16 +31,16 @@ $this->params['breadcrumbs'][] = $this->title;
 
 	<p>
 		<?= "<?= " ?>Html::a('Update', ['update', <?= $urlParams ?>], ['class' => 'btn btn-primary']) ?>
-		<?= "<?php " ?>echo Html::a('Delete', ['delete', <?= $urlParams ?>], [
+		<?= "<?= " ?>Html::a('Delete', ['delete', <?= $urlParams ?>], [
 			'class' => 'btn btn-danger',
 			'data' => [
 				'confirm' => Yii::t('app', 'Are you sure to delete this item?'),
 				'method' => 'post',
 			],
-		]); ?>
+		]) ?>
 	</p>
 
-	<?= "<?php " ?>echo DetailView::widget([
+	<?= "<?= " ?>DetailView::widget([
 		'model' => $model,
 		'attributes' => [
 <?php
@@ -56,6 +56,6 @@ if (($tableSchema = $generator->getTableSchema()) === false) {
 }
 ?>
 		],
-	]); ?>
+	]) ?>
 
 </div>
diff --git a/extensions/gii/generators/extension/Generator.php b/extensions/gii/generators/extension/Generator.php
new file mode 100644
index 0000000..094d476
--- /dev/null
+++ b/extensions/gii/generators/extension/Generator.php
@@ -0,0 +1,264 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\gii\generators\extension;
+
+use Yii;
+use yii\gii\CodeFile;
+
+/**
+ * This generator will generate the skeleton files needed by an extension.
+ *
+ * @author Tobias Munk <schmunk@usrbin.de>
+ * @since 2.0
+ */
+class Generator extends \yii\gii\Generator
+{
+	public $vendorName;
+	public $packageName = "yii2-";
+	public $namespace;
+	public $type = "yii2-extension";
+	public $keywords = "yii2,extension";
+	public $title;
+	public $description;
+	public $outputPath = "@app/runtime/tmp-extensions";
+	public $license;
+	public $authorName;
+	public $authorEmail;
+
+	/**
+	 * @inheritdoc
+	 */
+	public function getName()
+	{
+		return 'Extension Generator';
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function getDescription()
+	{
+		return 'This generator helps you to generate the files needed by a Yii extension.';
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function rules()
+	{
+		return array_merge(
+			parent::rules(),
+			[
+				[['vendorName', 'packageName'], 'filter', 'filter' => 'trim'],
+				[
+					[
+						'vendorName',
+						'packageName',
+						'namespace',
+						'type',
+						'license',
+						'title',
+						'description',
+						'authorName',
+						'authorEmail',
+						'outputPath'
+					],
+					'required'
+				],
+				[['keywords'], 'safe'],
+				[['authorEmail'], 'email'],
+				[
+					['vendorName', 'packageName'],
+					'match',
+					'pattern' => '/^[a-z0-9\-\.]+$/',
+					'message' => 'Only lowercase word characters, dashes and dots are allowed.'
+				],
+				[
+					['namespace'],
+					'match',
+					'pattern' => '/^[a-zA-Z0-9\\\]+\\\$/',
+					'message' => 'Only letters, numbers and backslashes are allowed. PSR-4 namespaces must end with a namespace separator.'
+				],
+			]
+		);
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function attributeLabels()
+	{
+		return [
+			'vendorName'  => 'Vendor Name',
+			'packageName' => 'Package Name',
+			'license'     => 'License',
+		];
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function hints()
+	{
+		return [
+			'vendorName'  => 'This refers to the name of the publisher, your GitHub user name is usually a good choice, eg. <code>myself</code>.',
+			'packageName' => 'This is the name of the extension on packagist, eg. <code>yii2-foobar</code>.',
+			'namespace'   => 'PSR-4, eg. <code>myself\foobar\</code> This will be added to your autoloading by composer. Do not use yii or yii2 in the namespace.',
+			'keywords'    => 'Comma separated keywords for this extension.',
+			'outputPath'  => 'The temporary location of the generated files.',
+			'title'       => 'A more descriptive name of your application for the README file.',
+			'description' => 'A sentence or subline describing the main purpose of the extension.',
+		];
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function stickyAttributes()
+	{
+		return ['vendorName', 'outputPath', 'authorName', 'authorEmail'];
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function successMessage()
+	{
+		$outputPath = realpath(\Yii::getAlias($this->outputPath));
+		$output1 = <<<EOD
+<p><em>The extension has been generated successfully.</em></p>
+<p>To enable it in your application, you need to create a git repository
+and require it via composer.</p>
+EOD;
+		$code1 = <<<EOD
+cd {$outputPath}/{$this->packageName}
+
+git init
+git add -A
+git commit
+git remote add origin https://path.to/your/repo
+git push -u origin master
+EOD;
+		$output2 = <<<EOD
+<p>The next step is just for <em>initial development</em>, skip it if you directly publish the extension on packagist.org</p>
+<p>Add the newly created repo to your composer.json.</p>
+EOD;
+		$code2 = <<<EOD
+"repositories":[
+	{
+		"type": "git",
+		"url": "https://path.to/your/repo"
+	}
+]
+EOD;
+		$output3 = <<<EOD
+<p class="well">Note: You may use the url <code>file://{$outputPath}/{$this->packageName}</code> for testing.</p>
+<p>Require the package with composer</p>
+EOD;
+		$code3 = <<<EOD
+composer.phar require {$this->vendorName}/{$this->packageName}:dev-master
+EOD;
+		$output4 = <<<EOD
+<p>And use it in your application.</p>
+EOD;
+		$code4 = <<<EOD
+\\{$this->namespace}AutoloadExample::widget();
+EOD;
+		$output5 = <<<EOD
+<p>When you have finished development register your extension at <a href='https://packagist.org/' target='_blank'>packagist.org</a>.</p>
+EOD;
+
+		$return = $output1 . '<pre>' . highlight_string($code1, true) . '</pre>';
+		$return .= $output2 . '<pre>' . highlight_string($code2, true) . '</pre>';
+		$return .= $output3 . '<pre>' . highlight_string($code3, true) . '</pre>';
+		$return .= $output4 . '<pre>' . highlight_string($code4, true) . '</pre>';
+		$return .= $output5;
+		return $return;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function requiredTemplates()
+	{
+		return ['composer.json', 'AutoloadExample.php', 'README.md'];
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function generate()
+	{
+		$files = [];
+		$modulePath = $this->getOutputPath();
+		$files[] = new CodeFile(
+			$modulePath . '/' . $this->packageName . '/composer.json',
+			$this->render("composer.json")
+		);
+		$files[] = new CodeFile(
+			$modulePath . '/' . $this->packageName . '/AutoloadExample.php',
+			$this->render("AutoloadExample.php")
+		);
+		$files[] = new CodeFile(
+			$modulePath . '/' . $this->packageName . '/README.md',
+			$this->render("README.md")
+		);
+		return $files;
+	}
+
+	/**
+	 * @return boolean the directory that contains the module class
+	 */
+	public function getOutputPath()
+	{
+		return Yii::getAlias($this->outputPath);
+	}
+
+	/**
+	 * @return string a json encoded array with the given keywords
+	 */
+	public function getKeywordsArrayJson()
+	{
+		return json_encode(explode(',', $this->keywords));
+	}
+
+	/**
+	 * @return array options for type drop-down
+	 */
+	public function optsType()
+	{
+		$licenses = [
+			'yii2-extension',
+			'library',
+		];
+		return array_combine($licenses, $licenses);
+	}
+
+	/**
+	 * @return array options for license drop-down
+	 */
+	public function optsLicense()
+	{
+		$licenses = [
+			'Apache-2.0',
+			'BSD-2-Clause',
+			'BSD-3-Clause',
+			'BSD-4-Clause',
+			'GPL-2.0',
+			'GPL-2.0+',
+			'GPL-3.0',
+			'GPL-3.0+',
+			'LGPL-2.1',
+			'LGPL-2.1+',
+			'LGPL-3.0',
+			'LGPL-3.0+',
+			'MIT'
+		];
+		return array_combine($licenses, $licenses);
+	}
+}
diff --git a/extensions/gii/generators/extension/form.php b/extensions/gii/generators/extension/form.php
new file mode 100644
index 0000000..98a250c
--- /dev/null
+++ b/extensions/gii/generators/extension/form.php
@@ -0,0 +1,27 @@
+<?php
+/**
+ * @var yii\web\View $this
+ * @var yii\widgets\ActiveForm $form
+ * @var yii\gii\generators\module\Generator $generator
+ */
+?>
+<div class="alert alert-info">
+	Please read the
+    <?= \yii\helpers\Html::a('Extension Guidelines', 'https://github.com/yiisoft/yii2/blob/master/docs/guide/extensions.md', ['target'=>'new']) ?>
+	before creating an extension.
+</div>
+<div class="module-form">
+<?php
+	echo $form->field($generator, 'vendorName');
+	echo $form->field($generator, 'packageName');
+	echo $form->field($generator, 'namespace');
+	echo $form->field($generator, 'type')->dropDownList($generator->optsType());
+	echo $form->field($generator, 'keywords');
+	echo $form->field($generator, 'license')->dropDownList($generator->optsLicense(), ['prompt'=>'Choose...']);
+	echo $form->field($generator, 'title');
+	echo $form->field($generator, 'description');
+	echo $form->field($generator, 'authorName');
+	echo $form->field($generator, 'authorEmail');
+	echo $form->field($generator, 'outputPath');
+?>
+</div>
diff --git a/extensions/gii/generators/extension/templates/AutoloadExample.php b/extensions/gii/generators/extension/templates/AutoloadExample.php
new file mode 100644
index 0000000..194ba72
--- /dev/null
+++ b/extensions/gii/generators/extension/templates/AutoloadExample.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * This is just an example.
+ */
+echo "<?php\n";
+?>
+
+namespace <?= substr($generator->namespace, 0, -1) ?>;
+
+class AutoloadExample extends \yii\base\widget {
+    function run() {
+        return "Hello!";
+    }
+}
diff --git a/extensions/gii/generators/extension/templates/README.md b/extensions/gii/generators/extension/templates/README.md
new file mode 100644
index 0000000..08101f3
--- /dev/null
+++ b/extensions/gii/generators/extension/templates/README.md
@@ -0,0 +1,35 @@
+<?= $generator->title ?>
+
+<?= str_repeat('=', mb_strlen($generator->title, \Yii::$app->charset)) ?>
+
+<?= $generator->description ?>
+
+
+Installation
+------------
+
+The preferred way to install this extension is through [composer](http://getcomposer.org/download/).
+
+Either run
+
+```
+php composer.phar require --prefer-dist <?= $generator->vendorName ?>/<?= $generator->packageName ?> "*"
+```
+
+or add
+
+```
+"<?= $generator->vendorName ?>/<?= $generator->packageName ?>": "*"
+```
+
+to the require section of your `composer.json` file.
+
+
+Usage
+-----
+
+Once the extension is installed, simply use it in your code by  :
+
+```php
+<?= "<?= \\{$generator->namespace}AutoloadExample::widget(); ?>" ?>
+```
\ No newline at end of file
diff --git a/extensions/gii/generators/extension/templates/composer.json b/extensions/gii/generators/extension/templates/composer.json
new file mode 100644
index 0000000..941a9a5
--- /dev/null
+++ b/extensions/gii/generators/extension/templates/composer.json
@@ -0,0 +1,18 @@
+{
+    "name": "<?= $generator->vendorName ?>/<?= $generator->packageName ?>",
+    "description": "<?= $generator->description ?>",
+    "type": "<?= $generator->type ?>",
+    "keywords": <?= $generator->keywordsArrayJson ?>,
+    "license": "<?= $generator->license ?>",
+    "authors": [
+        {
+            "name": "<?= $generator->authorName ?>",
+            "email": "<?= $generator->authorEmail ?>"
+        }
+    ],
+    "autoload": {
+        "psr-4": {
+            "<?= str_replace('\\','\\\\',$generator->namespace) ?>": ""
+        }
+    }
+}
diff --git a/extensions/gii/generators/model/Generator.php b/extensions/gii/generators/model/Generator.php
index 538637c..893cd7f 100644
--- a/extensions/gii/generators/model/Generator.php
+++ b/extensions/gii/generators/model/Generator.php
@@ -471,7 +471,7 @@ class Generator extends \yii\gii\Generator
 	 */
 	public function validateTableName()
 	{
-		if (($pos = strpos($this->tableName, '*')) !== false && substr($this->tableName, -1) !== '*') {
+		if (strpos($this->tableName, '*') !== false && substr($this->tableName, -1) !== '*') {
 			$this->addError('tableName', 'Asterisk is only allowed as the last character.');
 			return;
 		}
diff --git a/extensions/gii/views/default/view/files.php b/extensions/gii/views/default/view/files.php
index 947c8f8..fbe5e69 100644
--- a/extensions/gii/views/default/view/files.php
+++ b/extensions/gii/views/default/view/files.php
@@ -81,7 +81,14 @@ use yii\gii\CodeFile;
 			<div class="modal-content">
 				<div class="modal-header">
 					<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
-					<h4><a class="modal-refresh glyphicon glyphicon-refresh" href="#"></a> <span class="modal-title">Modal title</span></h4>
+					<div class="btn-group pull-left">
+						<a class="modal-previous btn btn-xs btn-default" href="#" title="Previous File (Left Arrow)"><span class="glyphicon glyphicon-arrow-left"></span></a>
+						<a class="modal-next btn btn-xs btn-default" href="#" title="Next File (Right Arrow)"><span class="glyphicon glyphicon-arrow-right"></span></a>
+						<a class="modal-refresh btn btn-xs btn-default" href="#" title="Refresh File (R)"><span class="glyphicon glyphicon-refresh"></span></a>
+						&nbsp;
+					</div>
+					<strong class="modal-title pull-left">Modal title</strong>
+					<div class="clearfix"></div>
 				</div>
 				<div class="modal-body">
 					<p>Please wait ...</p>
diff --git a/extensions/imagine/composer.json b/extensions/imagine/composer.json
index e032ff9..84f227c 100644
--- a/extensions/imagine/composer.json
+++ b/extensions/imagine/composer.json
@@ -1,7 +1,7 @@
 {
     "name": "yiisoft/yii2-imagine",
     "description": "The Imagine integration for the Yii framework",
-    "keywords": ["yii", "imagine", "image", "helper"],
+    "keywords": ["yii2", "imagine", "image", "helper"],
     "type": "yii2-extension",
     "license": "BSD-3-Clause",
     "support": {
diff --git a/extensions/jui/CHANGELOG.md b/extensions/jui/CHANGELOG.md
index 55c5e33..2730ae3 100644
--- a/extensions/jui/CHANGELOG.md
+++ b/extensions/jui/CHANGELOG.md
@@ -6,6 +6,7 @@ Yii Framework 2 jui extension Change Log
 
 - 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)
+- Enh #2573: Jui datepicker now uses the current appliaction language by default. (andy5)
 
 2.0.0 alpha, December 1, 2013
 -----------------------------
diff --git a/extensions/jui/DatePicker.php b/extensions/jui/DatePicker.php
index 6fc73bb..fff6582 100644
--- a/extensions/jui/DatePicker.php
+++ b/extensions/jui/DatePicker.php
@@ -7,11 +7,12 @@
 
 namespace yii\jui;
 
+use Yii;
 use yii\helpers\Html;
 use yii\helpers\Json;
 
 /**
- * DatePicker renders an datepicker jQuery UI widget.
+ * DatePicker renders a datepicker jQuery UI widget.
  *
  * For example:
  *
@@ -46,9 +47,9 @@ class DatePicker extends InputWidget
 {
 	/**
 	 * @var string the locale ID (eg 'fr', 'de') for the language to be used by the date picker.
-	 * If this property set to false, I18N will not be involved. That is, the date picker will show in English.
+	 * If this property is empty, then the current application language will be used.
 	 */
-	public $language = false;
+	public $language;
 	/**
 	 * @var boolean If true, shows the widget as an inline calendar and the input as a hidden field.
 	 */
@@ -77,15 +78,16 @@ class DatePicker extends InputWidget
 	{
 		echo $this->renderWidget() . "\n";
 		$containerID = $this->inline ? $this->containerOptions['id'] : $this->options['id'];
-		if ($this->language !== false) {
+		$language = $this->language ? $this->language : Yii::$app->language;
+		if ($language != 'en') {
 			$view = $this->getView();
 			DatePickerRegionalAsset::register($view);
 
 			$options = Json::encode($this->clientOptions);
-			$view->registerJs("$('#{$containerID}').datepicker($.extend({}, $.datepicker.regional['{$this->language}'], $options));");
+			$view->registerJs("$('#{$containerID}').datepicker($.extend({}, $.datepicker.regional['{$language}'], $options));");
 
 			$options = $this->clientOptions;
-			$this->clientOptions = false;  // the datepicker js widget is already registered
+			$this->clientOptions = false; // the datepicker js widget is already registered
 			$this->registerWidget('datepicker', DatePickerAsset::className(), $containerID);
 			$this->clientOptions = $options;
 		} else {
diff --git a/extensions/jui/assets/jquery.ui.datepicker-i18n.js b/extensions/jui/assets/jquery.ui.datepicker-i18n.js
old mode 100755
new mode 100644
diff --git a/extensions/jui/assets/jquery.ui.effect-all.js b/extensions/jui/assets/jquery.ui.effect-all.js
old mode 100755
new mode 100644
diff --git a/extensions/jui/assets/theme/images/animated-overlay.gif b/extensions/jui/assets/theme/images/animated-overlay.gif
old mode 100755
new mode 100644
Binary files a/extensions/jui/assets/theme/images/animated-overlay.gif and b/extensions/jui/assets/theme/images/animated-overlay.gif differ
diff --git a/extensions/jui/assets/theme/images/ui-bg_flat_0_aaaaaa_40x100.png b/extensions/jui/assets/theme/images/ui-bg_flat_0_aaaaaa_40x100.png
old mode 100755
new mode 100644
Binary files a/extensions/jui/assets/theme/images/ui-bg_flat_0_aaaaaa_40x100.png and b/extensions/jui/assets/theme/images/ui-bg_flat_0_aaaaaa_40x100.png differ
diff --git a/extensions/jui/assets/theme/images/ui-bg_flat_75_ffffff_40x100.png b/extensions/jui/assets/theme/images/ui-bg_flat_75_ffffff_40x100.png
old mode 100755
new mode 100644
Binary files a/extensions/jui/assets/theme/images/ui-bg_flat_75_ffffff_40x100.png and b/extensions/jui/assets/theme/images/ui-bg_flat_75_ffffff_40x100.png differ
diff --git a/extensions/jui/assets/theme/images/ui-bg_glass_55_fbf9ee_1x400.png b/extensions/jui/assets/theme/images/ui-bg_glass_55_fbf9ee_1x400.png
old mode 100755
new mode 100644
Binary files a/extensions/jui/assets/theme/images/ui-bg_glass_55_fbf9ee_1x400.png and b/extensions/jui/assets/theme/images/ui-bg_glass_55_fbf9ee_1x400.png differ
diff --git a/extensions/jui/assets/theme/images/ui-bg_glass_65_ffffff_1x400.png b/extensions/jui/assets/theme/images/ui-bg_glass_65_ffffff_1x400.png
old mode 100755
new mode 100644
Binary files a/extensions/jui/assets/theme/images/ui-bg_glass_65_ffffff_1x400.png and b/extensions/jui/assets/theme/images/ui-bg_glass_65_ffffff_1x400.png differ
diff --git a/extensions/jui/assets/theme/images/ui-bg_glass_75_dadada_1x400.png b/extensions/jui/assets/theme/images/ui-bg_glass_75_dadada_1x400.png
old mode 100755
new mode 100644
Binary files a/extensions/jui/assets/theme/images/ui-bg_glass_75_dadada_1x400.png and b/extensions/jui/assets/theme/images/ui-bg_glass_75_dadada_1x400.png differ
diff --git a/extensions/jui/assets/theme/images/ui-bg_glass_75_e6e6e6_1x400.png b/extensions/jui/assets/theme/images/ui-bg_glass_75_e6e6e6_1x400.png
old mode 100755
new mode 100644
Binary files a/extensions/jui/assets/theme/images/ui-bg_glass_75_e6e6e6_1x400.png and b/extensions/jui/assets/theme/images/ui-bg_glass_75_e6e6e6_1x400.png differ
diff --git a/extensions/jui/assets/theme/images/ui-bg_glass_95_fef1ec_1x400.png b/extensions/jui/assets/theme/images/ui-bg_glass_95_fef1ec_1x400.png
old mode 100755
new mode 100644
Binary files a/extensions/jui/assets/theme/images/ui-bg_glass_95_fef1ec_1x400.png and b/extensions/jui/assets/theme/images/ui-bg_glass_95_fef1ec_1x400.png differ
diff --git a/extensions/jui/assets/theme/images/ui-bg_highlight-soft_75_cccccc_1x100.png b/extensions/jui/assets/theme/images/ui-bg_highlight-soft_75_cccccc_1x100.png
old mode 100755
new mode 100644
Binary files a/extensions/jui/assets/theme/images/ui-bg_highlight-soft_75_cccccc_1x100.png and b/extensions/jui/assets/theme/images/ui-bg_highlight-soft_75_cccccc_1x100.png differ
diff --git a/extensions/jui/assets/theme/images/ui-icons_222222_256x240.png b/extensions/jui/assets/theme/images/ui-icons_222222_256x240.png
old mode 100755
new mode 100644
Binary files a/extensions/jui/assets/theme/images/ui-icons_222222_256x240.png and b/extensions/jui/assets/theme/images/ui-icons_222222_256x240.png differ
diff --git a/extensions/jui/assets/theme/images/ui-icons_2e83ff_256x240.png b/extensions/jui/assets/theme/images/ui-icons_2e83ff_256x240.png
old mode 100755
new mode 100644
Binary files a/extensions/jui/assets/theme/images/ui-icons_2e83ff_256x240.png and b/extensions/jui/assets/theme/images/ui-icons_2e83ff_256x240.png differ
diff --git a/extensions/jui/assets/theme/images/ui-icons_454545_256x240.png b/extensions/jui/assets/theme/images/ui-icons_454545_256x240.png
old mode 100755
new mode 100644
Binary files a/extensions/jui/assets/theme/images/ui-icons_454545_256x240.png and b/extensions/jui/assets/theme/images/ui-icons_454545_256x240.png differ
diff --git a/extensions/jui/assets/theme/images/ui-icons_888888_256x240.png b/extensions/jui/assets/theme/images/ui-icons_888888_256x240.png
old mode 100755
new mode 100644
Binary files a/extensions/jui/assets/theme/images/ui-icons_888888_256x240.png and b/extensions/jui/assets/theme/images/ui-icons_888888_256x240.png differ
diff --git a/extensions/jui/assets/theme/images/ui-icons_cd0a0a_256x240.png b/extensions/jui/assets/theme/images/ui-icons_cd0a0a_256x240.png
old mode 100755
new mode 100644
Binary files a/extensions/jui/assets/theme/images/ui-icons_cd0a0a_256x240.png and b/extensions/jui/assets/theme/images/ui-icons_cd0a0a_256x240.png differ
diff --git a/extensions/jui/assets/theme/jquery.ui.css b/extensions/jui/assets/theme/jquery.ui.css
old mode 100755
new mode 100644
diff --git a/extensions/jui/composer.json b/extensions/jui/composer.json
index 126df39..f129fef 100644
--- a/extensions/jui/composer.json
+++ b/extensions/jui/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-jui",
 	"description": "The Jquery UI extension for the Yii framework",
-	"keywords": ["yii", "Jquery UI", "renderer"],
+	"keywords": ["yii2", "Jquery UI", "renderer"],
 	"type": "yii2-extension",
 	"license": "BSD-3-Clause",
 	"support": {
diff --git a/extensions/mongodb/Collection.php b/extensions/mongodb/Collection.php
index 38144bc..6f595f8 100644
--- a/extensions/mongodb/Collection.php
+++ b/extensions/mongodb/Collection.php
@@ -260,7 +260,7 @@ class Collection extends Object
 	}
 
 	/**
-	 * Returns a a single document.
+	 * Returns 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.
@@ -272,6 +272,32 @@ class Collection extends Object
 	}
 
 	/**
+	 * Updates a document and returns it.
+	 * @param array $condition query condition
+	 * @param array $update update criteria
+	 * @param array $fields fields to be returned
+	 * @param array $options list of options in format: optionName => optionValue.
+	 * @return array|null the original document, or the modified document when $options['new'] is set.
+	 * @throws Exception on failure.
+	 * @see http://www.php.net/manual/en/mongocollection.findandmodify.php
+	 */
+	public function findAndModify($condition, $update, $fields = [], $options = [])
+	{
+		$condition = $this->buildCondition($condition);
+		$token = $this->composeLogToken('findAndModify', [$condition, $update, $fields, $options]);
+		Yii::info($token, __METHOD__);
+		try {
+			Yii::beginProfile($token, __METHOD__);
+			$result = $this->mongoCollection->findAndModify($condition, $update, $fields, $options);
+			Yii::endProfile($token, __METHOD__);
+			return $result;
+		} catch (\Exception $e) {
+			Yii::endProfile($token, __METHOD__);
+			throw new Exception($e->getMessage(), (int)$e->getCode(), $e);
+		}
+	}
+
+	/**
 	 * Inserts new data into collection.
 	 * @param array|object $data data to be inserted.
 	 * @param array $options list of options in format: optionName => optionValue.
diff --git a/extensions/mongodb/composer.json b/extensions/mongodb/composer.json
index d69a1a1..6556bd0 100644
--- a/extensions/mongodb/composer.json
+++ b/extensions/mongodb/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-mongodb",
 	"description": "MongoDb extension for the Yii framework",
-	"keywords": ["yii", "mongo", "mongodb", "active-record"],
+	"keywords": ["yii2", "mongo", "mongodb", "active-record"],
 	"type": "yii2-extension",
 	"license": "BSD-3-Clause",
 	"support": {
diff --git a/extensions/redis/LuaScriptBuilder.php b/extensions/redis/LuaScriptBuilder.php
index 3da5d3d..1c1082b 100644
--- a/extensions/redis/LuaScriptBuilder.php
+++ b/extensions/redis/LuaScriptBuilder.php
@@ -7,6 +7,7 @@
 
 namespace yii\redis;
 
+use yii\base\InvalidParamException;
 use yii\base\NotSupportedException;
 use yii\db\Exception;
 use yii\db\Expression;
diff --git a/extensions/redis/composer.json b/extensions/redis/composer.json
index 3740013..7561028 100644
--- a/extensions/redis/composer.json
+++ b/extensions/redis/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-redis",
 	"description": "Redis Cache, Session and ActiveRecord for the Yii framework",
-	"keywords": ["yii", "redis", "active-record", "cache", "session"],
+	"keywords": ["yii2", "redis", "active-record", "cache", "session"],
 	"type": "yii2-extension",
 	"license": "BSD-3-Clause",
 	"support": {
diff --git a/extensions/smarty/composer.json b/extensions/smarty/composer.json
index 16b48bf..7d32b6d 100644
--- a/extensions/smarty/composer.json
+++ b/extensions/smarty/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-smarty",
 	"description": "The Smarty integration for the Yii framework",
-	"keywords": ["yii", "smarty", "renderer"],
+	"keywords": ["yii2", "smarty", "renderer"],
 	"type": "yii2-extension",
 	"license": "BSD-3-Clause",
 	"support": {
diff --git a/extensions/sphinx/QueryBuilder.php b/extensions/sphinx/QueryBuilder.php
index e24a3a8..8e1d5c4 100644
--- a/extensions/sphinx/QueryBuilder.php
+++ b/extensions/sphinx/QueryBuilder.php
@@ -77,7 +77,7 @@ class QueryBuilder extends Object
 
 		$clauses = [
 			$this->buildSelect($query->select, $params, $query->distinct, $query->selectOption),
-			$this->buildFrom($from, $Params),
+			$this->buildFrom($from, $params),
 			$this->buildWhere($query->from, $query->where, $params),
 			$this->buildGroupBy($query->groupBy),
 			$this->buildWithin($query->within),
diff --git a/extensions/sphinx/composer.json b/extensions/sphinx/composer.json
index 9f31226..db3e928 100644
--- a/extensions/sphinx/composer.json
+++ b/extensions/sphinx/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-sphinx",
 	"description": "Sphinx full text search engine extension for the Yii framework",
-	"keywords": ["yii", "sphinx", "active-record", "search", "fulltext"],
+	"keywords": ["yii2", "sphinx", "active-record", "search", "fulltext"],
 	"type": "yii2-extension",
 	"license": "BSD-3-Clause",
 	"support": {
diff --git a/extensions/swiftmailer/Mailer.php b/extensions/swiftmailer/Mailer.php
index 891f2b5..8a87ba2 100644
--- a/extensions/swiftmailer/Mailer.php
+++ b/extensions/swiftmailer/Mailer.php
@@ -19,7 +19,7 @@ use yii\mail\BaseMailer;
  * ~~~
  * 'components' => [
  *     ...
- *     'email' => [
+ *     'mail' => [
  *         'class' => 'yii\swiftmailer\Mailer',
  *         'transport' => [
  *             'class' => 'Swift_SmtpTransport',
diff --git a/extensions/swiftmailer/composer.json b/extensions/swiftmailer/composer.json
index 4c9e831..f248942 100644
--- a/extensions/swiftmailer/composer.json
+++ b/extensions/swiftmailer/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-swiftmailer",
 	"description": "The SwiftMailer integration for the Yii framework",
-	"keywords": ["yii", "swift", "swiftmailer", "mail", "email", "mailer"],
+	"keywords": ["yii2", "swift", "swiftmailer", "mail", "email", "mailer"],
 	"type": "yii2-extension",
 	"license": "BSD-3-Clause",
 	"support": {
diff --git a/extensions/twig/composer.json b/extensions/twig/composer.json
index 1638058..ee7b0f8 100644
--- a/extensions/twig/composer.json
+++ b/extensions/twig/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2-twig",
 	"description": "The Twig integration for the Yii framework",
-	"keywords": ["yii", "twig", "renderer"],
+	"keywords": ["yii2", "twig", "renderer"],
 	"type": "yii2-extension",
 	"license": "BSD-3-Clause",
 	"support": {
diff --git a/framework/BaseYii.php b/framework/BaseYii.php
index 9152afc..c70808e 100644
--- a/framework/BaseYii.php
+++ b/framework/BaseYii.php
@@ -356,7 +356,7 @@ class BaseYii
 			$config = array_merge(static::$objectConfig[$class], $config);
 		}
 
-		if (($n = func_num_args()) > 1) {
+		if (func_num_args() > 1) {
 			/** @var \ReflectionClass $reflection */
 			if (isset($reflections[$class])) {
 				$reflection = $reflections[$class];
diff --git a/framework/CHANGELOG.md b/framework/CHANGELOG.md
index 4ce41d2..06eb826 100644
--- a/framework/CHANGELOG.md
+++ b/framework/CHANGELOG.md
@@ -49,6 +49,9 @@ Yii Framework 2 Change Log
 - 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 #2527: Source language for `app` message category was always `en` no matter which application `sourceLanguage` was used (samdark)
+- Bug #2559: Going back on browser history breaks GridView filtering with `Pjax` (tonydspaniard)
+- Bug #2607: `yii message` tool wasn't updating `message` table (mitalcoi)
+- Bug #2624: Html::textArea() should respect "name" option. (qiangxue)
 - 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)
@@ -59,6 +62,7 @@ Yii Framework 2 Change Log
 - Bug: Fixed an issue with Filehelper and not accessable directories which resulted in endless loop (cebe)
 - Bug: Fixed `$model->load($data)` returned `true` if `$data` and `formName` were empty (samdark)
 - Bug: Fixed issue with `ActiveRelationTrait` preventing `ActiveQuery` from clearing events and behaviors on clone (jom)
+- Bug: `Query::queryScalar` wasn't making `SELECT DISTINCT` queries subqueries (jom)
 - Enh #46: Added Image extension based on [Imagine library](http://imagine.readthedocs.org) (tonydspaniard)
 - Enh #364: Improve Inflector::slug with `intl` transliteration. Improved transliteration char map. (tonydspaniard)
 - Enh #797: Added support for validating multiple columns by `UniqueValidator` and `ExistValidator` (qiangxue)
@@ -67,10 +71,12 @@ Yii Framework 2 Change Log
 - Enh #1293: Replaced Console::showProgress() with a better approach. See Console::startProgress() for details (cebe)
 - Enh #1406: DB Schema support for Oracle Database (p0larbeer, qiangxue)
 - Enh #1437: Added ListView::viewParams (qiangxue)
+- Enh #1467: Added support for organizing controllers in subdirectories (qiangxue)
 - Enh #1469: ActiveRecord::find() now works with default conditions (default scope) applied by createQuery (cebe)
 - Enh #1476: Add yii\web\Session::handler property (nineinchnick)
 - Enh #1499: Added `ActionColumn::controller` property to support customizing the controller for handling GridView actions (qiangxue)
 - Enh #1523: Query conditions now allow to use the NOT operator (cebe)
+- Enh #1535: Improved `yii\web\User` to start session only when needed. Also prepared it for use without session. (qiangxue)
 - Enh #1562: Added `yii\bootstrap\Tabs::linkOptions` (kartik-v)
 - Enh #1572: Added `yii\web\Controller::createAbsoluteUrl()` (samdark)
 - Enh #1579: throw exception when the given AR relation name does not match in a case sensitive manner (qiangxue)
@@ -118,12 +124,14 @@ Yii Framework 2 Change Log
 - 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 #2411: Added Gii extension generator (schmunk42)
+- Enh #2415: Added support for inverse relations (qiangxue)
 - 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 #2525: Added support for formatting file sizes with `yii\base\Formatter` (VinceG)
 - 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)
@@ -143,8 +151,14 @@ Yii Framework 2 Change Log
 - Enh: Added `yii\web\Response::clearOutputBuffers()` (qiangxue)
 - Enh: Improved `QueryBuilder::buildLimit()` to support big numbers (qiangxue)
 - Enh: Added support for building SQLs with sub-queries (qiangxue)
+- Enh: Added `Pagination::getLinks()` (qiangxue)
+- Enh: Added support for reading page size from query parameters by `Pagination` (qiangxue)
+- Enh: LinkPager can now register relational link tags in the html header for prev, next, first and last page (cebe)
+- Enh: Added `yii\web\UrlRuleInterface` and `yii\web\CompositeUrlRule` (qiangxue)
+- Enh: Added `yii\web\Request::getAuthUser()` and `getAuthPassword()` (qiangxue)
 - Chg #1186: Changed `Sort` to use comma to separate multiple sort fields and use negative sign to indicate descending sort (qiangxue)
 - Chg #1519: `yii\web\User::loginRequired()` now returns the `Response` object instead of exiting the application (qiangxue)
+- Chg #1564: Removed `yii\web\Session::autoStart` and added `hasSessionId`. Session will be automatically started when accessing session data (qiangxue)
 - Chg #1586: `QueryBuilder::buildLikeCondition()` will now escape special characters and use percentage characters by default (qiangxue)
 - Chg #1610: `Html::activeCheckboxList()` and `Html::activeRadioList()` will submit an empty string if no checkbox/radio is selected (qiangxue)
 - Chg #1643: Added default value for `Captcha::options` (qiangxue)
@@ -171,6 +185,7 @@ Yii Framework 2 Change Log
 - 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 #2544: Changed `DetailView`'s `name:format:label` to `attribute:format:label` to match `GridView` (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)
@@ -202,7 +217,7 @@ Yii Framework 2 Change Log
 	- Renamed `yii\web\User::authTimeoutVar` to `authTimeoutParam`
 	- Renamed `yii\web\User::returnUrlVar` to `returnUrlParam`
 - Chg: Added `View::viewFile` and removed `ViewEvent::viewFile` (qiangxue)
-
+- Chg: Changed `Controller::afterAction()`, `Module::afterAction()` and `ActionFilter::afterAction()` to pass `$result` by value instead of reference (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)
 - New #1393: [Codeception testing framework integration](https://github.com/yiisoft/yii2-codeception) (Ragazzo)
diff --git a/framework/assets/pjax/LICENSE b/framework/assets/pjax/LICENSE
deleted file mode 100644
index 42c0317..0000000
--- a/framework/assets/pjax/LICENSE
+++ /dev/null
@@ -1,20 +0,0 @@
-Copyright (c) Chris Wanstrath
-
-Permission is hereby granted, free of charge, to any person obtaining
-a copy of this software and associated documentation files (the
-Software), to deal in the Software without restriction, including
-without limitation the rights to use, copy, modify, merge, publish,
-distribute, sublicense, and/or sell copies of the Software, and to
-permit persons to whom the Software is furnished to do so, subject to
-the following conditions:
-
-The above copyright notice and this permission notice shall be
-included in all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/framework/assets/pjax/jquery.pjax.js b/framework/assets/pjax/jquery.pjax.js
deleted file mode 100644
index 1934d80..0000000
--- a/framework/assets/pjax/jquery.pjax.js
+++ /dev/null
@@ -1,839 +0,0 @@
-// jquery.pjax.js
-// copyright chris wanstrath
-// https://github.com/defunkt/jquery-pjax
-
-(function($){
-
-// When called on a container with a selector, fetches the href with
-// ajax into the container or with the data-pjax attribute on the link
-// itself.
-//
-// Tries to make sure the back button and ctrl+click work the way
-// you'd expect.
-//
-// Exported as $.fn.pjax
-//
-// Accepts a jQuery ajax options object that may include these
-// pjax specific options:
-//
-//
-// container - Where to stick the response body. Usually a String selector.
-//             $(container).html(xhr.responseBody)
-//             (default: current jquery context)
-//      push - Whether to pushState the URL. Defaults to true (of course).
-//   replace - Want to use replaceState instead? That's cool.
-//
-// For convenience the second parameter can be either the container or
-// the options object.
-//
-// Returns the jQuery object
-	function fnPjax(selector, container, options) {
-		var context = this
-		return this.on('click.pjax', selector, function(event) {
-			var opts = $.extend({}, optionsFor(container, options))
-			if (!opts.container)
-				opts.container = $(this).attr('data-pjax') || context
-			handleClick(event, opts)
-		})
-	}
-
-// Public: pjax on click handler
-//
-// Exported as $.pjax.click.
-//
-// event   - "click" jQuery.Event
-// options - pjax options
-//
-// Examples
-//
-//   $(document).on('click', 'a', $.pjax.click)
-//   // is the same as
-//   $(document).pjax('a')
-//
-//  $(document).on('click', 'a', function(event) {
-//    var container = $(this).closest('[data-pjax-container]')
-//    $.pjax.click(event, container)
-//  })
-//
-// Returns nothing.
-	function handleClick(event, container, options) {
-		options = optionsFor(container, options)
-
-		var link = event.currentTarget
-
-		if (link.tagName.toUpperCase() !== 'A')
-			throw "$.fn.pjax or $.pjax.click requires an anchor element"
-
-		// Middle click, cmd click, and ctrl click should open
-		// links in a new tab as normal.
-		if ( event.which > 1 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey )
-			return
-
-		// Ignore cross origin links
-		if ( location.protocol !== link.protocol || location.hostname !== link.hostname )
-			return
-
-		// Ignore anchors on the same page
-		if (link.hash && link.href.replace(link.hash, '') ===
-			location.href.replace(location.hash, ''))
-			return
-
-		// Ignore empty anchor "foo.html#"
-		if (link.href === location.href + '#')
-			return
-
-		var defaults = {
-			url: link.href,
-			container: $(link).attr('data-pjax'),
-			target: link
-		}
-
-		var opts = $.extend({}, defaults, options)
-		var clickEvent = $.Event('pjax:click')
-		$(link).trigger(clickEvent, [opts])
-
-		if (!clickEvent.isDefaultPrevented()) {
-			pjax(opts)
-			event.preventDefault()
-			$(link).trigger('pjax:clicked', [opts])
-		}
-	}
-
-// Public: pjax on form submit handler
-//
-// Exported as $.pjax.submit
-//
-// event   - "click" jQuery.Event
-// options - pjax options
-//
-// Examples
-//
-//  $(document).on('submit', 'form', function(event) {
-//    var container = $(this).closest('[data-pjax-container]')
-//    $.pjax.submit(event, container)
-//  })
-//
-// Returns nothing.
-	function handleSubmit(event, container, options) {
-		options = optionsFor(container, options)
-
-		var form = event.currentTarget
-
-		if (form.tagName.toUpperCase() !== 'FORM')
-			throw "$.pjax.submit requires a form element"
-
-		var defaults = {
-			type: form.method.toUpperCase(),
-			url: form.action,
-			data: $(form).serializeArray(),
-			container: $(form).attr('data-pjax'),
-			target: form
-		}
-
-		pjax($.extend({}, defaults, options))
-
-		event.preventDefault()
-	}
-
-// Loads a URL with ajax, puts the response body inside a container,
-// then pushState()'s the loaded URL.
-//
-// Works just like $.ajax in that it accepts a jQuery ajax
-// settings object (with keys like url, type, data, etc).
-//
-// Accepts these extra keys:
-//
-// container - Where to stick the response body.
-//             $(container).html(xhr.responseBody)
-//      push - Whether to pushState the URL. Defaults to true (of course).
-//   replace - Want to use replaceState instead? That's cool.
-//
-// Use it just like $.ajax:
-//
-//   var xhr = $.pjax({ url: this.href, container: '#main' })
-//   console.log( xhr.readyState )
-//
-// Returns whatever $.ajax returns.
-	function pjax(options) {
-		options = $.extend(true, {}, $.ajaxSettings, pjax.defaults, options)
-
-		if ($.isFunction(options.url)) {
-			options.url = options.url()
-		}
-
-		var target = options.target
-
-		var hash = parseURL(options.url).hash
-
-		var context = options.context = findContainerFor(options.container)
-
-		// We want the browser to maintain two separate internal caches: one
-		// for pjax'd partial page loads and one for normal page loads.
-		// Without adding this secret parameter, some browsers will often
-		// confuse the two.
-		if (!options.data) options.data = {}
-		options.data._pjax = context.selector
-
-		function fire(type, args) {
-			var event = $.Event(type, { relatedTarget: target })
-			context.trigger(event, args)
-			return !event.isDefaultPrevented()
-		}
-
-		var timeoutTimer
-
-		options.beforeSend = function(xhr, settings) {
-			// No timeout for non-GET requests
-			// Its not safe to request the resource again with a fallback method.
-			if (settings.type !== 'GET') {
-				settings.timeout = 0
-			}
-
-			xhr.setRequestHeader('X-PJAX', 'true')
-			xhr.setRequestHeader('X-PJAX-Container', context.selector)
-
-			if (!fire('pjax:beforeSend', [xhr, settings]))
-				return false
-
-			if (settings.timeout > 0) {
-				timeoutTimer = setTimeout(function() {
-					if (fire('pjax:timeout', [xhr, options]))
-						xhr.abort('timeout')
-				}, settings.timeout)
-
-				// Clear timeout setting so jquerys internal timeout isn't invoked
-				settings.timeout = 0
-			}
-
-			options.requestUrl = parseURL(settings.url).href
-		}
-
-		options.complete = function(xhr, textStatus) {
-			if (timeoutTimer)
-				clearTimeout(timeoutTimer)
-
-			fire('pjax:complete', [xhr, textStatus, options])
-
-			fire('pjax:end', [xhr, options])
-		}
-
-		options.error = function(xhr, textStatus, errorThrown) {
-			var container = extractContainer("", xhr, options)
-
-			var allowed = fire('pjax:error', [xhr, textStatus, errorThrown, options])
-			if (options.type == 'GET' && textStatus !== 'abort' && allowed) {
-				locationReplace(container.url)
-			}
-		}
-
-		options.success = function(data, status, xhr) {
-			// If $.pjax.defaults.version is a function, invoke it first.
-			// Otherwise it can be a static string.
-			var currentVersion = (typeof $.pjax.defaults.version === 'function') ?
-				$.pjax.defaults.version() :
-				$.pjax.defaults.version
-
-			var latestVersion = xhr.getResponseHeader('X-PJAX-Version')
-
-			var container = extractContainer(data, xhr, options)
-
-			// If there is a layout version mismatch, hard load the new url
-			if (currentVersion && latestVersion && currentVersion !== latestVersion) {
-				locationReplace(container.url)
-				return
-			}
-
-			// If the new response is missing a body, hard load the page
-			if (!container.contents) {
-				locationReplace(container.url)
-				return
-			}
-
-			pjax.state = {
-				id: options.id || uniqueId(),
-				url: container.url,
-				title: container.title,
-				container: context.selector,
-				fragment: options.fragment,
-				timeout: options.timeout
-			}
-
-			if (options.push || options.replace) {
-				window.history.replaceState(pjax.state, container.title, container.url)
-			}
-
-			// Clear out any focused controls before inserting new page contents.
-			document.activeElement.blur()
-
-			if (container.title) document.title = container.title
-			context.html(container.contents)
-
-			// FF bug: Won't autofocus fields that are inserted via JS.
-			// This behavior is incorrect. So if theres no current focus, autofocus
-			// the last field.
-			//
-			// http://www.w3.org/html/wg/drafts/html/master/forms.html
-			var autofocusEl = context.find('input[autofocus], textarea[autofocus]').last()[0]
-			if (autofocusEl && document.activeElement !== autofocusEl) {
-				autofocusEl.focus();
-			}
-
-			executeScriptTags(container.scripts)
-
-			// Scroll to top by default
-			if (typeof options.scrollTo === 'number')
-				$(window).scrollTop(options.scrollTo)
-
-			// If the URL has a hash in it, make sure the browser
-			// knows to navigate to the hash.
-			if ( hash !== '' ) {
-				// Avoid using simple hash set here. Will add another history
-				// entry. Replace the url with replaceState and scroll to target
-				// by hand.
-				//
-				//   window.location.hash = hash
-				var url = parseURL(container.url)
-				url.hash = hash
-
-				pjax.state.url = url.href
-				window.history.replaceState(pjax.state, container.title, url.href)
-
-				var target = $(url.hash)
-				if (target.length) $(window).scrollTop(target.offset().top)
-			}
-
-			fire('pjax:success', [data, status, xhr, options])
-		}
-
-
-		// Initialize pjax.state for the initial page load. Assume we're
-		// using the container and options of the link we're loading for the
-		// back button to the initial page. This ensures good back button
-		// behavior.
-		if (!pjax.state) {
-			pjax.state = {
-				id: uniqueId(),
-				url: window.location.href,
-				title: document.title,
-				container: context.selector,
-				fragment: options.fragment,
-				timeout: options.timeout
-			}
-			window.history.replaceState(pjax.state, document.title)
-		}
-
-		// Cancel the current request if we're already pjaxing
-		var xhr = pjax.xhr
-		if ( xhr && xhr.readyState < 4) {
-			xhr.onreadystatechange = $.noop
-			xhr.abort()
-		}
-
-		pjax.options = options
-		var xhr = pjax.xhr = $.ajax(options)
-
-		if (xhr.readyState > 0) {
-			if (options.push && !options.replace) {
-				// Cache current container element before replacing it
-				cachePush(pjax.state.id, context.clone().contents())
-
-				window.history.pushState(null, "", stripPjaxParam(options.requestUrl))
-			}
-
-			fire('pjax:start', [xhr, options])
-			fire('pjax:send', [xhr, options])
-		}
-
-		return pjax.xhr
-	}
-
-// Public: Reload current page with pjax.
-//
-// Returns whatever $.pjax returns.
-	function pjaxReload(container, options) {
-		var defaults = {
-			url: window.location.href,
-			push: false,
-			replace: true,
-			scrollTo: false
-		}
-
-		return pjax($.extend(defaults, optionsFor(container, options)))
-	}
-
-// Internal: Hard replace current state with url.
-//
-// Work for around WebKit
-//   https://bugs.webkit.org/show_bug.cgi?id=93506
-//
-// Returns nothing.
-	function locationReplace(url) {
-		window.history.replaceState(null, "", "#")
-		window.location.replace(url)
-	}
-
-
-	var initialPop = true
-	var initialURL = window.location.href
-	var initialState = window.history.state
-
-// Initialize $.pjax.state if possible
-// Happens when reloading a page and coming forward from a different
-// session history.
-	if (initialState && initialState.container) {
-		pjax.state = initialState
-	}
-
-// Non-webkit browsers don't fire an initial popstate event
-	if ('state' in window.history) {
-		initialPop = false
-	}
-
-// popstate handler takes care of the back and forward buttons
-//
-// You probably shouldn't use pjax on pages with other pushState
-// stuff yet.
-	function onPjaxPopstate(event) {
-		var state = event.state
-
-		if (state && state.container) {
-			// When coming forward from a separate history session, will get an
-			// initial pop with a state we are already at. Skip reloading the current
-			// page.
-			if (initialPop && initialURL == state.url) return
-
-			// If popping back to the same state, just skip.
-			// Could be clicking back from hashchange rather than a pushState.
-			if (pjax.state.id === state.id) return
-
-			var container = $(state.container)
-			if (container.length) {
-				var direction, contents = cacheMapping[state.id]
-
-				if (pjax.state) {
-					// Since state ids always increase, we can deduce the history
-					// direction from the previous state.
-					direction = pjax.state.id < state.id ? 'forward' : 'back'
-
-					// Cache current container before replacement and inform the
-					// cache which direction the history shifted.
-					cachePop(direction, pjax.state.id, container.clone().contents())
-				}
-
-				var popstateEvent = $.Event('pjax:popstate', {
-					state: state,
-					direction: direction
-				})
-				container.trigger(popstateEvent)
-
-				var options = {
-					id: state.id,
-					url: state.url,
-					container: container,
-					push: false,
-					fragment: state.fragment,
-					timeout: state.timeout,
-					scrollTo: false
-				}
-
-				if (contents) {
-					container.trigger('pjax:start', [null, options])
-
-					if (state.title) document.title = state.title
-					container.html(contents)
-					pjax.state = state
-
-					container.trigger('pjax:end', [null, options])
-				} else {
-					pjax(options)
-				}
-
-				// Force reflow/relayout before the browser tries to restore the
-				// scroll position.
-				container[0].offsetHeight
-			} else {
-				locationReplace(location.href)
-			}
-		}
-		initialPop = false
-	}
-
-// Fallback version of main pjax function for browsers that don't
-// support pushState.
-//
-// Returns nothing since it retriggers a hard form submission.
-	function fallbackPjax(options) {
-		var url = $.isFunction(options.url) ? options.url() : options.url,
-			method = options.type ? options.type.toUpperCase() : 'GET'
-
-		var form = $('<form>', {
-			method: method === 'GET' ? 'GET' : 'POST',
-			action: url,
-			style: 'display:none'
-		})
-
-		if (method !== 'GET' && method !== 'POST') {
-			form.append($('<input>', {
-				type: 'hidden',
-				name: '_method',
-				value: method.toLowerCase()
-			}))
-		}
-
-		var data = options.data
-		if (typeof data === 'string') {
-			$.each(data.split('&'), function(index, value) {
-				var pair = value.split('=')
-				form.append($('<input>', {type: 'hidden', name: pair[0], value: pair[1]}))
-			})
-		} else if (typeof data === 'object') {
-			for (key in data)
-				form.append($('<input>', {type: 'hidden', name: key, value: data[key]}))
-		}
-
-		$(document.body).append(form)
-		form.submit()
-	}
-
-// Internal: Generate unique id for state object.
-//
-// Use a timestamp instead of a counter since ids should still be
-// unique across page loads.
-//
-// Returns Number.
-	function uniqueId() {
-		return (new Date).getTime()
-	}
-
-// Internal: Strips _pjax param from url
-//
-// url - String
-//
-// Returns String.
-	function stripPjaxParam(url) {
-		return url
-			.replace(/\?_pjax=[^&]+&?/, '?')
-			.replace(/_pjax=[^&]+&?/, '')
-			.replace(/[\?&]$/, '')
-	}
-
-// Internal: Parse URL components and returns a Locationish object.
-//
-// url - String URL
-//
-// Returns HTMLAnchorElement that acts like Location.
-	function parseURL(url) {
-		var a = document.createElement('a')
-		a.href = url
-		return a
-	}
-
-// Internal: Build options Object for arguments.
-//
-// For convenience the first parameter can be either the container or
-// the options object.
-//
-// Examples
-//
-//   optionsFor('#container')
-//   // => {container: '#container'}
-//
-//   optionsFor('#container', {push: true})
-//   // => {container: '#container', push: true}
-//
-//   optionsFor({container: '#container', push: true})
-//   // => {container: '#container', push: true}
-//
-// Returns options Object.
-	function optionsFor(container, options) {
-		// Both container and options
-		if ( container && options )
-			options.container = container
-
-		// First argument is options Object
-		else if ( $.isPlainObject(container) )
-			options = container
-
-		// Only container
-		else
-			options = {container: container}
-
-		// Find and validate container
-		if (options.container)
-			options.container = findContainerFor(options.container)
-
-		return options
-	}
-
-// Internal: Find container element for a variety of inputs.
-//
-// Because we can't persist elements using the history API, we must be
-// able to find a String selector that will consistently find the Element.
-//
-// container - A selector String, jQuery object, or DOM Element.
-//
-// Returns a jQuery object whose context is `document` and has a selector.
-	function findContainerFor(container) {
-		container = $(container)
-
-		if ( !container.length ) {
-			throw "no pjax container for " + container.selector
-		} else if ( container.selector !== '' && container.context === document ) {
-			return container
-		} else if ( container.attr('id') ) {
-			return $('#' + container.attr('id'))
-		} else {
-			throw "cant get selector for pjax container!"
-		}
-	}
-
-// Internal: Filter and find all elements matching the selector.
-//
-// Where $.fn.find only matches descendants, findAll will test all the
-// top level elements in the jQuery object as well.
-//
-// elems    - jQuery object of Elements
-// selector - String selector to match
-//
-// Returns a jQuery object.
-	function findAll(elems, selector) {
-		return elems.filter(selector).add(elems.find(selector));
-	}
-
-	function parseHTML(html) {
-		return $.parseHTML(html, document, true)
-	}
-
-// Internal: Extracts container and metadata from response.
-//
-// 1. Extracts X-PJAX-URL header if set
-// 2. Extracts inline <title> tags
-// 3. Builds response Element and extracts fragment if set
-//
-// data    - String response data
-// xhr     - XHR response
-// options - pjax options Object
-//
-// Returns an Object with url, title, and contents keys.
-	function extractContainer(data, xhr, options) {
-		var obj = {}
-
-		// Prefer X-PJAX-URL header if it was set, otherwise fallback to
-		// using the original requested url.
-		obj.url = stripPjaxParam(xhr.getResponseHeader('X-PJAX-URL') || options.requestUrl)
-
-		// Attempt to parse response html into elements
-		if (/<html/i.test(data)) {
-			var $head = $(parseHTML(data.match(/<head[^>]*>([\s\S.]*)<\/head>/i)[0]))
-			var $body = $(parseHTML(data.match(/<body[^>]*>([\s\S.]*)<\/body>/i)[0]))
-		} else {
-			var $head = $body = $(parseHTML(data))
-		}
-
-		// If response data is empty, return fast
-		if ($body.length === 0)
-			return obj
-
-		// If there's a <title> tag in the header, use it as
-		// the page's title.
-		obj.title = findAll($head, 'title').last().text()
-
-		if (options.fragment) {
-			// If they specified a fragment, look for it in the response
-			// and pull it out.
-			if (options.fragment === 'body') {
-				var $fragment = $body
-			} else {
-				var $fragment = findAll($body, options.fragment).first()
-			}
-
-			if ($fragment.length) {
-				obj.contents = $fragment.contents()
-
-				// If there's no title, look for data-title and title attributes
-				// on the fragment
-				if (!obj.title)
-					obj.title = $fragment.attr('title') || $fragment.data('title')
-			}
-
-		} else if (!/<html/i.test(data)) {
-			obj.contents = $body
-		}
-
-		// Clean up any <title> tags
-		if (obj.contents) {
-			// Remove any parent title elements
-			obj.contents = obj.contents.not(function() { return $(this).is('title') })
-
-			// Then scrub any titles from their descendants
-			obj.contents.find('title').remove()
-
-			// Gather all script[src] elements
-			obj.scripts = findAll(obj.contents, 'script[src]').remove()
-			obj.contents = obj.contents.not(obj.scripts)
-		}
-
-		// Trim any whitespace off the title
-		if (obj.title) obj.title = $.trim(obj.title)
-
-		return obj
-	}
-
-// Load an execute scripts using standard script request.
-//
-// Avoids jQuery's traditional $.getScript which does a XHR request and
-// globalEval.
-//
-// scripts - jQuery object of script Elements
-//
-// Returns nothing.
-	function executeScriptTags(scripts) {
-		if (!scripts) return
-
-		var existingScripts = $('script[src]')
-
-		scripts.each(function() {
-			var src = this.src
-			var matchedScripts = existingScripts.filter(function() {
-				return this.src === src
-			})
-			if (matchedScripts.length) return
-
-			var script = document.createElement('script')
-			script.type = $(this).attr('type')
-			script.src = $(this).attr('src')
-			document.head.appendChild(script)
-		})
-	}
-
-// Internal: History DOM caching class.
-	var cacheMapping      = {}
-	var cacheForwardStack = []
-	var cacheBackStack    = []
-
-// Push previous state id and container contents into the history
-// cache. Should be called in conjunction with `pushState` to save the
-// previous container contents.
-//
-// id    - State ID Number
-// value - DOM Element to cache
-//
-// Returns nothing.
-	function cachePush(id, value) {
-		cacheMapping[id] = value
-		cacheBackStack.push(id)
-
-		// Remove all entires in forward history stack after pushing
-		// a new page.
-		while (cacheForwardStack.length)
-			delete cacheMapping[cacheForwardStack.shift()]
-
-		// Trim back history stack to max cache length.
-		while (cacheBackStack.length > pjax.defaults.maxCacheLength)
-			delete cacheMapping[cacheBackStack.shift()]
-	}
-
-// Shifts cache from directional history cache. Should be
-// called on `popstate` with the previous state id and container
-// contents.
-//
-// direction - "forward" or "back" String
-// id        - State ID Number
-// value     - DOM Element to cache
-//
-// Returns nothing.
-	function cachePop(direction, id, value) {
-		var pushStack, popStack
-		cacheMapping[id] = value
-
-		if (direction === 'forward') {
-			pushStack = cacheBackStack
-			popStack  = cacheForwardStack
-		} else {
-			pushStack = cacheForwardStack
-			popStack  = cacheBackStack
-		}
-
-		pushStack.push(id)
-		if (id = popStack.pop())
-			delete cacheMapping[id]
-	}
-
-// Public: Find version identifier for the initial page load.
-//
-// Returns String version or undefined.
-	function findVersion() {
-		return $('meta').filter(function() {
-			var name = $(this).attr('http-equiv')
-			return name && name.toUpperCase() === 'X-PJAX-VERSION'
-		}).attr('content')
-	}
-
-// Install pjax functions on $.pjax to enable pushState behavior.
-//
-// Does nothing if already enabled.
-//
-// Examples
-//
-//     $.pjax.enable()
-//
-// Returns nothing.
-	function enable() {
-		$.fn.pjax = fnPjax
-		$.pjax = pjax
-		$.pjax.enable = $.noop
-		$.pjax.disable = disable
-		$.pjax.click = handleClick
-		$.pjax.submit = handleSubmit
-		$.pjax.reload = pjaxReload
-		$.pjax.defaults = {
-			timeout: 650,
-			push: true,
-			replace: false,
-			type: 'GET',
-			dataType: 'html',
-			scrollTo: 0,
-			maxCacheLength: 20,
-			version: findVersion
-		}
-		$(window).on('popstate.pjax', onPjaxPopstate)
-	}
-
-// Disable pushState behavior.
-//
-// This is the case when a browser doesn't support pushState. It is
-// sometimes useful to disable pushState for debugging on a modern
-// browser.
-//
-// Examples
-//
-//     $.pjax.disable()
-//
-// Returns nothing.
-	function disable() {
-		$.fn.pjax = function() { return this }
-		$.pjax = fallbackPjax
-		$.pjax.enable = enable
-		$.pjax.disable = $.noop
-		$.pjax.click = $.noop
-		$.pjax.submit = $.noop
-		$.pjax.reload = function() { window.location.reload() }
-
-		$(window).off('popstate.pjax', onPjaxPopstate)
-	}
-
-
-// Add the state property to jQuery's event object so we can use it in
-// $(window).bind('popstate')
-	if ( $.inArray('state', $.event.props) < 0 )
-		$.event.props.push('state')
-
-// Is pjax supported by this browser?
-	$.support.pjax =
-		window.history && window.history.pushState && window.history.replaceState &&
-			// pushState isn't reliable on iOS until 5.
-			!navigator.userAgent.match(/((iPod|iPhone|iPad).+\bOS\s+[1-4]|WebApps\/.+CFNetwork)/)
-
-	$.support.pjax ? enable() : disable()
-
-})(jQuery);
diff --git a/framework/assets/yii.gridView.js b/framework/assets/yii.gridView.js
index 496e3cd..ca2b99a 100644
--- a/framework/assets/yii.gridView.js
+++ b/framework/assets/yii.gridView.js
@@ -26,17 +26,18 @@
 		filterSelector: undefined
 	};
 
+	var gridData = {};
+
 	var methods = {
 		init: function (options) {
 			return this.each(function () {
 				var $e = $(this);
 				var settings = $.extend({}, defaults, options || {});
-				$e.data('yiiGridView', {
-					settings: settings
-				});
+				gridData[$e.prop('id')] = {settings: settings};
 
 				var enterPressed = false;
-				$(settings.filterSelector).on('change.yiiGridView keydown.yiiGridView', function (event) {
+				$(document).off('change.yiiGridView keydown.yiiGridView', settings.filterSelector)
+					.on('change.yiiGridView keydown.yiiGridView', settings.filterSelector, function (event) {
 					if (event.type === 'keydown') {
 						if (event.keyCode !== 13) {
 							return; // only react to enter key
@@ -60,7 +61,7 @@
 
 		applyFilter: function () {
 			var $grid = $(this);
-			var settings = $grid.data('yiiGridView').settings;
+			var settings = gridData[$grid.prop('id')].settings;
 			var data = {};
 			$.each($(settings.filterSelector).serializeArray(), function () {
 				data[this.name] = this.value;
@@ -85,15 +86,16 @@
 
 		setSelectionColumn: function (options) {
 			var $grid = $(this);
-			var data = $grid.data('yiiGridView');
-			data.selectionColumn = options.name;
+			var id = $(this).prop('id');
+			gridData[id].selectionColumn = options.name;
 			if (!options.multiple) {
 				return;
 			}
-			$grid.on('click.yiiGridView', "input[name='" + options.checkAll + "']", function () {
+			var inputs = "#" + id + " input[name='" + options.checkAll + "']";
+			$(document).off('click.yiiGridView', inputs).on('click.yiiGridView', inputs, function () {
 				$grid.find("input[name='" + options.name + "']:enabled").prop('checked', this.checked);
 			});
-			$grid.on('click.yiiGridView', "input[name='" + options.name + "']:enabled", function () {
+			$(document).off('click.yiiGridView', inputs + ":enabled").on('click.yiiGridView', inputs + ":enabled", function () {
 				var all = $grid.find("input[name='" + options.name + "']").length == $grid.find("input[name='" + options.name + "']:checked").length;
 				$grid.find("input[name='" + options.checkAll + "']").prop('checked', all);
 			});
@@ -101,7 +103,7 @@
 
 		getSelectedRows: function () {
 			var $grid = $(this);
-			var data = $grid.data('yiiGridView');
+			var data = gridData[$grid.prop('id')];
 			var keys = [];
 			if (data.selectionColumn) {
 				$grid.find("input[name='" + data.selectionColumn + "']:checked").each(function () {
@@ -118,8 +120,9 @@
 			});
 		},
 
-		data: function() {
-			return this.data('yiiGridView');
+		data: function () {
+			var id = $(this).prop('id');
+			return gridData[id];
 		}
 	};
 })(window.jQuery);
diff --git a/framework/base/Action.php b/framework/base/Action.php
index ab80832..fd9c361 100644
--- a/framework/base/Action.php
+++ b/framework/base/Action.php
@@ -84,7 +84,13 @@ class Action extends Component
 		if (Yii::$app->requestedParams === null) {
 			Yii::$app->requestedParams = $args;
 		}
-		return call_user_func_array([$this, 'run'], $args);
+		if ($this->beforeRun()) {
+			$result = call_user_func_array([$this, 'run'], $args);
+			$this->afterRun();
+			return $result;
+		} else {
+			return null;
+		}
 	}
 
 	/**
diff --git a/framework/base/ActionFilter.php b/framework/base/ActionFilter.php
index 3b8f8eb..6fb114c 100644
--- a/framework/base/ActionFilter.php
+++ b/framework/base/ActionFilter.php
@@ -65,7 +65,7 @@ class ActionFilter extends Behavior
 	public function afterFilter($event)
 	{
 		if ($this->isActive($event->action)) {
-			$this->afterAction($event->action, $event->result);
+			$event->result = $this->afterAction($event->action, $event->result);
 		}
 	}
 
@@ -85,9 +85,11 @@ class ActionFilter extends Behavior
 	 * You may override this method to do some postprocessing for the action.
 	 * @param Action $action the action just executed.
 	 * @param mixed $result the action execution result
+	 * @return mixed the processed action result.
 	 */
-	public function afterAction($action, &$result)
+	public function afterAction($action, $result)
 	{
+		return $result;
 	}
 
 	/**
diff --git a/framework/base/ArrayableTrait.php b/framework/base/ArrayableTrait.php
new file mode 100644
index 0000000..f5bc9d6
--- /dev/null
+++ b/framework/base/ArrayableTrait.php
@@ -0,0 +1,163 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\base;
+
+use Yii;
+use yii\helpers\ArrayHelper;
+use yii\web\Link;
+use yii\web\Linkable;
+
+/**
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+trait ArrayableTrait
+{
+	/**
+	 * Returns the list of fields that should be returned by default by [[toArray()]] when no specific fields are specified.
+	 *
+	 * A field is a named element in the returned array by [[toArray()]].
+	 *
+	 * This method should return an array of field names or field definitions.
+	 * If the former, the field name will be treated as an object property name whose value will be used
+	 * as the field value. If the latter, the array key should be the field name while the array value should be
+	 * the corresponding field definition which can be either an object property name or a PHP callable
+	 * returning the corresponding field value. The signature of the callable should be:
+	 *
+	 * ```php
+	 * function ($field, $model) {
+	 *     // return field value
+	 * }
+	 * ```
+	 *
+	 * For example, the following code declares four fields:
+	 *
+	 * - `email`: the field name is the same as the property name `email`;
+	 * - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their
+	 *   values are obtained from the `first_name` and `last_name` properties;
+	 * - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name`
+	 *   and `last_name`.
+	 *
+	 * ```php
+	 * return [
+	 *     'email',
+	 *     'firstName' => 'first_name',
+	 *     'lastName' => 'last_name',
+	 *     'fullName' => function () {
+	 *         return $this->first_name . ' ' . $this->last_name;
+	 *     },
+	 * ];
+	 * ```
+	 *
+	 * In this method, you may also want to return different lists of fields based on some context
+	 * information. For example, depending on the privilege of the current application user,
+	 * you may return different sets of visible fields or filter out some fields.
+	 *
+	 * The default implementation of this method returns the public object member variables.
+	 *
+	 * @return array the list of field names or field definitions.
+	 * @see toArray()
+	 */
+	public function fields()
+	{
+		$fields = array_keys(Yii::getObjectVars($this));
+		return array_combine($fields, $fields);
+	}
+
+	/**
+	 * Returns the list of fields that can be expanded further and returned by [[toArray()]].
+	 *
+	 * This method is similar to [[fields()]] except that the list of fields returned
+	 * by this method are not returned by default by [[toArray()]]. Only when field names
+	 * to be expanded are explicitly specified when calling [[toArray()]], will their values
+	 * be exported.
+	 *
+	 * The default implementation returns an empty array.
+	 *
+	 * You may override this method to return a list of expandable fields based on some context information
+	 * (e.g. the current application user).
+	 *
+	 * @return array the list of expandable field names or field definitions. Please refer
+	 * to [[fields()]] on the format of the return value.
+	 * @see toArray()
+	 * @see fields()
+	 */
+	public function extraFields()
+	{
+		return [];
+	}
+
+	/**
+	 * Converts the model into an array.
+	 *
+	 * This method will first identify which fields to be included in the resulting array by calling [[resolveFields()]].
+	 * It will then turn the model into an array with these fields. If `$recursive` is true,
+	 * any embedded objects will also be converted into arrays.
+	 *
+	 * If the model implements the [[Linkable]] interface, the resulting array will also have a `_link` element
+	 * which refers to a list of links as specified by the interface.
+	 *
+	 * @param array $fields the fields being requested. If empty, all fields as specified by [[fields()]] will be returned.
+	 * @param array $expand the additional fields being requested for exporting. Only fields declared in [[extraFields()]]
+	 * will be considered.
+	 * @param boolean $recursive whether to recursively return array representation of embedded objects.
+	 * @return array the array representation of the object
+	 */
+	public function toArray(array $fields = [], array $expand = [], $recursive = true)
+	{
+		$data = [];
+		foreach ($this->resolveFields($fields, $expand) as $field => $definition) {
+			$data[$field] = is_string($definition) ? $this->$definition : call_user_func($definition, $field, $this);
+		}
+
+		if ($this instanceof Linkable) {
+			$data['_links'] = Link::serialize($this->getLinks());
+		}
+
+		return $recursive ? ArrayHelper::toArray($data) : $data;
+	}
+
+	/**
+	 * Determines which fields can be returned by [[toArray()]].
+	 * This method will check the requested fields against those declared in [[fields()]] and [[extraFields()]]
+	 * to determine which fields can be returned.
+	 * @param array $fields the fields being requested for exporting
+	 * @param array $expand the additional fields being requested for exporting
+	 * @return array the list of fields to be exported. The array keys are the field names, and the array values
+	 * are the corresponding object property names or PHP callables returning the field values.
+	 */
+	protected function resolveFields(array $fields, array $expand)
+	{
+		$result = [];
+
+		foreach ($this->fields() as $field => $definition) {
+			if (is_integer($field)) {
+				$field = $definition;
+			}
+			if (empty($fields) || in_array($field, $fields, true)) {
+				$result[$field] = $definition;
+			}
+		}
+
+		if (empty($expand)) {
+			return $result;
+		}
+
+		foreach ($this->extraFields() as $field => $definition) {
+			if (is_integer($field)) {
+				$field = $definition;
+			}
+			if (in_array($field, $expand, true)) {
+				$result[$field] = $definition;
+			}
+		}
+
+		return $result;
+	}
+}
diff --git a/framework/base/Controller.php b/framework/base/Controller.php
index 333c99a..27d2345 100644
--- a/framework/base/Controller.php
+++ b/framework/base/Controller.php
@@ -126,11 +126,12 @@ class Controller extends Component implements ViewContextInterface
 			Yii::$app->trigger(Application::EVENT_BEFORE_ACTION, $event);
 			if ($event->isValid && $this->module->beforeAction($action) && $this->beforeAction($action)) {
 				$result = $action->runWithParams($params);
-				$this->afterAction($action, $result);
-				$this->module->afterAction($action, $result);
+				$result = $this->afterAction($action, $result);
+				$result = $this->module->afterAction($action, $result);
 				$event = new ActionEvent($action);
-				$event->result = &$result;
+				$event->result = $result;
 				Yii::$app->trigger(Application::EVENT_AFTER_ACTION, $event);
+				$result = $event->result;
 			}
 			$this->action = $oldAction;
 			return $result;
@@ -222,14 +223,17 @@ class Controller extends Component implements ViewContextInterface
 	 * This method is invoked right after an action is executed.
 	 * You may override this method to do some postprocessing for the action.
 	 * If you override this method, please make sure you call the parent implementation first.
+	 * Also make sure you return the action result, whether it is processed or not.
 	 * @param Action $action the action just executed.
 	 * @param mixed $result the action return result.
+	 * @return mixed the processed action result.
 	 */
-	public function afterAction($action, &$result)
+	public function afterAction($action, $result)
 	{
 		$event = new ActionEvent($action);
-		$event->result = &$result;
+		$event->result = $result;
 		$this->trigger(self::EVENT_AFTER_ACTION, $event);
+		return $event->result;
 	}
 
 	/**
diff --git a/framework/base/ErrorHandler.php b/framework/base/ErrorHandler.php
index bf1faad..606a795 100644
--- a/framework/base/ErrorHandler.php
+++ b/framework/base/ErrorHandler.php
@@ -93,10 +93,9 @@ class ErrorHandler extends Component
 			return;
 		}
 
-		$useErrorView = !YII_DEBUG || $exception instanceof UserException;
-
 		$response = Yii::$app->getResponse();
-		$response->getHeaders()->removeAll();
+
+		$useErrorView = $response->format === \yii\web\Response::FORMAT_HTML && (!YII_DEBUG || $exception instanceof UserException);
 
 		if ($useErrorView && $this->errorAction !== null) {
 			$result = Yii::$app->runAction($this->errorAction);
@@ -121,7 +120,7 @@ class ErrorHandler extends Component
 				]);
 			}
 		} elseif ($exception instanceof Arrayable) {
-			$response->data = $exception;
+			$response->data = $exception->toArray();
 		} else {
 			$response->data = [
 				'type' => get_class($exception),
diff --git a/framework/base/Formatter.php b/framework/base/Formatter.php
index 7214cb5..80c08e1 100644
--- a/framework/base/Formatter.php
+++ b/framework/base/Formatter.php
@@ -66,6 +66,16 @@ class Formatter extends Component
 	 * If not set, "," will be used.
 	 */
 	public $thousandSeparator;
+	/**
+	 * @var array the format used to format size (bytes). Three elements may be specified: "base", "decimals" and "decimalSeparator".
+	 * They correspond to the base at which a kilobyte is calculated (1000 or 1024 bytes per kilobyte, defaults to 1024),
+	 * the number of digits after the decimal point (defaults to 2) and the character displayed as the decimal point.
+	 */
+	public $sizeFormat = [
+		'base' => 1024,
+		'decimals' => 2,
+		'decimalSeparator' => null,
+	];
 
 	/**
 	 * Initializes the component.
@@ -111,7 +121,7 @@ class Formatter extends Component
 			$params = [$value];
 		}
 		$method = 'as' . $format;
-		if (method_exists($this, $method)) {
+		if ($this->hasMethod($method)) {
 			return call_user_func_array([$this, $method], $params);
 		} else {
 			throw new InvalidParamException("Unknown type: $format");
@@ -404,4 +414,45 @@ class Formatter extends Component
 		$ts = isset($this->thousandSeparator) ? $this->thousandSeparator: ',';
 		return number_format($value, $decimals, $ds, $ts);
 	}
+
+	/**
+	 * Formats the value in bytes as a size in human readable form.
+	 * @param integer $value value in bytes to be formatted
+	 * @param boolean $verbose if full names should be used (e.g. bytes, kilobytes, ...).
+	 * Defaults to false meaning that short names will be used (e.g. B, KB, ...).
+	 * @return string the formatted result
+	 * @see sizeFormat
+	 */
+	public function asSize($value, $verbose = false)
+	{
+		$position = 0;
+
+		do {
+			if ($value < $this->sizeFormat['base']) {
+				break;
+			}
+
+			$value = $value / $this->sizeFormat['base'];
+			$position++;
+		} while ($position < 6);
+
+		$value = round($value, $this->sizeFormat['decimals']);
+		$formattedValue = isset($this->sizeFormat['decimalSeparator']) ? str_replace('.', $this->sizeFormat['decimalSeparator'], $value) : $value;
+		$params = ['n' => $formattedValue];
+		
+		switch($position) {
+			case 0:
+				return $verbose ? Yii::t('yii', '{n, plural, =1{# byte} other{# bytes}}', $params) : Yii::t('yii', '{n} B', $params);
+			case 1:
+				return $verbose ? Yii::t('yii', '{n, plural, =1{# kilobyte} other{# kilobytes}}', $params) : Yii::t('yii', '{n} KB', $params);
+			case 2:
+				return $verbose ? Yii::t('yii', '{n, plural, =1{# megabyte} other{# megabytes}}', $params) : Yii::t('yii', '{n} MB', $params);
+			case 3:
+				return $verbose ? Yii::t('yii', '{n, plural, =1{# gigabyte} other{# gigabytes}}', $params) : Yii::t('yii', '{n} GB', $params);
+			case 4:
+				return $verbose ? Yii::t('yii', '{n, plural, =1{# terabyte} other{# terabytes}}', $params) : Yii::t('yii', '{n} TB', $params);
+			default:
+				return $verbose ? Yii::t('yii', '{n, plural, =1{# petabyte} other{# petabytes}}', $params) : Yii::t('yii', '{n} PB', $params);
+		}
+	}
 }
diff --git a/framework/base/Model.php b/framework/base/Model.php
index 8ec6e40..8159bb9 100644
--- a/framework/base/Model.php
+++ b/framework/base/Model.php
@@ -13,9 +13,12 @@ use ArrayObject;
 use ArrayIterator;
 use ReflectionClass;
 use IteratorAggregate;
+use yii\helpers\ArrayHelper;
 use yii\helpers\Inflector;
 use yii\validators\RequiredValidator;
 use yii\validators\Validator;
+use yii\web\Link;
+use yii\web\Linkable;
 
 /**
  * Model is the base class for data models.
@@ -54,11 +57,12 @@ use yii\validators\Validator;
  */
 class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayable
 {
+	use ArrayableTrait;
+
 	/**
 	 * The name of the default scenario.
 	 */
 	const SCENARIO_DEFAULT = 'default';
-
 	/**
 	 * @event ModelEvent an event raised at the beginning of [[validate()]]. You may set
 	 * [[ModelEvent::isValid]] to be false to stop the validation.
@@ -516,7 +520,8 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
 
 	/**
 	 * Returns the first error of every attribute in the model.
-	 * @return array the first errors. An empty array will be returned if there is no error.
+	 * @return array the first errors. The array keys are the attribute names, and the array
+	 * values are the corresponding error messages. An empty array will be returned if there is no error.
 	 * @see getErrors()
 	 * @see getFirstError()
 	 */
@@ -526,13 +531,13 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
 			return [];
 		} else {
 			$errors = [];
-			foreach ($this->_errors as $attributeErrors) {
-				if (isset($attributeErrors[0])) {
-					$errors[] = $attributeErrors[0];
+			foreach ($this->_errors as $name => $es) {
+				if (!empty($es)) {
+					$errors[$name] = reset($es);
 				}
 			}
+			return $errors;
 		}
-		return $errors;
 	}
 
 	/**
@@ -789,13 +794,92 @@ class Model extends Component implements IteratorAggregate, ArrayAccess, Arrayab
 	}
 
 	/**
-	 * Converts the object into an array.
-	 * The default implementation will return [[attributes]].
-	 * @return array the array representation of the object
+	 * Returns the list of fields that should be returned by default by [[toArray()]] when no specific fields are specified.
+	 *
+	 * A field is a named element in the returned array by [[toArray()]].
+	 *
+	 * This method should return an array of field names or field definitions.
+	 * If the former, the field name will be treated as an object property name whose value will be used
+	 * as the field value. If the latter, the array key should be the field name while the array value should be
+	 * the corresponding field definition which can be either an object property name or a PHP callable
+	 * returning the corresponding field value. The signature of the callable should be:
+	 *
+	 * ```php
+	 * function ($field, $model) {
+	 *     // return field value
+	 * }
+	 * ```
+	 *
+	 * For example, the following code declares four fields:
+	 *
+	 * - `email`: the field name is the same as the property name `email`;
+	 * - `firstName` and `lastName`: the field names are `firstName` and `lastName`, and their
+	 *   values are obtained from the `first_name` and `last_name` properties;
+	 * - `fullName`: the field name is `fullName`. Its value is obtained by concatenating `first_name`
+	 *   and `last_name`.
+	 *
+	 * ```php
+	 * return [
+	 *     'email',
+	 *     'firstName' => 'first_name',
+	 *     'lastName' => 'last_name',
+	 *     'fullName' => function () {
+	 *         return $this->first_name . ' ' . $this->last_name;
+	 *     },
+	 * ];
+	 * ```
+	 *
+	 * In this method, you may also want to return different lists of fields based on some context
+	 * information. For example, depending on [[scenario]] or the privilege of the current application user,
+	 * you may return different sets of visible fields or filter out some fields.
+	 *
+	 * The default implementation of this method returns [[attributes()]] indexed by the same attribute names.
+	 *
+	 * @return array the list of field names or field definitions.
+	 * @see toArray()
+	 */
+	public function fields()
+	{
+		$fields = $this->attributes();
+		return array_combine($fields, $fields);
+	}
+
+	/**
+	 * Determines which fields can be returned by [[toArray()]].
+	 * This method will check the requested fields against those declared in [[fields()]] and [[extraFields()]]
+	 * to determine which fields can be returned.
+	 * @param array $fields the fields being requested for exporting
+	 * @param array $expand the additional fields being requested for exporting
+	 * @return array the list of fields to be exported. The array keys are the field names, and the array values
+	 * are the corresponding object property names or PHP callables returning the field values.
 	 */
-	public function toArray()
+	protected function resolveFields(array $fields, array $expand)
 	{
-		return $this->getAttributes();
+		$result = [];
+
+		foreach ($this->fields() as $field => $definition) {
+			if (is_integer($field)) {
+				$field = $definition;
+			}
+			if (empty($fields) || in_array($field, $fields, true)) {
+				$result[$field] = $definition;
+			}
+		}
+
+		if (empty($expand)) {
+			return $result;
+		}
+
+		foreach ($this->extraFields() as $field => $definition) {
+			if (is_integer($field)) {
+				$field = $definition;
+			}
+			if (in_array($field, $expand, true)) {
+				$result[$field] = $definition;
+			}
+		}
+
+		return $result;
 	}
 
 	/**
diff --git a/framework/base/Module.php b/framework/base/Module.php
index b65ad8b..cba919d 100644
--- a/framework/base/Module.php
+++ b/framework/base/Module.php
@@ -359,6 +359,9 @@ class Module extends Component
 				return $this->_modules[$id];
 			} elseif ($load) {
 				Yii::trace("Loading module: $id", __METHOD__);
+				if (is_array($this->_modules[$id]) && !isset($this->_modules[$id]['class'])) {
+					$this->_modules[$id]['class'] = 'yii\base\Module';
+				}
 				return $this->_modules[$id] = Yii::createObject($this->_modules[$id], $id, $this);
 			}
 		}
@@ -593,12 +596,21 @@ class Module extends Component
 	}
 
 	/**
-	 * Creates a controller instance based on the controller ID.
+	 * Creates a controller instance based on the given route.
+	 *
+	 * The route should be relative to this module. The method implements the following algorithm
+	 * to resolve the given route:
 	 *
-	 * The controller is created within this module. The method first attempts to
-	 * create the controller based on the [[controllerMap]] of the module. If not available,
-	 * it will look for the controller class under the [[controllerPath]] and create an
-	 * instance of it.
+	 * 1. If the route is empty, use [[defaultRoute]];
+	 * 2. If the first segment of the route is a valid module ID as declared in [[modules]],
+	 *    call the module's `createController()` with the rest part of the route;
+	 * 3. If the first segment of the route is found in [[controllerMap]], create a controller
+	 *    based on the corresponding configuration found in [[controllerMap]];
+	 * 4. The given route is in the format of `abc/def/xyz`. Try either `abc\DefController`
+	 *    or `abc\def\XyzController` class within the [[controllerNamespace|controller namespace]].
+	 *
+	 * If any of the above steps resolves into a controller, it is returned together with the rest
+	 * part of the route which will be treated as the action ID. Otherwise, false will be returned.
 	 *
 	 * @param string $route the route consisting of module, controller and action IDs.
 	 * @return array|boolean If the controller is created successfully, it will be returned together
@@ -610,6 +622,13 @@ class Module extends Component
 		if ($route === '') {
 			$route = $this->defaultRoute;
 		}
+
+		// double slashes or leading/ending slashes may cause substr problem
+		$route = trim($route, '/');
+		if (strpos($route, '//') !== false) {
+			return false;
+		}
+
 		if (strpos($route, '/') !== false) {
 			list ($id, $route) = explode('/', $route, 2);
 		} else {
@@ -617,29 +636,73 @@ class Module extends Component
 			$route = '';
 		}
 
+		// module and controller map take precedence
 		$module = $this->getModule($id);
 		if ($module !== null) {
 			return $module->createController($route);
 		}
-
 		if (isset($this->controllerMap[$id])) {
 			$controller = Yii::createObject($this->controllerMap[$id], $id, $this);
-		} elseif (preg_match('/^[a-z0-9\\-_]+$/', $id) && strpos($id, '--') === false && trim($id, '-') === $id) {
-			$className = str_replace(' ', '', ucwords(str_replace('-', ' ', $id))) . 'Controller';
-			$classFile = $this->controllerPath . DIRECTORY_SEPARATOR . $className . '.php';
-			if (!is_file($classFile)) {
-				return false;
-			}
-			$className = ltrim($this->controllerNamespace . '\\' . $className, '\\');
-			Yii::$classMap[$className] = $classFile;
-			if (is_subclass_of($className, 'yii\base\Controller')) {
-				$controller = new $className($id, $this);
-			} elseif (YII_DEBUG) {
-				throw new InvalidConfigException("Controller class must extend from \\yii\\base\\Controller.");
-			}
+			return [$controller, $route];
+		}
+
+		if (($pos = strrpos($route, '/')) !== false) {
+			$id .= '/' . substr($route, 0, $pos);
+			$route = substr($route, $pos + 1);
+		}
+
+		$controller = $this->createControllerByID($id);
+		if ($controller === null && $route !== '') {
+			$controller = $this->createControllerByID($id . '/' . $route);
+			$route = '';
 		}
 
-		return isset($controller) ? [$controller, $route] : false;
+		return $controller === null ? false : [$controller, $route];
+	}
+
+	/**
+	 * Creates a controller based on the given controller ID.
+	 *
+	 * The controller ID is relative to this module. The controller class
+	 * should be located under [[controllerPath]] and namespaced under [[controllerNamespace]].
+	 *
+	 * Note that this method does not check [[modules]] or [[controllerMap]].
+	 *
+	 * @param string $id the controller ID
+	 * @return Controller the newly created controller instance, or null if the controller ID is invalid.
+	 * @throws InvalidConfigException if the controller class and its file name do not match.
+	 * This exception is only thrown when in debug mode.
+	 */
+	public function createControllerByID($id)
+	{
+		if (!preg_match('%^[a-z0-9\\-_/]+$%', $id)) {
+			return null;
+		}
+
+		$pos = strrpos($id, '/');
+		if ($pos === false) {
+			$prefix = '';
+			$className = $id;
+		} else {
+			$prefix = substr($id, 0, $pos + 1);
+			$className = substr($id, $pos + 1);
+		}
+
+		$className = str_replace(' ', '', ucwords(str_replace('-', ' ', $className))) . 'Controller';
+		$classFile = $this->controllerPath . '/' . $prefix . $className . '.php';
+		$className = ltrim($this->controllerNamespace . '\\' . str_replace('/', '\\', $prefix)  . $className, '\\');
+		if (strpos($className, '-') !== false || !is_file($classFile)) {
+			return null;
+		}
+
+		Yii::$classMap[$className] = $classFile;
+		if (is_subclass_of($className, 'yii\base\Controller')) {
+			return new $className($id, $this);
+		} elseif (YII_DEBUG) {
+			throw new InvalidConfigException("Controller class must extend from \\yii\\base\\Controller.");
+		} else {
+			return null;
+		}
 	}
 
 	/**
@@ -658,10 +721,13 @@ class Module extends Component
 	 * This method is invoked right after an action of this module has been executed.
 	 * You may override this method to do some postprocessing for the action.
 	 * Make sure you call the parent implementation so that the relevant event is triggered.
+	 * Also make sure you return the action result, whether it is processed or not.
 	 * @param Action $action the action just executed.
 	 * @param mixed $result the action return result.
+	 * @return mixed the processed action result.
 	 */
-	public function afterAction($action, &$result)
+	public function afterAction($action, $result)
 	{
+		return $result;
 	}
 }
diff --git a/framework/base/Request.php b/framework/base/Request.php
index b76886e..eb9f805 100644
--- a/framework/base/Request.php
+++ b/framework/base/Request.php
@@ -6,6 +6,7 @@
  */
 
 namespace yii\base;
+
 use Yii;
 
 /**
diff --git a/framework/base/Theme.php b/framework/base/Theme.php
index 01a6964..63382ad 100644
--- a/framework/base/Theme.php
+++ b/framework/base/Theme.php
@@ -13,7 +13,7 @@ use yii\helpers\FileHelper;
 /**
  * Theme represents an application theme.
  *
- * When [[View]] renders a view file, it will check the [[Application::theme|active theme]]
+ * When [[View]] renders a view file, it will check the [[View::theme|active theme]]
  * to see if there is a themed version of the view file exists. If so, the themed version will be rendered instead.
  *
  * A theme is a directory consisting of view files which are meant to replace their non-themed counterparts.
diff --git a/framework/caching/FileDependency.php b/framework/caching/FileDependency.php
index 11afde3..200cdde 100644
--- a/framework/caching/FileDependency.php
+++ b/framework/caching/FileDependency.php
@@ -6,6 +6,7 @@
  */
 
 namespace yii\caching;
+
 use yii\base\InvalidConfigException;
 
 /**
diff --git a/framework/caching/GroupDependency.php b/framework/caching/GroupDependency.php
index 1cf7869..bcac858 100644
--- a/framework/caching/GroupDependency.php
+++ b/framework/caching/GroupDependency.php
@@ -6,6 +6,7 @@
  */
 
 namespace yii\caching;
+
 use yii\base\InvalidConfigException;
 
 /**
diff --git a/framework/composer.json b/framework/composer.json
index bd11fc7..5a34ab0 100644
--- a/framework/composer.json
+++ b/framework/composer.json
@@ -1,7 +1,7 @@
 {
 	"name": "yiisoft/yii2",
 	"description": "Yii PHP Framework Version 2",
-	"keywords": ["yii", "framework"],
+	"keywords": ["yii2", "framework"],
 	"homepage": "http://www.yiiframework.com/",
 	"type": "library",
 	"license": "BSD-3-Clause",
@@ -54,6 +54,7 @@
 		"lib-pcre": "*",
 		"yiisoft/yii2-composer": "*",
 		"yiisoft/jquery": "~2.0 | ~1.10",
+		"yiisoft/jquery-pjax": "*",
 		"ezyang/htmlpurifier": "4.6.*",
 		"cebe/markdown": "0.9.*"
 	},
diff --git a/framework/console/controllers/FixtureController.php b/framework/console/controllers/FixtureController.php
index 858f08f..c72efd2 100644
--- a/framework/console/controllers/FixtureController.php
+++ b/framework/console/controllers/FixtureController.php
@@ -64,7 +64,7 @@ class FixtureController extends Controller
 	public function globalOptions()
 	{
 		return array_merge(parent::globalOptions(), [
-			'namespace','globalFixtures'
+			'namespace', 'globalFixtures'
 		]);
 	}
 
@@ -74,6 +74,7 @@ class FixtureController extends Controller
 	 * whitespace between names. Note that if you are loading fixtures to storage, for example: database or nosql,
 	 * storage will not be cleared, data will be appended to already existed.
 	 * @param array $fixtures
+	 * @param array $except
 	 * @throws \yii\console\Exception
 	 */
 	public function actionLoad(array $fixtures, array $except = [])
@@ -99,7 +100,7 @@ class FixtureController extends Controller
 		}
 
 		$filtered = array_diff($foundFixtures, $except);
-		$fixtures = $this->getFixturesConfig(array_merge($this->globalFixtures ,$filtered));
+		$fixtures = $this->getFixturesConfig(array_merge($this->globalFixtures, $filtered));
 
 		if (!$fixtures) {
 			throw new Exception('No fixtures were found in namespace: "' . $this->namespace . '"' . '');
@@ -317,5 +318,4 @@ class FixtureController extends Controller
 	{
 		return Yii::getAlias('@' . str_replace('\\', '/', $this->namespace));
 	}
-
 }
diff --git a/framework/console/controllers/MessageController.php b/framework/console/controllers/MessageController.php
index 0345b69..cb34a38 100644
--- a/framework/console/controllers/MessageController.php
+++ b/framework/console/controllers/MessageController.php
@@ -134,11 +134,14 @@ class MessageController extends Controller
 				throw new Exception('The "db" option must refer to a valid database application component.');
 			}
 			$sourceMessageTable = isset($config['sourceMessageTable']) ? $config['sourceMessageTable'] : '{{%source_message}}';
+			$messageTable = isset($config['messageTable']) ? $config['messageTable'] : '{{%message}}';
 			$this->saveMessagesToDb(
 				$messages,
 				$db,
 				$sourceMessageTable,
-				$config['removeUnused']
+				$messageTable,
+				$config['removeUnused'],
+				$config['languages']
 			);
 		}
 	}
@@ -149,9 +152,11 @@ class MessageController extends Controller
 	 * @param array $messages
 	 * @param \yii\db\Connection $db
 	 * @param string $sourceMessageTable
+	 * @param string $messageTable
 	 * @param boolean $removeUnused
+	 * @param array $languages
 	 */
-	protected function saveMessagesToDb($messages, $db, $sourceMessageTable, $removeUnused)
+	protected function saveMessagesToDb($messages, $db, $sourceMessageTable, $messageTable, $removeUnused, $languages)
 	{
 		$q = new \yii\db\Query;
 		$current = [];
@@ -190,12 +195,17 @@ class MessageController extends Controller
 		echo "Inserting new messages...";
 		$savedFlag = false;
 
-		foreach ($new  as $category => $msgs) {
+		foreach ($new as $category => $msgs) {
 			foreach ($msgs as $m) {
 				$savedFlag = true;
 
 				$db->createCommand()
-					->insert($sourceMessageTable, ['category' => $category, 'message' => $m])->execute();
+				->insert($sourceMessageTable, ['category' => $category, 'message' => $m])->execute();
+				$lastId = $db->getLastInsertID();
+				foreach ($languages as $language) {
+					$db->createCommand()
+					->insert($messageTable, ['id' => $lastId, 'language' => $language])->execute();
+				}
 			}
 		}
 
@@ -207,15 +217,20 @@ class MessageController extends Controller
 		} else {
 			if ($removeUnused) {
 				$db->createCommand()
-					->delete($sourceMessageTable, ['in', 'id', $obsolete])->execute();
-			echo "deleted.\n";
+				->delete($sourceMessageTable, ['in', 'id', $obsolete])->execute();
+				echo "deleted.\n";
 			} else {
+				$last_id = $db->getLastInsertID();
 				$db->createCommand()
-					->update(
+				->update(
 						$sourceMessageTable,
 						['message' => new \yii\db\Expression("CONCAT('@@',message,'@@')")],
 						['in', 'id', $obsolete]
 					)->execute();
+				foreach ($languages as $language) {
+					$db->createCommand()
+					->insert($messageTable, ['id' => $last_id, 'language' => $language])->execute();
+				}
 				echo "updated.\n";
 			}
 		}
@@ -268,7 +283,7 @@ class MessageController extends Controller
 	{
 		echo "Saving messages to $fileName...";
 		if (is_file($fileName)) {
-			if($format === 'po'){
+			if ($format === 'po') {
 				$translated = file_get_contents($fileName);
 				preg_match_all('/(?<=msgid ").*(?="\n(#*)msgstr)/', $translated, $keys);
 				preg_match_all('/(?<=msgstr ").*(?="\n\n)/', $translated, $values);
@@ -285,7 +300,7 @@ class MessageController extends Controller
 			$merged = [];
 			$untranslated = [];
 			foreach ($messages as $message) {
-				if($format === 'po'){
+				if ($format === 'po') {
 					$message = preg_replace('/\"/', '\"', $message);
 				}
 				if (array_key_exists($message, $translated) && strlen($translated[$message]) > 0) {
@@ -317,9 +332,9 @@ class MessageController extends Controller
 			if (false === $overwrite) {
 				$fileName .= '.merged';
 			}
-			if ($format === 'po'){
+			if ($format === 'po') {
 				$output = '';
-				foreach ($merged as $k => $v){
+				foreach ($merged as $k => $v) {
 					$k = preg_replace('/(\")|(\\\")/', "\\\"", $k);
 					$v = preg_replace('/(\")|(\\\")/', "\\\"", $v);
 					if (substr($v, 0, 2) === '@@' && substr($v, -2) === '@@') {
@@ -338,7 +353,7 @@ class MessageController extends Controller
 			if ($format === 'po') {
 				$merged = '';
 				sort($messages);
-				foreach($messages as $message) {
+				foreach ($messages as $message) {
 					$message = preg_replace('/(\")|(\\\")/', '\\\"', $message);
 					$merged .= "msgid \"$message\"\n";
 					$merged .= "msgstr \"\"\n";
diff --git a/framework/data/BaseDataProvider.php b/framework/data/BaseDataProvider.php
index e001e86..31acc2d 100644
--- a/framework/data/BaseDataProvider.php
+++ b/framework/data/BaseDataProvider.php
@@ -188,6 +188,7 @@ abstract class BaseDataProvider extends Component implements DataProviderInterfa
 			$config = ['class' => Pagination::className()];
 			if ($this->id !== null) {
 				$config['pageParam'] = $this->id . '-page';
+				$config['pageSizeParam'] = $this->id . '-per-page';
 			}
 			$this->_pagination = Yii::createObject(array_merge($config, $value));
 		} elseif ($value instanceof Pagination || $value === false) {
diff --git a/framework/data/Pagination.php b/framework/data/Pagination.php
index 9e9359c..29bedb7 100644
--- a/framework/data/Pagination.php
+++ b/framework/data/Pagination.php
@@ -8,7 +8,10 @@
 namespace yii\data;
 
 use Yii;
+use yii\base\Arrayable;
 use yii\base\Object;
+use yii\web\Link;
+use yii\web\Linkable;
 use yii\web\Request;
 
 /**
@@ -65,14 +68,24 @@ use yii\web\Request;
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
  */
-class Pagination extends Object
+class Pagination extends Object implements Linkable, Arrayable
 {
+	const LINK_NEXT = 'next';
+	const LINK_PREV = 'prev';
+	const LINK_FIRST = 'first';
+	const LINK_LAST = 'last';
+
 	/**
-	 * @var string name of the parameter storing the current page index. Defaults to 'page'.
+	 * @var string name of the parameter storing the current page index.
 	 * @see params
 	 */
 	public $pageParam = 'page';
 	/**
+	 * @var string name of the parameter storing the page size.
+	 * @see params
+	 */
+	public $pageSizeParam = 'per-page';
+	/**
 	 * @var boolean whether to always have the page parameter in the URL created by [[createUrl()]].
 	 * If false and [[page]] is 0, the page parameter will not be put in the URL.
 	 */
@@ -88,8 +101,8 @@ class Pagination extends Object
 	 *
 	 * In order to add hash to all links use `array_merge($_GET, ['#' => 'my-hash'])`.
 	 *
-	 * The array element indexed by [[pageParam]] is considered to be the current page number.
-	 * If the element does not exist, the current page number is considered 0.
+	 * The array element indexed by [[pageParam]] is considered to be the current page number (defaults to 0);
+	 * while the element indexed by [[pageSizeParam]] is treated as the page size (defaults to [[defaultPageSize]]).
 	 */
 	public $params;
 	/**
@@ -106,14 +119,24 @@ class Pagination extends Object
 	 */
 	public $validatePage = true;
 	/**
-	 * @var integer number of items on each page. Defaults to 20.
-	 * If it is less than 1, it means the page size is infinite, and thus a single page contains all items.
-	 */
-	public $pageSize = 20;
-	/**
 	 * @var integer total number of items.
 	 */
 	public $totalCount = 0;
+	/**
+	 * @var integer the default page size. This property will be returned by [[pageSize]] when page size
+	 * cannot be determined by [[pageSizeParam]] from [[params]].
+	 */
+	public $defaultPageSize = 20;
+	/**
+	 * @var array|boolean the page size limits. The first array element stands for the minimal page size, and the second
+	 * the maximal page size. If this is false, it means [[pageSize]] should always return the value of [[defaultPageSize]].
+	 */
+	public $pageSizeLimit = [1, 50];
+	/**
+	 * @var integer number of items on each page.
+	 * If it is less than 1, it means the page size is infinite, and thus a single page contains all items.
+	 */
+	private $_pageSize;
 
 
 	/**
@@ -121,11 +144,12 @@ class Pagination extends Object
 	 */
 	public function getPageCount()
 	{
-		if ($this->pageSize < 1) {
+		$pageSize = $this->getPageSize();
+		if ($pageSize < 1) {
 			return $this->totalCount > 0 ? 1 : 0;
 		} else {
 			$totalCount = $this->totalCount < 0 ? 0 : (int)$this->totalCount;
-			return (int)(($totalCount + $this->pageSize - 1) / $this->pageSize);
+			return (int)(($totalCount + $pageSize - 1) / $pageSize);
 		}
 	}
 
@@ -139,24 +163,8 @@ class Pagination extends Object
 	public function getPage($recalculate = false)
 	{
 		if ($this->_page === null || $recalculate) {
-			if (($params = $this->params) === null) {
-				$request = Yii::$app->getRequest();
-				$params = $request instanceof Request ? $request->getQueryParams() : [];
-			}
-			if (isset($params[$this->pageParam]) && is_scalar($params[$this->pageParam])) {
-				$this->_page = (int)$params[$this->pageParam] - 1;
-				if ($this->validatePage) {
-					$pageCount = $this->getPageCount();
-					if ($this->_page >= $pageCount) {
-						$this->_page = $pageCount - 1;
-					}
-				}
-				if ($this->_page < 0) {
-					$this->_page = 0;
-				}
-			} else {
-				$this->_page = 0;
-			}
+			$page = (int)$this->getQueryParam($this->pageParam, 1) - 1;
+			$this->setPage($page, true);
 		}
 		return $this->_page;
 	}
@@ -164,10 +172,68 @@ class Pagination extends Object
 	/**
 	 * Sets the current page number.
 	 * @param integer $value the zero-based index of the current page.
+	 * @param boolean $validatePage whether to validate the page number. Note that in order
+	 * to validate the page number, both [[validatePage]] and this parameter must be true.
+	 */
+	public function setPage($value, $validatePage = false)
+	{
+		if ($value === null) {
+			$this->_page = null;
+		} else {
+			$value = (int)$value;
+			if ($validatePage && $this->validatePage) {
+				$pageCount = $this->getPageCount();
+				if ($value >= $pageCount) {
+					$value = $pageCount - 1;
+				}
+			}
+			if ($value < 0) {
+				$value = 0;
+			}
+			$this->_page = $value;
+		}
+	}
+
+	/**
+	 * Returns the number of items per page.
+	 * By default, this method will try to determine the page size by [[pageSizeParam]] in [[params]].
+	 * If the page size cannot be determined this way, [[defaultPageSize]] will be returned.
+	 * @return integer the number of items per page.
+	 * @see pageSizeLimit
+	 */
+	public function getPageSize()
+	{
+		if ($this->_pageSize === null) {
+			if (empty($this->pageSizeLimit)) {
+				$pageSize = $this->defaultPageSize;
+				$this->setPageSize($pageSize);
+			} else {
+				$pageSize = (int)$this->getQueryParam($this->pageSizeParam, $this->defaultPageSize);
+				$this->setPageSize($pageSize, true);
+			}
+		}
+		return $this->_pageSize;
+	}
+
+	/**
+	 * @param integer $value the number of items per page.
+	 * @param boolean $validatePageSize whether to validate page size.
 	 */
-	public function setPage($value)
+	public function setPageSize($value, $validatePageSize = false)
 	{
-		$this->_page = $value;
+		if ($value === null) {
+			$this->_pageSize = null;
+		} else {
+			$value = (int)$value;
+			if ($validatePageSize && count($this->pageSizeLimit) === 2 && isset($this->pageSizeLimit[0], $this->pageSizeLimit[1])) {
+				if ($value < $this->pageSizeLimit[0]) {
+					$value = $this->pageSizeLimit[0];
+				} elseif ($value > $this->pageSizeLimit[1]) {
+					$value = $this->pageSizeLimit[1];
+				}
+			}
+			$this->_pageSize = $value;
+		}
 	}
 
 	/**
@@ -190,6 +256,12 @@ class Pagination extends Object
 		} else {
 			unset($params[$this->pageParam]);
 		}
+		$pageSize = $this->getPageSize();
+		if ($pageSize != $this->defaultPageSize) {
+			$params[$this->pageSizeParam] = $pageSize;
+		} else {
+			unset($params[$this->pageSizeParam]);
+		}
 		$params[0] = $this->route === null ? Yii::$app->controller->getRoute() : $this->route;
 		$urlManager = $this->urlManager === null ? Yii::$app->getUrlManager() : $this->urlManager;
 		if ($absolute) {
@@ -205,7 +277,8 @@ class Pagination extends Object
 	 */
 	public function getOffset()
 	{
-		return $this->pageSize < 1 ? 0 : $this->getPage() * $this->pageSize;
+		$pageSize = $this->getPageSize();
+		return $pageSize < 1 ? 0 : $this->getPage() * $pageSize;
 	}
 
 	/**
@@ -215,6 +288,60 @@ class Pagination extends Object
 	 */
 	public function getLimit()
 	{
-		return $this->pageSize < 1 ? -1 : $this->pageSize;
+		$pageSize = $this->getPageSize();
+		return $pageSize < 1 ? -1 : $pageSize;
+	}
+
+	/**
+	 * Returns a whole set of links for navigating to the first, last, next and previous pages.
+	 * @param boolean $absolute whether the generated URLs should be absolute.
+	 * @return array the links for navigational purpose. The array keys specify the purpose of the links (e.g. [[LINK_FIRST]]),
+	 * and the array values are the corresponding URLs.
+	 */
+	public function getLinks($absolute = false)
+	{
+		$currentPage = $this->getPage();
+		$pageCount = $this->getPageCount();
+		$links = [
+			Link::REL_SELF => $this->createUrl($currentPage, $absolute),
+		];
+		if ($currentPage > 0) {
+			$links[self::LINK_FIRST] = $this->createUrl(0, $absolute);
+			$links[self::LINK_PREV] = $this->createUrl($currentPage - 1, $absolute);
+		}
+		if ($currentPage < $pageCount - 1) {
+			$links[self::LINK_NEXT] = $this->createUrl($currentPage + 1, $absolute);
+			$links[self::LINK_LAST] = $this->createUrl($pageCount - 1, $absolute);
+		}
+		return $links;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function toArray()
+	{
+		return [
+			'totalCount' => $this->totalCount,
+			'pageCount' => $this->getPageCount(),
+			'currentPage' => $this->getPage(),
+			'perPage' => $this->getPageSize(),
+		];
+	}
+
+	/**
+	 * Returns the value of the specified query parameter.
+	 * This method returns the named parameter value from [[params]]. Null is returned if the value does not exist.
+	 * @param string $name the parameter name
+	 * @param string $defaultValue the value to be returned when the specified parameter does not exist in [[params]].
+	 * @return string the parameter value
+	 */
+	protected function getQueryParam($name, $defaultValue = null)
+	{
+		if (($params = $this->params) === null) {
+			$request = Yii::$app->getRequest();
+			$params = $request instanceof Request ? $request->getQueryParams() : [];
+		}
+		return isset($params[$name]) && is_scalar($params[$name]) ? $params[$name] : $defaultValue;
 	}
 }
diff --git a/framework/db/ActiveQuery.php b/framework/db/ActiveQuery.php
index 7fcb5e0..23e7f1f 100644
--- a/framework/db/ActiveQuery.php
+++ b/framework/db/ActiveQuery.php
@@ -86,6 +86,10 @@ class ActiveQuery extends Query implements ActiveQueryInterface
 	 * @see onCondition()
 	 */
 	public $on;
+	/**
+	 * @var array a list of relations that this query should be joined with
+	 */
+	public $joinWith;
 
 
 	/**
@@ -116,7 +120,7 @@ class ActiveQuery extends Query implements ActiveQueryInterface
 			$this->findWith($this->with, $models);
 		}
 		if (!$this->asArray) {
-			foreach($models as $model) {
+			foreach ($models as $model) {
 				$model->afterFind();
 			}
 		}
@@ -232,6 +236,10 @@ class ActiveQuery extends Query implements ActiveQueryInterface
 	 */
 	protected function createCommandInternal($db)
 	{
+		if (!empty($this->joinWith)) {
+			$this->buildJoinWith();
+		}
+
 		/** @var ActiveRecord $modelClass */
 		$modelClass = $this->modelClass;
 		if ($db === null) {
@@ -330,24 +338,32 @@ class ActiveQuery extends Query implements ActiveQueryInterface
 	 */
 	public function joinWith($with, $eagerLoading = true, $joinType = 'LEFT JOIN')
 	{
-		$with = (array)$with;
-		$this->joinWithRelations(new $this->modelClass, $with, $joinType);
+		$this->joinWith[] = [(array)$with, $eagerLoading, $joinType];
+		return $this;
+	}
 
-		if (is_array($eagerLoading)) {
-			foreach ($with as $name => $callback) {
-				if (is_integer($name)) {
-					if (!in_array($callback, $eagerLoading, true)) {
+	private function buildJoinWith()
+	{
+		foreach ($this->joinWith as $config) {
+			list ($with, $eagerLoading, $joinType) = $config;
+			$this->joinWithRelations(new $this->modelClass, $with, $joinType);
+
+			if (is_array($eagerLoading)) {
+				foreach ($with as $name => $callback) {
+					if (is_integer($name)) {
+						if (!in_array($callback, $eagerLoading, true)) {
+							unset($with[$name]);
+						}
+					} elseif (!in_array($name, $eagerLoading, true)) {
 						unset($with[$name]);
 					}
-				} elseif (!in_array($name, $eagerLoading, true)) {
-					unset($with[$name]);
 				}
+			} elseif (!$eagerLoading) {
+				$with = [];
 			}
-		} elseif (!$eagerLoading) {
-			$with = [];
-		}
 
-		return $this->with($with);
+			$this->with($with);
+		}
 	}
 
 	/**
diff --git a/framework/db/ActiveRecord.php b/framework/db/ActiveRecord.php
index e7461c6..539508b 100644
--- a/framework/db/ActiveRecord.php
+++ b/framework/db/ActiveRecord.php
@@ -35,9 +35,6 @@ use yii\helpers\StringHelper;
  *
  * 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';
diff --git a/framework/db/ActiveRecordInterface.php b/framework/db/ActiveRecordInterface.php
index 47cdb75..737abbf 100644
--- a/framework/db/ActiveRecordInterface.php
+++ b/framework/db/ActiveRecordInterface.php
@@ -87,6 +87,13 @@ interface ActiveRecordInterface
 	public function getOldPrimaryKey($asArray = false);
 
 	/**
+	 * Returns a value indicating whether the given set of attributes represents the primary key for this model
+	 * @param array $keys the set of attributes to check
+	 * @return boolean whether the given set of attributes represents the primary key for this model
+	 */
+	public static function isPrimaryKey($keys);
+
+	/**
 	 * Creates an [[ActiveQueryInterface|ActiveQuery]] instance for query purpose.
 	 *
 	 * This method is usually ment to be used like this:
diff --git a/framework/db/ActiveRelationTrait.php b/framework/db/ActiveRelationTrait.php
index 91df098..32f6953 100644
--- a/framework/db/ActiveRelationTrait.php
+++ b/framework/db/ActiveRelationTrait.php
@@ -136,13 +136,13 @@ trait ActiveRelationTrait
 	 * 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
+	 * @param ActiveRecordInterface|BaseActiveRecord $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)) {
+		if ($model->hasMethod('get' . $name)) {
 			$method = new \ReflectionMethod($model, 'get' . $name);
 			$realName = lcfirst(substr($method->getName(), 3));
 			if ($realName !== $name) {
@@ -288,7 +288,7 @@ trait ActiveRelationTrait
 				foreach ($primaryModels as $i => $primaryModel) {
 					if ($primaryModels[$i][$primaryName] instanceof ActiveRecordInterface) {
 						$primaryModels[$i][$primaryName]->populateRelation($name, $primaryModel);
-					} elseif  (!empty($primaryModels[$i][$primaryName])) {
+					} elseif (!empty($primaryModels[$i][$primaryName])) {
 						$primaryModels[$i][$primaryName][$name] = $primaryModel;
 					}
 				}
diff --git a/framework/db/BaseActiveRecord.php b/framework/db/BaseActiveRecord.php
index 2f4da5c..69bc1da 100644
--- a/framework/db/BaseActiveRecord.php
+++ b/framework/db/BaseActiveRecord.php
@@ -19,7 +19,7 @@ use yii\base\InvalidCallException;
 /**
  * ActiveRecord is the base class for classes representing relational data in terms of objects.
  *
- * @include @yii/db/ActiveRecord.md
+ * See [[yii\db\ActiveRecord]] for a concrete implementation.
  *
  * @property array $dirtyAttributes The changed attribute values (name-value pairs). This property is
  * read-only.
@@ -344,7 +344,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 	 */
 	public function hasOne($class, $link)
 	{
-		/** @var ActiveRecord $class */
+		/** @var ActiveRecordInterface $class */
 		return $class::createQuery([
 			'modelClass' => $class,
 			'primaryModel' => $this,
@@ -385,7 +385,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 	 */
 	public function hasMany($class, $link)
 	{
-		/** @var ActiveRecord $class */
+		/** @var ActiveRecordInterface $class */
 		return $class::createQuery([
 			'modelClass' => $class,
 			'primaryModel' => $this,
@@ -398,7 +398,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 	 * Populates the named relation with the related records.
 	 * Note that this method does not check if the relation exists or not.
 	 * @param string $name the relation name (case-sensitive)
-	 * @param ActiveRecord|array|null $records the related records to be populated into the relation.
+	 * @param ActiveRecordInterface|array|null $records the related records to be populated into the relation.
 	 */
 	public function populateRelation($name, $records)
 	{
@@ -938,7 +938,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 	 * Returns a value indicating whether the given active record is the same as the current one.
 	 * The comparison is made by comparing the table names and the primary key values of the two active records.
 	 * If one of the records [[isNewRecord|is new]] they are also considered not equal.
-	 * @param ActiveRecord $record record to compare to
+	 * @param ActiveRecordInterface $record record to compare to
 	 * @return boolean whether the two active records refer to the same row in the same database table.
 	 */
 	public function equals($record)
@@ -1106,7 +1106,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 	 * Note that this method requires that the primary key value is not null.
 	 *
 	 * @param string $name the case sensitive name of the relationship
-	 * @param ActiveRecord $model the model to be linked with the current one.
+	 * @param ActiveRecordInterface $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 [[ActiveRelationTrait::via()]] or `[[ActiveQuery::viaTable()]]`.)
@@ -1141,8 +1141,8 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 				$columns[$k] = $v;
 			}
 			if (is_array($relation->via)) {
-				/** @var $viaClass ActiveRecord */
-				/** @var $record ActiveRecord */
+				/** @var $viaClass ActiveRecordInterface */
+				/** @var $record ActiveRecordInterface */
 				$record = new $viaClass();
 				foreach ($columns as $column => $value) {
 					$record->$column = $value;
@@ -1193,7 +1193,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 	 * Otherwise, the foreign key will be set null and the model will be saved without validation.
 	 *
 	 * @param string $name the case sensitive name of the relationship.
-	 * @param ActiveRecord $model the model to be unlinked from the current one.
+	 * @param ActiveRecordInterface $model the model to be unlinked from the current one.
 	 * @param boolean $delete whether to delete the model that contains the foreign key.
 	 * If false, the model's foreign key will be set null and saved.
 	 * If true, the model containing the foreign key will be deleted.
@@ -1221,7 +1221,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 				$columns[$b] = $model->$a;
 			}
 			if (is_array($relation->via)) {
-				/** @var $viaClass ActiveRecord */
+				/** @var $viaClass ActiveRecordInterface */
 				if ($delete) {
 					$viaClass::deleteAll($columns);
 				} else {
@@ -1233,6 +1233,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 				}
 			} else {
 				/** @var $viaTable string */
+				/** @var Command $command */
 				$command = static::getDb()->createCommand();
 				if ($delete) {
 					$command->delete($viaTable, $columns)->execute();
@@ -1265,7 +1266,7 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 		if (!$relation->multiple) {
 			unset($this->_related[$name]);
 		} elseif (isset($this->_related[$name])) {
-			/** @var ActiveRecord $b */
+			/** @var ActiveRecordInterface $b */
 			foreach ($this->_related[$name] as $a => $b) {
 				if ($model->getPrimaryKey() == $b->getPrimaryKey()) {
 					unset($this->_related[$name][$a]);
@@ -1346,4 +1347,26 @@ abstract class BaseActiveRecord extends Model implements ActiveRecordInterface
 
 		return $this->generateAttributeLabel($attribute);
 	}
+
+	/**
+	 * @inheritdoc
+	 *
+	 * The default implementation returns the names of the columns whose values have been populated into this record.
+	 */
+	public function fields()
+	{
+		$fields = array_keys($this->_attributes);
+		return array_combine($fields, $fields);
+	}
+
+	/**
+	 * @inheritdoc
+	 *
+	 * The default implementation returns the names of the relations that have been populated into this record.
+	 */
+	public function extraFields()
+	{
+		$fields = array_keys($this->getRelatedRecords());
+		return array_combine($fields, $fields);
+	}
 }
diff --git a/framework/db/BatchQueryResult.php b/framework/db/BatchQueryResult.php
index 774d416..3a1ba11 100644
--- a/framework/db/BatchQueryResult.php
+++ b/framework/db/BatchQueryResult.php
@@ -108,6 +108,7 @@ class BatchQueryResult extends Object implements \Iterator
 	{
 		if ($this->_batch === null || !$this->each || $this->each && next($this->_batch) === false) {
 			$this->_batch = $this->fetchData();
+			reset($this->_batch);
 		}
 
 		if ($this->each) {
diff --git a/framework/db/Query.php b/framework/db/Query.php
index 15d43d5..582e7f5 100644
--- a/framework/db/Query.php
+++ b/framework/db/Query.php
@@ -356,7 +356,7 @@ class Query extends Component implements QueryInterface
 		$this->limit = $limit;
 		$this->offset = $offset;
 
-		if (empty($this->groupBy)) {
+		if (empty($this->groupBy) && !$this->distinct) {
 			return $command->queryScalar();
 		} else {
 			return (new Query)->select([$selectExpression])
diff --git a/framework/db/QueryBuilder.php b/framework/db/QueryBuilder.php
index e6b6e01..124848b 100644
--- a/framework/db/QueryBuilder.php
+++ b/framework/db/QueryBuilder.php
@@ -599,7 +599,7 @@ class QueryBuilder extends \yii\base\Object
 				if (strpos($column, '(') === false) {
 					$column = $this->db->quoteColumnName($column);
 				}
-				$columns[$i] = "$column AS " . $this->db->quoteColumnName($i);;
+				$columns[$i] = "$column AS " . $this->db->quoteColumnName($i);
 			} elseif (strpos($column, '(') === false) {
 				if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)([\w\-_\.]+)$/', $column, $matches)) {
 					$columns[$i] = $this->db->quoteColumnName($matches[1]) . ' AS ' . $this->db->quoteColumnName($matches[2]);
diff --git a/framework/db/mssql/Schema.php b/framework/db/mssql/Schema.php
index 1503a7e..4b3b1da 100644
--- a/framework/db/mssql/Schema.php
+++ b/framework/db/mssql/Schema.php
@@ -368,7 +368,7 @@ SQL;
 		}
 
 		$sql = <<<SQL
-SELECT [t].[table]
+SELECT [t].[table_name]
 FROM [information_schema].[tables] AS [t]
 WHERE [t].[table_schema] = :schema AND [t].[table_type] = 'BASE TABLE'
 SQL;
diff --git a/framework/db/oci/Schema.php b/framework/db/oci/Schema.php
index e29a77b..1edbf6b 100644
--- a/framework/db/oci/Schema.php
+++ b/framework/db/oci/Schema.php
@@ -242,7 +242,7 @@ EOD;
 		} elseif (strpos($dbType, 'NUMBER') !== false || strpos($dbType, 'INTEGER') !== false) {
 			if (strpos($dbType, '(') && preg_match('/\((.*)\)/', $dbType, $matches)) {
 				$values = explode(',', $matches[1]);
-				if (isset($values[1]) and (((int)$values[1]) > 0)) {
+				if (isset($values[1]) && (((int)$values[1]) > 0)) {
 					$column->type = 'double';
 				} else {
 					$column->type = 'integer';
diff --git a/framework/grid/ActionColumn.php b/framework/grid/ActionColumn.php
index ffcfe0a..7d42bec 100644
--- a/framework/grid/ActionColumn.php
+++ b/framework/grid/ActionColumn.php
@@ -88,6 +88,7 @@ class ActionColumn extends Column
 			$this->buttons['view'] = function ($url, $model) {
 				return Html::a('<span class="glyphicon glyphicon-eye-open"></span>', $url, [
 					'title' => Yii::t('yii', 'View'),
+					'data-pjax' => '0',
 				]);
 			};
 		}
@@ -95,6 +96,7 @@ class ActionColumn extends Column
 			$this->buttons['update'] = function ($url, $model) {
 				return Html::a('<span class="glyphicon glyphicon-pencil"></span>', $url, [
 					'title' => Yii::t('yii', 'Update'),
+					'data-pjax' => '0',
 				]);
 			};
 		}
@@ -104,6 +106,7 @@ class ActionColumn extends Column
 					'title' => Yii::t('yii', 'Delete'),
 					'data-confirm' => Yii::t('yii', 'Are you sure to delete this item?'),
 					'data-method' => 'post',
+					'data-pjax' => '0',
 				]);
 			};
 		}
diff --git a/framework/grid/Column.php b/framework/grid/Column.php
index f09564d..77e0dca 100644
--- a/framework/grid/Column.php
+++ b/framework/grid/Column.php
@@ -116,22 +116,35 @@ class Column extends Object
 	}
 
 	/**
-	 * Renders the data cell content.
+	 * Returns the raw data cell content.
+	 * This method is called by [[renderDataCellContent()]] when rendering the content of a data cell.
 	 * @param mixed $model the data model
 	 * @param mixed $key the key associated with the data model
 	 * @param integer $index the zero-based index of the data model among the models array returned by [[GridView::dataProvider]].
 	 * @return string the rendering result
 	 */
-	protected function renderDataCellContent($model, $key, $index)
+	protected function getDataCellContent($model, $key, $index)
 	{
 		if ($this->content !== null) {
 			return call_user_func($this->content, $model, $key, $index, $this);
 		} else {
-			return $this->grid->emptyCell;
+			return null;
 		}
 	}
 
 	/**
+	 * Renders the data cell content.
+	 * @param mixed $model the data model
+	 * @param mixed $key the key associated with the data model
+	 * @param integer $index the zero-based index of the data model among the models array returned by [[GridView::dataProvider]].
+	 * @return string the rendering result
+	 */
+	protected function renderDataCellContent($model, $key, $index)
+	{
+		return $this->content !== null ? $this->getDataCellContent($model, $key, $index) : $this->grid->emptyCell;
+	}
+
+	/**
 	 * Renders the filter cell content.
 	 * The default implementation simply renders a space.
 	 * This method may be overridden to customize the rendering of the filter cell (if any).
diff --git a/framework/grid/DataColumn.php b/framework/grid/DataColumn.php
index 559e3ab..7b0f493 100644
--- a/framework/grid/DataColumn.php
+++ b/framework/grid/DataColumn.php
@@ -138,7 +138,7 @@ class DataColumn extends Column
 	/**
 	 * @inheritdoc
 	 */
-	protected function renderDataCellContent($model, $key, $index)
+	protected function getDataCellContent($model, $key, $index)
 	{
 		if ($this->value !== null) {
 			if (is_string($this->value)) {
@@ -149,8 +149,16 @@ class DataColumn extends Column
 		} elseif ($this->content === null && $this->attribute !== null) {
 			$value = ArrayHelper::getValue($model, $this->attribute);
 		} else {
-			return parent::renderDataCellContent($model, $key, $index);
+			return parent::getDataCellContent($model, $key, $index);
 		}
-		return $this->grid->formatter->format($value, $this->format);
+		return $value;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	protected function renderDataCellContent($model, $key, $index)
+	{
+		return $this->grid->formatter->format($this->getDataCellContent($model, $key, $index), $this->format);
 	}
 }
diff --git a/framework/helpers/BaseArrayHelper.php b/framework/helpers/BaseArrayHelper.php
index 0177494..278eaf0 100644
--- a/framework/helpers/BaseArrayHelper.php
+++ b/framework/helpers/BaseArrayHelper.php
@@ -58,35 +58,42 @@ class BaseArrayHelper
 	 */
 	public static function toArray($object, $properties = [], $recursive = true)
 	{
-		if (!empty($properties) && is_object($object)) {
-			$className = get_class($object);
-			if (!empty($properties[$className])) {
-				$result = [];
-				foreach ($properties[$className] as $key => $name) {
-					if (is_int($key)) {
-						$result[$name] = $object->$name;
-					} else {
-						$result[$key] = static::getValue($object, $name);
+		if (is_array($object)) {
+			if ($recursive) {
+				foreach ($object as $key => $value) {
+					if (is_array($value) || is_object($value)) {
+						$object[$key] = static::toArray($value, true);
 					}
 				}
-				return $result;
 			}
-		}
-		if ($object instanceof Arrayable) {
-			$object = $object->toArray();
-			if (!$recursive) {
-				return $object;
+			return $object;
+		} elseif (is_object($object)) {
+			if (!empty($properties)) {
+				$className = get_class($object);
+				if (!empty($properties[$className])) {
+					$result = [];
+					foreach ($properties[$className] as $key => $name) {
+						if (is_int($key)) {
+							$result[$name] = $object->$name;
+						} else {
+							$result[$key] = static::getValue($object, $name);
+						}
+					}
+					return $recursive ? static::toArray($result) : $result;
+				}
 			}
-		}
-		$result = [];
-		foreach ($object as $key => $value) {
-			if ($recursive && (is_array($value) || is_object($value))) {
-				$result[$key] = static::toArray($value, $properties, true);
+			if ($object instanceof Arrayable) {
+				$result = $object->toArray();
 			} else {
-				$result[$key] = $value;
+				$result = [];
+				foreach ($object as $key => $value) {
+					$result[$key] = $value;
+				}
 			}
+			return $recursive ? static::toArray($result) : $result;
+		} else {
+			return [$object];
 		}
-		return $result;
 	}
 
 	/**
diff --git a/framework/helpers/BaseFileHelper.php b/framework/helpers/BaseFileHelper.php
index c544a91..6da4712 100644
--- a/framework/helpers/BaseFileHelper.php
+++ b/framework/helpers/BaseFileHelper.php
@@ -147,6 +147,7 @@ class BaseFileHelper
 	 * @param string $src the source directory
 	 * @param string $dst the destination directory
 	 * @param array $options options for directory copy. Valid options are:
+	 * @throws \yii\base\InvalidParamException if unable to open directory
 	 *
 	 * - dirMode: integer, the permission to be set for newly copied directories. Defaults to 0775.
 	 * - fileMode:  integer, the permission to be set for newly copied files. Defaults to the current environment setting.
@@ -280,14 +281,14 @@ class BaseFileHelper
 			$options['basePath'] = realpath($dir);
 			// this should also be done only once
 			if (isset($options['except'])) {
-				foreach($options['except'] as $key=>$value) {
+				foreach ($options['except'] as $key => $value) {
 					if (is_string($value)) {
 						$options['except'][$key] = static::parseExcludePattern($value);
 					}
 				}
 			}
 			if (isset($options['only'])) {
-				foreach($options['only'] as $key=>$value) {
+				foreach ($options['only'] as $key => $value) {
 					if (is_string($value)) {
 						$options['only'][$key] = static::parseExcludePattern($value);
 					}
@@ -397,7 +398,7 @@ class BaseFileHelper
 			if ($pattern === $baseName) {
 				return true;
 			}
-		} else if ($flags & self::PATTERN_ENDSWITH) {
+		} elseif ($flags & self::PATTERN_ENDSWITH) {
 			/* "*literal" matching against "fooliteral" */
 			$n = StringHelper::byteLength($pattern);
 			if (StringHelper::byteSubstr($pattern, 1, $n) === StringHelper::byteSubstr($baseName, -$n, $n)) {
@@ -472,7 +473,7 @@ class BaseFileHelper
 	 */
 	private static function lastExcludeMatchingFromList($basePath, $path, $excludes)
 	{
-		foreach(array_reverse($excludes) as $exclude) {
+		foreach (array_reverse($excludes) as $exclude) {
 			if (is_string($exclude)) {
 				$exclude = self::parseExcludePattern($exclude);
 			}
@@ -508,13 +509,14 @@ class BaseFileHelper
 		if (!is_string($pattern)) {
 			throw new InvalidParamException('Exclude/include pattern must be a string.');
 		}
-		$result = array(
+		$result = [
 			'pattern' => $pattern,
 			'flags' => 0,
 			'firstWildcard' => false,
-		);
-		if (!isset($pattern[0]))
+		];
+		if (!isset($pattern[0])) {
 			return $result;
+		}
 
 		if ($pattern[0] == '!') {
 			$result['flags'] |= self::PATTERN_NEGATIVE;
@@ -526,11 +528,13 @@ class BaseFileHelper
 			$len--;
 			$result['flags'] |= self::PATTERN_MUSTBEDIR;
 		}
-		if (strpos($pattern, '/') === false)
+		if (strpos($pattern, '/') === false) {
 			$result['flags'] |= self::PATTERN_NODIR;
+		}
 		$result['firstWildcard'] = self::firstWildcardInPattern($pattern);
-		if ($pattern[0] == '*' && self::firstWildcardInPattern(StringHelper::byteSubstr($pattern, 1, StringHelper::byteLength($pattern))) === false)
+		if ($pattern[0] == '*' && self::firstWildcardInPattern(StringHelper::byteSubstr($pattern, 1, StringHelper::byteLength($pattern))) === false) {
 			$result['flags'] |= self::PATTERN_ENDSWITH;
+		}
 		$result['pattern'] = $pattern;
 		return $result;
 	}
@@ -542,8 +546,8 @@ class BaseFileHelper
 	 */
 	private static function firstWildcardInPattern($pattern)
 	{
-		$wildcards = array('*','?','[','\\');
-		$wildcardSearch = function($r, $c) use ($pattern) {
+		$wildcards = ['*', '?', '[', '\\'];
+		$wildcardSearch = function ($r, $c) use ($pattern) {
 			$p = strpos($pattern, $c);
 			return $r===false ? $p : ($p===false ? $r : min($r, $p));
 		};
diff --git a/framework/helpers/BaseHtml.php b/framework/helpers/BaseHtml.php
index 01151ec..f0fe5b4 100644
--- a/framework/helpers/BaseHtml.php
+++ b/framework/helpers/BaseHtml.php
@@ -120,7 +120,7 @@ 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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated HTML tag
 	 * @see beginTag()
@@ -138,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated start tag
 	 * @see endTag()
 	 * @see tag()
@@ -167,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated style tag
 	 */
 	public static function style($content, $options = [])
@@ -182,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated script tag
 	 */
 	public static function script($content, $options = [])
@@ -196,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated link tag
 	 * @see url()
 	 */
@@ -215,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated script tag
 	 * @see url()
 	 */
@@ -235,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated form start tag.
 	 * @see endForm()
 	 */
@@ -304,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated hyperlink
 	 * @see url()
 	 */
@@ -326,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated mailto link
 	 */
 	public static function mailto($text, $email = null, $options = [])
@@ -341,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated image tag
 	 */
 	public static function img($src, $options = [])
@@ -363,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated label tag
 	 */
 	public static function label($content, $for = null, $options = [])
@@ -380,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated button tag
 	 */
 	public static function button($content = 'Button', $options = [])
@@ -396,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated submit button tag
 	 */
 	public static function submitButton($content = 'Submit', $options = [])
@@ -413,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated reset button tag
 	 */
 	public static function resetButton($content = 'Reset', $options = [])
@@ -430,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated input tag
 	 */
 	public static function input($type, $name = null, $value = null, $options = [])
@@ -447,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated button tag
 	 */
 	public static function buttonInput($label = 'Button', $options = [])
@@ -463,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated button tag
 	 */
 	public static function submitInput($label = 'Submit', $options = [])
@@ -478,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated button tag
 	 */
 	public static function resetInput($label = 'Reset', $options = [])
@@ -495,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated button tag
 	 */
 	public static function textInput($name, $value = null, $options = [])
@@ -510,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated button tag
 	 */
 	public static function hiddenInput($name, $value = null, $options = [])
@@ -525,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated button tag
 	 */
 	public static function passwordInput($name, $value = null, $options = [])
@@ -543,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated button tag
 	 */
 	public static function fileInput($name, $value = null, $options = [])
@@ -558,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated text area tag
 	 */
 	public static function textarea($name, $value = '', $options = [])
@@ -586,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated radio button tag
 	 */
@@ -636,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated checkbox tag
 	 */
@@ -697,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated drop-down list tag
 	 */
@@ -744,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated list box tag
 	 */
@@ -800,7 +800,7 @@ class BaseHtml
 	 * 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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated checkbox list
 	 */
@@ -871,7 +871,7 @@ class BaseHtml
 	 * 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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated radio button list
 	 */
@@ -930,7 +930,7 @@ 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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated unordered list. An empty string is returned if `$items` is empty.
 	 */
@@ -974,7 +974,7 @@ 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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated ordered list. An empty string is returned if `$items` is empty.
 	 */
@@ -999,7 +999,7 @@ 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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated label tag
 	 */
@@ -1025,7 +1025,7 @@ 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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated label tag
 	 */
@@ -1048,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated input tag
 	 */
 	public static function activeInput($type, $model, $attribute, $options = [])
@@ -1070,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated input tag
 	 */
 	public static function activeTextInput($model, $attribute, $options = [])
@@ -1087,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated input tag
 	 */
 	public static function activeHiddenInput($model, $attribute, $options = [])
@@ -1104,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated input tag
 	 */
 	public static function activePasswordInput($model, $attribute, $options = [])
@@ -1121,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated input tag
 	 */
 	public static function activeFileInput($model, $attribute, $options = [])
@@ -1140,12 +1140,12 @@ 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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 * @return string the generated textarea tag
 	 */
 	public static function activeTextarea($model, $attribute, $options = [])
 	{
-		$name = static::getInputName($model, $attribute);
+		$name = isset($options['name']) ? $options['name'] : static::getInputName($model, $attribute);
 		$value = static::getAttributeValue($model, $attribute);
 		if (!array_key_exists('id', $options)) {
 			$options['id'] = static::getInputId($model, $attribute);
@@ -1173,7 +1173,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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated radio button tag
 	 */
@@ -1216,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated checkbox tag
 	 */
@@ -1272,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated drop-down list tag
 	 */
@@ -1324,7 +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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated list box tag
 	 */
@@ -1369,7 +1369,7 @@ class BaseHtml
 	 * 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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated checkbox list
 	 */
@@ -1413,7 +1413,7 @@ class BaseHtml
 	 * 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.
+	 * See [[renderTagAttributes()]] for details on how these are being rendered.
 	 *
 	 * @return string the generated radio button list
 	 */
@@ -1579,7 +1579,7 @@ class BaseHtml
 	{
 		if (isset($options['class'])) {
 			$classes = ' ' . $options['class'] . ' ';
-			if (($pos = strpos($classes, ' ' . $class . ' ')) === false) {
+			if (strpos($classes, ' ' . $class . ' ') === false) {
 				$options['class'] .= ' ' . $class;
 			}
 		} else {
diff --git a/framework/helpers/BaseInflector.php b/framework/helpers/BaseInflector.php
index f9f19a8..3865588 100644
--- a/framework/helpers/BaseInflector.php
+++ b/framework/helpers/BaseInflector.php
@@ -275,7 +275,18 @@ class BaseInflector
 		'Ā' => 'A', 'Č' => 'C', 'Ē' => 'E', 'Ģ' => 'G', 'Ī' => 'i', 'Ķ' => 'k', 'Ļ' => 'L', 'Ņ' => 'N',
 		'Š' => 'S', 'Ū' => 'u', 'Ž' => 'Z',
 		'ā' => 'a', 'č' => 'c', 'ē' => 'e', 'ģ' => 'g', 'ī' => 'i', 'ķ' => 'k', 'ļ' => 'l', 'ņ' => 'n',
-		'š' => 's', 'ū' => 'u', 'ž' => 'z'
+		'š' => 's', 'ū' => 'u', 'ž' => 'z',
+		//Vietnamese
+		'Ấ' => 'A', 'Ầ' => 'A', 'Ẩ' => 'A', 'Ẫ' => 'A', 'Ậ' => 'A',
+		'Ắ' => 'A', 'Ằ' => 'A', 'Ẳ' => 'A', 'Ẵ' => 'A', 'Ặ' => 'A',
+		'Ố' => 'O', 'Ồ' => 'O', 'Ổ' => 'O', 'Ỗ' => 'O', 'Ộ' => 'O',
+		'Ớ' => 'O', 'Ờ' => 'O', 'Ở' => 'O', 'Ỡ' => 'O', 'Ợ' => 'O',
+		'Ế' => 'E', 'Ề' => 'E', 'Ể' => 'E', 'Ễ' => 'E', 'Ệ' => 'E',
+		'ấ' => 'a', 'ầ' => 'a', 'ẩ' => 'a', 'ẫ' => 'a', 'ậ' => 'a',
+		'ắ' => 'a', 'ằ' => 'a', 'ẳ' => 'a', 'ẵ' => 'a', 'ặ' => 'a',
+		'ố' => 'o', 'ồ' => 'o', 'ổ' => 'o', 'ỗ' => 'o', 'ộ' => 'o',
+		'ớ' => 'o', 'ờ' => 'o', 'ở' => 'o', 'ỡ' => 'o', 'ợ' => 'o',
+		'ế' => 'e', 'ề' => 'e', 'ể' => 'e', 'ễ' => 'e', 'ệ' => 'e'
 	];
 
 	/**
diff --git a/framework/helpers/BaseMarkdown.php b/framework/helpers/BaseMarkdown.php
index a22e45e..c72e079 100644
--- a/framework/helpers/BaseMarkdown.php
+++ b/framework/helpers/BaseMarkdown.php
@@ -86,7 +86,7 @@ class BaseMarkdown
 		/** @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])) {
+		} elseif (!is_object($config = static::$flavors[$flavor])) {
 			$parser = Yii::createObject($config);
 			if (is_array($config)) {
 				foreach ($config as $name => $value) {
diff --git a/framework/helpers/BaseSecurity.php b/framework/helpers/BaseSecurity.php
index d9459d9..8750a54 100644
--- a/framework/helpers/BaseSecurity.php
+++ b/framework/helpers/BaseSecurity.php
@@ -107,10 +107,10 @@ class BaseSecurity
 	*/
 	protected static function stripPadding($data)
 	{
-		$end = StringHelper::byteSubstr($data, -1, NULL);
+		$end = StringHelper::byteSubstr($data, -1, null);
 		$last = ord($end);
 		$n = StringHelper::byteLength($data) - $last;
-		if (StringHelper::byteSubstr($data, $n, NULL) == str_repeat($end, $last)) {
+		if (StringHelper::byteSubstr($data, $n, null) == str_repeat($end, $last)) {
 			return StringHelper::byteSubstr($data, 0, $n);
 		}
 		return false;
diff --git a/framework/i18n/GettextMessageSource.php b/framework/i18n/GettextMessageSource.php
index 8da8edc..fc6d87f 100644
--- a/framework/i18n/GettextMessageSource.php
+++ b/framework/i18n/GettextMessageSource.php
@@ -70,9 +70,9 @@ class GettextMessageSource extends MessageSource
 
 			if ($messages === null && $fallbackMessages === null && $fallbackLanguage != $this->sourceLanguage) {
 				Yii::error("The message file for category '$category' does not exist: $messageFile Fallback file does not exist as well: $fallbackMessageFile", __METHOD__);
-			} else if (empty($messages)) {
+			} elseif (empty($messages)) {
 				return $fallbackMessages;
-			} else if (!empty($fallbackMessages)) {
+			} elseif (!empty($fallbackMessages)) {
 				foreach ($fallbackMessages as $key => $value) {
 					if (!empty($value) && empty($messages[$key])) {
 						$messages[$key] = $fallbackMessages[$key];
diff --git a/framework/i18n/I18N.php b/framework/i18n/I18N.php
index d0bbfcc..6be3899 100644
--- a/framework/i18n/I18N.php
+++ b/framework/i18n/I18N.php
@@ -126,7 +126,7 @@ class I18N extends Component
 		}
 
 		$p = [];
-		foreach($params as $name => $value) {
+		foreach ($params as $name => $value) {
 			$p['{' . $name . '}'] = $value;
 		}
 		return strtr($message, $p);
diff --git a/framework/i18n/MessageFormatter.php b/framework/i18n/MessageFormatter.php
index 4ea7083..c3d8c56 100644
--- a/framework/i18n/MessageFormatter.php
+++ b/framework/i18n/MessageFormatter.php
@@ -143,7 +143,7 @@ class MessageFormatter extends Component
 			return false;
 		}
 		$map = [];
-		foreach($tokens as $i => $token) {
+		foreach ($tokens as $i => $token) {
 			if (is_array($token)) {
 				$param = trim($token[0]);
 				if (!isset($map[$param])) {
@@ -169,7 +169,7 @@ class MessageFormatter extends Component
 			return false;
 		} else {
 			$values = [];
-			foreach($result as $key => $value) {
+			foreach ($result as $key => $value) {
 				$values[$map[$key]] = $value;
 			}
 			return $values;
@@ -190,7 +190,7 @@ class MessageFormatter extends Component
 		if (($tokens = self::tokenizePattern($pattern)) === false) {
 			return false;
 		}
-		foreach($tokens as $i => $token) {
+		foreach ($tokens as $i => $token) {
 			if (!is_array($token)) {
 				continue;
 			}
@@ -210,7 +210,7 @@ class MessageFormatter extends Component
 			}
 			$type = isset($token[1]) ? trim($token[1]) : 'none';
 			// replace plural and select format recursively
-			if ($type == 'plural' || $type == 'select')	{
+			if ($type == 'plural' || $type == 'select') {
 				if (!isset($token[2])) {
 					return false;
 				}
@@ -244,7 +244,7 @@ class MessageFormatter extends Component
 			$this->_errorMessage = "Message pattern is invalid.";
 			return false;
 		}
-		foreach($tokens as $i => $token) {
+		foreach ($tokens as $i => $token) {
 			if (is_array($token)) {
 				if (($tokens[$i] = $this->parseToken($token, $args, $locale)) === false) {
 					$this->_errorCode = -1;
diff --git a/framework/i18n/PhpMessageSource.php b/framework/i18n/PhpMessageSource.php
index 2e611f5..7866511 100644
--- a/framework/i18n/PhpMessageSource.php
+++ b/framework/i18n/PhpMessageSource.php
@@ -73,9 +73,9 @@ class PhpMessageSource extends MessageSource
 
 			if ($messages === null && $fallbackMessages === null && $fallbackLanguage != $this->sourceLanguage) {
 				Yii::error("The message file for category '$category' does not exist: $messageFile Fallback file does not exist as well: $fallbackMessageFile", __METHOD__);
-			} else if (empty($messages)) {
+			} elseif (empty($messages)) {
 				return $fallbackMessages;
-			} else if (!empty($fallbackMessages)) {
+			} elseif (!empty($fallbackMessages)) {
 				foreach ($fallbackMessages as $key => $value) {
 					if (!empty($value) && empty($messages[$key])) {
 						$messages[$key] = $fallbackMessages[$key];
diff --git a/framework/mail/BaseMailer.php b/framework/mail/BaseMailer.php
index 139b4af..01f6635 100644
--- a/framework/mail/BaseMailer.php
+++ b/framework/mail/BaseMailer.php
@@ -348,5 +348,4 @@ abstract class BaseMailer extends Component implements MailerInterface, ViewCont
 		$event = new MailEvent(['message' => $message, 'isSuccessful' => $isSuccessful]);
 		$this->trigger(self::EVENT_AFTER_SEND, $event);
 	}
-
 }
diff --git a/framework/messages/pt-PT/yii.php b/framework/messages/pt-PT/yii.php
index 375b4d6..2d383c0 100644
--- a/framework/messages/pt-PT/yii.php
+++ b/framework/messages/pt-PT/yii.php
@@ -1,4 +1,4 @@
-<?php
+<?php
 /**
  * Message translations.
  *
diff --git a/framework/requirements/views/web/index.php b/framework/requirements/views/web/index.php
index 287d4bb..a2aa59d 100644
--- a/framework/requirements/views/web/index.php
+++ b/framework/requirements/views/web/index.php
@@ -27,7 +27,7 @@
 		</p>
 		<p>
 		There are two kinds of requirements being checked. Mandatory requirements are those that have to be met
-		to allow Yii to work as expected. There are also some optional requirements beeing checked which will
+		to allow Yii to work as expected. There are also some optional requirements being checked which will
 		show you a warning when they do not meet. You can use Yii framework without them but some specific
 		functionality may be not available in this case.
 		</p>
diff --git a/framework/rest/Action.php b/framework/rest/Action.php
new file mode 100644
index 0000000..1a98539
--- /dev/null
+++ b/framework/rest/Action.php
@@ -0,0 +1,106 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\InvalidConfigException;
+use yii\db\ActiveRecordInterface;
+use yii\web\NotFoundHttpException;
+
+/**
+ * Action is the base class for action classes that implement RESTful API.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class Action extends \yii\base\Action
+{
+	/**
+	 * @var string class name of the model which will be handled by this action.
+	 * The model class must implement [[ActiveRecordInterface]].
+	 * This property must be set.
+	 */
+	public $modelClass;
+	/**
+	 * @var callable a PHP callable that will be called to return the model corresponding
+	 * to the specified primary key value. If not set, [[findModel()]] will be used instead.
+	 * The signature of the callable should be:
+	 *
+	 * ```php
+	 * function ($id, $action) {
+	 *     // $id is the primary key value. If composite primary key, the key values
+	 *     // will be separated by comma.
+	 *     // $action is the action object currently running
+	 * }
+	 * ```
+	 *
+	 * The callable should return the model found, or throw an exception if not found.
+	 */
+	public $findModel;
+	/**
+	 * @var callable a PHP callable that will be called when running an action to determine
+	 * if the current user has the permission to execute the action. If not set, the access
+	 * check will not be performed. The signature of the callable should be as follows,
+	 *
+	 * ```php
+	 * function ($action, $model = null) {
+	 *     // $model is the requested model instance.
+	 *     // If null, it means no specific model (e.g. IndexAction)
+	 * }
+	 * ```
+	 */
+	public $checkAccess;
+
+
+	/**
+	 * @inheritdoc
+	 */
+	public function init()
+	{
+		if ($this->modelClass === null) {
+			throw new InvalidConfigException(get_class($this) . '::$modelClass must be set.');
+		}
+	}
+
+	/**
+	 * Returns the data model based on the primary key given.
+	 * If the data model is not found, a 404 HTTP exception will be raised.
+	 * @param string $id the ID of the model to be loaded. If the model has a composite primary key,
+	 * the ID must be a string of the primary key values separated by commas.
+	 * The order of the primary key values should follow that returned by the `primaryKey()` method
+	 * of the model.
+	 * @return ActiveRecordInterface the model found
+	 * @throws NotFoundHttpException if the model cannot be found
+	 */
+	public function findModel($id)
+	{
+		if ($this->findModel !== null) {
+			return call_user_func($this->findModel, $id, $this);
+		}
+
+		/**
+		 * @var ActiveRecordInterface $modelClass
+		 */
+		$modelClass = $this->modelClass;
+		$keys = $modelClass::primaryKey();
+		if (count($keys) > 1) {
+			$values = explode(',', $id);
+			if (count($keys) === count($values)) {
+				$model = $modelClass::find(array_combine($keys, $values));
+			}
+		} elseif ($id !== null) {
+			$model = $modelClass::find($id);
+		}
+
+		if (isset($model)) {
+			return $model;
+		} else {
+			throw new NotFoundHttpException("Object not found: $id");
+		}
+	}
+}
diff --git a/framework/rest/ActiveController.php b/framework/rest/ActiveController.php
new file mode 100644
index 0000000..75a4f55
--- /dev/null
+++ b/framework/rest/ActiveController.php
@@ -0,0 +1,126 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use yii\base\InvalidConfigException;
+use yii\base\Model;
+
+/**
+ * ActiveController implements a common set of actions for supporting RESTful access to ActiveRecord.
+ *
+ * The class of the ActiveRecord should be specified via [[modelClass]], which must implement [[\yii\db\ActiveRecordInterface]].
+ * By default, the following actions are supported:
+ *
+ * - `index`: list of models
+ * - `view`: return the details of a model
+ * - `create`: create a new model
+ * - `update`: update an existing model
+ * - `delete`: delete an existing model
+ * - `options`: return the allowed HTTP methods
+ *
+ * You may disable some of these actions by overriding [[actions()]] and unsetting the corresponding actions.
+ *
+ * To add a new action, either override [[actions()]] by appending a new action class or write a new action method.
+ * Make sure you also override [[verbs()]] to properly declare what HTTP methods are allowed by the new action.
+ *
+ * You should usually override [[checkAccess()]] to check whether the current user has the privilege to perform
+ * the specified action against the specified model.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class ActiveController extends Controller
+{
+	/**
+	 * @var string the model class name. This property must be set.
+	 */
+	public $modelClass;
+	/**
+	 * @var string the scenario used for updating a model.
+	 * @see \yii\base\Model::scenarios()
+	 */
+	public $updateScenario = Model::SCENARIO_DEFAULT;
+	/**
+	 * @var string the scenario used for creating a model.
+	 * @see \yii\base\Model::scenarios()
+	 */
+	public $createScenario = Model::SCENARIO_DEFAULT;
+	/**
+	 * @var boolean whether to use a DB transaction when creating, updating or deleting a model.
+	 * This property is only useful for relational database.
+	 */
+	public $transactional = true;
+
+
+	/**
+	 * @inheritdoc
+	 */
+	public function init()
+	{
+		parent::init();
+		if ($this->modelClass === null) {
+			throw new InvalidConfigException('The "modelClass" property must be set.');
+		}
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function actions()
+	{
+		return [
+			'index' => [
+				'class' => 'yii\rest\IndexAction',
+				'modelClass' => $this->modelClass,
+				'checkAccess' => [$this, 'checkAccess'],
+			],
+			'view' => [
+				'class' => 'yii\rest\ViewAction',
+				'modelClass' => $this->modelClass,
+				'checkAccess' => [$this, 'checkAccess'],
+			],
+			'create' => [
+				'class' => 'yii\rest\CreateAction',
+				'modelClass' => $this->modelClass,
+				'checkAccess' => [$this, 'checkAccess'],
+				'scenario' => $this->createScenario,
+				'transactional' => $this->transactional,
+			],
+			'update' => [
+				'class' => 'yii\rest\UpdateAction',
+				'modelClass' => $this->modelClass,
+				'checkAccess' => [$this, 'checkAccess'],
+				'scenario' => $this->updateScenario,
+				'transactional' => $this->transactional,
+			],
+			'delete' => [
+				'class' => 'yii\rest\DeleteAction',
+				'modelClass' => $this->modelClass,
+				'checkAccess' => [$this, 'checkAccess'],
+				'transactional' => $this->transactional,
+			],
+			'options' => [
+				'class' => 'yii\rest\OptionsAction',
+			],
+		];
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	protected function verbs()
+	{
+		return [
+			'index' => ['GET', 'HEAD'],
+			'view' => ['GET', 'HEAD'],
+			'create' => ['POST'],
+			'update' => ['PUT', 'PATCH'],
+			'delete' => ['DELETE'],
+		];
+	}
+}
diff --git a/framework/rest/AuthInterface.php b/framework/rest/AuthInterface.php
new file mode 100644
index 0000000..30ccc9f
--- /dev/null
+++ b/framework/rest/AuthInterface.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use yii\web\User;
+use yii\web\Request;
+use yii\web\Response;
+use yii\web\IdentityInterface;
+use yii\web\UnauthorizedHttpException;
+
+/**
+ * AuthInterface is the interface required by classes that support user authentication.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+interface AuthInterface
+{
+	/**
+	 * Authenticates the current user.
+	 *
+	 * @param User $user
+	 * @param Request $request
+	 * @param Response $response
+	 * @return IdentityInterface the authenticated user identity. If authentication information is not provided, null will be returned.
+	 * @throws UnauthorizedHttpException if authentication information is provided but is invalid.
+	 */
+	public function authenticate($user, $request, $response);
+	/**
+	 * Handles authentication failure.
+	 * The implementation should normally throw UnauthorizedHttpException to indicate authentication failure.
+	 * @param Response $response
+	 * @throws UnauthorizedHttpException
+	 */
+	public function handleFailure($response);
+}
diff --git a/framework/rest/Controller.php b/framework/rest/Controller.php
new file mode 100644
index 0000000..7900b15
--- /dev/null
+++ b/framework/rest/Controller.php
@@ -0,0 +1,247 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\InvalidConfigException;
+use yii\web\Response;
+use yii\web\UnauthorizedHttpException;
+use yii\web\UnsupportedMediaTypeHttpException;
+use yii\web\TooManyRequestsHttpException;
+use yii\web\VerbFilter;
+use yii\web\ForbiddenHttpException;
+
+/**
+ * Controller is the base class for RESTful API controller classes.
+ *
+ * Controller implements the following steps in a RESTful API request handling cycle:
+ *
+ * 1. Resolving response format and API version number (see [[supportedFormats]], [[supportedVersions]] and [[version]]);
+ * 2. Validating request method (see [[verbs()]]).
+ * 3. Authenticating user (see [[authenticate()]]);
+ * 4. Formatting response data (see [[serializeData()]]).
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class Controller extends \yii\web\Controller
+{
+	/**
+	 * @var string the name of the header parameter representing the API version number.
+	 */
+	public $versionHeaderParam = 'version';
+	/**
+	 * @var string|array the configuration for creating the serializer that formats the response data.
+	 */
+	public $serializer = 'yii\rest\Serializer';
+	/**
+	 * @inheritdoc
+	 */
+	public $enableCsrfValidation = false;
+	/**
+	 * @var array the supported authentication methods. This property should take a list of supported
+	 * authentication methods, each represented by an authentication class or configuration.
+	 * If this is not set or empty, it means authentication is disabled.
+	 */
+	public $authMethods;
+	/**
+	 * @var string|array the rate limiter class or configuration. If this is not set or empty,
+	 * the rate limiting will be disabled. Note that if the user is not authenticated, the rate limiting
+	 * will also NOT be performed.
+	 * @see checkRateLimit()
+	 * @see authMethods
+	 */
+	public $rateLimiter = 'yii\rest\RateLimiter';
+	/**
+	 * @var string the chosen API version number, or null if [[supportedVersions]] is empty.
+	 * @see supportedVersions
+	 */
+	public $version;
+	/**
+	 * @var array list of supported API version numbers. If the current request does not specify a version
+	 * number, the first element will be used as the [[version|chosen version number]]. For this reason, you should
+	 * put the latest version number at the first. If this property is empty, [[version]] will not be set.
+	 */
+	public $supportedVersions = [];
+	/**
+	 * @var array list of supported response formats. The array keys are the requested content MIME types,
+	 * and the array values are the corresponding response formats. The first element will be used
+	 * as the response format if the current request does not specify a content type.
+	 */
+	public $supportedFormats = [
+		'application/json' => Response::FORMAT_JSON,
+		'application/xml' => Response::FORMAT_XML,
+	];
+
+	/**
+	 * @inheritdoc
+	 */
+	public function behaviors()
+	{
+		return [
+			'verbFilter' => [
+				'class' => VerbFilter::className(),
+				'actions' => $this->verbs(),
+			],
+		];
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function init()
+	{
+		parent::init();
+		$this->resolveFormatAndVersion();
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function beforeAction($action)
+	{
+		if (parent::beforeAction($action)) {
+			$this->authenticate();
+			$this->checkRateLimit($action);
+			return true;
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function afterAction($action, $result)
+	{
+		$result = parent::afterAction($action, $result);
+		return $this->serializeData($result);
+	}
+
+	/**
+	 * Resolves the response format and the API version number.
+	 * @throws UnsupportedMediaTypeHttpException
+	 */
+	protected function resolveFormatAndVersion()
+	{
+		$this->version = empty($this->supportedVersions) ? null : reset($this->supportedVersions);
+		Yii::$app->getResponse()->format = reset($this->supportedFormats);
+		$types = Yii::$app->getRequest()->getAcceptableContentTypes();
+		if (empty($types)) {
+			$types['*/*'] = [];
+		}
+
+		foreach ($types as $type => $params) {
+			if (isset($this->supportedFormats[$type])) {
+				Yii::$app->getResponse()->format = $this->supportedFormats[$type];
+				if (isset($params[$this->versionHeaderParam])) {
+					if (in_array($params[$this->versionHeaderParam], $this->supportedVersions, true)) {
+						$this->version = $params[$this->versionHeaderParam];
+					} else {
+						throw new UnsupportedMediaTypeHttpException('You are requesting an invalid version number.');
+					}
+				}
+				return;
+			}
+		}
+
+		if (!isset($types['*/*'])) {
+			throw new UnsupportedMediaTypeHttpException('None of your requested content types is supported.');
+		}
+	}
+
+	/**
+	 * Declares the allowed HTTP verbs.
+	 * Please refer to [[VerbFilter::actions]] on how to declare the allowed verbs.
+	 * @return array the allowed HTTP verbs.
+	 */
+	protected function verbs()
+	{
+		return [];
+	}
+
+	/**
+	 * Authenticates the user.
+	 * This method implements the user authentication based on an access token sent through the `Authorization` HTTP header.
+	 * @throws UnauthorizedHttpException if the user is not authenticated successfully
+	 */
+	protected function authenticate()
+	{
+		if (empty($this->authMethods)) {
+			return;
+		}
+
+		$user = Yii::$app->getUser();
+		$request = Yii::$app->getRequest();
+		$response = Yii::$app->getResponse();
+		foreach ($this->authMethods as $i => $auth) {
+			$this->authMethods[$i] = $auth = Yii::createObject($auth);
+			if (!$auth instanceof AuthInterface) {
+				throw new InvalidConfigException(get_class($auth) . ' must implement yii\rest\AuthInterface');
+			} elseif ($auth->authenticate($user, $request, $response) !== null) {
+				return;
+			}
+		}
+
+		/** @var AuthInterface $auth */
+		$auth = reset($this->authMethods);
+		$auth->handleFailure($response);
+	}
+
+	/**
+	 * Ensures the rate limit is not exceeded.
+	 *
+	 * This method will use [[rateLimiter]] to check rate limit. In order to perform rate limiting check,
+	 * the user must be authenticated and the user identity object (`Yii::$app->user->identity`) must
+	 * implement [[RateLimitInterface]].
+	 *
+	 * @param \yii\base\Action $action the action to be executed
+	 * @throws TooManyRequestsHttpException if the rate limit is exceeded.
+	 */
+	protected function checkRateLimit($action)
+	{
+		if (empty($this->rateLimiter)) {
+			return;
+		}
+
+		$identity = Yii::$app->getUser()->getIdentity(false);
+		if ($identity instanceof RateLimitInterface) {
+			/** @var RateLimiter $rateLimiter */
+			$rateLimiter = Yii::createObject($this->rateLimiter);
+			$rateLimiter->check($identity, Yii::$app->getRequest(), Yii::$app->getResponse(), $action);
+		}
+	}
+
+	/**
+	 * Serializes the specified data.
+	 * The default implementation will create a serializer based on the configuration given by [[serializer]].
+	 * It then uses the serializer to serialize the given data.
+	 * @param mixed $data the data to be serialized
+	 * @return mixed the serialized data.
+	 */
+	protected function serializeData($data)
+	{
+		return Yii::createObject($this->serializer)->serialize($data);
+	}
+
+	/**
+	 * Checks the privilege of the current user.
+	 *
+	 * This method should be overridden to check whether the current user has the privilege
+	 * to run the specified action against the specified data model.
+	 * If the user does not have access, a [[ForbiddenHttpException]] should be thrown.
+	 *
+	 * @param string $action the ID of the action to be executed
+	 * @param object $model the model to be accessed. If null, it means no specific model is being accessed.
+	 * @param array $params additional parameters
+	 * @throws ForbiddenHttpException if the user does not have access
+	 */
+	public function checkAccess($action, $model = null, $params = [])
+	{
+	}
+}
diff --git a/framework/rest/CreateAction.php b/framework/rest/CreateAction.php
new file mode 100644
index 0000000..fa818c2
--- /dev/null
+++ b/framework/rest/CreateAction.php
@@ -0,0 +1,80 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\Model;
+use yii\db\ActiveRecord;
+
+/**
+ * CreateAction implements the API endpoint for creating a new model from the given data.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class CreateAction extends Action
+{
+	/**
+	 * @var string the scenario to be assigned to the new model before it is validated and saved.
+	 */
+	public $scenario = Model::SCENARIO_DEFAULT;
+	/**
+	 * @var boolean whether to start a DB transaction when saving the model.
+	 */
+	public $transactional = true;
+	/**
+	 * @var string the name of the view action. This property is need to create the URL when the mode is successfully created.
+	 */
+	public $viewAction = 'view';
+
+
+	/**
+	 * Creates a new model.
+	 * @return \yii\db\ActiveRecordInterface the model newly created
+	 * @throws \Exception if there is any error when creating the model
+	 */
+	public function run()
+	{
+		if ($this->checkAccess) {
+			call_user_func($this->checkAccess, $this->id);
+		}
+
+		/**
+		 * @var \yii\db\ActiveRecord $model
+		 */
+		$model = new $this->modelClass([
+			'scenario' => $this->scenario,
+		]);
+
+		$model->load(Yii::$app->getRequest()->getBodyParams(), '');
+
+		if ($this->transactional && $model instanceof ActiveRecord) {
+			if ($model->validate()) {
+				$transaction = $model->getDb()->beginTransaction();
+				try {
+					$model->insert(false);
+					$transaction->commit();
+				} catch (\Exception $e) {
+					$transaction->rollback();
+					throw $e;
+				}
+			}
+		} else {
+			$model->save();
+		}
+
+		if (!$model->hasErrors()) {
+			$response = Yii::$app->getResponse();
+			$response->setStatusCode(201);
+			$id = implode(',', array_values($model->getPrimaryKey(true)));
+			$response->getHeaders()->set('Location', $this->controller->createAbsoluteUrl([$this->viewAction, 'id' => $id]));
+		}
+
+		return $model;
+	}
+}
diff --git a/framework/rest/DeleteAction.php b/framework/rest/DeleteAction.php
new file mode 100644
index 0000000..a0355c8
--- /dev/null
+++ b/framework/rest/DeleteAction.php
@@ -0,0 +1,53 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\db\ActiveRecord;
+
+/**
+ * DeleteAction implements the API endpoint for deleting a model.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class DeleteAction extends Action
+{
+	/**
+	 * @var boolean whether to start a DB transaction when deleting the model.
+	 */
+	public $transactional = true;
+
+
+	/**
+	 * Deletes a model.
+	 */
+	public function run($id)
+	{
+		$model = $this->findModel($id);
+
+		if ($this->checkAccess) {
+			call_user_func($this->checkAccess, $this->id, $model);
+		}
+
+		if ($this->transactional && $model instanceof ActiveRecord) {
+			$transaction = $model->getDb()->beginTransaction();
+			try {
+				$model->delete();
+				$transaction->commit();
+			} catch (\Exception $e) {
+				$transaction->rollback();
+				throw $e;
+			}
+		} else {
+			$model->delete();
+		}
+
+		Yii::$app->getResponse()->setStatusCode(204);
+	}
+}
diff --git a/framework/rest/HttpBasicAuth.php b/framework/rest/HttpBasicAuth.php
new file mode 100644
index 0000000..7a69c15
--- /dev/null
+++ b/framework/rest/HttpBasicAuth.php
@@ -0,0 +1,50 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\Component;
+use yii\web\UnauthorizedHttpException;
+
+/**
+ * HttpBasicAuth implements the HTTP Basic authentication method.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class HttpBasicAuth extends Component implements AuthInterface
+{
+	/**
+	 * @var string the HTTP authentication realm
+	 */
+	public $realm = 'api';
+
+	/**
+	 * @inheritdoc
+	 */
+	public function authenticate($user, $request, $response)
+	{
+		if (($accessToken = $request->getAuthUser()) !== null) {
+			$identity = $user->loginByAccessToken($accessToken);
+			if ($identity !== null) {
+				return $identity;
+			}
+			$this->handleFailure($response);
+		}
+		return null;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function handleFailure($response)
+	{
+		$response->getHeaders()->set('WWW-Authenticate', "Basic realm=\"{$this->realm}\"");
+		throw new UnauthorizedHttpException('You are requesting with an invalid access token.');
+	}
+}
diff --git a/framework/rest/HttpBearerAuth.php b/framework/rest/HttpBearerAuth.php
new file mode 100644
index 0000000..81033c9
--- /dev/null
+++ b/framework/rest/HttpBearerAuth.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\Component;
+use yii\web\UnauthorizedHttpException;
+
+/**
+ * HttpBearerAuth implements the authentication method based on HTTP Bearer token.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class HttpBearerAuth extends Component implements AuthInterface
+{
+	/**
+	 * @var string the HTTP authentication realm
+	 */
+	public $realm = 'api';
+
+	/**
+	 * @inheritdoc
+	 */
+	public function authenticate($user, $request, $response)
+	{
+		$authHeader = $request->getHeaders()->get('Authorization');
+		if ($authHeader !== null && preg_match("/^Bearer\\s+(.*?)$/", $authHeader, $matches)) {
+			$identity = $user->loginByAccessToken($matches[1]);
+			if ($identity !== null) {
+				return $identity;
+			}
+
+			$this->handleFailure($response);
+		}
+		return null;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function handleFailure($response)
+	{
+		$response->getHeaders()->set('WWW-Authenticate', "Basic realm=\"{$this->realm}\"");
+		throw new UnauthorizedHttpException('You are requesting with an invalid access token.');
+	}
+}
diff --git a/framework/rest/IndexAction.php b/framework/rest/IndexAction.php
new file mode 100644
index 0000000..ca30220
--- /dev/null
+++ b/framework/rest/IndexAction.php
@@ -0,0 +1,65 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\data\ActiveDataProvider;
+
+/**
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class IndexAction extends Action
+{
+	/**
+	 * @var callable a PHP callable that will be called to prepare a data provider that
+	 * should return a collection of the models. If not set, [[prepareDataProvider()]] will be used instead.
+	 * The signature of the callable should be:
+	 *
+	 * ```php
+	 * function ($action) {
+	 *     // $action is the action object currently running
+	 * }
+	 * ```
+	 *
+	 * The callable should return an instance of [[ActiveDataProvider]].
+	 */
+	public $prepareDataProvider;
+
+
+	/**
+	 * @return ActiveDataProvider
+	 */
+	public function run()
+	{
+		if ($this->checkAccess) {
+			call_user_func($this->checkAccess, $this->id);
+		}
+
+		return $this->prepareDataProvider();
+	}
+
+	/**
+	 * Prepares the data provider that should return the requested collection of the models.
+	 * @return ActiveDataProvider
+	 */
+	protected function prepareDataProvider()
+	{
+		if ($this->prepareDataProvider !== null) {
+			return call_user_func($this->prepareDataProvider, $this);
+		}
+
+		/**
+		 * @var \yii\db\BaseActiveRecord $modelClass
+		 */
+		$modelClass = $this->modelClass;
+		return new ActiveDataProvider([
+			'query' => $modelClass::find(),
+		]);
+	}
+}
diff --git a/framework/rest/OptionsAction.php b/framework/rest/OptionsAction.php
new file mode 100644
index 0000000..0f9561f
--- /dev/null
+++ b/framework/rest/OptionsAction.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+
+/**
+ * OptionsAction responds to the OPTIONS request by sending back an `Allow` header.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class OptionsAction extends \yii\base\Action
+{
+	/**
+	 * @var array the HTTP verbs that are supported by the collection URL
+	 */
+	public $collectionOptions = ['GET', 'POST', 'HEAD', 'OPTIONS'];
+	/**
+	 * @var array the HTTP verbs that are supported by the resource URL
+	 */
+	public $resourceOptions = ['GET', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];
+
+
+	/**
+	 * Responds to the OPTIONS request.
+	 * @param string $id
+	 */
+	public function run($id = null)
+	{
+		if (Yii::$app->getRequest()->getMethod() !== 'OPTIONS') {
+			Yii::$app->getResponse()->setStatusCode(405);
+		}
+		$options = $id === null ? $this->collectionOptions : $this->resourceOptions;
+		Yii::$app->getResponse()->getHeaders()->set('Allow', implode(', ', $options));
+	}
+}
diff --git a/framework/rest/QueryParamAuth.php b/framework/rest/QueryParamAuth.php
new file mode 100644
index 0000000..f45e4c8
--- /dev/null
+++ b/framework/rest/QueryParamAuth.php
@@ -0,0 +1,52 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\Component;
+use yii\web\UnauthorizedHttpException;
+
+/**
+ * QueryParamAuth implements the authentication method based on the access token passed through a query parameter.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class QueryParamAuth extends Component implements AuthInterface
+{
+	/**
+	 * @var string the parameter name for passing the access token
+	 */
+	public $tokenParam = 'access-token';
+
+	/**
+	 * @inheritdoc
+	 */
+	public function authenticate($user, $request, $response)
+	{
+		$accessToken = $request->get($this->tokenParam);
+		if (is_string($accessToken)) {
+			$identity = $user->loginByAccessToken($accessToken);
+			if ($identity !== null) {
+				return $identity;
+			}
+		}
+		if ($accessToken !== null) {
+			$this->handleFailure($response);
+		}
+		return null;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function handleFailure($response)
+	{
+		throw new UnauthorizedHttpException('You are requesting with an invalid access token.');
+	}
+}
diff --git a/framework/rest/RateLimitInterface.php b/framework/rest/RateLimitInterface.php
new file mode 100644
index 0000000..07f60e0
--- /dev/null
+++ b/framework/rest/RateLimitInterface.php
@@ -0,0 +1,39 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+/**
+ * RateLimitInterface is the interface that may be implemented by an identity object to enforce rate limiting.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+interface RateLimitInterface
+{
+	/**
+	 * Returns the maximum number of allowed requests and the window size.
+	 * @param array $params the additional parameters associated with the rate limit.
+	 * @return array an array of two elements. The first element is the maximum number of allowed requests,
+	 * and the second element is the size of the window in seconds.
+	 */
+	public function getRateLimit($params = []);
+	/**
+	 * Loads the number of allowed requests and the corresponding timestamp from a persistent storage.
+	 * @param array $params the additional parameters associated with the rate limit.
+	 * @return array an array of two elements. The first element is the number of allowed requests,
+	 * and the second element is the corresponding UNIX timestamp.
+	 */
+	public function loadAllowance($params = []);
+	/**
+	 * Saves the number of allowed requests and the corresponding timestamp to a persistent storage.
+	 * @param integer $allowance the number of allowed requests remaining.
+	 * @param integer $timestamp the current timestamp.
+	 * @param array $params the additional parameters associated with the rate limit.
+	 */
+	public function saveAllowance($allowance, $timestamp, $params = []);
+}
diff --git a/framework/rest/RateLimiter.php b/framework/rest/RateLimiter.php
new file mode 100644
index 0000000..753a0f0
--- /dev/null
+++ b/framework/rest/RateLimiter.php
@@ -0,0 +1,85 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use yii\base\Component;
+use yii\base\Action;
+use yii\web\Request;
+use yii\web\Response;
+use yii\web\TooManyRequestsHttpException;
+
+/**
+ * RateLimiter implements a rate limiting algorithm based on the [leaky bucket algorithm](http://en.wikipedia.org/wiki/Leaky_bucket).
+ *
+ * You may call [[check()]] to enforce rate limiting.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class RateLimiter extends Component
+{
+	/**
+	 * @var boolean whether to include rate limit headers in the response
+	 */
+	public $enableRateLimitHeaders = true;
+	/**
+	 * @var string the message to be displayed when rate limit exceeds
+	 */
+	public $errorMessage = 'Rate limit exceeded.';
+
+	/**
+	 * Checks whether the rate limit exceeds.
+	 * @param RateLimitInterface $user the current user
+	 * @param Request $request
+	 * @param Response $response
+	 * @param Action $action the action to be executed
+	 * @throws TooManyRequestsHttpException if rate limit exceeds
+	 */
+	public function check($user, $request, $response, $action)
+	{
+		$current = time();
+		$params = [
+			'request' => $request,
+			'action' => $action,
+		];
+
+		list ($limit, $window) = $user->getRateLimit($params);
+		list ($allowance, $timestamp) = $user->loadAllowance($params);
+
+		$allowance += (int)(($current - $timestamp) * $limit / $window);
+		if ($allowance > $limit) {
+			$allowance = $limit;
+		}
+
+		if ($allowance < 1) {
+			$user->saveAllowance(0, $current, $params);
+			$this->addRateLimitHeaders($response, $limit, 0, $window);
+			throw new TooManyRequestsHttpException($this->errorMessage);
+		} else {
+			$user->saveAllowance($allowance - 1, $current, $params);
+			$this->addRateLimitHeaders($response, $limit, 0, (int)(($limit - $allowance) * $window / $limit));
+		}
+	}
+
+	/**
+	 * Adds the rate limit headers to the response
+	 * @param Response $response
+	 * @param integer $limit the maximum number of allowed requests during a period
+	 * @param integer $remaining the remaining number of allowed requests within the current period
+	 * @param integer $reset the number of seconds to wait before having maximum number of allowed requests again
+	 */
+	protected function addRateLimitHeaders($response, $limit, $remaining, $reset)
+	{
+		if ($this->enableRateLimitHeaders) {
+			$response->getHeaders()
+				->set('X-Rate-Limit-Limit', $limit)
+				->set('X-Rate-Limit-Remaining', $remaining)
+				->set('X-Rate-Limit-Reset', $reset);
+		}
+	}
+}
diff --git a/framework/rest/Serializer.php b/framework/rest/Serializer.php
new file mode 100644
index 0000000..75a6664
--- /dev/null
+++ b/framework/rest/Serializer.php
@@ -0,0 +1,248 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\Component;
+use yii\base\Model;
+use yii\data\DataProviderInterface;
+use yii\data\Pagination;
+use yii\helpers\ArrayHelper;
+use yii\web\Link;
+use yii\web\Request;
+use yii\web\Response;
+
+/**
+ * Serializer converts resource objects and collections into array representation.
+ *
+ * Serializer is mainly used by REST controllers to convert different objects into array representation
+ * so that they can be further turned into different formats, such as JSON, XML, by response formatters.
+ *
+ * The default implementation handles resources as [[Model]] objects and collections as objects
+ * implementing [[DataProviderInterface]]. You may override [[serialize()]] to handle more types.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class Serializer extends Component
+{
+	/**
+	 * @var string the name of the query parameter containing the information about which fields should be returned
+	 * for a [[Model]] object. If the parameter is not provided or empty, the default set of fields as defined
+	 * by [[Model::fields()]] will be returned.
+	 */
+	public $fieldsParam = 'fields';
+	/**
+	 * @var string the name of the query parameter containing the information about which fields should be returned
+	 * in addition to those listed in [[fieldsParam]] for a resource object.
+	 */
+	public $expandParam = 'expand';
+	/**
+	 * @var string the name of the HTTP header containing the information about total number of data items.
+	 * This is used when serving a resource collection with pagination.
+	 */
+	public $totalCountHeader = 'X-Pagination-Total-Count';
+	/**
+	 * @var string the name of the HTTP header containing the information about total number of pages of data.
+	 * This is used when serving a resource collection with pagination.
+	 */
+	public $pageCountHeader = 'X-Pagination-Page-Count';
+	/**
+	 * @var string the name of the HTTP header containing the information about the current page number (1-based).
+	 * This is used when serving a resource collection with pagination.
+	 */
+	public $currentPageHeader = 'X-Pagination-Current-Page';
+	/**
+	 * @var string the name of the HTTP header containing the information about the number of data items in each page.
+	 * This is used when serving a resource collection with pagination.
+	 */
+	public $perPageHeader = 'X-Pagination-Per-Page';
+	/**
+	 * @var string the name of the envelope (e.g. `items`) for returning the resource objects in a collection.
+	 * This is used when serving a resource collection. When this is set and pagination is enabled, the serializer
+	 * will return a collection in the following format:
+	 *
+	 * ```php
+	 * [
+	 *     'items' => [...],  // assuming collectionEnvelope is "items"
+	 *     '_links' => {  // pagination links as returned by Pagination::getLinks()
+	 *         'self' => '...',
+	 *         'next' => '...',
+	 *         'last' => '...',
+	 *     },
+	 *     '_meta' => {  // meta information as returned by Pagination::toArray()
+	 *         'totalCount' => 100,
+	 *         'pageCount' => 5,
+	 *         'currentPage' => 1,
+	 *         'perPage' => 20,
+	 *     },
+	 * ]
+	 * ```
+	 *
+	 * If this property is not set, the resource arrays will be directly returned without using envelope.
+	 * The pagination information as shown in `_links` and `_meta` can be accessed from the response HTTP headers.
+	 */
+	public $collectionEnvelope;
+	/**
+	 * @var Request the current request. If not set, the `request` application component will be used.
+	 */
+	public $request;
+	/**
+	 * @var Response the response to be sent. If not set, the `response` application component will be used.
+	 */
+	public $response;
+
+	/**
+	 * @inheritdoc
+	 */
+	public function init()
+	{
+		if ($this->request === null) {
+			$this->request = Yii::$app->getRequest();
+		}
+		if ($this->response === null) {
+			$this->response = Yii::$app->getResponse();
+		}
+	}
+
+	/**
+	 * Serializes the given data into a format that can be easily turned into other formats.
+	 * This method mainly converts the objects of recognized types into array representation.
+	 * It will not do conversion for unknown object types or non-object data.
+	 * The default implementation will handle [[Model]] and [[DataProviderInterface]].
+	 * You may override this method to support more object types.
+	 * @param mixed $data the data to be serialized.
+	 * @return mixed the converted data.
+	 */
+	public function serialize($data)
+	{
+		if ($data instanceof Model) {
+			return $data->hasErrors() ? $this->serializeModelErrors($data) : $this->serializeModel($data);
+		} elseif ($data instanceof DataProviderInterface) {
+			return $this->serializeDataProvider($data);
+		} else {
+			return $data;
+		}
+	}
+
+	/**
+	 * @return array the names of the requested fields. The first element is an array
+	 * representing the list of default fields requested, while the second element is
+	 * an array of the extra fields requested in addition to the default fields.
+	 * @see Model::fields()
+	 * @see Model::extraFields()
+	 */
+	protected function getRequestedFields()
+	{
+		$fields = $this->request->get($this->fieldsParam);
+		$expand = $this->request->get($this->expandParam);
+		return [
+			preg_split('/\s*,\s*/', $fields, -1, PREG_SPLIT_NO_EMPTY),
+			preg_split('/\s*,\s*/', $expand, -1, PREG_SPLIT_NO_EMPTY),
+		];
+	}
+
+	/**
+	 * Serializes a data provider.
+	 * @param DataProviderInterface $dataProvider
+	 * @return array the array representation of the data provider.
+	 */
+	protected function serializeDataProvider($dataProvider)
+	{
+		$models = $this->serializeModels($dataProvider->getModels());
+
+		if (($pagination = $dataProvider->getPagination()) !== false) {
+			$this->addPaginationHeaders($pagination);
+		}
+
+		if ($this->request->getIsHead()) {
+			return null;
+		} elseif ($this->collectionEnvelope === null) {
+			return $models;
+		} else {
+			$result = [
+				$this->collectionEnvelope => $models,
+			];
+			if ($pagination !== false) {
+				$result['_links'] = Link::serialize($pagination->getLinks());
+				$result['_meta'] = $pagination->toArray();
+			}
+			return $result;
+		}
+	}
+
+	/**
+	 * Adds HTTP headers about the pagination to the response.
+	 * @param Pagination $pagination
+	 */
+	protected function addPaginationHeaders($pagination)
+	{
+		$links = [];
+		foreach ($pagination->getLinks(true) as $rel => $url) {
+			$links[] = "<$url>; rel=$rel";
+		}
+
+		$this->response->getHeaders()
+			->set($this->totalCountHeader, $pagination->totalCount)
+			->set($this->pageCountHeader, $pagination->getPageCount())
+			->set($this->currentPageHeader, $pagination->getPage() + 1)
+			->set($this->perPageHeader, $pagination->pageSize)
+			->set('Link', implode(', ', $links));
+	}
+
+	/**
+	 * Serializes a model object.
+	 * @param Model $model
+	 * @return array the array representation of the model
+	 */
+	protected function serializeModel($model)
+	{
+		if ($this->request->getIsHead()) {
+			return null;
+		} else {
+			list ($fields, $expand) = $this->getRequestedFields();
+			return $model->toArray($fields, $expand);
+		}
+	}
+
+	/**
+	 * Serializes the validation errors in a model.
+	 * @param Model $model
+	 * @return array the array representation of the errors
+	 */
+	protected function serializeModelErrors($model)
+	{
+		$this->response->setStatusCode(422, 'Data Validation Failed.');
+		$result = [];
+		foreach ($model->getFirstErrors() as $name => $message) {
+			$result[] = [
+				'field' => $name,
+				'message' => $message,
+			];
+		}
+		return $result;
+	}
+
+	/**
+	 * Serializes a set of models.
+	 * @param array $models
+	 * @return array the array representation of the models
+	 */
+	protected function serializeModels(array $models)
+	{
+		list ($fields, $expand) = $this->getRequestedFields();
+		foreach ($models as $i => $model) {
+			if ($model instanceof Model) {
+				$models[$i] = $model->toArray($fields, $expand);
+			} elseif (is_array($model)) {
+				$models[$i] = ArrayHelper::toArray($model);
+			}
+		}
+		return $models;
+	}
+}
diff --git a/framework/rest/UpdateAction.php b/framework/rest/UpdateAction.php
new file mode 100644
index 0000000..7a14a0a
--- /dev/null
+++ b/framework/rest/UpdateAction.php
@@ -0,0 +1,67 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\Model;
+use yii\db\ActiveRecord;
+
+/**
+ * UpdateAction implements the API endpoint for updating a model.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class UpdateAction extends Action
+{
+	/**
+	 * @var string the scenario to be assigned to the model before it is validated and updated.
+	 */
+	public $scenario = Model::SCENARIO_DEFAULT;
+	/**
+	 * @var boolean whether to start a DB transaction when saving the model.
+	 */
+	public $transactional = true;
+
+
+	/**
+	 * Updates an existing model.
+	 * @param string $id the primary key of the model.
+	 * @return \yii\db\ActiveRecordInterface the model being updated
+	 * @throws \Exception if there is any error when updating the model
+	 */
+	public function run($id)
+	{
+		/** @var ActiveRecord $model */
+		$model = $this->findModel($id);
+
+		if ($this->checkAccess) {
+			call_user_func($this->checkAccess, $this->id, $model);
+		}
+
+		$model->scenario = $this->scenario;
+		$model->load(Yii::$app->getRequest()->getBodyParams(), '');
+
+		if ($this->transactional && $model instanceof ActiveRecord) {
+			if ($model->validate()) {
+				$transaction = $model->getDb()->beginTransaction();
+				try {
+					$model->update(false);
+					$transaction->commit();
+				} catch (\Exception $e) {
+					$transaction->rollback();
+					throw $e;
+				}
+			}
+		} else {
+			$model->save();
+		}
+
+		return $model;
+	}
+}
diff --git a/framework/rest/UrlRule.php b/framework/rest/UrlRule.php
new file mode 100644
index 0000000..5e4b218
--- /dev/null
+++ b/framework/rest/UrlRule.php
@@ -0,0 +1,250 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+use yii\base\InvalidConfigException;
+use yii\helpers\Inflector;
+use yii\web\CompositeUrlRule;
+
+/**
+ * UrlRule is provided to simplify the creation of URL rules for RESTful API support.
+ *
+ * The simplest usage of UrlRule is to declare a rule like the following in the application configuration,
+ *
+ * ```php
+ * [
+ *     'class' => 'yii\rest\UrlRule',
+ *     'controller' => 'user',
+ * ]
+ * ```
+ *
+ * The above code will create a whole set of URL rules supporting the following RESTful API endpoints:
+ *
+ * - `'PUT,PATCH users/<id>' => 'user/update'`: update a user
+ * - `'DELETE users/<id>' => 'user/delete'`: delete a user
+ * - `'GET,HEAD users/<id>' => 'user/view'`: return the details/overview/options of a user
+ * - `'POST users' => 'user/create'`: create a new user
+ * - `'GET,HEAD users' => 'user/index'`: return a list/overview/options of users
+ * - `'users/<id>' => 'user/options'`: process all unhandled verbs of a user
+ * - `'users' => 'user/options'`: process all unhandled verbs of user collection
+ *
+ * You may configure [[only]] and/or [[except]] to disable some of the above rules.
+ * You may configure [[patterns]] to completely redefine your own list of rules.
+ * You may configure [[controller]] with multiple controller IDs to generate rules for all these controllers.
+ * For example, the following code will disable the `delete` rule and generate rules for both `user` and `post` controllers:
+ *
+ * ```php
+ * [
+ *     'class' => 'yii\rest\UrlRule',
+ *     'controller' => ['user', 'post'],
+ *     'except' => ['delete'],
+ * ]
+ * ```
+ *
+ * The property [[controller]] is required and should be the controller ID. It should be prefixed with
+ * the module ID if the controller is within a module.
+ *
+ * The controller ID used in the pattern will be automatically pluralized (e.g. `user` becomes `users`
+ * as shown in the above examples). You may configure [[urlName]] to explicitly specify the controller ID
+ * in the pattern.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class UrlRule extends CompositeUrlRule
+{
+	/**
+	 * @var string the common prefix string shared by all patterns.
+	 */
+	public $prefix;
+	/**
+	 * @var string the suffix that will be assigned to [[\yii\web\UrlRule::suffix]] for every generated rule.
+	 */
+	public $suffix;
+	/**
+	 * @var string|array the controller ID (e.g. `user`, `post-comment`) that the rules in this composite rule
+	 * are dealing with. It should be prefixed with the module ID if the controller is within a module (e.g. `admin/user`).
+	 *
+	 * By default, the controller ID will be pluralized automatically when it is put in the patterns of the
+	 * generated rules. If you want to explicitly specify how the controller ID should appear in the patterns,
+	 * you may use an array with the array key being as the controller ID in the pattern, and the array value
+	 * the actual controller ID. For example, `['u' => 'user']`.
+	 *
+	 * You may also pass multiple controller IDs as an array. If this is the case, this composite rule will
+	 * generate applicable URL rules for EVERY specified controller. For example, `['user', 'post']`.
+	 */
+	public $controller;
+	/**
+	 * @var array list of acceptable actions. If not empty, only the actions within this array
+	 * will have the corresponding URL rules created.
+	 * @see patterns
+	 */
+	public $only = [];
+	/**
+	 * @var array list of actions that should be excluded. Any action found in this array
+	 * will NOT have its URL rules created.
+	 * @see patterns
+	 */
+	public $except = [];
+	/**
+	 * @var array patterns for supporting extra actions in addition to those listed in [[patterns]].
+	 * The keys are the patterns and the values are the corresponding action IDs.
+	 * These extra patterns will take precedence over [[patterns]].
+	 */
+	public $extraPatterns = [];
+	/**
+	 * @var array list of tokens that should be replaced for each pattern. The keys are the token names,
+	 * and the values are the corresponding replacements.
+	 * @see patterns
+	 */
+	public $tokens = [
+		'{id}' => '<id:\\d[\\d,]*>',
+	];
+	/**
+	 * @var array list of possible patterns and the corresponding actions for creating the URL rules.
+	 * The keys are the patterns and the values are the corresponding actions.
+	 * The format of patterns is `Verbs Pattern`, where `Verbs` stands for a list of HTTP verbs separated
+	 * by comma (without space). If `Verbs` is not specified, it means all verbs are allowed.
+	 * `Pattern` is optional. It will be prefixed with [[prefix]]/[[controller]]/,
+	 * and tokens in it will be replaced by [[tokens]].
+	 */
+	public $patterns = [
+		'PUT,PATCH {id}' => 'update',
+		'DELETE {id}' => 'delete',
+		'GET,HEAD {id}' => 'view',
+		'POST' => 'create',
+		'GET,HEAD' => 'index',
+		'{id}' => 'options',
+		'' => 'options',
+	];
+	/**
+	 * @var array the default configuration for creating each URL rule contained by this rule.
+	 */
+	public $ruleConfig = [
+		'class' => 'yii\web\UrlRule',
+	];
+	/**
+	 * @var boolean whether to automatically pluralize the URL names for controllers.
+	 * If true, a controller ID will appear in plural form in URLs. For example, `user` controller
+	 * will appear as `users` in URLs.
+	 * @see controllers
+	 */
+	public $pluralize = true;
+
+
+	/**
+	 * @inheritdoc
+	 */
+	public function init()
+	{
+		if (empty($this->controller)) {
+			throw new InvalidConfigException('"controller" must be set.');
+		}
+
+		$controllers = [];
+		foreach ((array)$this->controller as $urlName => $controller) {
+			if (is_integer($urlName)) {
+				$urlName = $this->pluralize ? Inflector::pluralize($controller) : $controller;
+			}
+			$controllers[$urlName] = $controller;
+		}
+		$this->controller = $controllers;
+
+		$this->prefix = trim($this->prefix, '/');
+
+		parent::init();
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	protected function createRules()
+	{
+		$only = array_flip($this->only);
+		$except = array_flip($this->except);
+		$patterns = array_merge($this->patterns, $this->extraPatterns);
+		$rules = [];
+		foreach ($this->controller as $urlName => $controller) {
+			$prefix = trim($this->prefix . '/' . $urlName, '/');
+			foreach ($patterns as $pattern => $action) {
+				if (!isset($except[$action]) && (empty($only) || isset($only[$action]))) {
+					$rules[$urlName][] = $this->createRule($pattern, $prefix, $controller . '/' . $action);
+				}
+			}
+		}
+		return $rules;
+	}
+
+	/**
+	 * Creates a URL rule using the given pattern and action.
+	 * @param string $pattern
+	 * @param string $prefix
+	 * @param string $action
+	 * @return \yii\web\UrlRuleInterface
+	 */
+	protected function createRule($pattern, $prefix, $action)
+	{
+		$verbs = 'GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS';
+		if (preg_match("/^((?:($verbs),)*($verbs))(?:\\s+(.*))?$/", $pattern, $matches)) {
+			$verbs = explode(',', $matches[1]);
+			$pattern = isset($matches[4]) ? $matches[4] : '';
+		} else {
+			$verbs = [];
+		}
+
+		$config = $this->ruleConfig;
+		$config['verb'] = $verbs;
+		$config['pattern'] = rtrim($prefix . '/' . strtr($pattern, $this->tokens), '/');
+		$config['route'] = $action;
+		if (!in_array('GET', $verbs)) {
+			$config['mode'] = \yii\web\UrlRule::PARSING_ONLY;
+		}
+		$config['suffix'] = $this->suffix;
+
+		return Yii::createObject($config);
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function parseRequest($manager, $request)
+	{
+		$pathInfo = $request->getPathInfo();
+		foreach ($this->rules as $urlName => $rules) {
+			if (strpos($pathInfo, $urlName) !== false) {
+				foreach ($rules as $rule) {
+					/** @var \yii\web\UrlRule $rule */
+					if (($result = $rule->parseRequest($manager, $request)) !== false) {
+						Yii::trace("Request parsed with URL rule: {$rule->name}", __METHOD__);
+						return $result;
+					}
+				}
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function createUrl($manager, $route, $params)
+	{
+		foreach ($this->controller as $urlName => $controller) {
+			if (strpos($route, $controller) !== false) {
+				foreach ($this->rules[$urlName] as $rule) {
+					/** @var \yii\web\UrlRule $rule */
+					if (($url = $rule->createUrl($manager, $route, $params)) !== false) {
+						return $url;
+					}
+				}
+			}
+		}
+		return false;
+	}
+}
diff --git a/framework/rest/ViewAction.php b/framework/rest/ViewAction.php
new file mode 100644
index 0000000..c37522f
--- /dev/null
+++ b/framework/rest/ViewAction.php
@@ -0,0 +1,33 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\rest;
+
+use Yii;
+
+/**
+ * ViewAction implements the API endpoint for returning the detailed information about a model.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class ViewAction extends Action
+{
+	/**
+	 * Displays a model.
+	 * @param string $id the primary key of the model.
+	 * @return \yii\db\ActiveRecordInterface the model being displayed
+	 */
+	public function run($id)
+	{
+		$model = $this->findModel($id);
+		if ($this->checkAccess) {
+			call_user_func($this->checkAccess, $this->id, $model);
+		}
+		return $model;
+	}
+}
diff --git a/framework/test/Fixture.php b/framework/test/Fixture.php
index 5a38ae5..e22a139 100644
--- a/framework/test/Fixture.php
+++ b/framework/test/Fixture.php
@@ -82,4 +82,3 @@ class Fixture extends Component
 	{
 	}
 }
-
diff --git a/framework/validators/ImageValidator.php b/framework/validators/ImageValidator.php
index 52fb235..fa9587d 100644
--- a/framework/validators/ImageValidator.php
+++ b/framework/validators/ImageValidator.php
@@ -124,7 +124,7 @@ class ImageValidator extends FileValidator
 		}
 		if ($this->underHeight === null) {
 			$this->underHeight = Yii::t('yii', 'The image "{file}" is too small. The height cannot be smaller than {limit, number} {limit, plural, one{pixel} other{pixels}}.');
-		}		
+		}
 		if ($this->overWidth === null) {
 			$this->overWidth = Yii::t('yii', 'The image "{file}" is too large. The width cannot be larger than {limit, number} {limit, plural, one{pixel} other{pixels}}.');
 		}
diff --git a/framework/validators/PunycodeAsset.php b/framework/validators/PunycodeAsset.php
index c0c1e2b..5f6a411 100644
--- a/framework/validators/PunycodeAsset.php
+++ b/framework/validators/PunycodeAsset.php
@@ -6,6 +6,7 @@
  */
 
 namespace yii\validators;
+
 use yii\web\AssetBundle;
 
 /**
diff --git a/framework/validators/ValidationAsset.php b/framework/validators/ValidationAsset.php
index 14d7ad0..e9bb79d 100644
--- a/framework/validators/ValidationAsset.php
+++ b/framework/validators/ValidationAsset.php
@@ -6,6 +6,7 @@
  */
 
 namespace yii\validators;
+
 use yii\web\AssetBundle;
 
 /**
diff --git a/framework/validators/Validator.php b/framework/validators/Validator.php
index 764de66..fac3706 100644
--- a/framework/validators/Validator.php
+++ b/framework/validators/Validator.php
@@ -136,7 +136,7 @@ class Validator extends Component
 	{
 		$params['attributes'] = $attributes;
 
-		if ($type instanceof \Closure || method_exists($object, $type)) {
+		if ($type instanceof \Closure || $object->hasMethod($type)) {
 			// method-based validator
 			$params['class'] = __NAMESPACE__ . '\InlineValidator';
 			$params['method'] = $type;
diff --git a/framework/web/AssetBundle.php b/framework/web/AssetBundle.php
index c634755..8fcdf2c 100644
--- a/framework/web/AssetBundle.php
+++ b/framework/web/AssetBundle.php
@@ -112,7 +112,7 @@ class AssetBundle extends Object
 
 	/**
 	 * @param View $view
-	 * @return AssetBundle the registered asset bundle instance
+	 * @return static the registered asset bundle instance
 	 */
 	public static function register($view)
 	{
diff --git a/framework/web/AssetConverter.php b/framework/web/AssetConverter.php
index 1b7d1c8..23062d9 100644
--- a/framework/web/AssetConverter.php
+++ b/framework/web/AssetConverter.php
@@ -82,7 +82,7 @@ class AssetConverter extends Component implements AssetConverterInterface
 		$proc = proc_open($command, $descriptor, $pipes, $basePath);
 		$stdout = stream_get_contents($pipes[1]);
 		$stderr = stream_get_contents($pipes[2]);
-		foreach($pipes as $pipe) {
+		foreach ($pipes as $pipe) {
 			fclose($pipe);
 		}
 		$status = proc_close($proc);
diff --git a/framework/web/CompositeUrlRule.php b/framework/web/CompositeUrlRule.php
new file mode 100644
index 0000000..2382ec5
--- /dev/null
+++ b/framework/web/CompositeUrlRule.php
@@ -0,0 +1,73 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\web;
+
+use Yii;
+use yii\base\Object;
+
+/**
+ * CompositeUrlRule represents a collection of related URL rules.
+ *
+ * These URL rules are typically created for a common purpose (e.g. RESTful API for a resource).
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+abstract class CompositeUrlRule extends Object implements UrlRuleInterface
+{
+	/**
+	 * @var UrlRuleInterface[] the URL rules contained in this composite rule.
+	 * This property is set in [[init()]] by the return value of [[createRules()]].
+	 */
+	protected $rules = [];
+
+
+	/**
+	 * Creates the URL rules that should be contained within this composite rule.
+	 * @return UrlRuleInterface[] the URL rules
+	 */
+	abstract protected function createRules();
+
+	/**
+	 * @inheritdoc
+	 */
+	public function init()
+	{
+		parent::init();
+		$this->rules = $this->createRules();
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function parseRequest($manager, $request)
+	{
+		foreach ($this->rules as $rule) {
+			/** @var \yii\web\UrlRule $rule */
+			if (($result = $rule->parseRequest($manager, $request)) !== false) {
+				Yii::trace("Request parsed with URL rule: {$rule->name}", __METHOD__);
+				return $result;
+			}
+		}
+		return false;
+	}
+
+	/**
+	 * @inheritdoc
+	 */
+	public function createUrl($manager, $route, $params)
+	{
+		foreach ($this->rules as $rule) {
+			/** @var \yii\web\UrlRule $rule */
+			if (($url = $rule->createUrl($manager, $route, $params)) !== false) {
+				return $url;
+			}
+		}
+		return false;
+	}
+}
diff --git a/framework/web/IdentityInterface.php b/framework/web/IdentityInterface.php
index c796b50..2aac17f 100644
--- a/framework/web/IdentityInterface.php
+++ b/framework/web/IdentityInterface.php
@@ -52,6 +52,14 @@ interface IdentityInterface
 	 */
 	public static function findIdentity($id);
 	/**
+	 * Finds an identity by the given secrete token.
+	 * @param string $token the secrete token
+	 * @return IdentityInterface the identity object that matches the given token.
+	 * Null should be returned if such an identity cannot be found
+	 * or the identity is not in an active state (disabled, deleted, etc.)
+	 */
+	public static function findIdentityByAccessToken($token);
+	/**
 	 * Returns an ID that can uniquely identify a user identity.
 	 * @return string|integer an ID that uniquely identifies a user identity.
 	 */
diff --git a/framework/web/Link.php b/framework/web/Link.php
new file mode 100644
index 0000000..9e10e9b
--- /dev/null
+++ b/framework/web/Link.php
@@ -0,0 +1,83 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\web;
+
+use yii\base\Arrayable;
+use yii\base\Object;
+
+/**
+ * Link represents a link object as defined in [JSON Hypermedia API Language](https://tools.ietf.org/html/draft-kelly-json-hal-03).
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class Link extends Object implements Arrayable
+{
+	/**
+	 * The self link.
+	 */
+	const REL_SELF = 'self';
+
+	/**
+	 * @var string a URI [RFC3986](https://tools.ietf.org/html/rfc3986) or
+	 * URI template [RFC6570](https://tools.ietf.org/html/rfc6570). This property is required.
+	 */
+	public $href;
+	/**
+	 * @var string a secondary key for selecting Link Objects which share the same relation type
+	 */
+	public $name;
+	/**
+	 * @var string a hint to indicate the media type expected when dereferencing the target resource
+	 */
+	public $type;
+	/**
+	 * @var boolean a value indicating whether [[href]] refers to a URI or URI template.
+	 */
+	public $templated = false;
+	/**
+	 * @var string a URI that hints about the profile of the target resource.
+	 */
+	public $profile;
+	/**
+	 * @var string a label describing the link
+	 */
+	public $title;
+	/**
+	 * @var string the language of the target resource
+	 */
+	public $hreflang;
+
+	/**
+	 * @inheritdoc
+	 */
+	public function toArray()
+	{
+		return array_filter((array)$this);
+	}
+
+	/**
+	 * Serializes a list of links into proper array format.
+	 * @param array $links the links to be serialized
+	 * @return array the proper array representation of the links.
+	 */
+	public static function serialize(array $links)
+	{
+		foreach ($links as $rel => $link) {
+			if (is_array($link)) {
+				foreach ($link as $i => $l) {
+					$link[$i] = $l instanceof self ? $l->toArray() : ['href' => $l];
+				}
+				$links[$rel] = $link;
+			} elseif (!$link instanceof self) {
+				$links[$rel] = ['href' => $link];
+			}
+		}
+		return $links;
+	}
+}
diff --git a/framework/web/Linkable.php b/framework/web/Linkable.php
new file mode 100644
index 0000000..8d1558b
--- /dev/null
+++ b/framework/web/Linkable.php
@@ -0,0 +1,42 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\web;
+
+/**
+ * Linkable is the interface that should be implemented by classes that typically represent locatable resources.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+interface Linkable
+{
+	/**
+	 * Returns a list of links.
+	 *
+	 * Each link is either a URI or a [[Link]] object. The return value of this method should
+	 * be an array whose keys are the relation names and values the corresponding links.
+	 *
+	 * If a relation name corresponds to multiple links, use an array to represent them.
+	 *
+	 * For example,
+	 *
+	 * ```php
+	 * [
+	 *     'self' => 'http://example.com/users/1',
+	 *     'friends' => [
+	 *         'http://example.com/users/2',
+	 *         'http://example.com/users/3',
+	 *     ],
+	 *     'manager' => $managerLink, // $managerLink is a Link object
+	 * ]
+	 * ```
+	 *
+	 * @return array the links
+	 */
+	public function getLinks();
+}
diff --git a/framework/web/PageCache.php b/framework/web/PageCache.php
index 4c8cc50..08857d5 100644
--- a/framework/web/PageCache.php
+++ b/framework/web/PageCache.php
@@ -136,15 +136,12 @@ class PageCache extends ActionFilter
 	}
 
 	/**
-	 * This method is invoked right after an action is executed.
-	 * You may override this method to do some postprocessing for the action.
-	 * @param Action $action the action just executed.
-	 * @param mixed $result the action execution result
+	 * @inheritdoc
 	 */
-	public function afterAction($action, &$result)
+	public function afterAction($action, $result)
 	{
 		echo $result;
 		$this->view->endCache();
-		$result = ob_get_clean();
+		return ob_get_clean();
 	}
 }
diff --git a/framework/web/Request.php b/framework/web/Request.php
index 01de665..0f9a211 100644
--- a/framework/web/Request.php
+++ b/framework/web/Request.php
@@ -819,6 +819,22 @@ class Request extends \yii\base\Request
 		return isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : null;
 	}
 
+	/**
+	 * @return string the username sent via HTTP authentication, null if the username is not given
+	 */
+	public function getAuthUser()
+	{
+		return isset($_SERVER['PHP_AUTH_USER']) ? $_SERVER['PHP_AUTH_USER'] : null;
+	}
+
+	/**
+	 * @return string the password sent via HTTP authentication, null if the password is not given
+	 */
+	public function getAuthPassword()
+	{
+		return isset($_SERVER['PHP_AUTH_PW']) ? $_SERVER['PHP_AUTH_PW'] : null;
+	}
+
 	private $_port;
 
 	/**
@@ -885,9 +901,23 @@ class Request extends \yii\base\Request
 
 	/**
 	 * Returns the content types acceptable by the end user.
-	 * This is determined by the `Accept` HTTP header.
-	 * @return array the content types ordered by the preference level. The first element
-	 * represents the most preferred content type.
+	 * This is determined by the `Accept` HTTP header. For example,
+	 *
+	 * ```php
+	 * $_SERVER['HTTP_ACCEPT'] = 'text/plain; q=0.5, application/json; version=1.0, application/xml; version=2.0;';
+	 * $types = $request->getAcceptableContentTypes();
+	 * print_r($types);
+	 * // displays:
+	 * // [
+	 * //     'application/json' => ['q' => 1, 'version' => '1.0'],
+	 * //      'application/xml' => ['q' => 1, 'version' => '2.0'],
+	 * //           'text/plain' => ['q' => 0.5],
+	 * // ]
+	 * ```
+	 *
+	 * @return array the content types ordered by the quality score. Types with the highest scores
+	 * will be returned first. The array keys are the content types, while the array values
+	 * are the corresponding quality score and other parameters as given in the header.
 	 */
 	public function getAcceptableContentTypes()
 	{
@@ -902,8 +932,12 @@ class Request extends \yii\base\Request
 	}
 
 	/**
+	 * Sets the acceptable content types.
+	 * Please refer to [[getAcceptableContentTypes()]] on the format of the parameter.
 	 * @param array $value the content types that are acceptable by the end user. They should
 	 * be ordered by the preference level.
+	 * @see getAcceptableContentTypes()
+	 * @see parseAcceptHeader()
 	 */
 	public function setAcceptableContentTypes($value)
 	{
@@ -937,7 +971,7 @@ class Request extends \yii\base\Request
 	{
 		if ($this->_languages === null) {
 			if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) {
-				$this->_languages = $this->parseAcceptHeader($_SERVER['HTTP_ACCEPT_LANGUAGE']);
+				$this->_languages = array_keys($this->parseAcceptHeader($_SERVER['HTTP_ACCEPT_LANGUAGE']));
 			} else {
 				$this->_languages = [];
 			}
@@ -956,45 +990,86 @@ class Request extends \yii\base\Request
 
 	/**
 	 * Parses the given `Accept` (or `Accept-Language`) header.
-	 * This method will return the acceptable values ordered by their preference level.
+	 *
+	 * This method will return the acceptable values with their quality scores and the corresponding parameters
+	 * as specified in the given `Accept` header. The array keys of the return value are the acceptable values,
+	 * while the array values consisting of the corresponding quality scores and parameters. The acceptable
+	 * values with the highest quality scores will be returned first. For example,
+	 *
+	 * ```php
+	 * $header = 'text/plain; q=0.5, application/json; version=1.0, application/xml; version=2.0;';
+	 * $accepts = $request->parseAcceptHeader($header);
+	 * print_r($accepts);
+	 * // displays:
+	 * // [
+	 * //     'application/json' => ['q' => 1, 'version' => '1.0'],
+	 * //      'application/xml' => ['q' => 1, 'version' => '2.0'],
+	 * //           'text/plain' => ['q' => 0.5],
+	 * // ]
+	 * ```
+	 *
 	 * @param string $header the header to be parsed
-	 * @return array the accept values ordered by their preference level.
+	 * @return array the acceptable values ordered by their quality score. The values with the highest scores
+	 * will be returned first.
 	 */
-	protected function parseAcceptHeader($header)
+	public function parseAcceptHeader($header)
 	{
 		$accepts = [];
-		$n = preg_match_all('/\s*([\w\/\-\*]+)\s*(?:;\s*q\s*=\s*([\d\.]+))?[^,]*/', $header, $matches, PREG_SET_ORDER);
-		for ($i = 0; $i < $n; ++$i) {
-			if (!empty($matches[$i][1])) {
-				$accepts[] = [$matches[$i][1], isset($matches[$i][2]) ? (float)$matches[$i][2] : 1, $i];
+		foreach (explode(',', $header) as $i => $part) {
+			$params = preg_split('/\s*;\s*/', trim($part), -1, PREG_SPLIT_NO_EMPTY);
+			if (empty($params)) {
+				continue;
+			}
+			$values = [
+				'q' => [$i, array_shift($params), 1],
+			];
+			foreach ($params as $param) {
+				if (strpos($param, '=') !== false) {
+					list ($key, $value) = explode('=', $param, 2);
+					if ($key === 'q') {
+						$values['q'][2] = (double)$value;
+					} else {
+						$values[$key] = $value;
+					}
+				} else {
+					$values[] = $param;
+				}
 			}
+			$accepts[] = $values;
 		}
+
 		usort($accepts, function ($a, $b) {
-			if ($a[1] > $b[1]) {
+			$a = $a['q']; // index, name, q
+			$b = $b['q'];
+			if ($a[2] > $b[2]) {
 				return -1;
-			} elseif ($a[1] < $b[1]) {
+			} elseif ($a[2] < $b[2]) {
 				return 1;
-			} elseif ($a[0] === $b[0]) {
-				return $a[2] > $b[2] ? 1 : -1;
-			} elseif ($a[0] === '*/*') {
+			} elseif ($a[1] === $b[1]) {
+				return $a[0] > $b[0] ? 1 : -1;
+			} elseif ($a[1] === '*/*') {
 				return 1;
-			} elseif ($b[0] === '*/*') {
+			} elseif ($b[1] === '*/*') {
 				return -1;
 			} else {
-				$wa = $a[0][strlen($a[0]) - 1] === '*';
-				$wb = $b[0][strlen($b[0]) - 1] === '*';
+				$wa = $a[1][strlen($a[1]) - 1] === '*';
+				$wb = $b[1][strlen($b[1]) - 1] === '*';
 				if ($wa xor $wb) {
 					return $wa ? 1 : -1;
 				} else {
-					return $a[2] > $b[2] ? 1 : -1;
+					return $a[0] > $b[0] ? 1 : -1;
 				}
 			}
 		});
+
 		$result = [];
 		foreach ($accepts as $accept) {
-			$result[] = $accept[0];
+			$name = $accept['q'][1];
+			$accept['q'] = $accept['q'][2];
+			$result[$name] = $accept;
 		}
-		return array_unique($result);
+
+		return $result;
 	}
 
 	/**
diff --git a/framework/web/Session.php b/framework/web/Session.php
index 407bfbf..b43b882 100644
--- a/framework/web/Session.php
+++ b/framework/web/Session.php
@@ -75,10 +75,6 @@ use yii\base\InvalidParamException;
 class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Countable, Arrayable
 {
 	/**
-	 * @var boolean whether the session should be automatically started when the session component is initialized.
-	 */
-	public $autoStart = true;
-	/**
 	 * @var string the name of the session variable that stores the flash message data.
 	 */
 	public $flashParam = '__flash';
@@ -100,9 +96,6 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 	public function init()
 	{
 		parent::init();
-		if ($this->autoStart) {
-			$this->open();
-		}
 		register_shutdown_function([$this, 'close']);
 	}
 
@@ -123,10 +116,32 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 	 */
 	public function open()
 	{
-		if (session_status() == PHP_SESSION_ACTIVE) {
+		if ($this->getIsActive()) {
 			return;
 		}
 
+		$this->registerSessionHandler();
+
+		$this->setCookieParamsInternal();
+
+		@session_start();
+
+		if ($this->getIsActive()) {
+			Yii::info('Session started', __METHOD__);
+			$this->updateFlashCounters();
+		} else {
+			$error = error_get_last();
+			$message = isset($error['message']) ? $error['message'] : 'Failed to start session.';
+			Yii::error($message, __METHOD__);
+		}
+	}
+
+	/**
+	 * Registers session handler.
+	 * @throws \yii\base\InvalidConfigException
+	 */
+	protected function registerSessionHandler()
+	{
 		if ($this->handler !== null) {
 			if (!is_object($this->handler)) {
 				$this->handler = Yii::createObject($this->handler);
@@ -145,18 +160,6 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 				[$this, 'gcSession']
 			);
 		}
-
-		$this->setCookieParamsInternal();
-
-		@session_start();
-
-		if (session_id() == '') {
-			$error = error_get_last();
-			$message = isset($error['message']) ? $error['message'] : 'Failed to start session.';
-			Yii::error($message, __METHOD__);
-		} else {
-			$this->updateFlashCounters();
-		}
 	}
 
 	/**
@@ -164,7 +167,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 	 */
 	public function close()
 	{
-		if (session_id() !== '') {
+		if ($this->getIsActive()) {
 			@session_write_close();
 		}
 	}
@@ -174,7 +177,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 	 */
 	public function destroy()
 	{
-		if (session_id() !== '') {
+		if ($this->getIsActive()) {
 			@session_unset();
 			@session_destroy();
 		}
@@ -188,6 +191,42 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 		return session_status() == PHP_SESSION_ACTIVE;
 	}
 
+	private $_hasSessionId;
+
+	/**
+	 * Returns a value indicating whether the current request has sent the session ID.
+	 * The default implementation will check cookie and $_GET using the session name.
+	 * If you send session ID via other ways, you may need to override this method
+	 * or call [[setHasSessionId()]] to explicitly set whether the session ID is sent.
+	 * @return boolean whether the current request has sent the session ID.
+	 */
+	public function getHasSessionId()
+	{
+		if ($this->_hasSessionId === null) {
+			$name = $this->getName();
+			$request = Yii::$app->getRequest();
+			if (ini_get('session.use_cookies') && !empty($_COOKIE[$name])) {
+				$this->_hasSessionId = true;
+			} elseif (!ini_get('use_only_cookies') && ini_get('use_trans_sid')) {
+				$this->_hasSessionId = $request->get($name) !== null;
+			} else {
+				$this->_hasSessionId = false;
+			}
+		}
+		return $this->_hasSessionId;
+	}
+
+	/**
+	 * Sets the value indicating whether the current request has sent the session ID.
+	 * This method is provided so that you can override the default way of determining
+	 * whether the session ID is sent.
+	 * @param boolean $value whether the current request has sent the session ID.
+	 */
+	public function setHasSessionId($value)
+	{
+		$this->_hasSessionId = $value;
+	}
+
 	/**
 	 * @return string the current session ID
 	 */
@@ -506,6 +545,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 	 */
 	public function get($key, $defaultValue = null)
 	{
+		$this->open();
 		return isset($_SESSION[$key]) ? $_SESSION[$key] : $defaultValue;
 	}
 
@@ -517,6 +557,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 	 */
 	public function set($key, $value)
 	{
+		$this->open();
 		$_SESSION[$key] = $value;
 	}
 
@@ -527,6 +568,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 	 */
 	public function remove($key)
 	{
+		$this->open();
 		if (isset($_SESSION[$key])) {
 			$value = $_SESSION[$key];
 			unset($_SESSION[$key]);
@@ -541,6 +583,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 	 */
 	public function removeAll()
 	{
+		$this->open();
 		foreach (array_keys($_SESSION) as $key) {
 			unset($_SESSION[$key]);
 		}
@@ -552,6 +595,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 	 */
 	public function has($key)
 	{
+		$this->open();
 		return isset($_SESSION[$key]);
 	}
 
@@ -560,6 +604,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 	 */
 	public function toArray()
 	{
+		$this->open();
 		return $_SESSION;
 	}
 
@@ -689,6 +734,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 	 */
 	public function offsetExists($offset)
 	{
+		$this->open();
 		return isset($_SESSION[$offset]);
 	}
 
@@ -699,6 +745,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 	 */
 	public function offsetGet($offset)
 	{
+		$this->open();
 		return isset($_SESSION[$offset]) ? $_SESSION[$offset] : null;
 	}
 
@@ -709,6 +756,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 	 */
 	public function offsetSet($offset, $item)
 	{
+		$this->open();
 		$_SESSION[$offset] = $item;
 	}
 
@@ -718,6 +766,7 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
 	 */
 	public function offsetUnset($offset)
 	{
+		$this->open();
 		unset($_SESSION[$offset]);
 	}
 }
diff --git a/framework/web/UrlManager.php b/framework/web/UrlManager.php
index 80f2cb6..85709c9 100644
--- a/framework/web/UrlManager.php
+++ b/framework/web/UrlManager.php
@@ -9,6 +9,7 @@ namespace yii\web;
 
 use Yii;
 use yii\base\Component;
+use yii\base\InvalidConfigException;
 use yii\caching\Cache;
 
 /**
@@ -156,17 +157,22 @@ class UrlManager extends Component
 		}
 
 		$rules = [];
+		$verbs = 'GET|HEAD|POST|PUT|PATCH|DELETE|OPTIONS';
 		foreach ($this->rules as $key => $rule) {
 			if (!is_array($rule)) {
 				$rule = ['route' => $rule];
-				if (preg_match('/^((?:(GET|HEAD|POST|PUT|PATCH|DELETE),)*(GET|HEAD|POST|PUT|PATCH|DELETE))\s+(.*)$/', $key, $matches)) {
+				if (preg_match("/^((?:($verbs),)*($verbs))\\s+(.*)$/", $key, $matches)) {
 					$rule['verb'] = explode(',', $matches[1]);
 					$rule['mode'] = UrlRule::PARSING_ONLY;
 					$key = $matches[4];
 				}
 				$rule['pattern'] = $key;
 			}
-			$rules[] = Yii::createObject(array_merge($this->ruleConfig, $rule));
+			$rule = Yii::createObject(array_merge($this->ruleConfig, $rule));
+			if (!$rule instanceof UrlRuleInterface) {
+				throw new InvalidConfigException('URL rule class must implement UrlRuleInterface.');
+			}
+			$rules[] = $rule;
 		}
 		$this->rules = $rules;
 
@@ -188,7 +194,6 @@ class UrlManager extends Component
 			/** @var UrlRule $rule */
 			foreach ($this->rules as $rule) {
 				if (($result = $rule->parseRequest($this, $request)) !== false) {
-					Yii::trace("Request parsed with URL rule: {$rule->name}", __METHOD__);
 					return $result;
 				}
 			}
@@ -245,7 +250,7 @@ class UrlManager extends Component
 			/** @var UrlRule $rule */
 			foreach ($this->rules as $rule) {
 				if (($url = $rule->createUrl($this, $route, $params)) !== false) {
-					if ($rule->host !== null) {
+					if (strpos($url, '://') !== false) {
 						if ($baseUrl !== '' && ($pos = strpos($url, '/', 8)) !== false) {
 							return substr($url, 0, $pos) . $baseUrl . substr($url, $pos);
 						} else {
diff --git a/framework/web/UrlRule.php b/framework/web/UrlRule.php
index 5a8c3f9..42c9097 100644
--- a/framework/web/UrlRule.php
+++ b/framework/web/UrlRule.php
@@ -7,6 +7,7 @@
 
 namespace yii\web;
 
+use Yii;
 use yii\base\Object;
 use yii\base\InvalidConfigException;
 
@@ -26,7 +27,7 @@ use yii\base\InvalidConfigException;
  * @author Qiang Xue <qiang.xue@gmail.com>
  * @since 2.0
  */
-class UrlRule extends Object
+class UrlRule extends Object implements UrlRuleInterface
 {
 	/**
 	 * Set [[mode]] with this value to mark that this rule is for URL parsing only
@@ -47,7 +48,7 @@ class UrlRule extends Object
 	 */
 	public $pattern;
 	/**
-	 * @var string the pattern used to parse and create the host info part of a URL.
+	 * @var string the pattern used to parse and create the host info part of a URL (e.g. `http://example.com`).
 	 * @see pattern
 	 */
 	public $host;
@@ -127,7 +128,8 @@ class UrlRule extends Object
 		$this->pattern = trim($this->pattern, '/');
 
 		if ($this->host !== null) {
-			$this->pattern = rtrim($this->host, '/') . rtrim('/' . $this->pattern, '/') . '/';
+			$this->host = rtrim($this->host, '/');
+			$this->pattern = rtrim($this->host . '/' . $this->pattern, '/');
 		} elseif ($this->pattern === '') {
 			$this->_template = '';
 			$this->pattern = '#^$#u';
@@ -157,7 +159,7 @@ class UrlRule extends Object
 			foreach ($matches as $match) {
 				$name = $match[1][0];
 				$pattern = isset($match[2][0]) ? $match[2][0] : '[^\/]+';
-				if (isset($this->defaults[$name])) {
+				if (array_key_exists($name, $this->defaults)) {
 					$length = strlen($match[0][0]);
 					$offset = $match[0][1];
 					if ($offset > 1 && $this->pattern[$offset - 1] === '/' && $this->pattern[$offset + $length] === '/') {
@@ -197,7 +199,7 @@ class UrlRule extends Object
 			return false;
 		}
 
-		if ($this->verb !== null && !in_array($request->getMethod(), $this->verb, true)) {
+		if (!empty($this->verb) && !in_array($request->getMethod(), $this->verb, true)) {
 			return false;
 		}
 
@@ -243,6 +245,9 @@ class UrlRule extends Object
 		} else {
 			$route = $this->route;
 		}
+
+		Yii::trace("Request parsed with URL rule: {$this->name}", __METHOD__);
+
 		return [$route, $params];
 	}
 
diff --git a/framework/web/UrlRuleInterface.php b/framework/web/UrlRuleInterface.php
new file mode 100644
index 0000000..e6a5385
--- /dev/null
+++ b/framework/web/UrlRuleInterface.php
@@ -0,0 +1,34 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yii\web;
+
+/**
+ * UrlRuleInterface is the interface that should be implemented URL rule classes.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+interface UrlRuleInterface
+{
+	/**
+	 * Parses the given request and returns the corresponding route and parameters.
+	 * @param UrlManager $manager the URL manager
+	 * @param Request $request the request component
+	 * @return array|boolean the parsing result. The route and the parameters are returned as an array.
+	 * If false, it means this rule cannot be used to parse this path info.
+	 */
+	public function parseRequest($manager, $request);
+	/**
+	 * Creates a URL according to the given route and parameters.
+	 * @param UrlManager $manager the URL manager
+	 * @param string $route the route. It should not have slashes at the beginning or the end.
+	 * @param array $params the parameters
+	 * @return string|boolean the created URL, or false if this rule cannot be used for creating this URL.
+	 */
+	public function createUrl($manager, $route, $params);
+}
diff --git a/framework/web/User.php b/framework/web/User.php
index 927d672..c32ae9c 100644
--- a/framework/web/User.php
+++ b/framework/web/User.php
@@ -14,11 +14,20 @@ use yii\base\InvalidConfigException;
 /**
  * User is the class for the "user" application component that manages the user authentication status.
  *
- * In particular, [[User::isGuest]] returns a value indicating whether the current user is a guest or not.
- * Through methods [[login()]] and [[logout()]], you can change the user authentication status.
+ * You may use [[isGuest]] to determine whether the current user is a guest or not.
+ * If the user is a guest, the [[identity]] property would return null. Otherwise, it would
+ * be an instance of [[IdentityInterface]].
  *
- * User works with a class implementing the [[IdentityInterface]]. This class implements
- * the actual user authentication logic and is often backed by a user database table.
+ * You may call various methods to change the user authentication status:
+ *
+ * - [[login()]]: sets the specified identity and remembers the authentication status in session and cookie.
+ * - [[logout()]]: marks the user as a guest and clears the relevant information from session and cookie.
+ * - [[setIdentity()]]: changes the user identity without touching session or cookie.
+ *   This is best used in stateless RESTful API implementation.
+ *
+ * Note that User only maintains the user authentication status. It does NOT handle how to authenticate
+ * a user. The logic of how to authenticate a user should be done in the class implementing [[IdentityInterface]].
+ * You are also required to set [[identityClass]] with the name of this class.
  *
  * User is configured as an application component in [[\yii\web\Application]] by default.
  * You can access that instance via `Yii::$app->user`.
@@ -124,66 +133,64 @@ class User extends Component
 		if ($this->enableAutoLogin && !isset($this->identityCookie['name'])) {
 			throw new InvalidConfigException('User::identityCookie must contain the "name" element.');
 		}
-
-		Yii::$app->getSession()->open();
-
-		$this->renewAuthStatus();
-
-		if ($this->enableAutoLogin) {
-			if ($this->getIsGuest()) {
-				$this->loginByCookie();
-			} elseif ($this->autoRenewCookie) {
-				$this->renewIdentityCookie();
-			}
-		}
 	}
 
 	private $_identity = false;
 
 	/**
-	 * Returns the identity object associated with the currently logged user.
-	 * @return IdentityInterface the identity object associated with the currently logged user.
+	 * Returns the identity object associated with the currently logged-in user.
+	 * @param boolean $checkSession whether to check the session if the identity has never been determined before.
+	 * If the identity is already determined (e.g., by calling [[setIdentity()]] or [[login()]]),
+	 * then this parameter has no effect.
+	 * @return IdentityInterface the identity object associated with the currently logged-in user.
 	 * Null is returned if the user is not logged in (not authenticated).
 	 * @see login()
 	 * @see logout()
 	 */
-	public function getIdentity()
+	public function getIdentity($checkSession = true)
 	{
 		if ($this->_identity === false) {
-			$id = $this->getId();
-			if ($id === null) {
-				$this->_identity = null;
+			if ($checkSession) {
+				$this->renewAuthStatus();
 			} else {
-				/** @var IdentityInterface $class */
-				$class = $this->identityClass;
-				$this->_identity = $class::findIdentity($id);
+				return null;
 			}
 		}
 		return $this->_identity;
 	}
 
 	/**
-	 * Sets the identity object.
-	 * This method should be mainly be used by the User component or its child class
-	 * to maintain the identity object.
+	 * Sets the user identity object.
+	 *
+	 * This method does nothing else except storing the specified identity object in the internal variable.
+	 * For this reason, this method is best used when the user authentication status should not be maintained
+	 * by session.
 	 *
-	 * You should normally update the user identity via methods [[login()]], [[logout()]]
-	 * or [[switchIdentity()]].
+	 * This method is also called by other more sophisticated methods, such as [[login()]], [[logout()]],
+	 * [[switchIdentity()]]. Those methods will try to use session and cookie to maintain the user authentication
+	 * status.
 	 *
 	 * @param IdentityInterface $identity the identity object associated with the currently logged user.
 	 */
 	public function setIdentity($identity)
 	{
 		$this->_identity = $identity;
+		$this->_access = [];
 	}
 
 	/**
 	 * Logs in a user.
 	 *
-	 * This method stores the necessary session information to keep track
-	 * of the user identity information. If `$duration` is greater than 0
-	 * and [[enableAutoLogin]] is true, it will also send out an identity
-	 * cookie to support cookie-based login.
+	 * By logging in a user, you may obtain the user identity information each time through [[identity]].
+	 *
+	 * The login status is maintained according to the `$duration` parameter:
+	 *
+	 * - `$duration == 0`: the identity information will be stored in session and will be available
+	 *   via [[identity]] as long as the session remains active.
+	 * - `$duration > 0`: the identity information will be stored in session. If [[enableAutoLogin]] is true,
+	 *   it will also be stored in a cookie which will expire in `$duration` seconds. As long as
+	 *   the cookie remains valid or the session is active, you may obtain the user identity information
+	 *   via [[identity]].
 	 *
 	 * @param IdentityInterface $identity the user identity (which should already be authenticated)
 	 * @param integer $duration number of seconds that the user can remain in logged-in status.
@@ -193,17 +200,34 @@ class User extends Component
 	 */
 	public function login($identity, $duration = 0)
 	{
-		if ($this->beforeLogin($identity, false)) {
+		if ($this->beforeLogin($identity, false, $duration)) {
 			$this->switchIdentity($identity, $duration);
 			$id = $identity->getId();
 			$ip = Yii::$app->getRequest()->getUserIP();
-			Yii::info("User '$id' logged in from $ip.", __METHOD__);
-			$this->afterLogin($identity, false);
+			Yii::info("User '$id' logged in from $ip with duration $duration.", __METHOD__);
+			$this->afterLogin($identity, false, $duration);
 		}
 		return !$this->getIsGuest();
 	}
 
 	/**
+	 * Logs in a user by the given access token.
+	 * Note that unlike [[login()]], this method will NOT start a session to remember the user authentication status.
+	 * Also if the access token is invalid, the user will remain as a guest.
+	 * @param string $token the access token
+	 * @return IdentityInterface the identity associated with the given access token. Null is returned if
+	 * the access token is invalid.
+	 */
+	public function loginByAccessToken($token)
+	{
+		/** @var IdentityInterface $class */
+		$class = $this->identityClass;
+		$identity = $class::findIdentityByAccessToken($token);
+		$this->setIdentity($identity);
+		return $identity;
+	}
+
+	/**
 	 * Logs in a user by cookie.
 	 *
 	 * This method attempts to log in a user using the ID and authKey information
@@ -221,11 +245,11 @@ class User extends Component
 				$class = $this->identityClass;
 				$identity = $class::findIdentity($id);
 				if ($identity !== null && $identity->validateAuthKey($authKey)) {
-					if ($this->beforeLogin($identity, true)) {
+					if ($this->beforeLogin($identity, true, $duration)) {
 						$this->switchIdentity($identity, $this->autoRenewCookie ? $duration : 0);
 						$ip = Yii::$app->getRequest()->getUserIP();
 						Yii::info("User '$id' logged in from $ip via cookie.", __METHOD__);
-						$this->afterLogin($identity, true);
+						$this->afterLogin($identity, true, $duration);
 					}
 				} elseif ($identity !== null) {
 					Yii::warning("Invalid auth key attempted for user '$id': $authKey", __METHOD__);
@@ -270,7 +294,8 @@ class User extends Component
 	 */
 	public function getId()
 	{
-		return Yii::$app->getSession()->get($this->idParam);
+		$identity = $this->getIdentity();
+		return $identity !== null ? $identity->getId() : null;
 	}
 
 	/**
@@ -345,13 +370,16 @@ class User extends Component
 	 * so that the event is triggered.
 	 * @param IdentityInterface $identity the user identity information
 	 * @param boolean $cookieBased whether the login is cookie-based
+	 * @param integer $duration number of seconds that the user can remain in logged-in status.
+	 * If 0, it means login till the user closes the browser or the session is manually destroyed.
 	 * @return boolean whether the user should continue to be logged in
 	 */
-	protected function beforeLogin($identity, $cookieBased)
+	protected function beforeLogin($identity, $cookieBased, $duration)
 	{
 		$event = new UserEvent([
 			'identity' => $identity,
 			'cookieBased' => $cookieBased,
+			'duration' => $duration,
 		]);
 		$this->trigger(self::EVENT_BEFORE_LOGIN, $event);
 		return $event->isValid;
@@ -364,12 +392,15 @@ class User extends Component
 	 * so that the event is triggered.
 	 * @param IdentityInterface $identity the user identity information
 	 * @param boolean $cookieBased whether the login is cookie-based
+	 * @param integer $duration number of seconds that the user can remain in logged-in status.
+	 * If 0, it means login till the user closes the browser or the session is manually destroyed.
 	 */
-	protected function afterLogin($identity, $cookieBased)
+	protected function afterLogin($identity, $cookieBased, $duration)
 	{
 		$this->trigger(self::EVENT_AFTER_LOGIN, new UserEvent([
 			'identity' => $identity,
 			'cookieBased' => $cookieBased,
+			'duration' => $duration,
 		]));
 	}
 
@@ -448,15 +479,14 @@ class User extends Component
 	/**
 	 * Switches to a new identity for the current user.
 	 *
-	 * This method will save necessary session information to keep track of the user authentication status.
-	 * If `$duration` is provided, it will also send out appropriate identity cookie
-	 * to support cookie-based login.
+	 * This method may use session and/or cookie to store the user identity information,
+	 * according to the value of `$duration`. Please refer to [[login()]] for more details.
 	 *
 	 * This method is mainly called by [[login()]], [[logout()]] and [[loginByCookie()]]
 	 * when the current user needs to be associated with the corresponding identity information.
 	 *
 	 * @param IdentityInterface $identity the identity information to be associated with the current user.
-	 * If null, it means switching to be a guest.
+	 * If null, it means switching the current user to be a guest.
 	 * @param integer $duration number of seconds that the user can remain in logged-in status.
 	 * This parameter is used only when `$identity` is not null.
 	 */
@@ -483,19 +513,44 @@ class User extends Component
 	}
 
 	/**
-	 * Updates the authentication status according to [[authTimeout]].
-	 * This method is called during [[init()]].
-	 * It will update the user's authentication status if it has not outdated yet.
-	 * Otherwise, it will logout the user.
+	 * Updates the authentication status using the information from session and cookie.
+	 *
+	 * This method will try to determine the user identity using the [[idParam]] session variable.
+	 *
+	 * If [[authTimeout]] is set, this method will refresh the timer.
+	 *
+	 * If the user identity cannot be determined by session, this method will try to [[loginByCookie()|login by cookie]]
+	 * if [[enableAutoLogin]] is true.
 	 */
 	protected function renewAuthStatus()
 	{
-		if ($this->authTimeout !== null && !$this->getIsGuest()) {
-			$expire = Yii::$app->getSession()->get($this->authTimeoutParam);
+		$session = Yii::$app->getSession();
+		$id = $session->getHasSessionId() ? $session->get($this->idParam) : null;
+
+		if ($id === null) {
+			$identity = null;
+		} else {
+			/** @var IdentityInterface $class */
+			$class = $this->identityClass;
+			$identity = $class::findIdentity($id);
+		}
+
+		$this->setIdentity($identity);
+
+		if ($this->authTimeout !== null && $identity !== null) {
+			$expire = $session->get($this->authTimeoutParam);
 			if ($expire !== null && $expire < time()) {
 				$this->logout(false);
 			} else {
-				Yii::$app->getSession()->set($this->authTimeoutParam, time() + $this->authTimeout);
+				$session->set($this->authTimeoutParam, time() + $this->authTimeout);
+			}
+		}
+
+		if ($this->enableAutoLogin) {
+			if ($this->getIsGuest()) {
+				$this->loginByCookie();
+			} elseif ($this->autoRenewCookie) {
+				$this->renewIdentityCookie();
 			}
 		}
 	}
diff --git a/framework/web/UserEvent.php b/framework/web/UserEvent.php
index 8577ef5..bc6d5fe 100644
--- a/framework/web/UserEvent.php
+++ b/framework/web/UserEvent.php
@@ -27,6 +27,11 @@ class UserEvent extends Event
 	 */
 	public $cookieBased;
 	/**
+	 * @var integer $duration number of seconds that the user can remain in logged-in status.
+	 * If 0, it means login till the user closes the browser or the session is manually destroyed.
+	 */
+	public $duration;
+	/**
 	 * @var boolean whether the login or logout should proceed.
 	 * Event handlers may modify this property to determine whether the login or logout should proceed.
 	 * This property is only meaningful for [[User::EVENT_BEFORE_LOGIN]] and [[User::EVENT_BEFORE_LOGOUT]] events.
diff --git a/framework/widgets/ActiveFormAsset.php b/framework/widgets/ActiveFormAsset.php
index 5acb5e1..94b00e5 100644
--- a/framework/widgets/ActiveFormAsset.php
+++ b/framework/widgets/ActiveFormAsset.php
@@ -6,6 +6,7 @@
  */
 
 namespace yii\widgets;
+
 use yii\web\AssetBundle;
 
 /**
diff --git a/framework/widgets/DetailView.php b/framework/widgets/DetailView.php
index 40ea29f..4ba6206 100644
--- a/framework/widgets/DetailView.php
+++ b/framework/widgets/DetailView.php
@@ -57,15 +57,15 @@ class DetailView extends Widget
 	 * @var array a list of attributes to be displayed in the detail view. Each array element
 	 * represents the specification for displaying one particular attribute.
 	 *
-	 * An attribute can be specified as a string in the format of "name", "name:format" or "name:format:label",
-	 * where "name" refers to the attribute name, and "format" represents the format of the attribute. The "format"
+	 * An attribute can be specified as a string in the format of "attribute", "attribute:format" or "attribute:format:label",
+	 * where "attribute" refers to the attribute name, and "format" represents the format of the attribute. The "format"
 	 * is passed to the [[Formatter::format()]] method to format an attribute value into a displayable text.
 	 * Please refer to [[Formatter]] for the supported types. Both "format" and "label" are optional.
 	 * They will take default values if absent.
 	 *
 	 * An attribute can also be specified in terms of an array with the following elements:
 	 *
-	 * - name: the attribute name. This is required if either "label" or "value" is not specified.
+	 * - attribute: the attribute name. This is required if either "label" or "value" is not specified.
 	 * - label: the label associated with the attribute. If this is not specified, it will be generated from the attribute name.
 	 * - value: the value to be displayed. If this is not specified, it will be retrieved from [[model]] using the attribute name
 	 *   by calling [[ArrayHelper::getValue()]]. Note that this value will be formatted into a displayable text
@@ -176,10 +176,10 @@ class DetailView extends Widget
 		foreach ($this->attributes as $i => $attribute) {
 			if (is_string($attribute)) {
 				if (!preg_match('/^([\w\.]+)(:(\w*))?(:(.*))?$/', $attribute, $matches)) {
-					throw new InvalidConfigException('The attribute must be specified in the format of "name", "name:format" or "name:format:label"');
+					throw new InvalidConfigException('The attribute must be specified in the format of "attribute", "attribute:format" or "attribute:format:label"');
 				}
 				$attribute = [
-					'name' => $matches[1],
+					'attribute' => $matches[1],
 					'format' => isset($matches[3]) ? $matches[3] : 'text',
 					'label' => isset($matches[5]) ? $matches[5] : null,
 				];
@@ -197,16 +197,16 @@ class DetailView extends Widget
 			if (!isset($attribute['format'])) {
 				$attribute['format'] = 'text';
 			}
-			if (isset($attribute['name'])) {
-				$name = $attribute['name'];
+			if (isset($attribute['attribute'])) {
+				$attributeName = $attribute['attribute'];
 				if (!isset($attribute['label'])) {
-					$attribute['label'] = $this->model instanceof Model ? $this->model->getAttributeLabel($name) : Inflector::camel2words($name, true);
+					$attribute['label'] = $this->model instanceof Model ? $this->model->getAttributeLabel($attributeName) : Inflector::camel2words($attributeName, true);
 				}
 				if (!array_key_exists('value', $attribute)) {
-					$attribute['value'] = ArrayHelper::getValue($this->model, $name);
+					$attribute['value'] = ArrayHelper::getValue($this->model, $attributeName);
 				}
 			} elseif (!isset($attribute['label']) || !array_key_exists('value', $attribute)) {
-				throw new InvalidConfigException('The attribute configuration requires the "name" element to determine the value and display label.');
+				throw new InvalidConfigException('The attribute configuration requires the "attribute" element to determine the value and display label.');
 			}
 
 			$this->attributes[$i] = $attribute;
diff --git a/framework/widgets/LinkPager.php b/framework/widgets/LinkPager.php
index 06d688a..22cfdf0 100644
--- a/framework/widgets/LinkPager.php
+++ b/framework/widgets/LinkPager.php
@@ -89,6 +89,13 @@ class LinkPager extends Widget
 	 * If this property is null, the "last" page button will not be displayed.
 	 */
 	public $lastPageLabel;
+	/**
+	 * @var bool whether to register link tags in the HTML header for prev, next, first and last page.
+	 * Defaults to `false` to avoid conflicts when multiple pagers are used on one page.
+	 * @see http://www.w3.org/TR/html401/struct/links.html#h-12.1.2
+	 * @see registerLinkTags()
+	 */
+	public $registerLinkTags = false;
 
 
 	/**
@@ -107,10 +114,26 @@ class LinkPager extends Widget
 	 */
 	public function run()
 	{
+		if ($this->registerLinkTags) {
+			$this->registerLinkTags();
+		}
 		echo $this->renderPageButtons();
 	}
 
 	/**
+	 * Registers relational link tags in the html header for prev, next, first and last page.
+	 * These links are generated using [[yii\data\Pagination::getLinks()]].
+	 * @see http://www.w3.org/TR/html401/struct/links.html#h-12.1.2
+	 */
+	protected function registerLinkTags()
+	{
+		$view = $this->getView();
+		foreach ($this->pagination->getLinks() as $rel => $href) {
+			$view->registerLinkTag(['rel' => $rel, 'href' => $href], $rel);
+		}
+	}
+
+	/**
 	 * Renders the page buttons.
 	 * @return string the rendering result
 	 */
diff --git a/framework/widgets/Pjax.php b/framework/widgets/Pjax.php
index 1d7046c..5a60498 100644
--- a/framework/widgets/Pjax.php
+++ b/framework/widgets/Pjax.php
@@ -14,7 +14,7 @@ use yii\helpers\Json;
 use yii\web\Response;
 
 /**
- * Pjax is a widget integrating the [pjax](https://github.com/defunkt/jquery-pjax) jQuery plugin.
+ * Pjax is a widget integrating the [pjax](https://github.com/yiisoft/jquery-pjax) jQuery plugin.
  *
  * Pjax only deals with the content enclosed between its [[begin()]] and [[end()]] calls, called the *body content* of the widget.
  * By default, any link click or form submission (for those forms with `data-pjax` attribute) within the body content
@@ -25,7 +25,9 @@ use yii\web\Response;
  * You may configure [[linkSelector]] to specify which links should trigger pjax, and configure [[formSelector]]
  * to specify which form submission may trigger pjax.
  *
- * The following example shows how to use Pjax with the [[\yii\gridview\GridView]] widget so that the grid pagination,
+ * You may disable pjax for a specific link inside the container by adding `data-pjax="0"` attribute to this link.
+ *
+ * The following example shows how to use Pjax with the [[\yii\grid\GridView]] widget so that the grid pagination,
  * sorting and filtering can be done via pjax:
  *
  * ```php
@@ -78,7 +80,7 @@ class Pjax extends Widget
 	public $scrollTo = false;
 	/**
 	 * @var array additional options to be passed to the pjax JS plugin. Please refer to
-	 * [pjax project page](https://github.com/defunkt/jquery-pjax) for available options.
+	 * [pjax project page](https://github.com/yiisoft/jquery-pjax) for available options.
 	 */
 	public $clientOptions;
 
diff --git a/framework/widgets/PjaxAsset.php b/framework/widgets/PjaxAsset.php
index f3e4912..3fcaf67 100644
--- a/framework/widgets/PjaxAsset.php
+++ b/framework/widgets/PjaxAsset.php
@@ -17,9 +17,9 @@ use yii\web\AssetBundle;
  */
 class PjaxAsset extends AssetBundle
 {
-	public $sourcePath = '@yii/assets';
+	public $sourcePath = '@vendor/yiisoft/jquery-pjax';
 	public $js = [
-		'pjax/jquery.pjax.js',
+		'jquery.pjax.js',
 	];
 	public $depends = [
 		'yii\web\YiiAsset',
diff --git a/tests/unit/data/ar/Customer.php b/tests/unit/data/ar/Customer.php
index 3a64e61..bac3850 100644
--- a/tests/unit/data/ar/Customer.php
+++ b/tests/unit/data/ar/Customer.php
@@ -12,6 +12,9 @@ use yiiunit\framework\db\ActiveRecordTest;
  * @property string $email
  * @property string $address
  * @property integer $status
+ *
+ * @method CustomerQuery|Customer|null find($q = null) static
+ * @method CustomerQuery findBySql($sql, $params = []) static
  */
 class Customer extends ActiveRecord
 {
@@ -25,6 +28,11 @@ class Customer extends ActiveRecord
 		return 'tbl_customer';
 	}
 
+	public function getProfile()
+	{
+		return $this->hasOne(Profile::className(), ['id' => 'profile_id']);
+	}
+
 	public function getOrders()
 	{
 		return $this->hasMany(Order::className(), ['customer_id' => 'id'])->orderBy('id');
diff --git a/tests/unit/data/ar/Profile.php b/tests/unit/data/ar/Profile.php
new file mode 100644
index 0000000..7651d88
--- /dev/null
+++ b/tests/unit/data/ar/Profile.php
@@ -0,0 +1,22 @@
+<?php
+/**
+ * @author Carsten Brandt <mail@cebe.cc>
+ */
+
+namespace yiiunit\data\ar;
+
+/**
+ * Class Profile
+ *
+ * @property integer $id
+ * @property string $description
+ *
+ */
+class Profile extends ActiveRecord
+{
+	public static function tableName()
+	{
+		return 'tbl_profile';
+	}
+
+} 
\ 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 4f3a257..06ad93a 100644
--- a/tests/unit/data/ar/redis/Customer.php
+++ b/tests/unit/data/ar/redis/Customer.php
@@ -13,7 +13,7 @@ class Customer extends ActiveRecord
 
 	public function attributes()
 	{
-		return ['id', 'email', 'name', 'address', 'status'];
+		return ['id', 'email', 'name', 'address', 'status', 'profile_id'];
 	}
 
 	/**
@@ -36,4 +36,4 @@ class Customer extends ActiveRecord
 		$config['modelClass'] = get_called_class();
 		return new CustomerQuery($config);
 	}
-}
\ No newline at end of file
+}
diff --git a/tests/unit/data/cubrid.sql b/tests/unit/data/cubrid.sql
index 1af3c5c..cecc66d 100644
--- a/tests/unit/data/cubrid.sql
+++ b/tests/unit/data/cubrid.sql
@@ -9,6 +9,7 @@ DROP TABLE IF EXISTS tbl_item;
 DROP TABLE IF EXISTS tbl_order;
 DROP TABLE IF EXISTS tbl_category;
 DROP TABLE IF EXISTS tbl_customer;
+DROP TABLE IF EXISTS tbl_profile;
 DROP TABLE IF EXISTS tbl_null_values;
 DROP TABLE IF EXISTS tbl_type;
 DROP TABLE IF EXISTS tbl_constraints;
@@ -20,12 +21,19 @@ CREATE TABLE `tbl_constraints`
 );
 
 
+CREATE TABLE `tbl_profile` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `description` varchar(128) NOT NULL,
+  PRIMARY KEY (`id`)
+);
+
 CREATE TABLE `tbl_customer` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `email` varchar(128) NOT NULL,
   `name` varchar(128),
   `address` string,
   `status` int (11) DEFAULT 0,
+  `profile_id` int(11),
   PRIMARY KEY (`id`)
 );
 
@@ -94,9 +102,12 @@ CREATE TABLE `tbl_composite_fk` (
   CONSTRAINT `FK_composite_fk_order_item` FOREIGN KEY (`order_id`,`item_id`) REFERENCES `tbl_order_item` (`order_id`,`item_id`) ON DELETE CASCADE
 );
 
-INSERT INTO tbl_customer (email, name, address, status) VALUES ('user1@example.com', 'user1', 'address1', 1);
+INSERT INTO tbl_profile (description) VALUES ('profile customer 1');
+INSERT INTO tbl_profile (description) VALUES ('profile customer 3');
+
+INSERT INTO tbl_customer (email, name, address, status, profile_id) VALUES ('user1@example.com', 'user1', 'address1', 1, 1);
 INSERT INTO tbl_customer (email, name, address, status) VALUES ('user2@example.com', 'user2', 'address2', 1);
-INSERT INTO tbl_customer (email, name, address, status) VALUES ('user3@example.com', 'user3', 'address3', 2);
+INSERT INTO tbl_customer (email, name, address, status, profile_id) VALUES ('user3@example.com', 'user3', 'address3', 2, 2);
 
 INSERT INTO tbl_category (name) VALUES ('Books');
 INSERT INTO tbl_category (name) VALUES ('Movies');
diff --git a/tests/unit/data/mssql.sql b/tests/unit/data/mssql.sql
index e25dbbb..0b9dfaa 100644
--- a/tests/unit/data/mssql.sql
+++ b/tests/unit/data/mssql.sql
@@ -3,15 +3,25 @@ IF OBJECT_ID('[dbo].[tbl_item]', 'U') IS NOT NULL DROP TABLE [dbo].[tbl_item];
 IF OBJECT_ID('[dbo].[tbl_order]', 'U') IS NOT NULL DROP TABLE [dbo].[tbl_order];
 IF OBJECT_ID('[dbo].[tbl_category]', 'U') IS NOT NULL DROP TABLE [dbo].[tbl_category];
 IF OBJECT_ID('[dbo].[tbl_customer]', 'U') IS NOT NULL DROP TABLE [dbo].[tbl_customer];
+IF OBJECT_ID('[dbo].[tbl_profile]', 'U') IS NOT NULL DROP TABLE [dbo].[tbl_profile];
 IF OBJECT_ID('[dbo].[tbl_type]', 'U') IS NOT NULL DROP TABLE [dbo].[tbl_type];
 IF OBJECT_ID('[dbo].[tbl_null_values]', 'U') IS NOT NULL DROP TABLE [dbo].[tbl_null_values];
 
+CREATE TABLE [dbo].[tbl_profile] (
+	[id] [int] IDENTITY(1,1) NOT NULL,
+	[description] [varchar](128) NOT NULL,
+	CONSTRAINT [PK_customer] PRIMARY KEY CLUSTERED (
+		[id] ASC
+	) ON [PRIMARY]
+);
+
 CREATE TABLE [dbo].[tbl_customer] (
 	[id] [int] IDENTITY(1,1) NOT NULL,
 	[email] [varchar](128) NOT NULL,
 	[name] [varchar](128),
 	[address] [text],
 	[status] [int] DEFAULT 0,
+  [profile_id] [int],
 	CONSTRAINT [PK_customer] PRIMARY KEY CLUSTERED (
 		[id] ASC
 	) ON [PRIMARY]
@@ -79,9 +89,12 @@ CREATE TABLE [dbo].[tbl_type] (
 	[bool_col2] [tinyint] DEFAULT '1'
 );
 
-INSERT INTO [dbo].[tbl_customer] ([email], [name], [address], [status]) VALUES ('user1@example.com', 'user1', 'address1', 1);
+INSERT INTO [dbo].[tbl_profile] ([description]) VALUES ('profile customer 1');
+INSERT INTO [dbo].[tbl_profile] ([description]) VALUES ('profile customer 3');
+
+INSERT INTO [dbo].[tbl_customer] ([email], [name], [address], [status], [profile_id]) VALUES ('user1@example.com', 'user1', 'address1', 1, 1);
 INSERT INTO [dbo].[tbl_customer] ([email], [name], [address], [status]) VALUES ('user2@example.com', 'user2', 'address2', 1);
-INSERT INTO [dbo].[tbl_customer] ([email], [name], [address], [status]) VALUES ('user3@example.com', 'user3', 'address3', 2);
+INSERT INTO [dbo].[tbl_customer] ([email], [name], [address], [status], [profile_id]) VALUES ('user3@example.com', 'user3', 'address3', 2, 2);
 
 INSERT INTO [dbo].[tbl_category] ([name]) VALUES ('Books');
 INSERT INTO [dbo].[tbl_category] ([name]) VALUES ('Movies');
diff --git a/tests/unit/data/mysql.sql b/tests/unit/data/mysql.sql
index 71d6159..f2e1e77 100644
--- a/tests/unit/data/mysql.sql
+++ b/tests/unit/data/mysql.sql
@@ -9,6 +9,7 @@ DROP TABLE IF EXISTS tbl_item CASCADE;
 DROP TABLE IF EXISTS tbl_order CASCADE;
 DROP TABLE IF EXISTS tbl_category CASCADE;
 DROP TABLE IF EXISTS tbl_customer CASCADE;
+DROP TABLE IF EXISTS tbl_profile CASCADE;
 DROP TABLE IF EXISTS tbl_null_values CASCADE;
 DROP TABLE IF EXISTS tbl_type CASCADE;
 DROP TABLE IF EXISTS tbl_constraints CASCADE;
@@ -20,12 +21,19 @@ CREATE TABLE `tbl_constraints`
 );
 
 
+CREATE TABLE `tbl_profile` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `description` varchar(128) NOT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8;
+
 CREATE TABLE `tbl_customer` (
   `id` int(11) NOT NULL AUTO_INCREMENT,
   `email` varchar(128) NOT NULL,
   `name` varchar(128),
   `address` text,
   `status` int (11) DEFAULT 0,
+  `profile_id` int(11),
   PRIMARY KEY (`id`)
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
@@ -96,9 +104,12 @@ CREATE TABLE `tbl_type` (
   `bool_col2` tinyint(1) DEFAULT '1'
 ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
 
-INSERT INTO tbl_customer (email, name, address, status) VALUES ('user1@example.com', 'user1', 'address1', 1);
+INSERT INTO tbl_profile (description) VALUES ('profile customer 1');
+INSERT INTO tbl_profile (description) VALUES ('profile customer 3');
+
+INSERT INTO tbl_customer (email, name, address, status, profile_id) VALUES ('user1@example.com', 'user1', 'address1', 1, 1);
 INSERT INTO tbl_customer (email, name, address, status) VALUES ('user2@example.com', 'user2', 'address2', 1);
-INSERT INTO tbl_customer (email, name, address, status) VALUES ('user3@example.com', 'user3', 'address3', 2);
+INSERT INTO tbl_customer (email, name, address, status, profile_id) VALUES ('user3@example.com', 'user3', 'address3', 2, 2);
 
 INSERT INTO tbl_category (name) VALUES ('Books');
 INSERT INTO tbl_category (name) VALUES ('Movies');
diff --git a/tests/unit/data/postgres.sql b/tests/unit/data/postgres.sql
index 2d8737b..32557b2 100644
--- a/tests/unit/data/postgres.sql
+++ b/tests/unit/data/postgres.sql
@@ -9,6 +9,7 @@ DROP TABLE IF EXISTS tbl_item CASCADE;
 DROP TABLE IF EXISTS tbl_order CASCADE;
 DROP TABLE IF EXISTS tbl_category CASCADE;
 DROP TABLE IF EXISTS tbl_customer CASCADE;
+DROP TABLE IF EXISTS tbl_profile CASCADE;
 DROP TABLE IF EXISTS tbl_type CASCADE;
 DROP TABLE IF EXISTS tbl_null_values CASCADE;
 DROP TABLE IF EXISTS tbl_constraints CASCADE;
@@ -19,12 +20,18 @@ CREATE TABLE tbl_constraints
   field1 varchar(255)
 );
 
+CREATE TABLE tbl_profile (
+  id serial not null primary key,
+  description varchar(128) NOT NULL
+);
+
 CREATE TABLE tbl_customer (
   id serial not null primary key,
   email varchar(128) NOT NULL,
   name varchar(128),
   address text,
-  status integer DEFAULT 0
+  status integer DEFAULT 0,
+  profile_id integer
 );
 
 comment on column public.tbl_customer.email is 'someone@example.com';
@@ -79,9 +86,12 @@ CREATE TABLE tbl_type (
   bool_col2 smallint DEFAULT '1'
 );
 
-INSERT INTO tbl_customer (email, name, address, status) VALUES ('user1@example.com', 'user1', 'address1', 1);
+INSERT INTO tbl_profile (description) VALUES ('profile customer 1');
+INSERT INTO tbl_profile (description) VALUES ('profile customer 3');
+
+INSERT INTO tbl_customer (email, name, address, status, profile_id) VALUES ('user1@example.com', 'user1', 'address1', 1, 1);
 INSERT INTO tbl_customer (email, name, address, status) VALUES ('user2@example.com', 'user2', 'address2', 1);
-INSERT INTO tbl_customer (email, name, address, status) VALUES ('user3@example.com', 'user3', 'address3', 2);
+INSERT INTO tbl_customer (email, name, address, status, profile_id) VALUES ('user3@example.com', 'user3', 'address3', 2, 2);
 
 INSERT INTO tbl_category (name) VALUES ('Books');
 INSERT INTO tbl_category (name) VALUES ('Movies');
diff --git a/tests/unit/data/sqlite.sql b/tests/unit/data/sqlite.sql
index fcdb68c..8d103d6 100644
--- a/tests/unit/data/sqlite.sql
+++ b/tests/unit/data/sqlite.sql
@@ -9,15 +9,23 @@ DROP TABLE IF EXISTS tbl_item;
 DROP TABLE IF EXISTS tbl_order;
 DROP TABLE IF EXISTS tbl_category;
 DROP TABLE IF EXISTS tbl_customer;
+DROP TABLE IF EXISTS tbl_profile;
 DROP TABLE IF EXISTS tbl_type;
 DROP TABLE IF EXISTS tbl_null_values;
 
+CREATE TABLE tbl_profile (
+  id INTEGER NOT NULL,
+  description varchar(128) NOT NULL,
+  PRIMARY KEY (id)
+);
+
 CREATE TABLE tbl_customer (
   id INTEGER NOT NULL,
   email varchar(128) NOT NULL,
   name varchar(128),
   address text,
   status INTEGER DEFAULT 0,
+  profile_id INTEGER,
   PRIMARY KEY (id)
 );
 
@@ -81,9 +89,12 @@ CREATE TABLE tbl_type (
   bool_col2 tinyint(1) DEFAULT '1'
 );
 
-INSERT INTO tbl_customer (email, name, address, status) VALUES ('user1@example.com', 'user1', 'address1', 1);
+INSERT INTO tbl_profile (description) VALUES ('profile customer 1');
+INSERT INTO tbl_profile (description) VALUES ('profile customer 3');
+
+INSERT INTO tbl_customer (email, name, address, status, profile_id) VALUES ('user1@example.com', 'user1', 'address1', 1, 1);
 INSERT INTO tbl_customer (email, name, address, status) VALUES ('user2@example.com', 'user2', 'address2', 1);
-INSERT INTO tbl_customer (email, name, address, status) VALUES ('user3@example.com', 'user3', 'address3', 2);
+INSERT INTO tbl_customer (email, name, address, status, profile_id) VALUES ('user3@example.com', 'user3', 'address3', 2, 2);
 
 INSERT INTO tbl_category (name) VALUES ('Books');
 INSERT INTO tbl_category (name) VALUES ('Movies');
diff --git a/tests/unit/data/travis/cubrid-setup.sh b/tests/unit/data/travis/cubrid-setup.sh
index 9c3bb74..4188e17 100755
--- a/tests/unit/data/travis/cubrid-setup.sh
+++ b/tests/unit/data/travis/cubrid-setup.sh
@@ -2,6 +2,11 @@
 #
 # install CUBRID DBMS
 
+if (php --version | grep -i HHVM > /dev/null); then
+    echo "Skipping CUBRID on HHVM"
+    exit 0
+fi
+
 # cubrid dbms
 echo 'yes' | sudo add-apt-repository ppa:cubrid/cubrid
 sudo apt-get update
diff --git a/tests/unit/data/travis/memcache-setup.sh b/tests/unit/data/travis/memcache-setup.sh
index 6b623d6..adb743b 100755
--- a/tests/unit/data/travis/memcache-setup.sh
+++ b/tests/unit/data/travis/memcache-setup.sh
@@ -1,4 +1,8 @@
 #!/bin/sh
 
-echo "extension=memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
-echo "extension=memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
+if (php --version | grep -i HHVM > /dev/null); then
+  echo "skipping memcache on HHVM"
+else
+  echo "extension=memcache.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
+  echo "extension=memcached.so" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini
+fi
diff --git a/tests/unit/extensions/mongodb/CollectionTest.php b/tests/unit/extensions/mongodb/CollectionTest.php
index 3eb97b6..9a0c0b4 100644
--- a/tests/unit/extensions/mongodb/CollectionTest.php
+++ b/tests/unit/extensions/mongodb/CollectionTest.php
@@ -187,6 +187,60 @@ class CollectionTest extends MongoDbTestCase
 		$this->assertNotEmpty($result[0]['items']);
 	}
 
+	public function testFindAndModify()
+	{
+		$collection = $this->getConnection()->getCollection('customer');
+		$rows = [
+			[
+				'name' => 'customer 1',
+				'status' => 1,
+				'amount' => 100,
+			],
+			[
+				'name' => 'customer 2',
+				'status' => 1,
+				'amount' => 200,
+			],
+		];
+		$collection->batchInsert($rows);
+
+		// increment field
+		$result = $collection->findAndModify(['name' => 'customer 1'], ['$inc' => ['status' => 1]]);
+		$this->assertEquals('customer 1', $result['name']);
+		$this->assertEquals(1, $result['status']);
+		$newResult = $collection->findOne(['name' => 'customer 1']);
+		$this->assertEquals(2, $newResult['status']);
+
+		// $set and return modified document
+		$result = $collection->findAndModify(
+			['name' => 'customer 2'],
+			['$set' => ['status' => 2]],
+			[],
+			['new' => true]
+		);
+		$this->assertEquals('customer 2', $result['name']);
+		$this->assertEquals(2, $result['status']);
+
+		// Full update document
+		$data = [
+			'name' => 'customer 3',
+			'city' => 'Minsk'
+		];
+		$result = $collection->findAndModify(
+			['name' => 'customer 2'],
+			$data,
+			[],
+			['new' => true]
+		);
+		$this->assertEquals('customer 3', $result['name']);
+		$this->assertEquals('Minsk', $result['city']);
+		$this->assertTrue(!isset($result['status']));
+
+		// Test exceptions
+		$this->setExpectedException('\yii\mongodb\Exception');
+		$collection->findAndModify(['name' => 'customer 1'], ['$wrongOperator' => ['status' => 1]]);
+	}
+
 	/**
 	 * @depends testBatchInsert
 	 */
@@ -240,54 +294,54 @@ class CollectionTest extends MongoDbTestCase
 		$this->assertEquals($expectedRows, $rows);
 	}
 
-    /**
-     * @depends testMapReduce
-     */
-    public function testMapReduceInline()
-    {
-        $collection = $this->getConnection()->getCollection('customer');
-        $rows = [
-            [
-                'name' => 'customer 1',
-                'status' => 1,
-                'amount' => 100,
-            ],
-            [
-                'name' => 'customer 2',
-                'status' => 1,
-                'amount' => 200,
-            ],
-            [
-                'name' => 'customer 2',
-                'status' => 2,
-                'amount' => 400,
-            ],
-            [
-                'name' => 'customer 2',
-                'status' => 3,
-                'amount' => 500,
-            ],
-        ];
-        $collection->batchInsert($rows);
-
-        $result = $collection->mapReduce(
-            'function () {emit(this.status, this.amount)}',
-            'function (key, values) {return Array.sum(values)}',
-            ['inline' => true],
-            ['status' => ['$lt' => 3]]
-        );
-        $expectedRows = [
-            [
-                '_id' => 1,
-                'value' => 300,
-            ],
-            [
-                '_id' => 2,
-                'value' => 400,
-            ],
-        ];
-        $this->assertEquals($expectedRows, $result);
-    }
+	/**
+	 * @depends testMapReduce
+	 */
+	public function testMapReduceInline()
+	{
+		$collection = $this->getConnection()->getCollection('customer');
+		$rows = [
+			[
+				'name' => 'customer 1',
+				'status' => 1,
+				'amount' => 100,
+			],
+			[
+				'name' => 'customer 2',
+				'status' => 1,
+				'amount' => 200,
+			],
+			[
+				'name' => 'customer 2',
+				'status' => 2,
+				'amount' => 400,
+			],
+			[
+				'name' => 'customer 2',
+				'status' => 3,
+				'amount' => 500,
+			],
+		];
+		$collection->batchInsert($rows);
+
+		$result = $collection->mapReduce(
+			'function () {emit(this.status, this.amount)}',
+			'function (key, values) {return Array.sum(values)}',
+			['inline' => true],
+			['status' => ['$lt' => 3]]
+		);
+		$expectedRows = [
+			[
+				'_id' => 1,
+				'value' => 300,
+			],
+			[
+				'_id' => 2,
+				'value' => 400,
+			],
+		];
+		$this->assertEquals($expectedRows, $result);
+	}
 
 	public function testCreateIndex()
 	{
diff --git a/tests/unit/extensions/redis/ActiveRecordTest.php b/tests/unit/extensions/redis/ActiveRecordTest.php
index 5a2102f..a66ef87 100644
--- a/tests/unit/extensions/redis/ActiveRecordTest.php
+++ b/tests/unit/extensions/redis/ActiveRecordTest.php
@@ -33,13 +33,13 @@ class ActiveRecordTest extends RedisTestCase
 		ActiveRecord::$db = $this->getConnection();
 
 		$customer = new Customer();
-		$customer->setAttributes(['email' => 'user1@example.com', 'name' => 'user1', 'address' => 'address1', 'status' => 1], false);
+		$customer->setAttributes(['email' => 'user1@example.com', 'name' => 'user1', 'address' => 'address1', 'status' => 1, 'profile_id' => 1], false);
 		$customer->save(false);
 		$customer = new Customer();
-		$customer->setAttributes(['email' => 'user2@example.com', 'name' => 'user2', 'address' => 'address2', 'status' => 1], false);
+		$customer->setAttributes(['email' => 'user2@example.com', 'name' => 'user2', 'address' => 'address2', 'status' => 1, 'profile_id' => null], false);
 		$customer->save(false);
 		$customer = new Customer();
-		$customer->setAttributes(['email' => 'user3@example.com', 'name' => 'user3', 'address' => 'address3', 'status' => 2], false);
+		$customer->setAttributes(['email' => 'user3@example.com', 'name' => 'user3', 'address' => 'address3', 'status' => 2, 'profile_id' => 2], false);
 		$customer->save(false);
 
 //		INSERT INTO tbl_category (name) VALUES ('Books');
diff --git a/tests/unit/framework/ar/ActiveRecordTestTrait.php b/tests/unit/framework/ar/ActiveRecordTestTrait.php
index 6bc27d4..cde8402 100644
--- a/tests/unit/framework/ar/ActiveRecordTestTrait.php
+++ b/tests/unit/framework/ar/ActiveRecordTestTrait.php
@@ -154,11 +154,12 @@ trait ActiveRecordTestTrait
 		// asArray
 		$customer = $this->callCustomerFind()->where(['id' => 2])->asArray()->one();
 		$this->assertEquals([
-			'id' => '2',
+			'id' => 2,
 			'email' => 'user2@example.com',
 			'name' => 'user2',
 			'address' => 'address2',
-			'status' => '1',
+			'status' => 1,
+			'profile_id' => null,
 		], $customer);
 	}
 
diff --git a/tests/unit/framework/base/ExceptionTest.php b/tests/unit/framework/base/ExceptionTest.php
index 635b55c..136fc28 100644
--- a/tests/unit/framework/base/ExceptionTest.php
+++ b/tests/unit/framework/base/ExceptionTest.php
@@ -15,7 +15,7 @@ class ExceptionTest extends TestCase
 		$this->assertEquals('bar', $array['message']);
 		$this->assertEquals('foo', $array['previous']['message']);
 		
-		$e = new InvalidCallException('bar', 0 ,new UserException('foo'));
+		$e = new InvalidCallException('bar', 0, new UserException('foo'));
 		$array = $e->toArray();
 		$this->assertEquals('bar', $array['message']);
 		$this->assertEquals('foo', $array['previous']['message']);
diff --git a/tests/unit/framework/base/ModelTest.php b/tests/unit/framework/base/ModelTest.php
index d304741..88926a9 100644
--- a/tests/unit/framework/base/ModelTest.php
+++ b/tests/unit/framework/base/ModelTest.php
@@ -147,7 +147,7 @@ class ModelTest extends TestCase
 		$this->assertTrue($speaker->hasErrors('firstName'));
 		$this->assertFalse($speaker->hasErrors('lastName'));
 
-		$this->assertEquals(['Something is wrong!'], $speaker->getFirstErrors());
+		$this->assertEquals(['firstName' => 'Something is wrong!'], $speaker->getFirstErrors());
 		$this->assertEquals('Something is wrong!', $speaker->getFirstError('firstName'));
 		$this->assertNull($speaker->getFirstError('lastName'));
 
diff --git a/tests/unit/framework/db/ActiveRecordTest.php b/tests/unit/framework/db/ActiveRecordTest.php
index 26515df..6ced6c7 100644
--- a/tests/unit/framework/db/ActiveRecordTest.php
+++ b/tests/unit/framework/db/ActiveRecordTest.php
@@ -7,6 +7,7 @@ use yiiunit\data\ar\NullValues;
 use yiiunit\data\ar\OrderItem;
 use yiiunit\data\ar\Order;
 use yiiunit\data\ar\Item;
+use yiiunit\data\ar\Profile;
 use yiiunit\framework\ar\ActiveRecordTestTrait;
 
 /**
@@ -257,6 +258,16 @@ class ActiveRecordTest extends DatabaseTestCase
 		$this->assertTrue($orders[0]->isRelationPopulated('customer'));
 		$this->assertTrue($orders[1]->isRelationPopulated('customer'));
 
+		// inner join filtering, eager loading, conditions on both primary and relation
+		$orders = Order::find()->innerJoinWith([
+			'customer' => function ($query) {
+				$query->where(['tbl_customer.id' => 2]);
+			},
+		])->where(['tbl_order.id' => [1, 2]])->orderBy('tbl_order.id')->all();
+		$this->assertEquals(1, count($orders));
+		$this->assertEquals(2, $orders[0]->id);
+		$this->assertTrue($orders[0]->isRelationPopulated('customer'));
+
 		// inner join filtering without eager loading
 		$orders = Order::find()->innerJoinWith([
 			'customer' => function ($query) {
@@ -269,6 +280,16 @@ class ActiveRecordTest extends DatabaseTestCase
 		$this->assertFalse($orders[0]->isRelationPopulated('customer'));
 		$this->assertFalse($orders[1]->isRelationPopulated('customer'));
 
+		// inner join filtering without eager loading, conditions on both primary and relation
+		$orders = Order::find()->innerJoinWith([
+			'customer' => function ($query) {
+					$query->where(['tbl_customer.id' => 2]);
+				},
+		], false)->where(['tbl_order.id' => [1, 2]])->orderBy('tbl_order.id')->all();
+		$this->assertEquals(1, count($orders));
+		$this->assertEquals(2, $orders[0]->id);
+		$this->assertFalse($orders[0]->isRelationPopulated('customer'));
+
 		// join with via-relation
 		$orders = Order::find()->innerJoinWith('books')->orderBy('tbl_order.id')->all();
 		$this->assertEquals(2, count($orders));
@@ -281,6 +302,9 @@ class ActiveRecordTest extends DatabaseTestCase
 
 		// join with sub-relation
 		$orders = Order::find()->innerJoinWith([
+			'items' => function ($q) {
+				$q->orderBy('tbl_item.id');
+			},
 			'items.category' => function ($q) {
 				$q->where('tbl_category.id = 2');
 			},
@@ -341,6 +365,38 @@ class ActiveRecordTest extends DatabaseTestCase
 		$this->assertEquals(1, count($orders[2]->books2));
 	}
 
+	public function testJoinWithAndScope()
+	{
+		// hasOne inner join
+		$customers = Customer::find()->active()->innerJoinWith('profile')->orderBy('tbl_customer.id')->all();
+		$this->assertEquals(1, count($customers));
+		$this->assertEquals(1, $customers[0]->id);
+		$this->assertTrue($customers[0]->isRelationPopulated('profile'));
+
+		// hasOne outer join
+		$customers = Customer::find()->active()->joinWith('profile')->orderBy('tbl_customer.id')->all();
+		$this->assertEquals(2, count($customers));
+		$this->assertEquals(1, $customers[0]->id);
+		$this->assertEquals(2, $customers[1]->id);
+		$this->assertTrue($customers[0]->isRelationPopulated('profile'));
+		$this->assertTrue($customers[1]->isRelationPopulated('profile'));
+		$this->assertInstanceOf(Profile::className(), $customers[0]->profile);
+		$this->assertNull($customers[1]->profile);
+
+		// hasMany
+		$customers = Customer::find()->active()->joinWith([
+			'orders' => function ($q) {
+				$q->orderBy('tbl_order.id');
+			}
+		])->orderBy('tbl_customer.id DESC, tbl_order.id')->all();
+		$this->assertEquals(2, count($customers));
+		$this->assertEquals(2, $customers[0]->id);
+		$this->assertEquals(1, $customers[1]->id);
+		$this->assertTrue($customers[0]->isRelationPopulated('orders'));
+		$this->assertTrue($customers[1]->isRelationPopulated('orders'));
+
+	}
+
 	public function testInverseOf()
 	{
 		// eager loading: find one and all
diff --git a/tests/unit/framework/rbac/ManagerTestCase.php b/tests/unit/framework/rbac/ManagerTestCase.php
index 6d80287..51c195e 100644
--- a/tests/unit/framework/rbac/ManagerTestCase.php
+++ b/tests/unit/framework/rbac/ManagerTestCase.php
@@ -173,7 +173,7 @@ abstract class ManagerTestCase extends TestCase
 		$this->assertTrue($this->auth->executeBizRule(null, [], null));
 		$this->assertTrue($this->auth->executeBizRule('return 1 == true;', [], null));
 		$this->assertTrue($this->auth->executeBizRule('return $params[0] == $params[1];', [1, '1'], null));
-		if (defined('HHVM_VERSION')) { // invalid code crashes on HHVM
+		if (!defined('HHVM_VERSION')) { // invalid code crashes on HHVM
 			$this->assertFalse($this->auth->executeBizRule('invalid;', [], null));
 		}
 	}
diff --git a/tests/unit/framework/web/RequestTest.php b/tests/unit/framework/web/RequestTest.php
new file mode 100644
index 0000000..656a517
--- /dev/null
+++ b/tests/unit/framework/web/RequestTest.php
@@ -0,0 +1,41 @@
+<?php
+/**
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright (c) 2008 Yii Software LLC
+ * @license http://www.yiiframework.com/license/
+ */
+
+namespace yiiunit\framework\web;
+
+use yii\web\Request;
+use yiiunit\TestCase;
+
+/**
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @since 2.0
+ */
+class RequestTest extends TestCase
+{
+	public function testParseAcceptHeader()
+	{
+		$request = new Request;
+
+		$this->assertEquals([], $request->parseAcceptHeader(' '));
+
+		$this->assertEquals([
+			'audio/basic' => ['q' => 1],
+			'audio/*' => ['q' => 0.2],
+		], $request->parseAcceptHeader('audio/*; q=0.2, audio/basic'));
+
+		$this->assertEquals([
+			'application/json' => ['q' => 1, 'version' => '1.0'],
+			'application/xml' => ['q' => 1, 'version' => '2.0', 'x'],
+			'text/x-c' => ['q' => 1],
+			'text/x-dvi' => ['q' => 0.8],
+			'text/plain' => ['q' => 0.5],
+		], $request->parseAcceptHeader('text/plain; q=0.5,
+			application/json; version=1.0,
+			application/xml; version=2.0; x,
+			text/x-dvi; q=0.8, text/x-c'));
+	}
+}
diff --git a/tests/unit/framework/web/UrlRuleTest.php b/tests/unit/framework/web/UrlRuleTest.php
index 39fa9bd..2909e35 100644
--- a/tests/unit/framework/web/UrlRuleTest.php
+++ b/tests/unit/framework/web/UrlRuleTest.php
@@ -12,6 +12,12 @@ use yiiunit\TestCase;
  */
 class UrlRuleTest extends TestCase
 {
+	protected function setUp()
+	{
+		parent::setUp();
+		$this->mockApplication();
+	}
+
 	public function testCreateUrl()
 	{
 		$manager = new UrlManager(['cache' => null]);