Commit 9634b728 by Carsten Brandt

Merge branch 'master' into redis

* master: (27 commits) form wip form wip form WIP script WIP Minor change about twig alias. Added Controller::populate(). Added bootstrap. form WIP form wip form wip Refactored UrlManager. refactored AccessControl activeform WIP Removed the $value parameter from radio and checkbox. Support alternative URL rule syntax. Support default standard component class. updated view renderers docs activeform wip Refactored view renderers. updated view renderers docs refactoring activeform. ...
parents eac48ff2 b07b0c14
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
<?php <?php
class SiteController extends \yii\web\Controller use yii\web\Controller;
use app\models\LoginForm;
class SiteController extends Controller
{ {
public function actionIndex() public function actionIndex()
{ {
...@@ -9,9 +12,14 @@ class SiteController extends \yii\web\Controller ...@@ -9,9 +12,14 @@ class SiteController extends \yii\web\Controller
public function actionLogin() public function actionLogin()
{ {
$user = app\models\User::findIdentity(100); $model = new LoginForm();
Yii::$app->getUser()->login($user); if ($this->populate($_POST, $model) && $model->login()) {
Yii::$app->getResponse()->redirect(array('site/index')); Yii::$app->getResponse()->redirect(array('site/index'));
} else {
echo $this->render('login', array(
'model' => $model,
));
}
} }
public function actionLogout() public function actionLogout()
......
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace app\models;
use Yii;
use yii\base\Model;
/**
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class LoginForm extends Model
{
public $username;
public $password;
public $rememberMe = true;
public function rules()
{
return array(
array('username', 'required'),
array('password', 'required'),
array('password', 'validatePassword'),
array('rememberMe', 'boolean'),
);
}
public function validatePassword()
{
$user = User::findByUsername($this->username);
if (!$user || !$user->validatePassword($this->password)) {
$this->addError('password', 'Incorrect username or password.');
}
}
public function login()
{
if ($this->validate()) {
$user = User::findByUsername($this->username);
Yii::$app->getUser()->login($user, $this->rememberMe ? 3600*24*30 : 0);
return true;
} else {
return false;
}
}
}
\ No newline at end of file
...@@ -5,19 +5,22 @@ namespace app\models; ...@@ -5,19 +5,22 @@ namespace app\models;
class User extends \yii\base\Object implements \yii\web\Identity class User extends \yii\base\Object implements \yii\web\Identity
{ {
public $id; public $id;
public $name; public $username;
public $password;
public $authKey; public $authKey;
private static $users = array( private static $users = array(
'100' => array( '100' => array(
'id' => '100', 'id' => '100',
'username' => 'admin',
'password' => 'admin',
'authKey' => 'test100key', 'authKey' => 'test100key',
'name' => 'admin',
), ),
'101' => array( '101' => array(
'id' => '101', 'id' => '101',
'username' => 'demo',
'password' => 'demo',
'authKey' => 'test101key', 'authKey' => 'test101key',
'name' => 'demo',
), ),
); );
...@@ -26,6 +29,16 @@ class User extends \yii\base\Object implements \yii\web\Identity ...@@ -26,6 +29,16 @@ class User extends \yii\base\Object implements \yii\web\Identity
return isset(self::$users[$id]) ? new self(self::$users[$id]) : null; return isset(self::$users[$id]) ? new self(self::$users[$id]) : null;
} }
public static function findByUsername($username)
{
foreach (self::$users as $user) {
if (strcasecmp($user['username'], $username) === 0) {
return new self($user);
}
}
return null;
}
public function getId() public function getId()
{ {
return $this->id; return $this->id;
...@@ -40,4 +53,9 @@ class User extends \yii\base\Object implements \yii\web\Identity ...@@ -40,4 +53,9 @@ class User extends \yii\base\Object implements \yii\web\Identity
{ {
return $this->authKey === $authKey; return $this->authKey === $authKey;
} }
public function validatePassword($password)
{
return $this->password === $password;
}
} }
\ No newline at end of file
...@@ -5,18 +5,22 @@ ...@@ -5,18 +5,22 @@
*/ */
use yii\helpers\Html; use yii\helpers\Html;
?> ?>
<!DOCTYPE html>
<html>
<?php $this->beginPage(); ?> <?php $this->beginPage(); ?>
<!DOCTYPE html>
<html lang="en">
<head> <head>
<meta charset="utf-8" />
<title><?php echo Html::encode($this->title); ?></title> <title><?php echo Html::encode($this->title); ?></title>
<?php echo Html::cssFile("css/bootstrap.min.css", array('media' => 'screen')); ?>
<?php $this->head(); ?> <?php $this->head(); ?>
</head> </head>
<body> <body>
<h1>Welcome</h1> <div class="container">
<?php $this->beginBody(); ?> <h1>Welcome</h1>
<?php echo $content; ?> <?php $this->beginBody(); ?>
<?php $this->endBody(); ?> <?php echo $content; ?>
<?php $this->endBody(); ?>
</div>
</body> </body>
<?php $this->endPage(); ?>
</html> </html>
<?php $this->endPage(); ?>
\ No newline at end of file
...@@ -9,7 +9,7 @@ $user = Yii::$app->getUser(); ...@@ -9,7 +9,7 @@ $user = Yii::$app->getUser();
if ($user->isGuest) { if ($user->isGuest) {
echo Html::a('login', array('login')); echo Html::a('login', array('login'));
} else { } else {
echo "You are logged in as " . $user->identity->name . "<br/>"; echo "You are logged in as " . $user->identity->username . "<br/>";
echo Html::a('logout', array('logout')); echo Html::a('logout', array('logout'));
} }
?> ?>
......
<?php
use yii\helpers\Html;
/**
* @var yii\base\View $this
* @var yii\widgets\ActiveForm $form
* @var app\models\LoginForm $model
*/
?>
<h1>Login</h1>
<p>Please fill out the following fields to login:</p>
<?php $form = $this->beginWidget('yii\widgets\ActiveForm', array('options' => array('class' => 'form-horizontal'))); ?>
<?php echo $form->field($model, 'username')->textInput(); ?>
<?php echo $form->field($model, 'password')->passwordInput(); ?>
<?php echo $form->field($model, 'rememberMe')->checkbox(); ?>
<div class="control-group">
<div class="controls">
<?php echo Html::submitButton('Login', null, null, array('class' => 'btn btn-primary')); ?>
</div>
</div>
<?php $this->endWidget(); ?>
\ No newline at end of file
Yii2 view renderers
===================
By default Yii uses PHP as template language but you can configure it to be able
to render templates with special engines such as Twig or Smarty.
The component responsible for rendering a view is called `view`. You can add
a custom template engines as follows:
```php
array(
'components' => array(
'view' => array(
'class' => 'yii\base\View',
'renderers' => array(
'tpl' => array(
'class' => 'yii\renderers\SmartyViewRenderer',
),
'twig' => array(
'class' => 'yii\renderers\TwigViewRenderer',
),
// ...
),
),
),
)
```
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()`
or `$this->renderPartial()` from your controller:
```php
echo $this->render('renderer.twig', array('username' => 'Alex'));
```
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()`
or `$this->renderPartial()` from your controller:
```php
echo $this->render('renderer.tpl', array('username' => 'Alex'));
```
<?php <?php
return array( return array(
'basePath' => __DIR__ . '/web/assets', 'jquery' => array(
'sourcePath' => __DIR__ . '/assets',
'js' => array(
'jquery.min.js',
),
),
'yii' => array(
'sourcePath' => __DIR__ . '/assets',
'js' => array(
'yii.js',
),
'depends' => array('jquery'),
),
'yii/validation' => array(
'sourcePath' => __DIR__ . '/assets',
'js' => array(
'yii.validation.js',
),
'depends' => array('yii'),
),
'yii/form' => array(
'sourcePath' => __DIR__ . '/assets',
'js' => array(
'yii.activeForm.js',
),
'depends' => array('yii', 'yii/validation'),
),
); );
\ No newline at end of file
/**
* Yii JavaScript module.
*
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
yii = (function ($) {
var pub = {
version: '2.0'
};
return pub;
})(jQuery);
jQuery(document).ready(function ($) {
// call the init() method of every module
var init = function (module) {
if ($.isFunction(module.init) && (module.trigger == undefined || $(module.trigger).length)) {
module.init();
}
$.each(module, function () {
if ($.isPlainObject(this)) {
init(this);
}
});
};
init(yii);
});
/**
* Yii validation module.
*
* This is the JavaScript widget used by the yii\widgets\ActiveForm widget.
*
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
yii.validation = (function ($) {
var pub = {
};
return pub;
})(jQuery);
...@@ -176,7 +176,7 @@ class Application extends Module ...@@ -176,7 +176,7 @@ class Application extends Module
*/ */
public function getRuntimePath() public function getRuntimePath()
{ {
if ($this->_runtimePath !== null) { if ($this->_runtimePath === null) {
$this->setRuntimePath($this->getBasePath() . DIRECTORY_SEPARATOR . 'runtime'); $this->setRuntimePath($this->getBasePath() . DIRECTORY_SEPARATOR . 'runtime');
} }
return $this->_runtimePath; return $this->_runtimePath;
......
...@@ -19,7 +19,14 @@ use yii\helpers\StringHelper; ...@@ -19,7 +19,14 @@ use yii\helpers\StringHelper;
*/ */
class Controller extends Component class Controller extends Component
{ {
/**
* @event ActionEvent an event raised right before executing a controller action.
* You may set [[ActionEvent::isValid]] to be false to cancel the action execution.
*/
const EVENT_BEFORE_ACTION = 'beforeAction'; const EVENT_BEFORE_ACTION = 'beforeAction';
/**
* @event ActionEvent an event raised right after executing a controller action.
*/
const EVENT_AFTER_ACTION = 'afterAction'; const EVENT_AFTER_ACTION = 'afterAction';
/** /**
...@@ -105,16 +112,15 @@ class Controller extends Component ...@@ -105,16 +112,15 @@ class Controller extends Component
if ($action !== null) { if ($action !== null) {
$oldAction = $this->action; $oldAction = $this->action;
$this->action = $action; $this->action = $action;
$status = 1;
if ($this->module->beforeAction($action)) {
if ($this->beforeAction($action)) { if ($this->beforeAction($action)) {
$status = $action->runWithParams($params); $status = $action->runWithParams($params);
$this->afterAction($action); $this->afterAction($action);
} else {
$status = 1;
} }
$this->module->afterAction($action);
}
$this->action = $oldAction; $this->action = $oldAction;
return $status; return $status;
} else { } else {
throw new InvalidRouteException('Unable to resolve the request: ' . $this->getUniqueId() . '/' . $id); throw new InvalidRouteException('Unable to resolve the request: ' . $this->getUniqueId() . '/' . $id);
...@@ -297,6 +303,34 @@ class Controller extends Component ...@@ -297,6 +303,34 @@ class Controller extends Component
} }
/** /**
* Populates one or multiple models from the given data array.
* @param array $data the data array. This is usually `$_POST` or `$_GET`, but can also be any valid array.
* @param Model $model the model to be populated. If there are more than one model to be populated,
* you may supply them as additional parameters.
* @return boolean whether at least one model is successfully populated with the data.
*/
public function populate($data, $model)
{
$success = false;
if (!empty($data) && is_array($data)) {
$models = func_get_args();
array_shift($models);
foreach ($models as $model) {
/** @var Model $model */
$scope = $model->formName();
if ($scope == '') {
$model->attributes = $data;
$success = true;
} elseif (isset($data[$scope])) {
$model->attributes = $data[$scope];
$success = true;
}
}
}
return $success;
}
/**
* Renders a view and applies layout if available. * Renders a view and applies layout if available.
* *
* The view to be rendered can be specified in one of the following formats: * The view to be rendered can be specified in one of the following formats:
......
...@@ -9,7 +9,6 @@ namespace yii\base; ...@@ -9,7 +9,6 @@ namespace yii\base;
use Yii; use Yii;
use yii\helpers\StringHelper; use yii\helpers\StringHelper;
use yii\helpers\FileHelper;
/** /**
* Module is the base class for module and application classes. * Module is the base class for module and application classes.
...@@ -39,6 +38,15 @@ use yii\helpers\FileHelper; ...@@ -39,6 +38,15 @@ use yii\helpers\FileHelper;
abstract class Module extends Component abstract class Module extends Component
{ {
/** /**
* @event ActionEvent an event raised before executing a controller action.
* You may set [[ActionEvent::isValid]] to be false to cancel the action execution.
*/
const EVENT_BEFORE_ACTION = 'beforeAction';
/**
* @event ActionEvent an event raised after executing a controller action.
*/
const EVENT_AFTER_ACTION = 'afterAction';
/**
* @var array custom module parameters (name => value). * @var array custom module parameters (name => value).
*/ */
public $params = array(); public $params = array();
...@@ -523,6 +531,9 @@ abstract class Module extends Component ...@@ -523,6 +531,9 @@ abstract class Module extends Component
public function setComponents($components) public function setComponents($components)
{ {
foreach ($components as $id => $component) { foreach ($components as $id => $component) {
if (isset($this->_components[$id]['class']) && !isset($component['class'])) {
$component['class'] = $this->_components[$id]['class'];
}
$this->_components[$id] = $component; $this->_components[$id] = $component;
} }
} }
...@@ -613,4 +624,27 @@ abstract class Module extends Component ...@@ -613,4 +624,27 @@ abstract class Module extends Component
return isset($controller) ? array($controller, $route) : false; return isset($controller) ? array($controller, $route) : false;
} }
/**
* 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.
* @param Action $action the action to be executed.
* @return boolean whether the action should continue to be executed.
*/
public function beforeAction($action)
{
$event = new ActionEvent($action);
$this->trigger(self::EVENT_BEFORE_ACTION, $event);
return $event->isValid;
}
/**
* This method is invoked right after an action is executed.
* You may override this method to do some postprocessing for the action.
* @param Action $action the action just executed.
*/
public function afterAction($action)
{
$this->trigger(self::EVENT_AFTER_ACTION, new ActionEvent($action));
}
} }
...@@ -70,10 +70,25 @@ class View extends Component ...@@ -70,10 +70,25 @@ class View extends Component
*/ */
public $params; public $params;
/** /**
* @var ViewRenderer|array the view renderer object or the configuration array for * @var array a list of available renderers indexed by their corresponding supported file extensions.
* creating the view renderer. If not set, view files will be treated as normal PHP files. * Each renderer may be a view renderer object or the configuration for creating the renderer object.
* For example,
*
* ~~~
* array(
* 'tpl' => array(
* 'class' => 'yii\renderers\SmartyRenderer',
* ),
* 'twig' => array(
* 'class' => 'yii\renderers\TwigRenderer',
* ),
* )
* ~~~
*
* If no renderer is available for the given view file, the view file will be treated as a normal PHP
* and rendered via [[renderPhpFile()]].
*/ */
public $renderer; public $renderers = array();
/** /**
* @var Theme|array the theme object or the configuration array for creating the theme object. * @var Theme|array the theme object or the configuration array for creating the theme object.
* If not set, it means theming is not enabled. * If not set, it means theming is not enabled.
...@@ -152,9 +167,6 @@ class View extends Component ...@@ -152,9 +167,6 @@ class View extends Component
public function init() public function init()
{ {
parent::init(); parent::init();
if (is_array($this->renderer)) {
$this->renderer = Yii::createObject($this->renderer);
}
if (is_array($this->theme)) { if (is_array($this->theme)) {
$this->theme = Yii::createObject($this->theme); $this->theme = Yii::createObject($this->theme);
} }
...@@ -226,8 +238,14 @@ class View extends Component ...@@ -226,8 +238,14 @@ class View extends Component
$output = ''; $output = '';
if ($this->beforeRender($viewFile)) { if ($this->beforeRender($viewFile)) {
if ($this->renderer !== null) { $ext = pathinfo($viewFile, PATHINFO_EXTENSION);
$output = $this->renderer->render($this, $viewFile, $params); if (isset($this->renderers[$ext])) {
if (is_array($this->renderers[$ext])) {
$this->renderers[$ext] = Yii::createObject($this->renderers[$ext]);
}
/** @var ViewRenderer $renderer */
$renderer = $this->renderers[$ext];
$output = $renderer->render($this, $viewFile, $params);
} else { } else {
$output = $this->renderPhpFile($viewFile, $params); $output = $this->renderPhpFile($viewFile, $params);
} }
...@@ -352,7 +370,7 @@ class View extends Component ...@@ -352,7 +370,7 @@ class View extends Component
if (!isset($properties['view'])) { if (!isset($properties['view'])) {
$properties['view'] = $this; $properties['view'] = $this;
} }
return Yii::createObject($properties, $this); return Yii::createObject($properties);
} }
/** /**
......
<?php
/**
* Smarty view renderer class file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\renderers;
use Yii;
use Smarty;
use yii\base\View;
use yii\base\ViewRenderer;
/**
* SmartyViewRenderer allows you to use Smarty templates in views.
*
* @author Alexander Makarov <sam@rmcreative.ru>
* @since 2.0
*/
class SmartyViewRenderer extends ViewRenderer
{
/**
* @var string the directory or path alias pointing to where Smarty code is located.
*/
public $smartyPath = '@app/vendors/Smarty';
/**
* @var string the directory or path alias pointing to where Smarty cache will be stored.
*/
public $cachePath = '@app/runtime/Smarty/cache';
/**
* @var string the directory or path alias pointing to where Smarty compiled templates will be stored.
*/
public $compilePath = '@app/runtime/Smarty/compile';
/**
* @var Smarty
*/
public $smarty;
public function init()
{
require_once(Yii::getAlias($this->smartyPath) . '/Smarty.class.php');
$this->smarty = new Smarty();
$this->smarty->setCompileDir(Yii::getAlias($this->compilePath));
$this->smarty->setCacheDir(Yii::getAlias($this->cachePath));
}
/**
* Renders a view file.
*
* This method is invoked by [[View]] whenever it tries to render a view.
* Child classes must implement this method to render the given view file.
*
* @param View $view the view object used for rendering the file.
* @param string $file the view file.
* @param array $params the parameters to be passed to the view file.
*
* @return string the rendering result
*/
public function render($view, $file, $params)
{
$ext = pathinfo($file, PATHINFO_EXTENSION);
/** @var \Smarty_Internal_Template $template */
$template = $this->smarty->createTemplate($file, null, null, $params, true);
return $template->fetch();
}
}
\ No newline at end of file
<?php
/**
* Twig view renderer class file.
*
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\renderers;
use Yii;
use yii\base\View;
use yii\base\ViewRenderer;
/**
* TwigViewRenderer allows you to use Twig templates in views.
*
* @author Alexander Makarov <sam@rmcreative.ru>
* @since 2.0
*/
class TwigViewRenderer extends ViewRenderer
{
/**
* @var string the directory or path alias pointing to where Twig code is located.
*/
public $twigPath = '@Twig';
/**
* @var string the directory or path alias pointing to where Twig cache will be stored.
*/
public $cachePath = '@app/runtime/Twig/cache';
/**
* @var array Twig options
* @see http://twig.sensiolabs.org/doc/api.html#environment-options
*/
public $options = array();
/**
* @var \Twig_Environment
*/
public $twig;
public function init()
{
if (!isset(Yii::$aliases['@Twig'])) {
Yii::setAlias('@Twig', $this->twigPath);
}
$loader = new \Twig_Loader_String();
$this->twig = new \Twig_Environment($loader, array_merge(array(
'cache' => Yii::getAlias($this->cachePath),
), $this->options));
}
/**
* Renders a view file.
*
* This method is invoked by [[View]] whenever it tries to render a view.
* Child classes must implement this method to render the given view file.
*
* @param View $view the view object used for rendering the file.
* @param string $file the view file.
* @param array $params the parameters to be passed to the view file.
*
* @return string the rendering result
*/
public function render($view, $file, $params)
{
return $this->twig->render(file_get_contents($file), $params);
}
}
...@@ -70,8 +70,8 @@ class BooleanValidator extends Validator ...@@ -70,8 +70,8 @@ class BooleanValidator extends Validator
*/ */
public function validateValue($value) public function validateValue($value)
{ {
return $this->strict && ($value == $this->trueValue || $value == $this->falseValue) return !$this->strict && ($value == $this->trueValue || $value == $this->falseValue)
|| !$this->strict && ($value === $this->trueValue || $value === $this->falseValue); || $this->strict && ($value === $this->trueValue || $value === $this->falseValue);
} }
/** /**
......
...@@ -47,7 +47,7 @@ class RequiredValidator extends Validator ...@@ -47,7 +47,7 @@ class RequiredValidator extends Validator
{ {
parent::init(); parent::init();
if ($this->message === null) { if ($this->message === null) {
$this->message = $this->requiredValue === null ? Yii::t('yii|{attribute} is invalid.') $this->message = $this->requiredValue === null ? Yii::t('yii|{attribute} cannot be blank.')
: Yii::t('yii|{attribute} must be "{requiredValue}".'); : Yii::t('yii|{attribute} must be "{requiredValue}".');
} }
} }
......
...@@ -33,12 +33,17 @@ class AccessControl extends ActionFilter ...@@ -33,12 +33,17 @@ class AccessControl extends ActionFilter
*/ */
public $denyCallback; public $denyCallback;
/** /**
* @var string the default class of the access rules. This is used when * @var array the default configuration of access rules. Individual rule configurations
* a rule is configured without specifying a class in [[rules]]. * specified via [[rules]] will take precedence when the same property of the rule is configured.
*/ */
public $defaultRuleClass = 'yii\web\AccessRule'; public $ruleConfig = array(
'class' => 'yii\web\AccessRule',
);
/** /**
* @var array a list of access rule objects or configurations for creating the rule objects. * @var array a list of access rule objects or configuration arrays for creating the rule objects.
* If a rule is specified via a configuration array, it will be merged with [[ruleConfig]] first
* before it is used for creating the rule object.
* @see ruleConfig
*/ */
public $rules = array(); public $rules = array();
...@@ -50,10 +55,7 @@ class AccessControl extends ActionFilter ...@@ -50,10 +55,7 @@ class AccessControl extends ActionFilter
parent::init(); parent::init();
foreach ($this->rules as $i => $rule) { foreach ($this->rules as $i => $rule) {
if (is_array($rule)) { if (is_array($rule)) {
if (!isset($rule['class'])) { $this->rules[$i] = Yii::createObject(array_merge($this->ruleConfig, $rule));
$rule['class'] = $this->defaultRuleClass;
}
$this->rules[$i] = Yii::createObject($rule);
} }
} }
} }
......
...@@ -18,6 +18,27 @@ use yii\base\InvalidConfigException; ...@@ -18,6 +18,27 @@ use yii\base\InvalidConfigException;
class Request extends \yii\base\Request class Request extends \yii\base\Request
{ {
/** /**
* @var boolean whether to enable CSRF (Cross-Site Request Forgery) validation. Defaults to false.
* By setting this property to true, forms submitted to an Yii Web application must be originated
* from the same application. If not, a 400 HTTP exception will be raised.
*
* Note, this feature requires that the user client accepts cookie. Also, to use this feature,
* forms submitted via POST method must contain a hidden input whose name is specified by [[csrfTokenName]].
* You may use [[\yii\web\Html::beginForm()]] to generate his hidden input.
* @see http://en.wikipedia.org/wiki/Cross-site_request_forgery
*/
public $enableCsrfValidation = false;
/**
* @var string the name of the token used to prevent CSRF. Defaults to 'YII_CSRF_TOKEN'.
* This property is effectively only when {@link enableCsrfValidation} is true.
*/
public $csrfTokenName = '_csrf';
/**
* @var array the configuration of the CSRF cookie. This property is used only when [[enableCsrfValidation]] is true.
* @see Cookie
*/
public $csrfCookie = array('httponly' => true);
/**
* @var boolean whether cookies should be validated to ensure they are not tampered. Defaults to true. * @var boolean whether cookies should be validated to ensure they are not tampered. Defaults to true.
*/ */
public $enableCookieValidation = true; public $enableCookieValidation = true;
...@@ -27,8 +48,7 @@ class Request extends \yii\base\Request ...@@ -27,8 +48,7 @@ class Request extends \yii\base\Request
public $cookieValidationKey; public $cookieValidationKey;
/** /**
* @var string|boolean the name of the POST parameter that is used to indicate if a request is a PUT or DELETE * @var string|boolean the name of the POST parameter that is used to indicate if a request is a PUT or DELETE
* request tunneled through POST. If false, it means disabling REST request tunneled through POST. * request tunneled through POST. Default to '_method'.
* Default to '_method'.
* @see getRequestMethod * @see getRequestMethod
* @see getRestParams * @see getRestParams
*/ */
...@@ -36,6 +56,7 @@ class Request extends \yii\base\Request ...@@ -36,6 +56,7 @@ class Request extends \yii\base\Request
private $_cookies; private $_cookies;
/** /**
* Resolves the current request into a route and the associated parameters. * Resolves the current request into a route and the associated parameters.
* @return array the first element is the route, and the second is the associated parameters. * @return array the first element is the route, and the second is the associated parameters.
...@@ -43,6 +64,8 @@ class Request extends \yii\base\Request ...@@ -43,6 +64,8 @@ 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;
...@@ -60,7 +83,7 @@ class Request extends \yii\base\Request ...@@ -60,7 +83,7 @@ class Request extends \yii\base\Request
*/ */
public function getRequestMethod() public function getRequestMethod()
{ {
if ($this->restVar !== false && isset($_POST[$this->restVar])) { if (isset($_POST[$this->restVar])) {
return strtoupper($_POST[$this->restVar]); return strtoupper($_POST[$this->restVar]);
} else { } else {
return isset($_SERVER['REQUEST_METHOD']) ? strtoupper($_SERVER['REQUEST_METHOD']) : 'GET'; return isset($_SERVER['REQUEST_METHOD']) ? strtoupper($_SERVER['REQUEST_METHOD']) : 'GET';
...@@ -123,7 +146,7 @@ class Request extends \yii\base\Request ...@@ -123,7 +146,7 @@ class Request extends \yii\base\Request
public function getRestParams() public function getRestParams()
{ {
if ($this->_restParams === null) { if ($this->_restParams === null) {
if ($this->restVar !== false && isset($_POST[$this->restVar])) { if (isset($_POST[$this->restVar])) {
$this->_restParams = $_POST; $this->_restParams = $_POST;
} else { } else {
$this->_restParams = array(); $this->_restParams = array();
...@@ -700,5 +723,72 @@ class Request extends \yii\base\Request ...@@ -700,5 +723,72 @@ class Request extends \yii\base\Request
} }
return $this->_cookies; return $this->_cookies;
} }
private $_csrfToken;
/**
* Returns the random token used to perform CSRF validation.
* The token will be read from cookie first. If not found, a new token will be generated.
* @return string the random token for CSRF validation.
* @see enableCsrfValidation
*/
public function getCsrfToken()
{
if ($this->_csrfToken === null) {
$cookies = $this->getCookies();
if (($this->_csrfToken = $cookies->getValue($this->csrfTokenName)) === null) {
$cookie = $this->createCsrfCookie();
$this->_csrfToken = $cookie->value;
$cookies->add($cookie);
}
}
return $this->_csrfToken;
}
/**
* Creates a cookie with a randomly generated CSRF token.
* Initial values specified in [[csrfCookie]] will be applied to the generated cookie.
* @return Cookie the generated cookie
* @see enableCsrfValidation
*/
protected function createCsrfCookie()
{
$options = $this->csrfCookie;
$options['name'] = $this->csrfTokenName;
$options['value'] = sha1(uniqid(mt_rand(), true));
return new Cookie($options);
}
/**
* 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
*/
public function validateCsrfToken()
{
if (!$this->enableCsrfValidation) {
return;
}
$method = $this->getRequestMethod();
if ($method === 'POST' || $method === 'PUT' || $method === 'DELETE') {
$cookies = $this->getCookies();
switch ($method) {
case 'POST':
$token = $this->getPost($this->csrfTokenName);
break;
case 'PUT':
$token = $this->getPut($this->csrfTokenName);
break;
case 'DELETE':
$token = $this->getDelete($this->csrfTokenName);
}
if (empty($token) || $cookies->getValue($this->csrfTokenName) !== $token) {
throw new HttpException(400, Yii::t('yii|Unable to verify your data submission.'));
}
}
}
} }
...@@ -29,8 +29,15 @@ class UrlManager extends Component ...@@ -29,8 +29,15 @@ class UrlManager extends Component
/** /**
* @var array the rules for creating and parsing URLs when [[enablePrettyUrl]] is true. * @var array the rules for creating and parsing URLs when [[enablePrettyUrl]] is true.
* This property is used only if [[enablePrettyUrl]] is true. Each element in the array * This property is used only if [[enablePrettyUrl]] is true. Each element in the array
* is the configuration of creating a single URL rule whose class by default is [[defaultRuleClass]]. * is the configuration array for creating a single URL rule. The configuration will
* If you modify this property after the UrlManager object is created, make sure * be merged with [[ruleConfig]] first before it is used for creating the rule object.
*
* A special shortcut format can be used if a rule only specifies [[UrlRule::pattern|pattern]]
* and [[UrlRule::route|route]]: `'pattern' => 'route'`. That is, instead of using a configuration
* array, one can use the key to represent the pattern and the value the corresponding route.
* For example, `'post/<id:\d+>' => 'post/view'`.
*
* Note that if you modify this property after the UrlManager object is created, make sure
* you populate the array with rule objects instead of rule configurations. * you populate the array with rule objects instead of rule configurations.
*/ */
public $rules = array(); public $rules = array();
...@@ -59,15 +66,16 @@ class UrlManager extends Component ...@@ -59,15 +66,16 @@ class UrlManager extends Component
*/ */
public $cache = 'cache'; public $cache = 'cache';
/** /**
* @var string the default class name for creating URL rule instances * @var array the default configuration of URL rules. Individual rule configurations
* when it is not specified in [[rules]]. * specified via [[rules]] will take precedence when the same property of the rule is configured.
*/ */
public $defaultRuleClass = 'yii\web\UrlRule'; public $ruleConfig = array(
'class' => 'yii\web\UrlRule',
);
private $_baseUrl; private $_baseUrl;
private $_hostInfo; private $_hostInfo;
/** /**
* Initializes UrlManager. * Initializes UrlManager.
*/ */
...@@ -97,12 +105,17 @@ class UrlManager extends Component ...@@ -97,12 +105,17 @@ class UrlManager extends Component
} }
} }
foreach ($this->rules as $i => $rule) { $rules = array();
if (!isset($rule['class'])) { foreach ($this->rules as $key => $rule) {
$rule['class'] = $this->defaultRuleClass; if (!is_array($rule)) {
$rule = array(
'pattern' => $key,
'route' => $rule,
);
} }
$this->rules[$i] = Yii::createObject($rule); $rules[] = Yii::createObject(array_merge($this->ruleConfig, $rule));
} }
$this->rules = $rules;
if (isset($key, $hash)) { if (isset($key, $hash)) {
$this->cache->set($key, array($this->rules, $hash)); $this->cache->set($key, array($this->rules, $hash));
......
...@@ -32,7 +32,7 @@ class User extends Component ...@@ -32,7 +32,7 @@ class User extends Component
const EVENT_AFTER_LOGOUT = 'afterLogout'; const EVENT_AFTER_LOGOUT = 'afterLogout';
/** /**
* @var string the class name or alias of the [[identity]] object. * @var string the class name of the [[identity]] object.
*/ */
public $identityClass; public $identityClass;
/** /**
...@@ -56,7 +56,7 @@ class User extends Component ...@@ -56,7 +56,7 @@ class User extends Component
* @var array the configuration of the identity cookie. This property is used only when [[enableAutoLogin]] is true. * @var array the configuration of the identity cookie. This property is used only when [[enableAutoLogin]] is true.
* @see Cookie * @see Cookie
*/ */
public $identityCookie = array('name' => '__identity', 'httponly' => true); public $identityCookie = array('name' => '_identity', 'httponly' => true);
/** /**
* @var integer the number of seconds in which the user will be logged out automatically if he * @var integer the number of seconds in which the user will be logged out automatically if he
* remains inactive. If this property is not set, the user will be logged out after * remains inactive. If this property is not set, the user will be logged out after
......
...@@ -157,20 +157,20 @@ class HtmlTest extends \yii\test\TestCase ...@@ -157,20 +157,20 @@ class HtmlTest extends \yii\test\TestCase
public function testButton() public function testButton()
{ {
$this->assertEquals('<button type="button">Button</button>', Html::button()); $this->assertEquals('<button type="button">Button</button>', Html::button());
$this->assertEquals('<button type="button" name="test" value="value">content<></button>', Html::button('test', 'value', 'content<>')); $this->assertEquals('<button type="button" name="test" value="value">content<></button>', Html::button('content<>', 'test', 'value'));
$this->assertEquals('<button type="submit" class="t" name="test" value="value">content<></button>', Html::button('test', 'value', 'content<>', array('type' => 'submit', 'class' => "t"))); $this->assertEquals('<button type="submit" class="t" name="test" value="value">content<></button>', Html::button('content<>', 'test', 'value', array('type' => 'submit', 'class' => "t")));
} }
public function testSubmitButton() public function testSubmitButton()
{ {
$this->assertEquals('<button type="submit">Submit</button>', Html::submitButton()); $this->assertEquals('<button type="submit">Submit</button>', Html::submitButton());
$this->assertEquals('<button type="submit" class="t" name="test" value="value">content<></button>', Html::submitButton('test', 'value', 'content<>', array('class' => 't'))); $this->assertEquals('<button type="submit" class="t" name="test" value="value">content<></button>', Html::submitButton('content<>', 'test', 'value', array('class' => 't')));
} }
public function testResetButton() public function testResetButton()
{ {
$this->assertEquals('<button type="reset">Reset</button>', Html::resetButton()); $this->assertEquals('<button type="reset">Reset</button>', Html::resetButton());
$this->assertEquals('<button type="reset" class="t" name="test" value="value">content<></button>', Html::resetButton('test', 'value', 'content<>', array('class' => 't'))); $this->assertEquals('<button type="reset" class="t" name="test" value="value">content<></button>', Html::resetButton('content<>', 'test', 'value', array('class' => 't')));
} }
public function testInput() public function testInput()
...@@ -230,15 +230,15 @@ class HtmlTest extends \yii\test\TestCase ...@@ -230,15 +230,15 @@ class HtmlTest extends \yii\test\TestCase
public function testRadio() public function testRadio()
{ {
$this->assertEquals('<input type="radio" name="test" value="1" />', Html::radio('test')); $this->assertEquals('<input type="radio" name="test" value="1" />', Html::radio('test'));
$this->assertEquals('<input type="radio" class="a" name="test" checked="checked" />', Html::radio('test', true, null, array('class' => 'a'))); $this->assertEquals('<input type="radio" class="a" name="test" checked="checked" />', Html::radio('test', true, array('class' => 'a', 'value' => null)));
$this->assertEquals('<input type="hidden" name="test" value="0" /><input type="radio" class="a" name="test" value="2" checked="checked" />', Html::radio('test', true, 2, array('class' => 'a' , 'uncheck' => '0'))); $this->assertEquals('<input type="hidden" name="test" value="0" /><input type="radio" class="a" name="test" value="2" checked="checked" />', Html::radio('test', true, array('class' => 'a' , 'uncheck' => '0', 'value' => 2)));
} }
public function testCheckbox() public function testCheckbox()
{ {
$this->assertEquals('<input type="checkbox" name="test" value="1" />', Html::checkbox('test')); $this->assertEquals('<input type="checkbox" name="test" value="1" />', Html::checkbox('test'));
$this->assertEquals('<input type="checkbox" class="a" name="test" checked="checked" />', Html::checkbox('test', true, null, array('class' => 'a'))); $this->assertEquals('<input type="checkbox" class="a" name="test" checked="checked" />', Html::checkbox('test', true, array('class' => 'a', 'value' => null)));
$this->assertEquals('<input type="hidden" name="test" value="0" /><input type="checkbox" class="a" name="test" value="2" checked="checked" />', Html::checkbox('test', true, 2, array('class' => 'a', 'uncheck' => '0'))); $this->assertEquals('<input type="hidden" name="test" value="0" /><input type="checkbox" class="a" name="test" value="2" checked="checked" />', Html::checkbox('test', true, array('class' => 'a', 'uncheck' => '0', 'value' => 2)));
} }
public function testDropDownList() public function testDropDownList()
...@@ -347,7 +347,7 @@ EOD; ...@@ -347,7 +347,7 @@ EOD;
EOD; EOD;
$this->assertEqualsWithoutLE($expected, Html::checkboxList('test', array('value2'), $this->getDataItems(), array( $this->assertEqualsWithoutLE($expected, Html::checkboxList('test', array('value2'), $this->getDataItems(), array(
'item' => function ($index, $label, $name, $checked, $value) { 'item' => function ($index, $label, $name, $checked, $value) {
return $index . Html::label($label . ' ' . Html::checkbox($name, $checked, $value)); return $index . Html::label($label . ' ' . Html::checkbox($name, $checked, array('value' => $value)));
} }
))); )));
} }
...@@ -383,7 +383,7 @@ EOD; ...@@ -383,7 +383,7 @@ EOD;
EOD; EOD;
$this->assertEqualsWithoutLE($expected, Html::radioList('test', array('value2'), $this->getDataItems(), array( $this->assertEqualsWithoutLE($expected, Html::radioList('test', array('value2'), $this->getDataItems(), array(
'item' => function ($index, $label, $name, $checked, $value) { 'item' => function ($index, $label, $name, $checked, $value) {
return $index . Html::label($label . ' ' . Html::radio($name, $checked, $value)); return $index . Html::label($label . ' ' . Html::radio($name, $checked, array('value' => $value)));
} }
))); )));
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment