diff --git a/apps/advanced/common/models/User.php b/apps/advanced/common/models/User.php
index e4101e8..62baf48 100644
--- a/apps/advanced/common/models/User.php
+++ b/apps/advanced/common/models/User.php
@@ -3,7 +3,7 @@ namespace common\models;
use yii\db\ActiveRecord;
use yii\helpers\Security;
-use yii\web\Identity;
+use yii\web\IdentityInterface;
/**
* Class User
@@ -20,7 +20,7 @@ use yii\web\Identity;
* @property integer $create_time
* @property integer $update_time
*/
-class User extends ActiveRecord implements Identity
+class User extends ActiveRecord implements IdentityInterface
{
/**
* @var string the raw password. Used to collect password input and isn't saved in database
@@ -49,7 +49,7 @@ class User extends ActiveRecord implements Identity
* Finds an identity by the given ID.
*
* @param string|integer $id the ID to be looked for
- * @return Identity|null the identity object that matches the given ID.
+ * @return IdentityInterface|null the identity object that matches the given ID.
*/
public static function findIdentity($id)
{
@@ -123,7 +123,6 @@ class User extends ActiveRecord implements Identity
{
return array(
'signup' => array('username', 'email', 'password'),
- 'login' => array('username', 'password'),
'resetPassword' => array('password'),
'requestPasswordResetToken' => array('email'),
);
diff --git a/apps/advanced/frontend/controllers/SiteController.php b/apps/advanced/frontend/controllers/SiteController.php
index be9a634..a9413de 100644
--- a/apps/advanced/frontend/controllers/SiteController.php
+++ b/apps/advanced/frontend/controllers/SiteController.php
@@ -164,7 +164,7 @@ class SiteController extends Controller
$headers = "From: $name <{$fromEmail}>\r\n" .
"MIME-Version: 1.0\r\n" .
"Content-type: text/plain; charset=UTF-8";
- return mail($fromEmail, $subject, $body, $headers);
+ return mail($email, $subject, $body, $headers);
}
return false;
diff --git a/apps/advanced/frontend/views/emails/passwordResetToken.php b/apps/advanced/frontend/views/emails/passwordResetToken.php
index 11aa8e9..1e7a855 100644
--- a/apps/advanced/frontend/views/emails/passwordResetToken.php
+++ b/apps/advanced/frontend/views/emails/passwordResetToken.php
@@ -13,4 +13,4 @@ Hello username)?>,
Follow the link below to reset your password:
-
\ No newline at end of file
+
diff --git a/apps/advanced/frontend/widgets/Alert.php b/apps/advanced/frontend/widgets/Alert.php
index 20f3372..b68bfb0 100644
--- a/apps/advanced/frontend/widgets/Alert.php
+++ b/apps/advanced/frontend/widgets/Alert.php
@@ -23,13 +23,13 @@ class Alert extends \yii\bootstrap\Alert
private $_doNotRender = false;
public function init()
{
- if ($this->body = \Yii::$app->getSession()->getFlash('error')) {
+ if ($this->body = \Yii::$app->getSession()->getFlash('error', null, true)) {
Html::addCssClass($this->options, 'alert-danger');
- } elseif ($this->body = \Yii::$app->getSession()->getFlash('success')) {
+ } elseif ($this->body = \Yii::$app->getSession()->getFlash('success', null, true)) {
Html::addCssClass($this->options, 'alert-success');
- } elseif ($this->body = \Yii::$app->getSession()->getFlash('info')) {
+ } elseif ($this->body = \Yii::$app->getSession()->getFlash('info', null, true)) {
Html::addCssClass($this->options, 'alert-info');
- } elseif ($this->body = \Yii::$app->getSession()->getFlash('warning')) {
+ } elseif ($this->body = \Yii::$app->getSession()->getFlash('warning', null, true)) {
Html::addCssClass($this->options, 'alert-warning');
} else {
$this->_doNotRender = true;
diff --git a/apps/basic/controllers/SiteController.php b/apps/basic/controllers/SiteController.php
index 785eddf..1196280 100644
--- a/apps/basic/controllers/SiteController.php
+++ b/apps/basic/controllers/SiteController.php
@@ -3,7 +3,9 @@
namespace app\controllers;
use Yii;
+use yii\web\AccessControl;
use yii\web\Controller;
+use yii\web\VerbFilter;
use app\models\LoginForm;
use app\models\ContactForm;
@@ -13,7 +15,7 @@ class SiteController extends Controller
{
return array(
'access' => array(
- 'class' => \yii\web\AccessControl::className(),
+ 'class' => AccessControl::className(),
'only' => array('login', 'logout'),
'rules' => array(
array(
@@ -28,6 +30,12 @@ class SiteController extends Controller
),
),
),
+ 'verbs' => array(
+ 'class' => VerbFilter::className(),
+ 'actions' => array(
+ 'logout' => array('post'),
+ ),
+ ),
);
}
diff --git a/apps/basic/models/User.php b/apps/basic/models/User.php
index afbf9f8..e1088a0 100644
--- a/apps/basic/models/User.php
+++ b/apps/basic/models/User.php
@@ -2,7 +2,7 @@
namespace app\models;
-class User extends \yii\base\Object implements \yii\web\Identity
+class User extends \yii\base\Object implements \yii\web\IdentityInterface
{
public $id;
public $username;
diff --git a/apps/basic/views/layouts/main.php b/apps/basic/views/layouts/main.php
index 240c2a3..1b7083d 100644
--- a/apps/basic/views/layouts/main.php
+++ b/apps/basic/views/layouts/main.php
@@ -36,7 +36,9 @@ app\config\AppAsset::register($this);
array('label' => 'Contact', 'url' => array('/site/contact')),
Yii::$app->user->isGuest ?
array('label' => 'Login', 'url' => array('/site/login')) :
- array('label' => 'Logout (' . Html::encode(Yii::$app->user->identity->username) .')' , 'url' => array('/site/logout')),
+ array('label' => 'Logout (' . Yii::$app->user->identity->username .')' ,
+ 'url' => array('/site/logout'),
+ 'linkOptions' => array('data-method' => 'post')),
),
));
NavBar::end();
diff --git a/docs/guide/bootstrap-widgets.md b/docs/guide/bootstrap-widgets.md
index 3f56839..432dcd8 100644
--- a/docs/guide/bootstrap-widgets.md
+++ b/docs/guide/bootstrap-widgets.md
@@ -1,17 +1,17 @@
Bootstrap widgets
=================
-Yii includes support for the [Bootstrap 3](http://getbootstrap.com/) markup and components framework out of the box. Bootstrap is an excellent, responsive framework that can greatly speed up your development process.
+Out of the box, Yii includes support for the [Bootstrap 3](http://getbootstrap.com/) markup and components framework (also known as "Twitter Bootstrap"). Bootstrap is an excellent, responsive framework that can greatly speed up the client-side of your development process.
The core of Bootstrap is represented by two parts:
-- CSS basics, such as grid layout system, typography, helper classes, and responsive utilities.
+- CSS basics, such as a grid layout system, typography, helper classes, and responsive utilities.
- Ready to use components, such as menus, pagination, modal boxes, tabs etc.
Basics
------
-Yii doesn't wrap bootstrap basics into PHP code since HTML is very simple by itself in this case. You can find details
+Yii doesn't wrap the bootstrap basics into PHP code since HTML is very simple by itself in this case. You can find details
about using the basics at [bootstrap documentation website](http://getbootstrap.com/css/). Still Yii provides a
convenient way to include bootstrap assets in your pages with a single line added to `AppAsset.php` located in your
`config` directory:
diff --git a/docs/guide/model.md b/docs/guide/model.md
index fec7ac3..b93fb7a 100644
--- a/docs/guide/model.md
+++ b/docs/guide/model.md
@@ -1,15 +1,15 @@
Model
=====
-A model in Yii is intended for application data storage and has the following basic features:
+In keeping with the MVC approach, a model in Yii is intended for storing or temporarily representing application data. Yii models have the following basic features:
-- attribute declaration: a model defines what is considered an attribute.
-- attribute labels: each attribute may be associated with a label for display purpose.
-- massive attribute assignment.
-- scenario-based data validation.
+- Attribute declaration: a model defines what is considered an attribute.
+- Attribute labels: each attribute may be associated with a label for display purpose.
+- Massive attribute assignment: the ability to populate multiple model attributes in one step.
+- Scenario-based data validation.
-Models extending from [[\yii\base\Model]] class are typically used to hold data and corresponding validation rules of complex web forms.
-The class is also a base for more advanced models with additional functionality such as [Active Record](active-record.md).
+Models in Yii extend from the [[\yii\base\Model]] class. Models are typically used to both hold data and define the validation rules for that data. The validation rules greatly simply the generation of models from complex web forms.
+The Model class is also the base for more advanced models with additional functionality such as [Active Record](active-record.md).
Attributes
----------
diff --git a/docs/guide/overview.md b/docs/guide/overview.md
index ef71aa0..835c511 100644
--- a/docs/guide/overview.md
+++ b/docs/guide/overview.md
@@ -24,7 +24,7 @@ Yii is a generic Web programming framework that can be used for developing
virtually any type of Web application. Because it is light-weight and
equipped with sophisticated caching mechanisms, it is especially suited
to high-traffic applications, such as portals, forums, content
-management systems (CMS), e-commerce systems, etc.
+management systems (CMS), e-commerce projects, etc.
How does Yii Compare with Other Frameworks?
diff --git a/docs/guide/security.md b/docs/guide/security.md
index f9adf7c..af30e5b 100644
--- a/docs/guide/security.md
+++ b/docs/guide/security.md
@@ -4,12 +4,9 @@ Security
Hashing and verifying passwords
------------------------------
-It is important not to store passwords in plain text but, contrary to popular belief, just using `md5` or `sha1` to
-compute and verify hashes isn't a good way either. Modern hardware allows to brute force these very fast.
+Most developers know that you cannot store passwords in plain text, but many believe it's safe to hash passwords using `md5` or `sha1`. There was a time when those hashing algorithms were sufficient, but modern hardware makes it possible to break those hashes very quickly using a brute force attack.
-In order to truly secure user passwords even in case your database is leaked you need to use a function that is resistant
-to brute-force such as bcrypt. In PHP it can be achieved by using [crypt function](http://php.net/manual/en/function.crypt.php)
-but since usage isn't trivial and one can easily misuse it, Yii provides two helper functions for generating hash from
+In order to truly secure user passwords, even in the worst case scenario (your database is broken into), you need to use a hashing algorithm that is resistant to brute force attacks. The best current choice is bcrypt. In PHP, you can create a bcrypt hash by using [crypt function](http://php.net/manual/en/function.crypt.php). However, this function is not easy to use properly, so Yii provides two helper functions for generating hash from
password and verifying existing hash.
When user sets his password we're taking password string from POST and then getting a hash:
diff --git a/docs/guide/template.md b/docs/guide/template.md
index 6d2db88..f9405ff 100644
--- a/docs/guide/template.md
+++ b/docs/guide/template.md
@@ -3,8 +3,8 @@ Using template engines
By default Yii uses PHP as template language, but you can configure it to support other rendering engines, such as [Twig](http://twig.sensiolabs.org/) or [Smarty](http://www.smarty.net/).
-The component responsible for rendering a view is called `view`. You can add
-a custom template engines as follows:
+The `view` component is responsible for rendering views. You can add
+a custom template engines by reconfiguring this component's behavior:
```php
array(
@@ -26,15 +26,13 @@ array(
)
```
-Note that Smarty and Twig are not bundled with Yii and you have to download and
-unpack these yourself and then specify `twigPath` and `smartyPath` respectively.
+Note that the Smarty and Twig packages themselves are not bundled with Yii. You must download them yourself. Then unpack the packages and place the resulting files in a logical location, such as the application's `protected/vendor` folder. Finally, specify the correct `smartyPath` or `twigPath`, as in the code above (for Twig).
Twig
----
-In order to use Twig you need to put you templates in files with extension `.twig`
-(or another one if configured differently).
-Also you need to specify this extension explicitly when calling `$this->render()`
+To use Twig, you need to create templates in files with the `.twig` extension (or use another file extension but configure the component accordingly).
+Unlike standard view files, when using Twig, you must include the extension when calling `$this->render()`
or `$this->renderPartial()` from your controller:
```php
@@ -43,25 +41,25 @@ echo $this->render('renderer.twig', array('username' => 'Alex'));
### Additional functions
-Additionally to regular Twig syntax the following is available in Yii:
+Yii adds the following construct to the standard Twig syntax:
```php
{{ post.title }}
```
-path function calls `Html::url()` internally.
+Internally, the `path()` function calls Yii's `Html::url()` method.
### Additional variables
-- `app` = `\Yii::$app`
-- `this` = current `View` object
+Within Twig templates, you can also make use of these variables:
+
+- `app`, which equates to `\Yii::$app`
+- `this`, which equates to the current `View` object
Smarty
------
-In order to use Smarty you need to put you templates in files with extension `.tpl`
-(or another one if configured differently).
-Also you need to specify this extension explicitly when calling `$this->render()`
+To use Smarty, you need to create templates in files with the `.tpl` extension (or use another file extension but configure the component accordingly). Unlike standard view files, when using Smarty, you must include the extension when calling `$this->render()`
or `$this->renderPartial()` from your controller:
```php
@@ -70,16 +68,18 @@ echo $this->render('renderer.tpl', array('username' => 'Alex'));
### Additional functions
-Additionally to regular Smarty syntax the following is available in Yii:
+Yii adds the following construct to the standard Smarty syntax:
```php
{$post.title}
```
-path function calls `Html::url()` internally.
+Internally, the `path()` function calls Yii's `Html::url()` method.
### Additional variables
-- `$app` = `\Yii::$app`
-- `$this` = current `View` object
+Within Smarty templates, you can also make use of these variables:
+
+- `$app`, which equates to `\Yii::$app`
+- `$this`, which equates to the current `View` object
diff --git a/docs/guide/upgrade-from-v1.md b/docs/guide/upgrade-from-v1.md
index ee2a3d5..f174864 100644
--- a/docs/guide/upgrade-from-v1.md
+++ b/docs/guide/upgrade-from-v1.md
@@ -450,11 +450,11 @@ This feature is especially useful if you are developing an application that supp
different DBMS.
-User and Identity
------------------
+User and IdentityInterface
+--------------------------
The `CWebUser` class in 1.1 is now replaced by `\yii\Web\User`, and there is no more
-`CUserIdentity` class. Instead, you should implement the `Identity` interface which
+`CUserIdentity` class. Instead, you should implement the `IdentityInterface` which
is much more straightforward to implement. The bootstrap application provides such an example.
diff --git a/framework/yii/assets/yii.js b/framework/yii/assets/yii.js
index eb5ecf6..add3a02 100644
--- a/framework/yii/assets/yii.js
+++ b/framework/yii/assets/yii.js
@@ -37,26 +37,116 @@
*
* Using this structure, you can define public and private functions/properties for a module.
* Private functions/properties are only visible within the module, while public functions/properties
- * may be accessed outside of the module. For example, you can access "yii.sample.init()".
+ * may be accessed outside of the module. For example, you can access "yii.sample.isActive".
*
* You must call "yii.initModule()" once for the root module of all your modules.
*/
yii = (function ($) {
var pub = {
/**
+ * The selector for clickable elements that need to support confirmation and form submission.
+ */
+ clickableSelector: 'a, button, input[type="submit"], input[type="button"], input[type="reset"], input[type="image"]',
+ /**
+ * The selector for changeable elements that need to support confirmation and form submission.
+ */
+ changeableSelector: 'select, input, textarea',
+
+ /**
* @return string|undefined the CSRF variable name. Undefined is returned is CSRF validation is not enabled.
*/
- getCsrfVar: function() {
+ getCsrfVar: function () {
return $('meta[name=csrf-var]').prop('content');
},
/**
* @return string|undefined the CSRF token. Undefined is returned is CSRF validation is not enabled.
*/
- getCsrfToken: function() {
+ getCsrfToken: function () {
return $('meta[name=csrf-token]').prop('content');
},
+ /**
+ * Displays a confirmation dialog.
+ * The default implementation simply displays a js confirmation dialog.
+ * You may override this by setting `yii.confirm`.
+ * @param message the confirmation message.
+ * @return boolean whether the user confirms with the message in the dialog
+ */
+ confirm: function (message) {
+ return confirm(message);
+ },
+
+ /**
+ * Returns a value indicating whether to allow executing the action defined for the specified element.
+ * This method recognizes the `data-confirm` attribute of the element and uses it
+ * as the message in a confirmation dialog. The method will return true if this special attribute
+ * is not defined or if the user confirms the message.
+ * @param $e the jQuery representation of the element
+ * @return boolean whether to allow executing the action defined for the specified element.
+ */
+ allowAction: function ($e) {
+ var message = $e.data('confirm');
+ return message === undefined || pub.confirm(message);
+ },
+
+ /**
+ * Handles the action triggered by user.
+ * This method recognizes the `data-method` attribute of the element. If the attribute exists,
+ * the method will submit the form containing this element. If there is no containing form, a form
+ * will be created and submitted using the method given by this attribute value (e.g. "post", "put").
+ * For hyperlinks, the form action will take the value of the "href" attribute of the link.
+ * For other elements, either the containing form action or the current page URL will be used
+ * as the form action URL.
+ *
+ * If the `data-method` attribute is not defined, the default element action will be performed.
+ *
+ * @param $e the jQuery representation of the element
+ * @return boolean whether to execute the default action for the element.
+ */
+ handleAction: function ($e) {
+ var method = $e.data('method');
+ if (method === undefined) {
+ return true;
+ }
+
+ var $form = $e.closest('form');
+ var newForm = !$form.length;
+ if (newForm) {
+ var action = $e.prop('href');
+ if (!action || !action.match(/(^\/|:\/\/)/)) {
+ action = window.location.href;
+ }
+ $form = $('
');
+ var target = $e.prop('target');
+ if (target) {
+ $form.attr('target', target);
+ }
+ if (!method.match(/(get|post)/i)) {
+ $form.append('');
+ }
+ var csrfVar = pub.getCsrfVar();
+ if (csrfVar) {
+ $form.append('');
+ }
+ $form.hide().appendTo('body');
+ }
+
+ var activeFormData = $form.data('yiiActiveForm');
+ if (activeFormData) {
+ // remember who triggers the form submission. This is used by yii.activeForm.js
+ activeFormData.submitObject = $e;
+ }
+
+ $form.trigger('submit');
+
+ if (newForm) {
+ $form.remove();
+ }
+
+ return false;
+ },
+
initModule: function (module) {
if (module.isActive === undefined || module.isActive) {
if ($.isFunction(module.init)) {
@@ -68,6 +158,47 @@ yii = (function ($) {
}
});
}
+ },
+
+ init: function () {
+ var $document = $(document);
+
+ // automatically send CSRF token for all AJAX requests
+ $.ajaxPrefilter(function (options, originalOptions, xhr) {
+ if (!options.crossDomain && pub.getCsrfVar()) {
+ xhr.setRequestHeader('X-CSRF-Token', pub.getCsrfToken());
+ }
+ });
+
+ // handle AJAX redirection
+ $document.ajaxComplete(function (event, xhr, settings) {
+ var url = xhr.getResponseHeader('X-Redirect');
+ if (url) {
+ window.location = url;
+ }
+ });
+
+ // handle data-confirm and data-method for clickable elements
+ $document.on('click.yii', pub.clickableSelector, function (event) {
+ var $this = $(this);
+ if (pub.allowAction($this)) {
+ return pub.handleAction($this);
+ } else {
+ event.stopImmediatePropagation();
+ return false;
+ }
+ });
+
+ // handle data-confirm and data-method for changeable elements
+ $document.on('change.yii', pub.changeableSelector, function (event) {
+ var $this = $(this);
+ if (pub.allowAction($this)) {
+ return pub.handleAction($this);
+ } else {
+ event.stopImmediatePropagation();
+ return false;
+ }
+ });
}
};
return pub;
diff --git a/framework/yii/base/Controller.php b/framework/yii/base/Controller.php
index 20f6e2b..3eebaa0 100644
--- a/framework/yii/base/Controller.php
+++ b/framework/yii/base/Controller.php
@@ -210,6 +210,7 @@ class Controller extends Component
/**
* This method is invoked right before an action is to be executed (after all possible filters.)
* You may override this method to do last-minute preparation for the action.
+ * If you override this method, please make sure you call the parent implementation first.
* @param Action $action the action to be executed.
* @return boolean whether the action should continue to be executed.
*/
@@ -223,6 +224,7 @@ class Controller extends Component
/**
* 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.
* @param Action $action the action just executed.
* @param mixed $result the action return result.
*/
diff --git a/framework/yii/base/ErrorHandler.php b/framework/yii/base/ErrorHandler.php
index 41fa7f9..40f5c37 100644
--- a/framework/yii/base/ErrorHandler.php
+++ b/framework/yii/base/ErrorHandler.php
@@ -93,6 +93,8 @@ class ErrorHandler extends Component
$response->getHeaders()->removeAll();
if ($useErrorView && $this->errorAction !== null) {
+ // disable CSRF validation so that errorAction can run in case the error is caused by CSRF validation failure
+ Yii::$app->getRequest()->enableCsrfValidation = false;
$result = Yii::$app->runAction($this->errorAction);
if ($result instanceof Response) {
$response = $result;
diff --git a/framework/yii/base/Module.php b/framework/yii/base/Module.php
index de75ce7..7df87a5 100644
--- a/framework/yii/base/Module.php
+++ b/framework/yii/base/Module.php
@@ -617,7 +617,7 @@ abstract class Module extends Component
if (isset($this->controllerMap[$id])) {
$controller = Yii::createObject($this->controllerMap[$id], $id, $this);
} elseif (preg_match('/^[a-z0-9\\-_]+$/', $id)) {
- $className = str_replace(' ', '', ucwords(implode(' ', explode('-', $id)))) . 'Controller';
+ $className = str_replace(' ', '', ucwords(str_replace('-', ' ', $id))) . 'Controller';
$classFile = $this->controllerPath . DIRECTORY_SEPARATOR . $className . '.php';
if (!is_file($classFile)) {
return false;
diff --git a/framework/yii/caching/MemCache.php b/framework/yii/caching/MemCache.php
index 53202f0..69a90b4 100644
--- a/framework/yii/caching/MemCache.php
+++ b/framework/yii/caching/MemCache.php
@@ -87,7 +87,14 @@ class MemCache extends Cache
parent::init();
$servers = $this->getServers();
$cache = $this->getMemCache();
- if (count($servers)) {
+ if (empty($servers)) {
+ $cache->addServer('127.0.0.1', 11211);
+ } else {
+ if (!$this->useMemcached) {
+ // different version of memcache may have different number of parameters for the addServer method.
+ $class = new \ReflectionClass($cache);
+ $paramCount = $class->getMethod('addServer')->getNumberOfParameters();
+ }
foreach ($servers as $server) {
if ($server->host === null) {
throw new InvalidConfigException("The 'host' property must be specified for every memcache server.");
@@ -97,15 +104,21 @@ class MemCache extends Cache
} else {
// $timeout is used for memcache versions that do not have timeoutms parameter
$timeout = (int) ($server->timeout / 1000) + (($server->timeout % 1000 > 0) ? 1 : 0);
- $cache->addServer(
- $server->host, $server->port, $server->persistent,
- $server->weight, $timeout, $server->retryInterval,
- $server->status, $server->failureCallback, $server->timeout
- );
+ if ($paramCount === 9) {
+ $cache->addServer(
+ $server->host, $server->port, $server->persistent,
+ $server->weight, $timeout, $server->retryInterval,
+ $server->status, $server->failureCallback, $server->timeout
+ );
+ } else {
+ $cache->addServer(
+ $server->host, $server->port, $server->persistent,
+ $server->weight, $timeout, $server->retryInterval,
+ $server->status, $server->failureCallback
+ );
+ }
}
}
- } else {
- $cache->addServer('127.0.0.1', 11211);
}
}
diff --git a/framework/yii/classes.php b/framework/yii/classes.php
index aee93c0..40ca225 100644
--- a/framework/yii/classes.php
+++ b/framework/yii/classes.php
@@ -49,6 +49,7 @@ return array(
'yii\bootstrap\Alert' => YII_PATH . '/bootstrap/Alert.php',
'yii\bootstrap\BootstrapAsset' => YII_PATH . '/bootstrap/BootstrapAsset.php',
'yii\bootstrap\BootstrapPluginAsset' => YII_PATH . '/bootstrap/BootstrapPluginAsset.php',
+ 'yii\bootstrap\BootstrapThemeAsset' => YII_PATH . '/bootstrap/BootstrapThemeAsset.php',
'yii\bootstrap\Button' => YII_PATH . '/bootstrap/Button.php',
'yii\bootstrap\ButtonDropdown' => YII_PATH . '/bootstrap/ButtonDropdown.php',
'yii\bootstrap\ButtonGroup' => YII_PATH . '/bootstrap/ButtonGroup.php',
@@ -60,7 +61,6 @@ return array(
'yii\bootstrap\NavBar' => YII_PATH . '/bootstrap/NavBar.php',
'yii\bootstrap\Progress' => YII_PATH . '/bootstrap/Progress.php',
'yii\bootstrap\Tabs' => YII_PATH . '/bootstrap/Tabs.php',
- 'yii\bootstrap\Typeahead' => YII_PATH . '/bootstrap/Typeahead.php',
'yii\bootstrap\Widget' => YII_PATH . '/bootstrap/Widget.php',
'yii\caching\ApcCache' => YII_PATH . '/caching/ApcCache.php',
'yii\caching\Cache' => YII_PATH . '/caching/Cache.php',
@@ -75,6 +75,7 @@ return array(
'yii\caching\GroupDependency' => YII_PATH . '/caching/GroupDependency.php',
'yii\caching\MemCache' => YII_PATH . '/caching/MemCache.php',
'yii\caching\MemCacheServer' => YII_PATH . '/caching/MemCacheServer.php',
+ 'yii\caching\RedisCache' => YII_PATH . '/caching/RedisCache.php',
'yii\caching\WinCache' => YII_PATH . '/caching/WinCache.php',
'yii\caching\XCache' => YII_PATH . '/caching/XCache.php',
'yii\caching\ZendDataCache' => YII_PATH . '/caching/ZendDataCache.php',
@@ -85,7 +86,7 @@ return array(
'yii\data\ActiveDataProvider' => YII_PATH . '/data/ActiveDataProvider.php',
'yii\data\ArrayDataProvider' => YII_PATH . '/data/ArrayDataProvider.php',
'yii\data\DataProvider' => YII_PATH . '/data/DataProvider.php',
- 'yii\data\IDataProvider' => YII_PATH . '/data/IDataProvider.php',
+ 'yii\data\DataProviderInterface' => YII_PATH . '/data/DataProviderInterface.php',
'yii\data\Pagination' => YII_PATH . '/data/Pagination.php',
'yii\data\Sort' => YII_PATH . '/data/Sort.php',
'yii\db\ActiveQuery' => YII_PATH . '/db/ActiveQuery.php',
@@ -104,10 +105,13 @@ return array(
'yii\db\StaleObjectException' => YII_PATH . '/db/StaleObjectException.php',
'yii\db\TableSchema' => YII_PATH . '/db/TableSchema.php',
'yii\db\Transaction' => YII_PATH . '/db/Transaction.php',
+ 'yii\db\cubrid\QueryBuilder' => YII_PATH . '/db/cubrid/QueryBuilder.php',
+ 'yii\db\cubrid\Schema' => YII_PATH . '/db/cubrid/Schema.php',
'yii\db\mssql\PDO' => YII_PATH . '/db/mssql/PDO.php',
'yii\db\mssql\QueryBuilder' => YII_PATH . '/db/mssql/QueryBuilder.php',
'yii\db\mssql\Schema' => YII_PATH . '/db/mssql/Schema.php',
'yii\db\mssql\SqlsrvPDO' => YII_PATH . '/db/mssql/SqlsrvPDO.php',
+ 'yii\db\mssql\TableSchema' => YII_PATH . '/db/mssql/TableSchema.php',
'yii\db\mysql\QueryBuilder' => YII_PATH . '/db/mysql/QueryBuilder.php',
'yii\db\mysql\Schema' => YII_PATH . '/db/mysql/Schema.php',
'yii\db\pgsql\QueryBuilder' => YII_PATH . '/db/pgsql/QueryBuilder.php',
@@ -162,6 +166,8 @@ return array(
'yii\rbac\Item' => YII_PATH . '/rbac/Item.php',
'yii\rbac\Manager' => YII_PATH . '/rbac/Manager.php',
'yii\rbac\PhpManager' => YII_PATH . '/rbac/PhpManager.php',
+ 'yii\redis\Connection' => YII_PATH . '/redis/Connection.php',
+ 'yii\redis\Transaction' => YII_PATH . '/redis/Transaction.php',
'yii\requirements\YiiRequirementChecker' => YII_PATH . '/requirements/YiiRequirementChecker.php',
'yii\validators\BooleanValidator' => YII_PATH . '/validators/BooleanValidator.php',
'yii\validators\CompareValidator' => YII_PATH . '/validators/CompareValidator.php',
@@ -177,6 +183,7 @@ return array(
'yii\validators\RangeValidator' => YII_PATH . '/validators/RangeValidator.php',
'yii\validators\RegularExpressionValidator' => YII_PATH . '/validators/RegularExpressionValidator.php',
'yii\validators\RequiredValidator' => YII_PATH . '/validators/RequiredValidator.php',
+ 'yii\validators\SafeValidator' => YII_PATH . '/validators/SafeValidator.php',
'yii\validators\StringValidator' => YII_PATH . '/validators/StringValidator.php',
'yii\validators\UniqueValidator' => YII_PATH . '/validators/UniqueValidator.php',
'yii\validators\UrlValidator' => YII_PATH . '/validators/UrlValidator.php',
@@ -197,15 +204,15 @@ return array(
'yii\web\HeaderCollection' => YII_PATH . '/web/HeaderCollection.php',
'yii\web\HttpCache' => YII_PATH . '/web/HttpCache.php',
'yii\web\HttpException' => YII_PATH . '/web/HttpException.php',
- 'yii\web\IAssetConverter' => YII_PATH . '/web/IAssetConverter.php',
- 'yii\web\Identity' => YII_PATH . '/web/Identity.php',
+ 'yii\web\AssetConverterInterface' => YII_PATH . '/web/AssetConverterInterface.php',
+ 'yii\web\IdentityInterface' => YII_PATH . '/web/IdentityInterface.php',
'yii\web\JqueryAsset' => YII_PATH . '/web/JqueryAsset.php',
'yii\web\JsExpression' => YII_PATH . '/web/JsExpression.php',
'yii\web\PageCache' => YII_PATH . '/web/PageCache.php',
'yii\web\Request' => YII_PATH . '/web/Request.php',
'yii\web\Response' => YII_PATH . '/web/Response.php',
'yii\web\ResponseEvent' => YII_PATH . '/web/ResponseEvent.php',
- 'yii\web\ResponseFormatter' => YII_PATH . '/web/ResponseFormatter.php',
+ 'yii\web\ResponseFormatterInterface' => YII_PATH . '/web/ResponseFormatterInterface.php',
'yii\web\Session' => YII_PATH . '/web/Session.php',
'yii\web\SessionIterator' => YII_PATH . '/web/SessionIterator.php',
'yii\web\UploadedFile' => YII_PATH . '/web/UploadedFile.php',
diff --git a/framework/yii/data/DataProvider.php b/framework/yii/data/DataProvider.php
index 84491d6..b29f616 100644
--- a/framework/yii/data/DataProvider.php
+++ b/framework/yii/data/DataProvider.php
@@ -14,7 +14,7 @@ use yii\base\InvalidParamException;
/**
* DataProvider is the base class of data provider classes.
*
- * It implements the [[getPagination()]] and [[getSort()]] methods as specified by the [[IDataProvider]] interface.
+ * It implements the [[getPagination()]] and [[getSort()]] methods as specified by the [[DataProviderInterface]].
*
* @property integer $count The number of data models in the current page. This property is read-only.
* @property Pagination|boolean $pagination The pagination object. If this is false, it means the pagination
@@ -26,7 +26,7 @@ use yii\base\InvalidParamException;
* @author Qiang Xue
* @since 2.0
*/
-abstract class DataProvider extends Component implements IDataProvider
+abstract class DataProvider extends Component implements DataProviderInterface
{
/**
* @var string an ID that uniquely identifies the data provider among all data providers.
diff --git a/framework/yii/data/IDataProvider.php b/framework/yii/data/DataProviderInterface.php
similarity index 92%
rename from framework/yii/data/IDataProvider.php
rename to framework/yii/data/DataProviderInterface.php
index 9ae5546..f0bc39d 100644
--- a/framework/yii/data/IDataProvider.php
+++ b/framework/yii/data/DataProviderInterface.php
@@ -8,7 +8,7 @@
namespace yii\data;
/**
- * IDataProvider is the interface that must be implemented by data provider classes.
+ * DataProviderInterface is the interface that must be implemented by data provider classes.
*
* Data providers are components that sort and paginate data, and provide them to widgets
* such as [[GridView]], [[ListView]].
@@ -16,7 +16,7 @@ namespace yii\data;
* @author Qiang Xue
* @since 2.0
*/
-interface IDataProvider
+interface DataProviderInterface
{
/**
* Returns the number of data models in the current page.
diff --git a/framework/yii/db/ActiveRelation.php b/framework/yii/db/ActiveRelation.php
index fa1def8..f6a6f6a 100644
--- a/framework/yii/db/ActiveRelation.php
+++ b/framework/yii/db/ActiveRelation.php
@@ -279,7 +279,9 @@ class ActiveRelation extends ActiveQuery
// single key
$attribute = reset($this->link);
foreach ($models as $model) {
- $values[] = $model[$attribute];
+ if (($value = $model[$attribute]) !== null) {
+ $values[] = $value;
+ }
}
} else {
// composite keys
diff --git a/framework/yii/db/QueryBuilder.php b/framework/yii/db/QueryBuilder.php
index 3cc7971..f210f65 100644
--- a/framework/yii/db/QueryBuilder.php
+++ b/framework/yii/db/QueryBuilder.php
@@ -585,7 +585,7 @@ class QueryBuilder extends \yii\base\Object
foreach ($tables as $i => $table) {
if (strpos($table, '(') === false) {
- if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/i', $table, $matches)) { // with alias
+ if (preg_match('/^(.*?)(?i:\s+as|)\s+([^ ]+)$/', $table, $matches)) { // with alias
$tables[$i] = $this->db->quoteTableName($matches[1]) . ' ' . $this->db->quoteTableName($matches[2]);
} else {
$tables[$i] = $this->db->quoteTableName($table);
@@ -619,7 +619,7 @@ class QueryBuilder extends \yii\base\Object
// 0:join type, 1:table name, 2:on-condition
$table = $join[1];
if (strpos($table, '(') === false) {
- if (preg_match('/^(.*?)(?i:\s+as\s+|\s+)(.*)$/', $table, $matches)) { // with alias
+ if (preg_match('/^(.*?)(?i:\s+as|)\s+([^ ]+)$/', $table, $matches)) { // with alias
$table = $this->db->quoteTableName($matches[1]) . ' ' . $this->db->quoteTableName($matches[2]);
} else {
$table = $this->db->quoteTableName($table);
diff --git a/framework/yii/gii/generators/crud/templates/controller.php b/framework/yii/gii/generators/crud/templates/controller.php
index fd33eda..f53f819 100644
--- a/framework/yii/gii/generators/crud/templates/controller.php
+++ b/framework/yii/gii/generators/crud/templates/controller.php
@@ -28,12 +28,25 @@ use searchModelClass, '\\'); ?>;
use yii\data\ActiveDataProvider;
use baseControllerClass, '\\'); ?>;
use yii\web\HttpException;
+use yii\web\VerbFilter;
/**
* implements the CRUD actions for model.
*/
class extends baseControllerClass) . "\n"; ?>
{
+ public function behaviors()
+ {
+ return array(
+ 'verbs' => array(
+ 'class' => VerbFilter::className(),
+ 'actions' => array(
+ 'delete' => array('post'),
+ ),
+ ),
+ );
+ }
+
/**
* Lists all models.
* @return mixed
diff --git a/framework/yii/gii/generators/crud/templates/views/index.php b/framework/yii/gii/generators/crud/templates/views/index.php
index 8efa53a..df76581 100644
--- a/framework/yii/gii/generators/crud/templates/views/index.php
+++ b/framework/yii/gii/generators/crud/templates/views/index.php
@@ -43,6 +43,8 @@ $this->params['breadcrumbs'][] = $this->title;
'dataProvider' => $dataProvider,
'filterModel' => $searchModel,
'columns' => array(
+ array('class' => 'yii\grid\SerialColumn'),
+
getTableSchema()->columns as $column) {
@@ -54,6 +56,8 @@ foreach ($generator->getTableSchema()->columns as $column) {
}
}
?>
+
+ array('class' => 'yii\grid\ActionColumn'),
),
)); ?>
@@ -63,7 +67,7 @@ foreach ($generator->getTableSchema()->columns as $column) {
'class' => 'item',
),
'itemView' => function ($model, $key, $index, $widget) {
- return Html::a(Html::encode($model->), array('view', );
+ return Html::a(Html::encode($model->), array('view', ));
},
)); ?>
diff --git a/framework/yii/gii/generators/crud/templates/views/view.php b/framework/yii/gii/generators/crud/templates/views/view.php
index d08ec23..a3b6cad 100644
--- a/framework/yii/gii/generators/crud/templates/views/view.php
+++ b/framework/yii/gii/generators/crud/templates/views/view.php
@@ -31,7 +31,11 @@ $this->params['breadcrumbs'][] = $this->title;
echo DetailView::widget(array(
diff --git a/framework/yii/grid/ActionColumn.php b/framework/yii/grid/ActionColumn.php
new file mode 100644
index 0000000..72fafb6
--- /dev/null
+++ b/framework/yii/grid/ActionColumn.php
@@ -0,0 +1,102 @@
+
+ * @since 2.0
+ */
+class ActionColumn extends Column
+{
+ public $template = '{view} {update} {delete}';
+ public $buttons = array();
+ public $urlCreator;
+
+ public function init()
+ {
+ parent::init();
+ $this->initDefaultButtons();
+ }
+
+ protected function initDefaultButtons()
+ {
+ if (!isset($this->buttons['view'])) {
+ $this->buttons['view'] = function ($model, $column) {
+ /** @var ActionColumn $column */
+ $url = $column->createUrl($model, 'view');
+ return Html::a('', $url, array(
+ 'title' => Yii::t('yii', 'View'),
+ ));
+ };
+ }
+ if (!isset($this->buttons['update'])) {
+ $this->buttons['update'] = function ($model, $column) {
+ /** @var ActionColumn $column */
+ $url = $column->createUrl($model, 'update');
+ return Html::a('', $url, array(
+ 'title' => Yii::t('yii', 'Update'),
+ ));
+ };
+ }
+ if (!isset($this->buttons['delete'])) {
+ $this->buttons['delete'] = function ($model, $column) {
+ /** @var ActionColumn $column */
+ $url = $column->createUrl($model, 'delete');
+ return Html::a('', $url, array(
+ 'title' => Yii::t('yii', 'Delete'),
+ 'data-confirm' => Yii::t('yii', 'Are you sure to delete this item?'),
+ 'data-method' => 'post',
+ ));
+ };
+ }
+ }
+
+ /**
+ * @param \yii\db\ActiveRecord $model
+ * @param string $action
+ * @return string
+ */
+ public function createUrl($model, $action)
+ {
+ if ($this->urlCreator instanceof Closure) {
+ return call_user_func($this->urlCreator, $model, $action);
+ } else {
+ $route = Inflector::camel2id(StringHelper::basename(get_class($model))) . '/' . $action;
+ $params = $model->getPrimaryKey(true);
+ if (count($params) === 1) {
+ $params = array('id' => reset($params));
+ }
+ return Yii::$app->getUrlManager()->createUrl($route, $params);
+ }
+ }
+
+ /**
+ * Renders the data cell content.
+ * @param mixed $model the data model
+ * @param integer $index the zero-based index of the data model among the models array returned by [[dataProvider]].
+ * @return string the rendering result
+ */
+ protected function renderDataCellContent($model, $index)
+ {
+ $column = $this;
+ return preg_replace_callback('/\\{(\w+)\\}/', function ($matches) use ($model, $column) {
+ $name = $matches[1];
+ if (isset($column->buttons[$name])) {
+ return call_user_func($column->buttons[$name], $model, $column);
+ } else {
+ return '';
+ }
+ }, $this->template);
+ }
+}
diff --git a/framework/yii/grid/SerialColumn.php b/framework/yii/grid/SerialColumn.php
index 2fdb770..6a875ae 100644
--- a/framework/yii/grid/SerialColumn.php
+++ b/framework/yii/grid/SerialColumn.php
@@ -15,6 +15,8 @@ namespace yii\grid;
*/
class SerialColumn extends Column
{
+ public $header = '#';
+
/**
* Renders the data cell content.
* @param mixed $model the data model
diff --git a/framework/yii/web/AssetConverter.php b/framework/yii/web/AssetConverter.php
index cd931c9..420a5bc 100644
--- a/framework/yii/web/AssetConverter.php
+++ b/framework/yii/web/AssetConverter.php
@@ -16,7 +16,7 @@ use yii\base\Component;
* @author Qiang Xue
* @since 2.0
*/
-class AssetConverter extends Component implements IAssetConverter
+class AssetConverter extends Component implements AssetConverterInterface
{
/**
* @var array the commands that are used to perform the asset conversion.
diff --git a/framework/yii/web/IAssetConverter.php b/framework/yii/web/AssetConverterInterface.php
similarity index 83%
rename from framework/yii/web/IAssetConverter.php
rename to framework/yii/web/AssetConverterInterface.php
index 6021963..51309c6 100644
--- a/framework/yii/web/IAssetConverter.php
+++ b/framework/yii/web/AssetConverterInterface.php
@@ -8,12 +8,12 @@
namespace yii\web;
/**
- * The IAssetConverter interface must be implemented by asset converter classes.
+ * The AssetConverterInterface must be implemented by asset converter classes.
*
* @author Qiang Xue
* @since 2.0
*/
-interface IAssetConverter
+interface AssetConverterInterface
{
/**
* Converts a given asset file into a CSS or JS file.
diff --git a/framework/yii/web/AssetManager.php b/framework/yii/web/AssetManager.php
index c6f7fea..500848b 100644
--- a/framework/yii/web/AssetManager.php
+++ b/framework/yii/web/AssetManager.php
@@ -16,7 +16,7 @@ use yii\helpers\FileHelper;
/**
* AssetManager manages asset bundles and asset publishing.
*
- * @property IAssetConverter $converter The asset converter. Note that the type of this property differs in
+ * @property AssetConverterInterface $converter The asset converter. Note that the type of this property differs in
* getter and setter. See [[getConverter()]] and [[setConverter()]] for details.
*
* @author Qiang Xue
@@ -116,7 +116,7 @@ class AssetManager extends Component
/**
* Returns the asset converter.
- * @return IAssetConverter the asset converter.
+ * @return AssetConverterInterface the asset converter.
*/
public function getConverter()
{
@@ -130,8 +130,8 @@ class AssetManager extends Component
/**
* Sets the asset converter.
- * @param array|IAssetConverter $value the asset converter. This can be either
- * an object implementing the [[IAssetConverter]] interface, or a configuration
+ * @param array|AssetConverterInterface $value the asset converter. This can be either
+ * an object implementing the [[AssetConverterInterface]], or a configuration
* array that can be used to create the asset converter object.
*/
public function setConverter($value)
diff --git a/framework/yii/web/CacheSession.php b/framework/yii/web/CacheSession.php
index bb387e1..b4ce2ae 100644
--- a/framework/yii/web/CacheSession.php
+++ b/framework/yii/web/CacheSession.php
@@ -42,13 +42,13 @@ class CacheSession extends Session
*/
public function init()
{
- parent::init();
if (is_string($this->cache)) {
$this->cache = Yii::$app->getComponent($this->cache);
}
if (!$this->cache instanceof Cache) {
throw new InvalidConfigException('CacheSession::cache must refer to the application component ID of a cache object.');
}
+ parent::init();
}
/**
diff --git a/framework/yii/web/Controller.php b/framework/yii/web/Controller.php
index adb1b4d..6b8afa4 100644
--- a/framework/yii/web/Controller.php
+++ b/framework/yii/web/Controller.php
@@ -20,6 +20,12 @@ use yii\helpers\Html;
class Controller extends \yii\base\Controller
{
/**
+ * @var boolean whether to enable CSRF validation for the actions in this controller.
+ * CSRF validation is enabled only when both this property and [[Request::enableCsrfValidation]] are true.
+ */
+ public $enableCsrfValidation = true;
+
+ /**
* Binds the parameters to the action.
* This method is invoked by [[Action]] when it begins to run with the given parameters.
* This method will check the parameter names that the action requires and return
@@ -62,6 +68,21 @@ class Controller extends \yii\base\Controller
}
/**
+ * @inheritdoc
+ */
+ public function beforeAction($action)
+ {
+ if (parent::beforeAction($action)) {
+ if ($this->enableCsrfValidation && !Yii::$app->getRequest()->validateCsrfToken()) {
+ throw new HttpException(400, Yii::t('yii', 'Unable to verify your data submission.'));
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
* Creates a URL using the given route and parameters.
*
* This method enhances [[UrlManager::createUrl()]] by supporting relative routes.
@@ -105,8 +126,7 @@ class Controller extends \yii\base\Controller
* Any relative URL will be converted into an absolute one by prepending it with the host info
* of the current request.
*
- * @param integer $statusCode the HTTP status code. If null, it will use 302
- * for normal requests, and [[ajaxRedirectCode]] for AJAX requests.
+ * @param integer $statusCode the HTTP status code. If null, it will use 302.
* See [[http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html]]
* for details about HTTP status code
* @return Response the current response object
diff --git a/framework/yii/web/Identity.php b/framework/yii/web/IdentityInterface.php
similarity index 88%
rename from framework/yii/web/Identity.php
rename to framework/yii/web/IdentityInterface.php
index 101ecdb..c796b50 100644
--- a/framework/yii/web/Identity.php
+++ b/framework/yii/web/IdentityInterface.php
@@ -8,13 +8,13 @@
namespace yii\web;
/**
- * Identity is the interface that should be implemented by a class providing identity information.
+ * IdentityInterface is the interface that should be implemented by a class providing identity information.
*
* This interface can typically be implemented by a user model class. For example, the following
* code shows how to implement this interface by a User ActiveRecord class:
*
* ~~~
- * class User extends ActiveRecord implements Identity
+ * class User extends ActiveRecord implements IdentityInterface
* {
* public static function findIdentity($id)
* {
@@ -41,12 +41,12 @@ namespace yii\web;
* @author Qiang Xue
* @since 2.0
*/
-interface Identity
+interface IdentityInterface
{
/**
* Finds an identity by the given ID.
* @param string|integer $id the ID to be looked for
- * @return Identity the identity object that matches the given ID.
+ * @return IdentityInterface the identity object that matches the given ID.
* Null should be returned if such an identity cannot be found
* or the identity is not in an active state (disabled, deleted, etc.)
*/
diff --git a/framework/yii/web/Request.php b/framework/yii/web/Request.php
index 9e625f7..4fb6257 100644
--- a/framework/yii/web/Request.php
+++ b/framework/yii/web/Request.php
@@ -73,10 +73,10 @@ class Request extends \yii\base\Request
/**
* The name of the HTTP header for sending CSRF token.
*/
- const CSRF_HEADER = 'X-CSRF-TOKEN';
+ const CSRF_HEADER = 'X-CSRF-Token';
/**
- * @var boolean whether to enable CSRF (Cross-Site Request Forgery) validation. Defaults to false.
+ * @var boolean whether to enable CSRF (Cross-Site Request Forgery) validation. Defaults to true.
* When CSRF validation is enabled, forms submitted to an Yii Web application must be originated
* from the same application. If not, a 400 HTTP exception will be raised.
*
@@ -87,9 +87,10 @@ class Request extends \yii\base\Request
* In JavaScript, you may get the values of [[csrfVar]] and [[csrfToken]] via `yii.getCsrfVar()` and
* `yii.getCsrfToken()`, respectively. The [[\yii\web\YiiAsset]] asset must be registered.
*
+ * @see Controller::enableCsrfValidation
* @see http://en.wikipedia.org/wiki/Cross-site_request_forgery
*/
- public $enableCsrfValidation = false;
+ public $enableCsrfValidation = true;
/**
* @var string the name of the token used to prevent CSRF. Defaults to '_csrf'.
* This property is used only when [[enableCsrfValidation]] is true.
@@ -122,8 +123,6 @@ class Request extends \yii\base\Request
*/
public function resolve()
{
- $this->validateCsrfToken();
-
$result = Yii::$app->getUrlManager()->parseRequest($this);
if ($result !== false) {
list ($route, $params) = $result;
@@ -1002,7 +1001,8 @@ class Request extends \yii\base\Request
*/
public function getCsrfTokenFromHeader()
{
- return isset($_SERVER[self::CSRF_HEADER]) ? $_SERVER[self::CSRF_HEADER] : null;
+ $key = 'HTTP_' . str_replace('-', '_', strtoupper(self::CSRF_HEADER));
+ return isset($_SERVER[$key]) ? $_SERVER[$key] : null;
}
/**
@@ -1023,34 +1023,30 @@ class Request extends \yii\base\Request
* Performs the CSRF validation.
* The method will compare the CSRF token obtained from a cookie and from a POST field.
* If they are different, a CSRF attack is detected and a 400 HTTP exception will be raised.
- * @throws HttpException if the validation fails
+ * This method is called in [[Controller::beforeAction()]].
+ * @return boolean whether CSRF token is valid. If [[enableCsrfValidation]] is false, this method will return true.
*/
public function validateCsrfToken()
{
- if (!$this->enableCsrfValidation) {
- return;
- }
$method = $this->getMethod();
- if ($method === 'POST' || $method === 'PUT' || $method === 'PATCH' || $method === 'DELETE') {
- $trueToken = $this->getCookies()->getValue($this->csrfVar);
- switch ($method) {
- case 'POST':
- $token = $this->getPost($this->csrfVar);
- break;
- case 'PUT':
- $token = $this->getPut($this->csrfVar);
- break;
- case 'PATCH':
- $token = $this->getPatch($this->csrfVar);
- break;
- case 'DELETE':
- $token = $this->getDelete($this->csrfVar);
- }
-
- $valid = !empty($token) && $token === $trueToken || $this->getCsrfTokenFromHeader() === $trueToken;
- if (!$valid) {
- throw new HttpException(400, Yii::t('yii', 'Unable to verify your data submission.'));
- }
+ if (!$this->enableCsrfValidation || !in_array($method, array('POST', 'PUT', 'PATCH', 'DELETE'), true)) {
+ return true;
+ }
+ $trueToken = $this->getCookies()->getValue($this->csrfVar);
+ switch ($method) {
+ case 'PUT':
+ $token = $this->getPut($this->csrfVar);
+ break;
+ case 'PATCH':
+ $token = $this->getPatch($this->csrfVar);
+ break;
+ case 'DELETE':
+ $token = $this->getDelete($this->csrfVar);
+ break;
+ default:
+ $token = $this->getPost($this->csrfVar);
+ break;
}
+ return $token === $trueToken || $this->getCsrfTokenFromHeader() === $trueToken;
}
}
diff --git a/framework/yii/web/Response.php b/framework/yii/web/Response.php
index cfbc537..e6505fd 100644
--- a/framework/yii/web/Response.php
+++ b/framework/yii/web/Response.php
@@ -112,13 +112,6 @@ class Response extends \yii\base\Response
*/
public $charset;
/**
- * @var integer the HTTP status code that should be used when redirecting in AJAX mode.
- * This is used by [[redirect()]]. A 2xx code should normally be used for this purpose
- * so that the AJAX handler will treat the response as a success.
- * @see redirect
- */
- public $ajaxRedirectCode = 278;
- /**
* @var string
*/
public $statusText;
@@ -565,17 +558,22 @@ class Response extends \yii\base\Response
/**
* Redirects the browser to the specified URL.
+ *
* This method will send out a "Location" header to achieve the redirection.
+ *
* In AJAX mode, this normally will not work as expected unless there are some
* client-side JavaScript code handling the redirection. To help achieve this goal,
- * this method will use [[ajaxRedirectCode]] as the HTTP status code when performing
- * redirection in AJAX mode. The following JavaScript code may be used on the client
- * side to handle the redirection response:
+ * this method will send out a "X-Redirect" header instead of "Location".
+ *
+ * If you use the "yii" JavaScript module, it will handle the AJAX redirection as
+ * described above. Otherwise, you should write the following JavaScript code to
+ * handle the redirection:
*
* ~~~
- * $(document).ajaxSuccess(function(event, xhr, settings) {
- * if (xhr.status == 278) {
- * window.location = xhr.getResponseHeader('Location');
+ * $document.ajaxComplete(function (event, xhr, settings) {
+ * var url = xhr.getResponseHeader('X-Redirect');
+ * if (url) {
+ * window.location = url;
* }
* });
* ~~~
@@ -597,8 +595,7 @@ class Response extends \yii\base\Response
* Any relative URL will be converted into an absolute one by prepending it with the host info
* of the current request.
*
- * @param integer $statusCode the HTTP status code. If null, it will use 302
- * for normal requests, and [[ajaxRedirectCode]] for AJAX requests.
+ * @param integer $statusCode the HTTP status code. If null, it will use 302.
* See [[http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html]]
* for details about HTTP status code
* @return Response the response object itself
@@ -613,11 +610,14 @@ class Response extends \yii\base\Response
if (strpos($url, '/') === 0 && strpos($url, '//') !== 0) {
$url = Yii::$app->getRequest()->getHostInfo() . $url;
}
- if ($statusCode === null) {
- $statusCode = Yii::$app->getRequest()->getIsAjax() ? $this->ajaxRedirectCode : 302;
+
+ if (Yii::$app->getRequest()->getIsAjax()) {
+ $this->getHeaders()->set('X-Redirect', $url);
+ } else {
+ $this->getHeaders()->set('Location', $url);
}
- $this->getHeaders()->set('Location', $url);
$this->setStatusCode($statusCode);
+
return $this;
}
@@ -766,10 +766,10 @@ class Response extends \yii\base\Response
if (!is_object($formatter)) {
$formatter = Yii::createObject($formatter);
}
- if ($formatter instanceof ResponseFormatter) {
+ if ($formatter instanceof ResponseFormatterInterface) {
$formatter->format($this);
} else {
- throw new InvalidConfigException("The '{$this->format}' response formatter is invalid. It must implement the ResponseFormatter interface.");
+ throw new InvalidConfigException("The '{$this->format}' response formatter is invalid. It must implement the ResponseFormatterInterface.");
}
} else {
switch ($this->format) {
diff --git a/framework/yii/web/ResponseFormatter.php b/framework/yii/web/ResponseFormatterInterface.php
similarity index 73%
rename from framework/yii/web/ResponseFormatter.php
rename to framework/yii/web/ResponseFormatterInterface.php
index dc7c979..689ee1e 100644
--- a/framework/yii/web/ResponseFormatter.php
+++ b/framework/yii/web/ResponseFormatterInterface.php
@@ -8,12 +8,12 @@
namespace yii\web;
/**
- * ResponseFormatter specifies the interface needed to format a response before it is sent out.
+ * ResponseFormatterInterface specifies the interface needed to format a response before it is sent out.
*
* @author Qiang Xue
* @since 2.0
*/
-interface ResponseFormatter
+interface ResponseFormatterInterface
{
/**
* Formats the specified response.
diff --git a/framework/yii/web/Session.php b/framework/yii/web/Session.php
index fc9fe16..92ec3ad 100644
--- a/framework/yii/web/Session.php
+++ b/framework/yii/web/Session.php
@@ -582,12 +582,22 @@ class Session extends Component implements \IteratorAggregate, \ArrayAccess, \Co
* A flash message is available only in the current request and the next request.
* @param string $key the key identifying the flash message
* @param mixed $defaultValue value to be returned if the flash message does not exist.
+ * @param boolean $delete whether to delete this flash message right after this method is called.
+ * If false, the flash message will be automatically deleted after the next request.
* @return mixed the flash message
*/
- public function getFlash($key, $defaultValue = null)
+ public function getFlash($key, $defaultValue = null, $delete = false)
{
$counters = $this->get($this->flashVar, array());
- return isset($counters[$key]) ? $this->get($key, $defaultValue) : $defaultValue;
+ if (isset($counters[$key])) {
+ $value = $this->get($key, $defaultValue);
+ if ($delete) {
+ $this->removeFlash($key);
+ }
+ return $value;
+ } else {
+ return $defaultValue;
+ }
}
/**
diff --git a/framework/yii/web/User.php b/framework/yii/web/User.php
index 22b85e5..f6a9bc8 100644
--- a/framework/yii/web/User.php
+++ b/framework/yii/web/User.php
@@ -18,12 +18,12 @@ use yii\base\InvalidParamException;
* 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.
*
- * User works with a class implementing the [[Identity]] interface. This class implements
+ * 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.
*
* @property string|integer $id The unique identifier for the user. If null, it means the user is a guest.
* This property is read-only.
- * @property Identity $identity The identity object associated with the currently logged user. Null is
+ * @property IdentityInterface $identity The identity object associated with the currently logged user. Null is
* returned if the user is not logged in (not authenticated).
* @property boolean $isGuest Whether the current user is a guest. This property is read-only.
* @property string $returnUrl The URL that the user should be redirected to after login. Note that the type
@@ -128,7 +128,7 @@ class User extends Component
/**
* Returns the identity object associated with the currently logged user.
- * @return Identity the identity object associated with the currently logged user.
+ * @return IdentityInterface the identity object associated with the currently logged user.
* Null is returned if the user is not logged in (not authenticated).
* @see login
* @see logout
@@ -140,7 +140,7 @@ class User extends Component
if ($id === null) {
$this->_identity = null;
} else {
- /** @var $class Identity */
+ /** @var $class IdentityInterface */
$class = $this->identityClass;
$this->_identity = $class::findIdentity($id);
}
@@ -156,7 +156,7 @@ class User extends Component
* You should normally update the user identity via methods [[login()]], [[logout()]]
* or [[switchIdentity()]].
*
- * @param Identity $identity the identity object associated with the currently logged user.
+ * @param IdentityInterface $identity the identity object associated with the currently logged user.
*/
public function setIdentity($identity)
{
@@ -171,7 +171,7 @@ class User extends Component
* and [[enableAutoLogin]] is true, it will also send out an identity
* cookie to support cookie-based login.
*
- * @param Identity $identity the user identity (which should already be authenticated)
+ * @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.
* Defaults to 0, meaning login till the user closes the browser or the session is manually destroyed.
* If greater than 0 and [[enableAutoLogin]] is true, cookie-based login will be supported.
@@ -200,7 +200,7 @@ class User extends Component
$data = json_decode($value, true);
if (count($data) === 3 && isset($data[0], $data[1], $data[2])) {
list ($id, $authKey, $duration) = $data;
- /** @var $class Identity */
+ /** @var $class IdentityInterface */
$class = $this->identityClass;
$identity = $class::findIdentity($id);
if ($identity !== null && $identity->validateAuthKey($authKey)) {
@@ -318,7 +318,7 @@ class User extends Component
* The default implementation will trigger the [[EVENT_BEFORE_LOGIN]] event.
* If you override this method, make sure you call the parent implementation
* so that the event is triggered.
- * @param Identity $identity the user identity information
+ * @param IdentityInterface $identity the user identity information
* @param boolean $cookieBased whether the login is cookie-based
* @return boolean whether the user should continue to be logged in
*/
@@ -337,7 +337,7 @@ class User extends Component
* The default implementation will trigger the [[EVENT_AFTER_LOGIN]] event.
* If you override this method, make sure you call the parent implementation
* so that the event is triggered.
- * @param Identity $identity the user identity information
+ * @param IdentityInterface $identity the user identity information
* @param boolean $cookieBased whether the login is cookie-based
*/
protected function afterLogin($identity, $cookieBased)
@@ -353,7 +353,7 @@ class User extends Component
* The default implementation will trigger the [[EVENT_BEFORE_LOGOUT]] event.
* If you override this method, make sure you call the parent implementation
* so that the event is triggered.
- * @param Identity $identity the user identity information
+ * @param IdentityInterface $identity the user identity information
* @return boolean whether the user should continue to be logged out
*/
protected function beforeLogout($identity)
@@ -370,7 +370,7 @@ class User extends Component
* The default implementation will trigger the [[EVENT_AFTER_LOGOUT]] event.
* If you override this method, make sure you call the parent implementation
* so that the event is triggered.
- * @param Identity $identity the user identity information
+ * @param IdentityInterface $identity the user identity information
*/
protected function afterLogout($identity)
{
@@ -402,9 +402,9 @@ class User extends Component
/**
* Sends an identity cookie.
* This method is used when [[enableAutoLogin]] is true.
- * It saves [[id]], [[Identity::getAuthKey()|auth key]], and the duration of cookie-based login
+ * It saves [[id]], [[IdentityInterface::getAuthKey()|auth key]], and the duration of cookie-based login
* information in the cookie.
- * @param Identity $identity
+ * @param IdentityInterface $identity
* @param integer $duration number of seconds that the user can remain in logged-in status.
* @see loginByCookie
*/
@@ -430,7 +430,7 @@ class User extends Component
* This method is mainly called by [[login()]], [[logout()]] and [[loginByCookie()]]
* when the current user needs to be associated with the corresponding identity information.
*
- * @param Identity $identity the identity information to be associated with the current user.
+ * @param IdentityInterface $identity the identity information to be associated with the current user.
* If null, it means switching 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.
@@ -444,7 +444,7 @@ class User extends Component
$this->setIdentity($identity);
$session->remove($this->idVar);
$session->remove($this->authTimeoutVar);
- if ($identity instanceof Identity) {
+ if ($identity instanceof IdentityInterface) {
$session->set($this->idVar, $identity->getId());
if ($this->authTimeout !== null) {
$session->set($this->authTimeoutVar, time() + $this->authTimeout);
diff --git a/framework/yii/web/UserEvent.php b/framework/yii/web/UserEvent.php
index 3e403da..8577ef5 100644
--- a/framework/yii/web/UserEvent.php
+++ b/framework/yii/web/UserEvent.php
@@ -18,7 +18,7 @@ use yii\base\Event;
class UserEvent extends Event
{
/**
- * @var Identity the identity object associated with this event
+ * @var IdentityInterface the identity object associated with this event
*/
public $identity;
/**
diff --git a/framework/yii/web/XmlResponseFormatter.php b/framework/yii/web/XmlResponseFormatter.php
index adf8807..737011d 100644
--- a/framework/yii/web/XmlResponseFormatter.php
+++ b/framework/yii/web/XmlResponseFormatter.php
@@ -20,7 +20,7 @@ use yii\helpers\StringHelper;
* @author Qiang Xue
* @since 2.0
*/
-class XmlResponseFormatter extends Component implements ResponseFormatter
+class XmlResponseFormatter extends Component implements ResponseFormatterInterface
{
/**
* @var string the Content-Type header for the response
diff --git a/framework/yii/widgets/ListViewBase.php b/framework/yii/widgets/ListViewBase.php
index 8c2f8f4..33186ae 100644
--- a/framework/yii/widgets/ListViewBase.php
+++ b/framework/yii/widgets/ListViewBase.php
@@ -25,7 +25,7 @@ abstract class ListViewBase extends Widget
*/
public $options = array();
/**
- * @var \yii\data\IDataProvider the data provider for the view. This property is required.
+ * @var \yii\data\DataProviderInterface the data provider for the view. This property is required.
*/
public $dataProvider;
/**
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
index 3100413..1f3056e 100644
--- a/phpunit.xml.dist
+++ b/phpunit.xml.dist
@@ -21,7 +21,7 @@
framework/yii/helpers/ArrayHelper.phpframework/yii/helpers/Console.phpframework/yii/i18n/GettextFile.php
- framework/yii/web/ResponseFormatter.php
+ framework/yii/web/ResponseFormatterInterface.phpframework/yii/baseframework/yii/db/mssqlframework/yii/bootstrap
diff --git a/tests/unit/data/base/Speaker.php b/tests/unit/data/base/Speaker.php
index 5668dad..b0acc6b 100644
--- a/tests/unit/data/base/Speaker.php
+++ b/tests/unit/data/base/Speaker.php
@@ -17,6 +17,13 @@ class Speaker extends Model
protected $protectedProperty;
private $_privateProperty;
+ public static $formName = 'Speaker';
+
+ public function formName()
+ {
+ return static::$formName;
+ }
+
public function attributeLabels()
{
return array(
diff --git a/tests/unit/framework/base/ModelTest.php b/tests/unit/framework/base/ModelTest.php
index b338c17..d500933 100644
--- a/tests/unit/framework/base/ModelTest.php
+++ b/tests/unit/framework/base/ModelTest.php
@@ -75,6 +75,32 @@ class ModelTest extends TestCase
$this->assertEquals('Qiang', $speaker->firstName);
}
+ public function testLoad()
+ {
+ $singer = new Singer();
+ $this->assertEquals('Singer', $singer->formName());
+
+ $post = array('firstName' => 'Qiang');
+
+ Speaker::$formName = '';
+ $model = new Speaker();
+ $model->setScenario('test');
+ $this->assertTrue($model->load($post));
+ $this->assertEquals('Qiang', $model->firstName);
+
+ Speaker::$formName = 'Speaker';
+ $model = new Speaker();
+ $model->setScenario('test');
+ $this->assertTrue($model->load(array('Speaker' => $post)));
+ $this->assertEquals('Qiang', $model->firstName);
+
+ Speaker::$formName = 'Speaker';
+ $model = new Speaker();
+ $model->setScenario('test');
+ $this->assertFalse($model->load(array('Example' => array())));
+ $this->assertEquals('', $model->firstName);
+ }
+
public function testActiveAttributes()
{
// by default mass assignment doesn't work at all
diff --git a/tests/unit/framework/helpers/HtmlTest.php b/tests/unit/framework/helpers/HtmlTest.php
index 857f9c2..88aa33a 100644
--- a/tests/unit/framework/helpers/HtmlTest.php
+++ b/tests/unit/framework/helpers/HtmlTest.php
@@ -19,6 +19,7 @@ class HtmlTest extends TestCase
'request' => array(
'class' => 'yii\web\Request',
'url' => '/test',
+ 'enableCsrfValidation' => false,
),
'response' => array(
'class' => 'yii\web\Response',
diff --git a/tests/unit/framework/web/CacheSessionTest.php b/tests/unit/framework/web/CacheSessionTest.php
new file mode 100644
index 0000000..e740596
--- /dev/null
+++ b/tests/unit/framework/web/CacheSessionTest.php
@@ -0,0 +1,39 @@
+mockApplication();
+ Yii::$app->setComponent('cache', new FileCache());
+ }
+
+ public function testCacheSession()
+ {
+ $session = new CacheSession();
+
+ $session->writeSession('test', 'sessionData');
+ $this->assertEquals('sessionData', $session->readSession('test'));
+ $session->destroySession('test');
+ $this->assertEquals('', $session->readSession('test'));
+ }
+
+ public function testInvalidCache()
+ {
+ $this->setExpectedException('yii\base\InvalidConfigException');
+
+ $session = new CacheSession(array(
+ 'cache' => 'invalid',
+ ));
+ }
+}