Browse Source

Merge branch 'master' into redis

* master: (30 commits)
  Added SerialColumn to crud generated code.
  Added ActionColumn. crud generator WIP.
  Fixes #823: consistent interface naming
  Advanced application template: Delete flash message after it was displayed
  Fixes #901: Added $delete parameter to Session::getFlash().
  Polished up the basic discussion of template alternatives
  Advanced application template: removed unused scenario from User model
  porting the fix from https://github.com/yiisoft/yii/pull/2894
  Edited introduction
  Fixes #898: supported different signature of MemCache::addServer().
  Fixes #897.
  Use str_replace() rather than implode-explode
  Fix parenthesis typo in CRUD index template
  Doing more editing...
  test break fix.
  Support ajax redirection.
  Enable CSRF validation by default.
  Supports more elements to use data-confirm and data-method attributes.
  refactored Request::validateCsrfToken().
  Fixed CSRF validation bug.
  ...
tags/2.0.0-alpha
Carsten Brandt 11 years ago
parent
commit
1eb877ad82
  1. 7
      apps/advanced/common/models/User.php
  2. 2
      apps/advanced/frontend/controllers/SiteController.php
  3. 2
      apps/advanced/frontend/views/emails/passwordResetToken.php
  4. 8
      apps/advanced/frontend/widgets/Alert.php
  5. 10
      apps/basic/controllers/SiteController.php
  6. 2
      apps/basic/models/User.php
  7. 4
      apps/basic/views/layouts/main.php
  8. 6
      docs/guide/bootstrap-widgets.md
  9. 14
      docs/guide/model.md
  10. 2
      docs/guide/overview.md
  11. 7
      docs/guide/security.md
  12. 36
      docs/guide/template.md
  13. 6
      docs/guide/upgrade-from-v1.md
  14. 133
      framework/yii/assets/yii.js
  15. 2
      framework/yii/base/Controller.php
  16. 2
      framework/yii/base/ErrorHandler.php
  17. 2
      framework/yii/base/Module.php
  18. 19
      framework/yii/caching/MemCache.php
  19. 17
      framework/yii/classes.php
  20. 4
      framework/yii/data/DataProvider.php
  21. 4
      framework/yii/data/DataProviderInterface.php
  22. 4
      framework/yii/db/ActiveRelation.php
  23. 4
      framework/yii/db/QueryBuilder.php
  24. 13
      framework/yii/gii/generators/crud/templates/controller.php
  25. 6
      framework/yii/gii/generators/crud/templates/views/index.php
  26. 6
      framework/yii/gii/generators/crud/templates/views/view.php
  27. 102
      framework/yii/grid/ActionColumn.php
  28. 2
      framework/yii/grid/SerialColumn.php
  29. 2
      framework/yii/web/AssetConverter.php
  30. 4
      framework/yii/web/AssetConverterInterface.php
  31. 8
      framework/yii/web/AssetManager.php
  32. 2
      framework/yii/web/CacheSession.php
  33. 24
      framework/yii/web/Controller.php
  34. 8
      framework/yii/web/IdentityInterface.php
  35. 36
      framework/yii/web/Request.php
  36. 40
      framework/yii/web/Response.php
  37. 4
      framework/yii/web/ResponseFormatterInterface.php
  38. 14
      framework/yii/web/Session.php
  39. 30
      framework/yii/web/User.php
  40. 2
      framework/yii/web/UserEvent.php
  41. 2
      framework/yii/web/XmlResponseFormatter.php
  42. 2
      framework/yii/widgets/ListViewBase.php
  43. 2
      phpunit.xml.dist
  44. 7
      tests/unit/data/base/Speaker.php
  45. 26
      tests/unit/framework/base/ModelTest.php
  46. 1
      tests/unit/framework/helpers/HtmlTest.php
  47. 39
      tests/unit/framework/web/CacheSessionTest.php

7
apps/advanced/common/models/User.php

@ -3,7 +3,7 @@ namespace common\models;
use yii\db\ActiveRecord; use yii\db\ActiveRecord;
use yii\helpers\Security; use yii\helpers\Security;
use yii\web\Identity; use yii\web\IdentityInterface;
/** /**
* Class User * Class User
@ -20,7 +20,7 @@ use yii\web\Identity;
* @property integer $create_time * @property integer $create_time
* @property integer $update_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 * @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. * Finds an identity by the given ID.
* *
* @param string|integer $id the ID to be looked for * @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) public static function findIdentity($id)
{ {
@ -123,7 +123,6 @@ class User extends ActiveRecord implements Identity
{ {
return array( return array(
'signup' => array('username', 'email', 'password'), 'signup' => array('username', 'email', 'password'),
'login' => array('username', 'password'),
'resetPassword' => array('password'), 'resetPassword' => array('password'),
'requestPasswordResetToken' => array('email'), 'requestPasswordResetToken' => array('email'),
); );

2
apps/advanced/frontend/controllers/SiteController.php

@ -164,7 +164,7 @@ class SiteController extends Controller
$headers = "From: $name <{$fromEmail}>\r\n" . $headers = "From: $name <{$fromEmail}>\r\n" .
"MIME-Version: 1.0\r\n" . "MIME-Version: 1.0\r\n" .
"Content-type: text/plain; charset=UTF-8"; "Content-type: text/plain; charset=UTF-8";
return mail($fromEmail, $subject, $body, $headers); return mail($email, $subject, $body, $headers);
} }
return false; return false;

2
apps/advanced/frontend/views/emails/passwordResetToken.php

@ -13,4 +13,4 @@ Hello <?php echo Html::encode($user->username)?>,
Follow the link below to reset your password: Follow the link below to reset your password:
<?php Html::a(Html::encode($resetLink), $resetLink)?> <?php echo Html::a(Html::encode($resetLink), $resetLink)?>

8
apps/advanced/frontend/widgets/Alert.php

@ -23,13 +23,13 @@ class Alert extends \yii\bootstrap\Alert
private $_doNotRender = false; private $_doNotRender = false;
public function init() 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'); 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'); 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'); 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'); Html::addCssClass($this->options, 'alert-warning');
} else { } else {
$this->_doNotRender = true; $this->_doNotRender = true;

10
apps/basic/controllers/SiteController.php

@ -3,7 +3,9 @@
namespace app\controllers; namespace app\controllers;
use Yii; use Yii;
use yii\web\AccessControl;
use yii\web\Controller; use yii\web\Controller;
use yii\web\VerbFilter;
use app\models\LoginForm; use app\models\LoginForm;
use app\models\ContactForm; use app\models\ContactForm;
@ -13,7 +15,7 @@ class SiteController extends Controller
{ {
return array( return array(
'access' => array( 'access' => array(
'class' => \yii\web\AccessControl::className(), 'class' => AccessControl::className(),
'only' => array('login', 'logout'), 'only' => array('login', 'logout'),
'rules' => array( 'rules' => array(
array( array(
@ -28,6 +30,12 @@ class SiteController extends Controller
), ),
), ),
), ),
'verbs' => array(
'class' => VerbFilter::className(),
'actions' => array(
'logout' => array('post'),
),
),
); );
} }

2
apps/basic/models/User.php

@ -2,7 +2,7 @@
namespace app\models; 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 $id;
public $username; public $username;

4
apps/basic/views/layouts/main.php

@ -36,7 +36,9 @@ app\config\AppAsset::register($this);
array('label' => 'Contact', 'url' => array('/site/contact')), array('label' => 'Contact', 'url' => array('/site/contact')),
Yii::$app->user->isGuest ? Yii::$app->user->isGuest ?
array('label' => 'Login', 'url' => array('/site/login')) : 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(); NavBar::end();

6
docs/guide/bootstrap-widgets.md

@ -1,17 +1,17 @@
Bootstrap widgets 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: 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. - Ready to use components, such as menus, pagination, modal boxes, tabs etc.
Basics 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 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 convenient way to include bootstrap assets in your pages with a single line added to `AppAsset.php` located in your
`config` directory: `config` directory:

14
docs/guide/model.md

@ -1,15 +1,15 @@
Model 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 declaration: a model defines what is considered an attribute.
- attribute labels: each attribute may be associated with a label for display purpose. - Attribute labels: each attribute may be associated with a label for display purpose.
- massive attribute assignment. - Massive attribute assignment: the ability to populate multiple model attributes in one step.
- scenario-based data validation. - 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. 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 class is also a base for more advanced models with additional functionality such as [Active Record](active-record.md). The Model class is also the base for more advanced models with additional functionality such as [Active Record](active-record.md).
Attributes Attributes
---------- ----------

2
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 virtually any type of Web application. Because it is light-weight and
equipped with sophisticated caching mechanisms, it is especially suited equipped with sophisticated caching mechanisms, it is especially suited
to high-traffic applications, such as portals, forums, content 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? How does Yii Compare with Other Frameworks?

7
docs/guide/security.md

@ -4,12 +4,9 @@ Security
Hashing and verifying passwords 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 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.
compute and verify hashes isn't a good way either. Modern hardware allows to brute force these very fast.
In order to truly secure user passwords even in case your database is leaked you need to use a function that is resistant 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
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
password and verifying existing hash. password and verifying existing hash.
When user sets his password we're taking password string from POST and then getting a hash: When user sets his password we're taking password string from POST and then getting a hash:

36
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/). 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 The `view` component is responsible for rendering views. You can add
a custom template engines as follows: a custom template engines by reconfiguring this component's behavior:
```php ```php
array( array(
@ -26,15 +26,13 @@ array(
) )
``` ```
Note that Smarty and Twig are not bundled with Yii and you have to download and 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).
unpack these yourself and then specify `twigPath` and `smartyPath` respectively.
Twig Twig
---- ----
In order to use Twig you need to put you templates in files with extension `.twig` To use Twig, you need to create templates in files with the `.twig` extension (or use another file extension but configure the component accordingly).
(or another one if configured differently). Unlike standard view files, when using Twig, you must include the extension when calling `$this->render()`
Also you need to specify this extension explicitly when calling `$this->render()`
or `$this->renderPartial()` from your controller: or `$this->renderPartial()` from your controller:
```php ```php
@ -43,25 +41,25 @@ echo $this->render('renderer.twig', array('username' => 'Alex'));
### Additional functions ### Additional functions
Additionally to regular Twig syntax the following is available in Yii: Yii adds the following construct to the standard Twig syntax:
```php ```php
<a href="{{ path('blog/view', {'alias' : post.alias}) }}">{{ post.title }}</a> <a href="{{ path('blog/view', {'alias' : post.alias}) }}">{{ post.title }}</a>
``` ```
path function calls `Html::url()` internally. Internally, the `path()` function calls Yii's `Html::url()` method.
### Additional variables ### Additional variables
- `app` = `\Yii::$app` Within Twig templates, you can also make use of these variables:
- `this` = current `View` object
- `app`, which equates to `\Yii::$app`
- `this`, which equates to the current `View` object
Smarty Smarty
------ ------
In order to use Smarty you need to put you templates in files with extension `.tpl` 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 another one if configured differently).
Also you need to specify this extension explicitly when calling `$this->render()`
or `$this->renderPartial()` from your controller: or `$this->renderPartial()` from your controller:
```php ```php
@ -70,16 +68,18 @@ echo $this->render('renderer.tpl', array('username' => 'Alex'));
### Additional functions ### Additional functions
Additionally to regular Smarty syntax the following is available in Yii: Yii adds the following construct to the standard Smarty syntax:
```php ```php
<a href="{path route='blog/view' alias=$post.alias}">{$post.title}</a> <a href="{path route='blog/view' alias=$post.alias}">{$post.title}</a>
``` ```
path function calls `Html::url()` internally. Internally, the `path()` function calls Yii's `Html::url()` method.
### Additional variables ### Additional variables
- `$app` = `\Yii::$app` Within Smarty templates, you can also make use of these variables:
- `$this` = current `View` object
- `$app`, which equates to `\Yii::$app`
- `$this`, which equates to the current `View` object

6
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. 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 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. is much more straightforward to implement. The bootstrap application provides such an example.

133
framework/yii/assets/yii.js

@ -37,13 +37,22 @@
* *
* Using this structure, you can define public and private functions/properties for a module. * 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 * 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. * You must call "yii.initModule()" once for the root module of all your modules.
*/ */
yii = (function ($) { yii = (function ($) {
var pub = { 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. * @return string|undefined the CSRF variable name. Undefined is returned is CSRF validation is not enabled.
*/ */
getCsrfVar: function () { getCsrfVar: function () {
@ -57,6 +66,87 @@ yii = (function ($) {
return $('meta[name=csrf-token]').prop('content'); 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 = $('<form method="' + method + '" action="' + action + '"></form>');
var target = $e.prop('target');
if (target) {
$form.attr('target', target);
}
if (!method.match(/(get|post)/i)) {
$form.append('<input name="_method" value="' + method + '" type="hidden">');
}
var csrfVar = pub.getCsrfVar();
if (csrfVar) {
$form.append('<input name="' + csrfVar + '" value="' + pub.getCsrfToken() + '" type="hidden">');
}
$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) { initModule: function (module) {
if (module.isActive === undefined || module.isActive) { if (module.isActive === undefined || module.isActive) {
if ($.isFunction(module.init)) { 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; return pub;

2
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.) * 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. * 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. * @param Action $action the action to be executed.
* @return boolean whether the action should continue 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. * This method is invoked right after an action is executed.
* You may override this method to do some postprocessing for the action. * 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 Action $action the action just executed.
* @param mixed $result the action return result. * @param mixed $result the action return result.
*/ */

2
framework/yii/base/ErrorHandler.php

@ -93,6 +93,8 @@ class ErrorHandler extends Component
$response->getHeaders()->removeAll(); $response->getHeaders()->removeAll();
if ($useErrorView && $this->errorAction !== null) { 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); $result = Yii::$app->runAction($this->errorAction);
if ($result instanceof Response) { if ($result instanceof Response) {
$response = $result; $response = $result;

2
framework/yii/base/Module.php

@ -617,7 +617,7 @@ abstract class Module extends Component
if (isset($this->controllerMap[$id])) { if (isset($this->controllerMap[$id])) {
$controller = Yii::createObject($this->controllerMap[$id], $id, $this); $controller = Yii::createObject($this->controllerMap[$id], $id, $this);
} elseif (preg_match('/^[a-z0-9\\-_]+$/', $id)) { } 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'; $classFile = $this->controllerPath . DIRECTORY_SEPARATOR . $className . '.php';
if (!is_file($classFile)) { if (!is_file($classFile)) {
return false; return false;

19
framework/yii/caching/MemCache.php

@ -87,7 +87,14 @@ class MemCache extends Cache
parent::init(); parent::init();
$servers = $this->getServers(); $servers = $this->getServers();
$cache = $this->getMemCache(); $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) { foreach ($servers as $server) {
if ($server->host === null) { if ($server->host === null) {
throw new InvalidConfigException("The 'host' property must be specified for every memcache server."); throw new InvalidConfigException("The 'host' property must be specified for every memcache server.");
@ -97,15 +104,21 @@ class MemCache extends Cache
} else { } else {
// $timeout is used for memcache versions that do not have timeoutms parameter // $timeout is used for memcache versions that do not have timeoutms parameter
$timeout = (int) ($server->timeout / 1000) + (($server->timeout % 1000 > 0) ? 1 : 0); $timeout = (int) ($server->timeout / 1000) + (($server->timeout % 1000 > 0) ? 1 : 0);
if ($paramCount === 9) {
$cache->addServer( $cache->addServer(
$server->host, $server->port, $server->persistent, $server->host, $server->port, $server->persistent,
$server->weight, $timeout, $server->retryInterval, $server->weight, $timeout, $server->retryInterval,
$server->status, $server->failureCallback, $server->timeout $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);
} }
} }

17
framework/yii/classes.php

@ -49,6 +49,7 @@ return array(
'yii\bootstrap\Alert' => YII_PATH . '/bootstrap/Alert.php', 'yii\bootstrap\Alert' => YII_PATH . '/bootstrap/Alert.php',
'yii\bootstrap\BootstrapAsset' => YII_PATH . '/bootstrap/BootstrapAsset.php', 'yii\bootstrap\BootstrapAsset' => YII_PATH . '/bootstrap/BootstrapAsset.php',
'yii\bootstrap\BootstrapPluginAsset' => YII_PATH . '/bootstrap/BootstrapPluginAsset.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\Button' => YII_PATH . '/bootstrap/Button.php',
'yii\bootstrap\ButtonDropdown' => YII_PATH . '/bootstrap/ButtonDropdown.php', 'yii\bootstrap\ButtonDropdown' => YII_PATH . '/bootstrap/ButtonDropdown.php',
'yii\bootstrap\ButtonGroup' => YII_PATH . '/bootstrap/ButtonGroup.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\NavBar' => YII_PATH . '/bootstrap/NavBar.php',
'yii\bootstrap\Progress' => YII_PATH . '/bootstrap/Progress.php', 'yii\bootstrap\Progress' => YII_PATH . '/bootstrap/Progress.php',
'yii\bootstrap\Tabs' => YII_PATH . '/bootstrap/Tabs.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\bootstrap\Widget' => YII_PATH . '/bootstrap/Widget.php',
'yii\caching\ApcCache' => YII_PATH . '/caching/ApcCache.php', 'yii\caching\ApcCache' => YII_PATH . '/caching/ApcCache.php',
'yii\caching\Cache' => YII_PATH . '/caching/Cache.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\GroupDependency' => YII_PATH . '/caching/GroupDependency.php',
'yii\caching\MemCache' => YII_PATH . '/caching/MemCache.php', 'yii\caching\MemCache' => YII_PATH . '/caching/MemCache.php',
'yii\caching\MemCacheServer' => YII_PATH . '/caching/MemCacheServer.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\WinCache' => YII_PATH . '/caching/WinCache.php',
'yii\caching\XCache' => YII_PATH . '/caching/XCache.php', 'yii\caching\XCache' => YII_PATH . '/caching/XCache.php',
'yii\caching\ZendDataCache' => YII_PATH . '/caching/ZendDataCache.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\ActiveDataProvider' => YII_PATH . '/data/ActiveDataProvider.php',
'yii\data\ArrayDataProvider' => YII_PATH . '/data/ArrayDataProvider.php', 'yii\data\ArrayDataProvider' => YII_PATH . '/data/ArrayDataProvider.php',
'yii\data\DataProvider' => YII_PATH . '/data/DataProvider.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\Pagination' => YII_PATH . '/data/Pagination.php',
'yii\data\Sort' => YII_PATH . '/data/Sort.php', 'yii\data\Sort' => YII_PATH . '/data/Sort.php',
'yii\db\ActiveQuery' => YII_PATH . '/db/ActiveQuery.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\StaleObjectException' => YII_PATH . '/db/StaleObjectException.php',
'yii\db\TableSchema' => YII_PATH . '/db/TableSchema.php', 'yii\db\TableSchema' => YII_PATH . '/db/TableSchema.php',
'yii\db\Transaction' => YII_PATH . '/db/Transaction.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\PDO' => YII_PATH . '/db/mssql/PDO.php',
'yii\db\mssql\QueryBuilder' => YII_PATH . '/db/mssql/QueryBuilder.php', 'yii\db\mssql\QueryBuilder' => YII_PATH . '/db/mssql/QueryBuilder.php',
'yii\db\mssql\Schema' => YII_PATH . '/db/mssql/Schema.php', 'yii\db\mssql\Schema' => YII_PATH . '/db/mssql/Schema.php',
'yii\db\mssql\SqlsrvPDO' => YII_PATH . '/db/mssql/SqlsrvPDO.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\QueryBuilder' => YII_PATH . '/db/mysql/QueryBuilder.php',
'yii\db\mysql\Schema' => YII_PATH . '/db/mysql/Schema.php', 'yii\db\mysql\Schema' => YII_PATH . '/db/mysql/Schema.php',
'yii\db\pgsql\QueryBuilder' => YII_PATH . '/db/pgsql/QueryBuilder.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\Item' => YII_PATH . '/rbac/Item.php',
'yii\rbac\Manager' => YII_PATH . '/rbac/Manager.php', 'yii\rbac\Manager' => YII_PATH . '/rbac/Manager.php',
'yii\rbac\PhpManager' => YII_PATH . '/rbac/PhpManager.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\requirements\YiiRequirementChecker' => YII_PATH . '/requirements/YiiRequirementChecker.php',
'yii\validators\BooleanValidator' => YII_PATH . '/validators/BooleanValidator.php', 'yii\validators\BooleanValidator' => YII_PATH . '/validators/BooleanValidator.php',
'yii\validators\CompareValidator' => YII_PATH . '/validators/CompareValidator.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\RangeValidator' => YII_PATH . '/validators/RangeValidator.php',
'yii\validators\RegularExpressionValidator' => YII_PATH . '/validators/RegularExpressionValidator.php', 'yii\validators\RegularExpressionValidator' => YII_PATH . '/validators/RegularExpressionValidator.php',
'yii\validators\RequiredValidator' => YII_PATH . '/validators/RequiredValidator.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\StringValidator' => YII_PATH . '/validators/StringValidator.php',
'yii\validators\UniqueValidator' => YII_PATH . '/validators/UniqueValidator.php', 'yii\validators\UniqueValidator' => YII_PATH . '/validators/UniqueValidator.php',
'yii\validators\UrlValidator' => YII_PATH . '/validators/UrlValidator.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\HeaderCollection' => YII_PATH . '/web/HeaderCollection.php',
'yii\web\HttpCache' => YII_PATH . '/web/HttpCache.php', 'yii\web\HttpCache' => YII_PATH . '/web/HttpCache.php',
'yii\web\HttpException' => YII_PATH . '/web/HttpException.php', 'yii\web\HttpException' => YII_PATH . '/web/HttpException.php',
'yii\web\IAssetConverter' => YII_PATH . '/web/IAssetConverter.php', 'yii\web\AssetConverterInterface' => YII_PATH . '/web/AssetConverterInterface.php',
'yii\web\Identity' => YII_PATH . '/web/Identity.php', 'yii\web\IdentityInterface' => YII_PATH . '/web/IdentityInterface.php',
'yii\web\JqueryAsset' => YII_PATH . '/web/JqueryAsset.php', 'yii\web\JqueryAsset' => YII_PATH . '/web/JqueryAsset.php',
'yii\web\JsExpression' => YII_PATH . '/web/JsExpression.php', 'yii\web\JsExpression' => YII_PATH . '/web/JsExpression.php',
'yii\web\PageCache' => YII_PATH . '/web/PageCache.php', 'yii\web\PageCache' => YII_PATH . '/web/PageCache.php',
'yii\web\Request' => YII_PATH . '/web/Request.php', 'yii\web\Request' => YII_PATH . '/web/Request.php',
'yii\web\Response' => YII_PATH . '/web/Response.php', 'yii\web\Response' => YII_PATH . '/web/Response.php',
'yii\web\ResponseEvent' => YII_PATH . '/web/ResponseEvent.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\Session' => YII_PATH . '/web/Session.php',
'yii\web\SessionIterator' => YII_PATH . '/web/SessionIterator.php', 'yii\web\SessionIterator' => YII_PATH . '/web/SessionIterator.php',
'yii\web\UploadedFile' => YII_PATH . '/web/UploadedFile.php', 'yii\web\UploadedFile' => YII_PATH . '/web/UploadedFile.php',

4
framework/yii/data/DataProvider.php

@ -14,7 +14,7 @@ use yii\base\InvalidParamException;
/** /**
* DataProvider is the base class of data provider classes. * 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 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 * @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 <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @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. * @var string an ID that uniquely identifies the data provider among all data providers.

4
framework/yii/data/IDataProvider.php → framework/yii/data/DataProviderInterface.php

@ -8,7 +8,7 @@
namespace yii\data; 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 * Data providers are components that sort and paginate data, and provide them to widgets
* such as [[GridView]], [[ListView]]. * such as [[GridView]], [[ListView]].
@ -16,7 +16,7 @@ namespace yii\data;
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
interface IDataProvider interface DataProviderInterface
{ {
/** /**
* Returns the number of data models in the current page. * Returns the number of data models in the current page.

4
framework/yii/db/ActiveRelation.php

@ -279,7 +279,9 @@ class ActiveRelation extends ActiveQuery
// single key // single key
$attribute = reset($this->link); $attribute = reset($this->link);
foreach ($models as $model) { foreach ($models as $model) {
$values[] = $model[$attribute]; if (($value = $model[$attribute]) !== null) {
$values[] = $value;
}
} }
} else { } else {
// composite keys // composite keys

4
framework/yii/db/QueryBuilder.php

@ -585,7 +585,7 @@ class QueryBuilder extends \yii\base\Object
foreach ($tables as $i => $table) { foreach ($tables as $i => $table) {
if (strpos($table, '(') === false) { 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]); $tables[$i] = $this->db->quoteTableName($matches[1]) . ' ' . $this->db->quoteTableName($matches[2]);
} else { } else {
$tables[$i] = $this->db->quoteTableName($table); $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 // 0:join type, 1:table name, 2:on-condition
$table = $join[1]; $table = $join[1];
if (strpos($table, '(') === false) { 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]); $table = $this->db->quoteTableName($matches[1]) . ' ' . $this->db->quoteTableName($matches[2]);
} else { } else {
$table = $this->db->quoteTableName($table); $table = $this->db->quoteTableName($table);

13
framework/yii/gii/generators/crud/templates/controller.php

@ -28,12 +28,25 @@ use <?php echo ltrim($generator->searchModelClass, '\\'); ?>;
use yii\data\ActiveDataProvider; use yii\data\ActiveDataProvider;
use <?php echo ltrim($generator->baseControllerClass, '\\'); ?>; use <?php echo ltrim($generator->baseControllerClass, '\\'); ?>;
use yii\web\HttpException; use yii\web\HttpException;
use yii\web\VerbFilter;
/** /**
* <?php echo $controllerClass; ?> implements the CRUD actions for <?php echo $modelClass; ?> model. * <?php echo $controllerClass; ?> implements the CRUD actions for <?php echo $modelClass; ?> model.
*/ */
class <?php echo $controllerClass; ?> extends <?php echo StringHelper::basename($generator->baseControllerClass) . "\n"; ?> class <?php echo $controllerClass; ?> extends <?php echo StringHelper::basename($generator->baseControllerClass) . "\n"; ?>
{ {
public function behaviors()
{
return array(
'verbs' => array(
'class' => VerbFilter::className(),
'actions' => array(
'delete' => array('post'),
),
),
);
}
/** /**
* Lists all <?php echo $modelClass; ?> models. * Lists all <?php echo $modelClass; ?> models.
* @return mixed * @return mixed

6
framework/yii/gii/generators/crud/templates/views/index.php

@ -43,6 +43,8 @@ $this->params['breadcrumbs'][] = $this->title;
'dataProvider' => $dataProvider, 'dataProvider' => $dataProvider,
'filterModel' => $searchModel, 'filterModel' => $searchModel,
'columns' => array( 'columns' => array(
array('class' => 'yii\grid\SerialColumn'),
<?php <?php
$count = 0; $count = 0;
foreach ($generator->getTableSchema()->columns as $column) { foreach ($generator->getTableSchema()->columns as $column) {
@ -54,6 +56,8 @@ foreach ($generator->getTableSchema()->columns as $column) {
} }
} }
?> ?>
array('class' => 'yii\grid\ActionColumn'),
), ),
)); ?> )); ?>
<?php else: ?> <?php else: ?>
@ -63,7 +67,7 @@ foreach ($generator->getTableSchema()->columns as $column) {
'class' => 'item', 'class' => 'item',
), ),
'itemView' => function ($model, $key, $index, $widget) { 'itemView' => function ($model, $key, $index, $widget) {
return Html::a(Html::encode($model-><?php echo $nameAttribute; ?>), array('view', <?php echo $urlParams; ?>); return Html::a(Html::encode($model-><?php echo $nameAttribute; ?>), array('view', <?php echo $urlParams; ?>));
}, },
)); ?> )); ?>
<?php endif; ?> <?php endif; ?>

6
framework/yii/gii/generators/crud/templates/views/view.php

@ -31,7 +31,11 @@ $this->params['breadcrumbs'][] = $this->title;
<div> <div>
<?php echo '<?php'; ?> echo Html::a('Update', array('update', <?php echo $urlParams; ?>), array('class' => 'btn btn-danger')); ?> <?php echo '<?php'; ?> echo Html::a('Update', array('update', <?php echo $urlParams; ?>), array('class' => 'btn btn-danger')); ?>
<?php echo '<?php'; ?> echo Html::a('Delete', array('delete', <?php echo $urlParams; ?>), array('class' => 'btn btn-danger')); ?> <?php echo '<?php'; ?> echo Html::a('Delete', array('delete', <?php echo $urlParams; ?>), array(
'class' => 'btn btn-danger',
'data-confirm' => Yii::t('app', 'Are you sure to delete this item?'),
'data-method' => 'post',
)); ?>
</div> </div>
<?php echo '<?php'; ?> echo DetailView::widget(array( <?php echo '<?php'; ?> echo DetailView::widget(array(

102
framework/yii/grid/ActionColumn.php

@ -0,0 +1,102 @@
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\grid;
use Yii;
use Closure;
use yii\helpers\Html;
use yii\helpers\Inflector;
use yii\helpers\StringHelper;
/**
* @author Qiang Xue <qiang.xue@gmail.com>
* @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('<span class="glyphicon glyphicon-eye-open"></span>', $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('<span class="glyphicon glyphicon-pencil"></span>', $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('<span class="glyphicon glyphicon-trash"></span>', $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);
}
}

2
framework/yii/grid/SerialColumn.php

@ -15,6 +15,8 @@ namespace yii\grid;
*/ */
class SerialColumn extends Column class SerialColumn extends Column
{ {
public $header = '#';
/** /**
* Renders the data cell content. * Renders the data cell content.
* @param mixed $model the data model * @param mixed $model the data model

2
framework/yii/web/AssetConverter.php

@ -16,7 +16,7 @@ use yii\base\Component;
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @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. * @var array the commands that are used to perform the asset conversion.

4
framework/yii/web/IAssetConverter.php → framework/yii/web/AssetConverterInterface.php

@ -8,12 +8,12 @@
namespace yii\web; 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 <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
interface IAssetConverter interface AssetConverterInterface
{ {
/** /**
* Converts a given asset file into a CSS or JS file. * Converts a given asset file into a CSS or JS file.

8
framework/yii/web/AssetManager.php

@ -16,7 +16,7 @@ use yii\helpers\FileHelper;
/** /**
* AssetManager manages asset bundles and asset publishing. * 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. * getter and setter. See [[getConverter()]] and [[setConverter()]] for details.
* *
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
@ -116,7 +116,7 @@ class AssetManager extends Component
/** /**
* Returns the asset converter. * Returns the asset converter.
* @return IAssetConverter the asset converter. * @return AssetConverterInterface the asset converter.
*/ */
public function getConverter() public function getConverter()
{ {
@ -130,8 +130,8 @@ class AssetManager extends Component
/** /**
* Sets the asset converter. * Sets the asset converter.
* @param array|IAssetConverter $value the asset converter. This can be either * @param array|AssetConverterInterface $value the asset converter. This can be either
* an object implementing the [[IAssetConverter]] interface, or a configuration * an object implementing the [[AssetConverterInterface]], or a configuration
* array that can be used to create the asset converter object. * array that can be used to create the asset converter object.
*/ */
public function setConverter($value) public function setConverter($value)

2
framework/yii/web/CacheSession.php

@ -42,13 +42,13 @@ class CacheSession extends Session
*/ */
public function init() public function init()
{ {
parent::init();
if (is_string($this->cache)) { if (is_string($this->cache)) {
$this->cache = Yii::$app->getComponent($this->cache); $this->cache = Yii::$app->getComponent($this->cache);
} }
if (!$this->cache instanceof Cache) { if (!$this->cache instanceof Cache) {
throw new InvalidConfigException('CacheSession::cache must refer to the application component ID of a cache object.'); throw new InvalidConfigException('CacheSession::cache must refer to the application component ID of a cache object.');
} }
parent::init();
} }
/** /**

24
framework/yii/web/Controller.php

@ -20,6 +20,12 @@ use yii\helpers\Html;
class Controller extends \yii\base\Controller 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. * Binds the parameters to the action.
* This method is invoked by [[Action]] when it begins to run with the given parameters. * 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 * 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. * Creates a URL using the given route and parameters.
* *
* This method enhances [[UrlManager::createUrl()]] by supporting relative routes. * 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 * Any relative URL will be converted into an absolute one by prepending it with the host info
* of the current request. * of the current request.
* *
* @param integer $statusCode the HTTP status code. If null, it will use 302 * @param integer $statusCode the HTTP status code. If null, it will use 302.
* for normal requests, and [[ajaxRedirectCode]] for AJAX requests.
* See [[http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html]] * See [[http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html]]
* for details about HTTP status code * for details about HTTP status code
* @return Response the current response object * @return Response the current response object

8
framework/yii/web/Identity.php → framework/yii/web/IdentityInterface.php

@ -8,13 +8,13 @@
namespace yii\web; 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 * 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: * 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) * public static function findIdentity($id)
* { * {
@ -41,12 +41,12 @@ namespace yii\web;
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
interface Identity interface IdentityInterface
{ {
/** /**
* Finds an identity by the given ID. * Finds an identity by the given ID.
* @param string|integer $id the ID to be looked for * @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 * Null should be returned if such an identity cannot be found
* or the identity is not in an active state (disabled, deleted, etc.) * or the identity is not in an active state (disabled, deleted, etc.)
*/ */

36
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. * 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 * 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. * 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 * 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. * `yii.getCsrfToken()`, respectively. The [[\yii\web\YiiAsset]] asset must be registered.
* *
* @see Controller::enableCsrfValidation
* @see http://en.wikipedia.org/wiki/Cross-site_request_forgery * @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'. * @var string the name of the token used to prevent CSRF. Defaults to '_csrf'.
* This property is used only when [[enableCsrfValidation]] is true. * This property is used only when [[enableCsrfValidation]] is true.
@ -122,8 +123,6 @@ class Request extends \yii\base\Request
*/ */
public function resolve() public function resolve()
{ {
$this->validateCsrfToken();
$result = Yii::$app->getUrlManager()->parseRequest($this); $result = Yii::$app->getUrlManager()->parseRequest($this);
if ($result !== false) { if ($result !== false) {
list ($route, $params) = $result; list ($route, $params) = $result;
@ -1002,7 +1001,8 @@ class Request extends \yii\base\Request
*/ */
public function getCsrfTokenFromHeader() 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,20 +1023,17 @@ class Request extends \yii\base\Request
* Performs the CSRF validation. * Performs the CSRF validation.
* The method will compare the CSRF token obtained from a cookie and from a POST field. * 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. * 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() public function validateCsrfToken()
{ {
if (!$this->enableCsrfValidation) {
return;
}
$method = $this->getMethod(); $method = $this->getMethod();
if ($method === 'POST' || $method === 'PUT' || $method === 'PATCH' || $method === 'DELETE') { if (!$this->enableCsrfValidation || !in_array($method, array('POST', 'PUT', 'PATCH', 'DELETE'), true)) {
return true;
}
$trueToken = $this->getCookies()->getValue($this->csrfVar); $trueToken = $this->getCookies()->getValue($this->csrfVar);
switch ($method) { switch ($method) {
case 'POST':
$token = $this->getPost($this->csrfVar);
break;
case 'PUT': case 'PUT':
$token = $this->getPut($this->csrfVar); $token = $this->getPut($this->csrfVar);
break; break;
@ -1045,12 +1042,11 @@ class Request extends \yii\base\Request
break; break;
case 'DELETE': case 'DELETE':
$token = $this->getDelete($this->csrfVar); $token = $this->getDelete($this->csrfVar);
break;
default:
$token = $this->getPost($this->csrfVar);
break;
} }
return $token === $trueToken || $this->getCsrfTokenFromHeader() === $trueToken;
$valid = !empty($token) && $token === $trueToken || $this->getCsrfTokenFromHeader() === $trueToken;
if (!$valid) {
throw new HttpException(400, Yii::t('yii', 'Unable to verify your data submission.'));
}
}
} }
} }

40
framework/yii/web/Response.php

@ -112,13 +112,6 @@ class Response extends \yii\base\Response
*/ */
public $charset; 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 * @var string
*/ */
public $statusText; public $statusText;
@ -565,17 +558,22 @@ class Response extends \yii\base\Response
/** /**
* Redirects the browser to the specified URL. * Redirects the browser to the specified URL.
*
* This method will send out a "Location" header to achieve the redirection. * 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 * 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, * client-side JavaScript code handling the redirection. To help achieve this goal,
* this method will use [[ajaxRedirectCode]] as the HTTP status code when performing * this method will send out a "X-Redirect" header instead of "Location".
* redirection in AJAX mode. The following JavaScript code may be used on the client *
* side to handle the redirection response: * 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) { * $document.ajaxComplete(function (event, xhr, settings) {
* if (xhr.status == 278) { * var url = xhr.getResponseHeader('X-Redirect');
* window.location = xhr.getResponseHeader('Location'); * 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 * Any relative URL will be converted into an absolute one by prepending it with the host info
* of the current request. * of the current request.
* *
* @param integer $statusCode the HTTP status code. If null, it will use 302 * @param integer $statusCode the HTTP status code. If null, it will use 302.
* for normal requests, and [[ajaxRedirectCode]] for AJAX requests.
* See [[http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html]] * See [[http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html]]
* for details about HTTP status code * for details about HTTP status code
* @return Response the response object itself * @return Response the response object itself
@ -613,11 +610,14 @@ class Response extends \yii\base\Response
if (strpos($url, '/') === 0 && strpos($url, '//') !== 0) { if (strpos($url, '/') === 0 && strpos($url, '//') !== 0) {
$url = Yii::$app->getRequest()->getHostInfo() . $url; $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); $this->setStatusCode($statusCode);
return $this; return $this;
} }
@ -766,10 +766,10 @@ class Response extends \yii\base\Response
if (!is_object($formatter)) { if (!is_object($formatter)) {
$formatter = Yii::createObject($formatter); $formatter = Yii::createObject($formatter);
} }
if ($formatter instanceof ResponseFormatter) { if ($formatter instanceof ResponseFormatterInterface) {
$formatter->format($this); $formatter->format($this);
} else { } 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 { } else {
switch ($this->format) { switch ($this->format) {

4
framework/yii/web/ResponseFormatter.php → framework/yii/web/ResponseFormatterInterface.php

@ -8,12 +8,12 @@
namespace yii\web; 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 <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
interface ResponseFormatter interface ResponseFormatterInterface
{ {
/** /**
* Formats the specified response. * Formats the specified response.

14
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. * A flash message is available only in the current request and the next request.
* @param string $key the key identifying the flash message * @param string $key the key identifying the flash message
* @param mixed $defaultValue value to be returned if the flash message does not exist. * @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 * @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()); $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;
}
} }
/** /**

30
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. * 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. * 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. * 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. * @property string|integer $id The unique identifier for the user. If null, it means the user is a guest.
* This property is read-only. * 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). * 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 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 * @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. * 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). * Null is returned if the user is not logged in (not authenticated).
* @see login * @see login
* @see logout * @see logout
@ -140,7 +140,7 @@ class User extends Component
if ($id === null) { if ($id === null) {
$this->_identity = null; $this->_identity = null;
} else { } else {
/** @var $class Identity */ /** @var $class IdentityInterface */
$class = $this->identityClass; $class = $this->identityClass;
$this->_identity = $class::findIdentity($id); $this->_identity = $class::findIdentity($id);
} }
@ -156,7 +156,7 @@ class User extends Component
* You should normally update the user identity via methods [[login()]], [[logout()]] * You should normally update the user identity via methods [[login()]], [[logout()]]
* or [[switchIdentity()]]. * 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) public function setIdentity($identity)
{ {
@ -171,7 +171,7 @@ class User extends Component
* and [[enableAutoLogin]] is true, it will also send out an identity * and [[enableAutoLogin]] is true, it will also send out an identity
* cookie to support cookie-based login. * 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. * @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. * 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. * 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); $data = json_decode($value, true);
if (count($data) === 3 && isset($data[0], $data[1], $data[2])) { if (count($data) === 3 && isset($data[0], $data[1], $data[2])) {
list ($id, $authKey, $duration) = $data; list ($id, $authKey, $duration) = $data;
/** @var $class Identity */ /** @var $class IdentityInterface */
$class = $this->identityClass; $class = $this->identityClass;
$identity = $class::findIdentity($id); $identity = $class::findIdentity($id);
if ($identity !== null && $identity->validateAuthKey($authKey)) { if ($identity !== null && $identity->validateAuthKey($authKey)) {
@ -318,7 +318,7 @@ class User extends Component
* The default implementation will trigger the [[EVENT_BEFORE_LOGIN]] event. * The default implementation will trigger the [[EVENT_BEFORE_LOGIN]] event.
* If you override this method, make sure you call the parent implementation * If you override this method, make sure you call the parent implementation
* so that the event is triggered. * 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 * @param boolean $cookieBased whether the login is cookie-based
* @return boolean whether the user should continue to be logged in * @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. * The default implementation will trigger the [[EVENT_AFTER_LOGIN]] event.
* If you override this method, make sure you call the parent implementation * If you override this method, make sure you call the parent implementation
* so that the event is triggered. * 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 * @param boolean $cookieBased whether the login is cookie-based
*/ */
protected function afterLogin($identity, $cookieBased) protected function afterLogin($identity, $cookieBased)
@ -353,7 +353,7 @@ class User extends Component
* The default implementation will trigger the [[EVENT_BEFORE_LOGOUT]] event. * The default implementation will trigger the [[EVENT_BEFORE_LOGOUT]] event.
* If you override this method, make sure you call the parent implementation * If you override this method, make sure you call the parent implementation
* so that the event is triggered. * 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 * @return boolean whether the user should continue to be logged out
*/ */
protected function beforeLogout($identity) protected function beforeLogout($identity)
@ -370,7 +370,7 @@ class User extends Component
* The default implementation will trigger the [[EVENT_AFTER_LOGOUT]] event. * The default implementation will trigger the [[EVENT_AFTER_LOGOUT]] event.
* If you override this method, make sure you call the parent implementation * If you override this method, make sure you call the parent implementation
* so that the event is triggered. * so that the event is triggered.
* @param Identity $identity the user identity information * @param IdentityInterface $identity the user identity information
*/ */
protected function afterLogout($identity) protected function afterLogout($identity)
{ {
@ -402,9 +402,9 @@ class User extends Component
/** /**
* Sends an identity cookie. * Sends an identity cookie.
* This method is used when [[enableAutoLogin]] is true. * 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. * 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. * @param integer $duration number of seconds that the user can remain in logged-in status.
* @see loginByCookie * @see loginByCookie
*/ */
@ -430,7 +430,7 @@ class User extends Component
* This method is mainly called by [[login()]], [[logout()]] and [[loginByCookie()]] * This method is mainly called by [[login()]], [[logout()]] and [[loginByCookie()]]
* when the current user needs to be associated with the corresponding identity information. * 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. * If null, it means switching to be a guest.
* @param integer $duration number of seconds that the user can remain in logged-in status. * @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. * This parameter is used only when `$identity` is not null.
@ -444,7 +444,7 @@ class User extends Component
$this->setIdentity($identity); $this->setIdentity($identity);
$session->remove($this->idVar); $session->remove($this->idVar);
$session->remove($this->authTimeoutVar); $session->remove($this->authTimeoutVar);
if ($identity instanceof Identity) { if ($identity instanceof IdentityInterface) {
$session->set($this->idVar, $identity->getId()); $session->set($this->idVar, $identity->getId());
if ($this->authTimeout !== null) { if ($this->authTimeout !== null) {
$session->set($this->authTimeoutVar, time() + $this->authTimeout); $session->set($this->authTimeoutVar, time() + $this->authTimeout);

2
framework/yii/web/UserEvent.php

@ -18,7 +18,7 @@ use yii\base\Event;
class UserEvent extends 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; public $identity;
/** /**

2
framework/yii/web/XmlResponseFormatter.php

@ -20,7 +20,7 @@ use yii\helpers\StringHelper;
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class XmlResponseFormatter extends Component implements ResponseFormatter class XmlResponseFormatter extends Component implements ResponseFormatterInterface
{ {
/** /**
* @var string the Content-Type header for the response * @var string the Content-Type header for the response

2
framework/yii/widgets/ListViewBase.php

@ -25,7 +25,7 @@ abstract class ListViewBase extends Widget
*/ */
public $options = array(); 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; public $dataProvider;
/** /**

2
phpunit.xml.dist

@ -21,7 +21,7 @@
<file>framework/yii/helpers/ArrayHelper.php</file> <file>framework/yii/helpers/ArrayHelper.php</file>
<file>framework/yii/helpers/Console.php</file> <file>framework/yii/helpers/Console.php</file>
<file>framework/yii/i18n/GettextFile.php</file> <file>framework/yii/i18n/GettextFile.php</file>
<file>framework/yii/web/ResponseFormatter.php</file> <file>framework/yii/web/ResponseFormatterInterface.php</file>
<directory suffix="Exception.php">framework/yii/base</directory> <directory suffix="Exception.php">framework/yii/base</directory>
<directory suffix=".php">framework/yii/db/mssql</directory> <directory suffix=".php">framework/yii/db/mssql</directory>
<directory suffix=".php">framework/yii/bootstrap</directory> <directory suffix=".php">framework/yii/bootstrap</directory>

7
tests/unit/data/base/Speaker.php

@ -17,6 +17,13 @@ class Speaker extends Model
protected $protectedProperty; protected $protectedProperty;
private $_privateProperty; private $_privateProperty;
public static $formName = 'Speaker';
public function formName()
{
return static::$formName;
}
public function attributeLabels() public function attributeLabels()
{ {
return array( return array(

26
tests/unit/framework/base/ModelTest.php

@ -75,6 +75,32 @@ class ModelTest extends TestCase
$this->assertEquals('Qiang', $speaker->firstName); $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() public function testActiveAttributes()
{ {
// by default mass assignment doesn't work at all // by default mass assignment doesn't work at all

1
tests/unit/framework/helpers/HtmlTest.php

@ -19,6 +19,7 @@ class HtmlTest extends TestCase
'request' => array( 'request' => array(
'class' => 'yii\web\Request', 'class' => 'yii\web\Request',
'url' => '/test', 'url' => '/test',
'enableCsrfValidation' => false,
), ),
'response' => array( 'response' => array(
'class' => 'yii\web\Response', 'class' => 'yii\web\Response',

39
tests/unit/framework/web/CacheSessionTest.php

@ -0,0 +1,39 @@
<?php
namespace yiiunit\framework\web;
use Yii;
use yii\caching\FileCache;
use yii\web\CacheSession;
/**
* @group web
*/
class CacheSessionTest extends \yiiunit\TestCase
{
protected function setUp()
{
parent::setUp();
$this->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',
));
}
}
Loading…
Cancel
Save