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-beta
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\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'),
);

2
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;

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:
<?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;
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;

10
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'),
),
),
);
}

2
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;

4
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();

6
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:

14
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
----------

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
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?

7
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:

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/).
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
<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
- `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
<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
- `$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

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.
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.

133
framework/yii/assets/yii.js

@ -37,13 +37,22 @@
*
* 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 () {
@ -57,6 +66,87 @@ yii = (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 = $('<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) {
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;

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.)
* 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.
*/

2
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;

2
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;

19
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);
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);
}
}

17
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',

4
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 <qiang.xue@gmail.com>
* @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.

4
framework/yii/data/IDataProvider.php → 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 <qiang.xue@gmail.com>
* @since 2.0
*/
interface IDataProvider
interface DataProviderInterface
{
/**
* 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
$attribute = reset($this->link);
foreach ($models as $model) {
$values[] = $model[$attribute];
if (($value = $model[$attribute]) !== null) {
$values[] = $value;
}
}
} else {
// composite keys

4
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);

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

@ -28,12 +28,25 @@ use <?php echo ltrim($generator->searchModelClass, '\\'); ?>;
use yii\data\ActiveDataProvider;
use <?php echo ltrim($generator->baseControllerClass, '\\'); ?>;
use yii\web\HttpException;
use yii\web\VerbFilter;
/**
* <?php echo $controllerClass; ?> implements the CRUD actions for <?php echo $modelClass; ?> model.
*/
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.
* @return mixed

6
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'),
<?php
$count = 0;
foreach ($generator->getTableSchema()->columns as $column) {
@ -54,6 +56,8 @@ foreach ($generator->getTableSchema()->columns as $column) {
}
}
?>
array('class' => 'yii\grid\ActionColumn'),
),
)); ?>
<?php else: ?>
@ -63,7 +67,7 @@ foreach ($generator->getTableSchema()->columns as $column) {
'class' => 'item',
),
'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; ?>

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

@ -31,7 +31,11 @@ $this->params['breadcrumbs'][] = $this->title;
<div>
<?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>
<?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
{
public $header = '#';
/**
* Renders the data cell content.
* @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>
* @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.

4
framework/yii/web/IAssetConverter.php → 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 <qiang.xue@gmail.com>
* @since 2.0
*/
interface IAssetConverter
interface AssetConverterInterface
{
/**
* 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.
*
* @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 <qiang.xue@gmail.com>
@ -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)

2
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();
}
/**

24
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

8
framework/yii/web/Identity.php → 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 <qiang.xue@gmail.com>
* @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.)
*/

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.
*/
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,20 +1023,17 @@ 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') {
if (!$this->enableCsrfValidation || !in_array($method, array('POST', 'PUT', 'PATCH', 'DELETE'), true)) {
return true;
}
$trueToken = $this->getCookies()->getValue($this->csrfVar);
switch ($method) {
case 'POST':
$token = $this->getPost($this->csrfVar);
break;
case 'PUT':
$token = $this->getPut($this->csrfVar);
break;
@ -1045,12 +1042,11 @@ class Request extends \yii\base\Request
break;
case 'DELETE':
$token = $this->getDelete($this->csrfVar);
break;
default:
$token = $this->getPost($this->csrfVar);
break;
}
$valid = !empty($token) && $token === $trueToken || $this->getCsrfTokenFromHeader() === $trueToken;
if (!$valid) {
throw new HttpException(400, Yii::t('yii', 'Unable to verify your data submission.'));
}
}
return $token === $trueToken || $this->getCsrfTokenFromHeader() === $trueToken;
}
}

40
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->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) {

4
framework/yii/web/ResponseFormatter.php → 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 <qiang.xue@gmail.com>
* @since 2.0
*/
interface ResponseFormatter
interface ResponseFormatterInterface
{
/**
* 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.
* @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;
}
}
/**

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.
* 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);

2
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;
/**

2
framework/yii/web/XmlResponseFormatter.php

@ -20,7 +20,7 @@ use yii\helpers\StringHelper;
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class XmlResponseFormatter extends Component implements ResponseFormatter
class XmlResponseFormatter extends Component implements ResponseFormatterInterface
{
/**
* @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();
/**
* @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;
/**

2
phpunit.xml.dist

@ -21,7 +21,7 @@
<file>framework/yii/helpers/ArrayHelper.php</file>
<file>framework/yii/helpers/Console.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=".php">framework/yii/db/mssql</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;
private $_privateProperty;
public static $formName = 'Speaker';
public function formName()
{
return static::$formName;
}
public function attributeLabels()
{
return array(

26
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

1
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',

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