Browse Source

Merge branch 'upstream' into 313-Inflector-Helper

* upstream:
  Enhanced the detection of secure connection.
  Refactored Modal.
  < PHP 5.3 compatibility for req checker
  code style and UI improvements to requirements checker
  readded accidentally removed .gitignore file
  Changed Yii.php case in some files where it was wrong
  Finished bootstrap Widget and Modal.
  PHP Intl extension has been added to default Yii requirements.
  moved optional packages to "suggest", updated platform requirements
  "vendor-dir": "vendor" is default, removed
  fixed auth manager unit test to not expect generic exception
  fixed range requests, tests improved
tags/2.0.0-beta
Antonio Ramirez 12 years ago
parent
commit
3a5f01cb6e
  1. 1
      apps/bootstrap/assets/.gitignore
  2. 3
      apps/bootstrap/composer.json
  3. 2
      apps/bootstrap/www/index.php
  4. 2
      apps/bootstrap/yii
  5. 10
      tests/unit/framework/rbac/ManagerTestBase.php
  6. 30
      tests/unit/framework/web/ResponseTest.php
  7. 2
      yii/base/View.php
  8. 355
      yii/bootstrap/Modal.php
  9. 123
      yii/bootstrap/Widget.php
  10. 14
      yii/composer.json
  11. 32
      yii/requirements/YiiRequirementChecker.php
  12. 11
      yii/requirements/requirements.php
  13. 20
      yii/requirements/views/web/index.php
  14. 3
      yii/web/Request.php
  15. 11
      yii/web/Response.php

1
apps/bootstrap/assets/.gitignore vendored

@ -0,0 +1 @@
*

3
apps/bootstrap/composer.json

@ -12,9 +12,6 @@
"irc": "irc://irc.freenode.net/yii", "irc": "irc://irc.freenode.net/yii",
"source": "https://github.com/yiisoft/yii2" "source": "https://github.com/yiisoft/yii2"
}, },
"config": {
"vendor-dir": "vendor"
},
"minimum-stability": "dev", "minimum-stability": "dev",
"require": { "require": {
"php": ">=5.3.0", "php": ">=5.3.0",

2
apps/bootstrap/www/index.php

@ -3,7 +3,7 @@
// comment out the following line to disable debug mode // comment out the following line to disable debug mode
defined('YII_DEBUG') or define('YII_DEBUG', true); defined('YII_DEBUG') or define('YII_DEBUG', true);
require(__DIR__ . '/../vendor/yiisoft/yii2/yii.php'); require(__DIR__ . '/../vendor/yiisoft/yii2/Yii.php');
require(__DIR__ . '/../vendor/autoload.php'); require(__DIR__ . '/../vendor/autoload.php');
$config = require(__DIR__ . '/../config/main.php'); $config = require(__DIR__ . '/../config/main.php');

2
apps/bootstrap/yii

@ -13,7 +13,7 @@ defined('YII_DEBUG') or define('YII_DEBUG', true);
// fcgi doesn't have STDIN defined by default // fcgi doesn't have STDIN defined by default
defined('STDIN') or define('STDIN', fopen('php://stdin', 'r')); defined('STDIN') or define('STDIN', fopen('php://stdin', 'r'));
require(__DIR__ . '/vendor/yiisoft/yii2/yii.php'); require(__DIR__ . '/vendor/yiisoft/yii2/Yii.php');
require(__DIR__ . '/vendor/autoload.php'); require(__DIR__ . '/vendor/autoload.php');
$config = require(__DIR__ . '/config/console.php'); $config = require(__DIR__ . '/config/console.php');

10
tests/unit/framework/rbac/ManagerTestBase.php

@ -32,7 +32,7 @@ abstract class ManagerTestBase extends TestCase
$this->assertEquals($item2->type, Item::TYPE_ROLE); $this->assertEquals($item2->type, Item::TYPE_ROLE);
// test adding an item with the same name // test adding an item with the same name
$this->setExpectedException('Exception'); $this->setExpectedException('\yii\base\Exception');
$this->auth->createItem($name, $type, $description, $bizRule, $data); $this->auth->createItem($name, $type, $description, $bizRule, $data);
} }
@ -69,14 +69,14 @@ abstract class ManagerTestBase extends TestCase
$this->auth->addItemChild('createPost', 'updatePost'); $this->auth->addItemChild('createPost', 'updatePost');
// test adding upper level item to lower one // test adding upper level item to lower one
$this->setExpectedException('Exception'); $this->setExpectedException('\yii\base\Exception');
$this->auth->addItemChild('readPost', 'reader'); $this->auth->addItemChild('readPost', 'reader');
} }
public function testAddItemChild2() public function testAddItemChild2()
{ {
// test adding inexistent items // test adding inexistent items
$this->setExpectedException('Exception'); $this->setExpectedException('\yii\base\Exception');
$this->assertFalse($this->auth->addItemChild('createPost2', 'updatePost')); $this->assertFalse($this->auth->addItemChild('createPost2', 'updatePost'));
} }
@ -105,7 +105,7 @@ abstract class ManagerTestBase extends TestCase
$this->assertEquals($auth->bizRule, 'rule'); $this->assertEquals($auth->bizRule, 'rule');
$this->assertEquals($auth->data, 'data'); $this->assertEquals($auth->data, 'data');
$this->setExpectedException('Exception'); $this->setExpectedException('\yii\base\Exception');
$this->auth->assign('new user', 'createPost2', 'rule', 'data'); $this->auth->assign('new user', 'createPost2', 'rule', 'data');
} }
@ -158,7 +158,7 @@ abstract class ManagerTestBase extends TestCase
public function testDetectLoop() public function testDetectLoop()
{ {
$this->setExpectedException('Exception'); $this->setExpectedException('\yii\base\Exception');
$this->auth->addItemChild('readPost', 'readPost'); $this->auth->addItemChild('readPost', 'readPost');
} }

30
tests/unit/framework/web/ResponseTest.php

@ -41,7 +41,7 @@ class ResponseTest extends \yiiunit\TestCase
static::$httpResponseCode = 200; static::$httpResponseCode = 200;
} }
public function ranges() public function rightRanges()
{ {
// TODO test more cases for range requests and check for rfc compatibility // TODO test more cases for range requests and check for rfc compatibility
// http://www.w3.org/Protocols/rfc2616/rfc2616.txt // http://www.w3.org/Protocols/rfc2616/rfc2616.txt
@ -53,14 +53,14 @@ class ResponseTest extends \yiiunit\TestCase
} }
/** /**
* @dataProvider ranges * @dataProvider rightRanges
*/ */
public function testSendFileRanges($rangeHeader, $expectedHeader, $length, $expectedFile) public function testSendFileRanges($rangeHeader, $expectedHeader, $length, $expectedFile)
{ {
$content = $this->generateTestFileContent(); $content = $this->generateTestFileContent();
$_SERVER['HTTP_RANGE'] = 'bytes=' . $rangeHeader; $_SERVER['HTTP_RANGE'] = 'bytes=' . $rangeHeader;
$sent = $this->runSendFile('testFile.txt', $content, null); $sent = $this->runSendFile('testFile.txt', $content, null);
$this->assertEquals($expectedFile, $sent); $this->assertEquals($expectedFile, $sent);
$this->assertTrue(in_array('HTTP/1.1 206 Partial Content', static::$headers)); $this->assertTrue(in_array('HTTP/1.1 206 Partial Content', static::$headers));
$this->assertTrue(in_array('Accept-Ranges: bytes', static::$headers)); $this->assertTrue(in_array('Accept-Ranges: bytes', static::$headers));
@ -69,6 +69,30 @@ class ResponseTest extends \yiiunit\TestCase
$this->assertTrue(in_array('Content-Length: ' . $length, static::$headers)); $this->assertTrue(in_array('Content-Length: ' . $length, static::$headers));
} }
public function wrongRanges()
{
// TODO test more cases for range requests and check for rfc compatibility
// http://www.w3.org/Protocols/rfc2616/rfc2616.txt
return array(
array('1-2,3-5,6-10'), // multiple range request not supported
array('5-1'), // last-byte-pos value is less than its first-byte-pos value
array('-100000'), // last-byte-pos bigger then content length
array('10000-'), // first-byte-pos bigger then content length
);
}
/**
* @dataProvider wrongRanges
*/
public function testSendFileWrongRanges($rangeHeader)
{
$this->setExpectedException('yii\base\HttpException', 'Requested Range Not Satisfiable');
$content = $this->generateTestFileContent();
$_SERVER['HTTP_RANGE'] = 'bytes=' . $rangeHeader;
$this->runSendFile('testFile.txt', $content, null);
}
protected function generateTestFileContent() protected function generateTestFileContent()
{ {
return '12ёжик3456798áèabcdefghijklmnopqrstuvwxyz!"§$%&/(ёжик)=?'; return '12ёжик3456798áèabcdefghijklmnopqrstuvwxyz!"§$%&/(ёжик)=?';

2
yii/base/View.php

@ -741,7 +741,7 @@ class View extends Component
$lines[] = Html::script(implode("\n", $this->js[self::POS_END]), array('type' => 'text/javascript')); $lines[] = Html::script(implode("\n", $this->js[self::POS_END]), array('type' => 'text/javascript'));
} }
if (!empty($this->js[self::POS_READY])) { if (!empty($this->js[self::POS_READY])) {
$js = "jQuery(document).ready(function(){\n{" . implode("\n", $this->js[self::POS_READY]) . "}\n});"; $js = "jQuery(document).ready(function(){\n" . implode("\n", $this->js[self::POS_READY]) . "\n});";
$lines[] = Html::script($js, array('type' => 'text/javascript')); $lines[] = Html::script($js, array('type' => 'text/javascript'));
} }
return empty($lines) ? '' : implode("\n", $lines) . "\n"; return empty($lines) ? '' : implode("\n", $lines) . "\n";

355
yii/bootstrap/Modal.php

@ -8,279 +8,234 @@
namespace yii\bootstrap; namespace yii\bootstrap;
use Yii; use Yii;
use yii\helpers\Html;
use yii\helpers\ArrayHelper; use yii\helpers\ArrayHelper;
use yii\helpers\Html;
/** /**
* Modal renders a bootstrap modal on the page for its use on your application. * Modal renders a modal window that can be toggled by clicking on a button.
*
* For example,
* *
* Basic usage: * ~~~php
* echo Modal::widget(array(
* 'header' => '<h2>Hello world</h2>',
* 'body' => 'Say hello...',
* 'toggleButton' => array(
* 'label' => 'click me',
* ),
* ));
* ~~~
*
* The following example will show the content enclosed between the [[begin()]]
* and [[end()]] calls within the modal window:
* *
* ```php * ~~~php
* $this->widget(Modal::className(), array( * Modal::begin(array(
* 'id' => 'myModal', * 'header' => '<h2>Hello world</h2>',
* 'header' => 'Modal Heading', * 'toggleButton' => array(
* 'content' => '<p>One fine body...</p>', * 'label' => 'click me',
* 'footer' => 'Modal Footer', * ),
* // if we wish to display a modal button
* 'buttonOptions' => array(
* 'label' => 'Show Modal',
* 'class' => 'btn btn-primary'
* )
* )); * ));
* ``` *
* echo 'Say hello...';
*
* Modal::end();
* ~~~
*
* @see http://twitter.github.io/bootstrap/javascript.html#modals * @see http://twitter.github.io/bootstrap/javascript.html#modals
* @author Antonio Ramirez <amigo.cobos@gmail.com> * @author Antonio Ramirez <amigo.cobos@gmail.com>
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class Modal extends Widget class Modal extends Widget
{ {
/** /**
* @var array The additional HTML attributes of the button that will show the modal. If empty array, only * @var string the header content in the modal window.
* the markup of the modal will be rendered on the page, so users can easily call the modal manually with their own
* scripts. The following special attributes are available:
* <ul>
* <li>label: string, the label of the button</li>
* </ul>
*
* For available options of the button trigger, see http://twitter.github.com/bootstrap/javascript.html#modals.
*/
public $buttonOptions = array();
/**
* @var boolean indicates whether the modal should use transitions. Defaults to 'true'.
*/
public $fade = true;
/**
* @var bool $keyboard, closes the modal when escape key is pressed.
*/
public $keyboard = true;
/**
* @var bool $show, shows the modal when initialized.
*/
public $show = false;
/**
* @var mixed includes a modal-backdrop element. Alternatively, specify `static` for a backdrop which doesn't close
* the modal on click.
*/
public $backdrop = true;
/**
* @var mixed the remote url. If a remote url is provided, content will be loaded via jQuery's load method and
* injected into the .modal-body of the modal.
*/
public $remote;
/**
* @var string a javascript function that will be invoked immediately when the `show` instance method is called.
*/
public $onShow;
/**
* @var string a javascript function that will be invoked when the modal has been made visible to the user
* (will wait for css transitions to complete).
*/
public $onShown;
/**
* @var string a javascript function that will be invoked immediately when the hide instance method has been called.
*/ */
public $onHide; public $header;
/**
* @var string a javascript function that will be invoked when the modal has finished being hidden from the user
* (will wait for css transitions to complete).
*/
public $onHidden;
/** /**
* @var string[] the Javascript event handlers. * @var string the body content in the modal window. Note that anything between
* the [[begin()]] and [[end()]] calls of the Modal widget will also be treated
* as the body content, and will be rendered before this.
*/ */
protected $events = array(); public $body;
/** /**
* @var array $pluginOptions the plugin options. * @var string the footer content in the modal window.
*/ */
protected $pluginOptions = array(); public $footer;
/**
* @var string
*/
public $closeText = '&times;';
/** /**
* @var string header content. Header can also be a path to a view file. * @var array the options for rendering the close button tag.
* The close button is displayed in the header of the modal window. Clicking
* on the button will hide the modal window. If this is null, no close button will be rendered.
*
* The following special options are supported:
*
* - tag: string, the tag name of the button. Defaults to 'button'.
* - label: string, the label of the button. Defaults to '&times;'.
*
* The rest of the options will be rendered as the HTML attributes of the button tag.
* Please refer to the [Modal plugin help](http://twitter.github.com/bootstrap/javascript.html#modals)
* for the supported HTML attributes.
*/ */
public $header; public $closeButton = array();
/** /**
* @var string body of modal. Body can also be a path to a view file. * @var array the options for rendering the toggle button tag.
* The toggle button is used to toggle the visibility of the modal window.
* If this property is null, no toggle button will be rendered.
*
* The following special options are supported:
*
* - tag: string, the tag name of the button. Defaults to 'button'.
* - label: string, the label of the button. Defaults to 'Show'.
*
* The rest of the options will be rendered as the HTML attributes of the button tag.
* Please refer to the [Modal plugin help](http://twitter.github.com/bootstrap/javascript.html#modals)
* for the supported HTML attributes.
*/ */
public $content; public $toggleButton;
/**
* @var string footer content. Content can also be a path to a view file.
*/
public $footer;
/** /**
* Widget's init method * Initializes the widget.
*/ */
public function init() public function init()
{ {
parent::init(); parent::init();
$this->name = 'modal'; $this->initOptions();
$this->defaultOption('id', $this->getId());
$this->defaultOption('role', 'dialog');
$this->defaultOption('tabindex', '-1');
$this->addClassName('modal');
$this->addClassName('hide');
if ($this->fade) echo $this->renderToggleButton() . "\n";
$this->addClassName('fade'); echo Html::beginTag('div', $this->options) . "\n";
echo $this->renderHeader() . "\n";
$this->initPluginOptions(); echo $this->renderBodyBegin() . "\n";
$this->initPluginEvents();
} }
/** /**
* Initialize plugin events if any * Renders the widget.
*/ */
public function initPluginEvents() public function run()
{ {
foreach (array('onShow', 'onShown', 'onHide', 'onHidden') as $event) { echo "\n" . $this->renderBodyEnd();
if ($this->{$event} !== null) { echo "\n" . $this->renderFooter();
$modalEvent = strtolower(substr($event, 2)); echo "\n" . Html::endTag('div');
if ($this->{$event} instanceof JsExpression)
$this->events[$modalEvent] = $this->$event; $this->registerPlugin('modal');
else
$this->events[$modalEvent] = new JsExpression($this->{$event});
}
}
} }
/** /**
* Initialize plugin options. * Renders the header HTML markup of the modal
* ***Important***: The display of the button overrides the initialization of the modal bootstrap widget. * @return string the rendering result
*/ */
public function initPluginOptions() protected function renderHeader()
{ {
if (null !== $this->remote) $button = $this->renderCloseButton();
$this->pluginOptions['remote'] = Html::url($this->remote); if ($button !== null) {
$this->header = $button . "\n" . $this->header;
foreach (array('backdrop', 'keyboard', 'show') as $option) {
$this->pluginOptions[$option] = isset($this->pluginOptions[$option])
? $this->pluginOptions[$option]
: $this->{$option};
} }
if ($this->header !== null) {
return Html::tag('div', "\n" . $this->header . "\n", array('class' => 'modal-header'));
} else {
return null;
} }
/**
* Widget's run method
*/
public function run()
{
$this->renderModal();
$this->renderButton();
$this->registerScript();
} }
/** /**
* Renders the button that will open the modal if its options have been configured * Renders the opening tag of the modal body.
* @return string the rendering result
*/ */
public function renderButton() protected function renderBodyBegin()
{ {
if (!empty($this->buttonOptions)) { return Html::beginTag('div', array('class' => 'modal-body'));
$this->buttonOptions['data-toggle'] = isset($this->buttonOptions['data-toggle'])
? $this->buttonOptions['data-toggle']
: 'modal';
if ($this->remote !== null && !isset($this->buttonOptions['data-remote']))
$this->buttonOptions['data-remote'] = Html::url($this->remote);
$label = ArrayHelper::remove($this->buttonOptions, 'label', 'Button');
$name = ArrayHelper::remove($this->buttonOptions, 'name');
$value = ArrayHelper::remove($this->buttonOptions, 'value');
$attr = isset($this->buttonOptions['data-remote'])
? 'data-target'
: 'href';
$this->buttonOptions[$attr] = isset($this->buttonOptions[$attr])
? $this->buttonOptions[$attr]
: '#' . ArrayHelper::getValue($this->options, 'id');
echo Html::button($label, $name, $value, $this->buttonOptions);
}
} }
/** /**
* Renders the modal markup * Renders the closing tag of the modal body.
* @return string the rendering result
*/ */
public function renderModal() protected function renderBodyEnd()
{ {
echo Html::beginTag('div', $this->options); return $this->body . "\n" . Html::endTag('div');
$this->renderModalHeader();
$this->renderModalBody();
$this->renderModalFooter();
echo Html::endTag('div');
} }
/** /**
* Renders the header HTML markup of the modal * Renders the HTML markup for the footer of the modal
* @return string the rendering result
*/ */
public function renderModalHeader() protected function renderFooter()
{ {
echo Html::beginTag('div', array('class'=>'modal-header')); if ($this->footer !== null) {
if ($this->closeText) return Html::tag('div', "\n" . $this->footer . "\n", array('class' => 'modal-footer'));
echo Html::button($this->closeText, null, null, array('data-dismiss' => 'modal', 'class'=>'close')); } else {
echo $this->header; return null;
echo Html::endTag('div'); }
} }
/** /**
* Renders the HTML markup for the body of the modal * Renders the toggle button.
* @return string the rendering result
*/ */
public function renderModalBody() protected function renderToggleButton()
{ {
echo Html::beginTag('div', array('class'=>'modal-body')); if ($this->toggleButton !== null) {
echo $this->content; $tag = ArrayHelper::remove($this->toggleButton, 'tag', 'button');
echo Html::endTag('div'); $label = ArrayHelper::remove($this->toggleButton, 'label', 'Show');
if ($tag === 'button' && !isset($this->toggleButton['type'])) {
$this->toggleButton['type'] = 'button';
}
return Html::tag($tag, $label, $this->toggleButton);
} else {
return null;
}
} }
/** /**
* Renders the HTML markup for the footer of the modal * Renders the close button.
* @return string the rendering result
*/ */
public function renderModalFooter() protected function renderCloseButton()
{ {
if ($this->closeButton !== null) {
echo Html::beginTag('div', array('class'=>'modal-footer')); $tag = ArrayHelper::remove($this->closeButton, 'tag', 'button');
echo $this->footer; $label = ArrayHelper::remove($this->closeButton, 'label', '&times;');
echo Html::endTag('div'); if ($tag === 'button' && !isset($this->closeButton['type'])) {
$this->closeButton['type'] = 'button';
}
return Html::tag($tag, $label, $this->closeButton);
} else {
return null;
}
} }
/** /**
* Registers client scripts * Initializes the widget options.
* This method sets the default values for various options.
*/ */
public function registerScript() protected function initOptions()
{ {
// do we render a button? If so, bootstrap will handle its behavior through its $this->options = array_merge(array(
// mark-up, otherwise, register the plugin. 'class' => 'modal hide',
if(empty($this->buttonOptions)) ), $this->options);
$this->registerPlugin('modal', $this->pluginOptions); $this->addCssClass($this->options, 'modal');
// register events $this->pluginOptions = array_merge(array(
$this->registerEvents($this->events); 'show' => false,
), $this->pluginOptions);
if ($this->closeButton !== null) {
$this->closeButton = array_merge(array(
'data-dismiss' => 'modal',
'aria-hidden' => 'true',
'class' => 'close',
), $this->closeButton);
} }
if ($this->toggleButton !== null) {
$this->toggleButton = array_merge(array(
'data-toggle' => 'modal',
), $this->toggleButton);
if (!isset($this->toggleButton['data-target']) && !isset($this->toggleButton['href'])) {
$this->toggleButton['data-target'] = '#' . $this->options['id'];
}
}
}
} }

123
yii/bootstrap/Widget.php

@ -9,19 +9,20 @@ namespace yii\bootstrap;
use Yii; use Yii;
use yii\base\View; use yii\base\View;
use yii\helpers\Json;
/** /**
* Bootstrap is the base class for bootstrap widgets. * \yii\bootstrap\Widget is the base class for all bootstrap widgets.
* *
* @author Antonio Ramirez <amigo.cobos@gmail.com> * @author Antonio Ramirez <amigo.cobos@gmail.com>
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0 * @since 2.0
*/ */
class Widget extends \yii\base\Widget class Widget extends \yii\base\Widget
{ {
/** /**
* @var bool whether to register the asset * @var boolean whether to use the responsive version of Bootstrap.
*/ */
public static $responsive = true; public static $responsive = true;
@ -29,95 +30,75 @@ class Widget extends \yii\base\Widget
* @var array the HTML attributes for the widget container tag. * @var array the HTML attributes for the widget container tag.
*/ */
public $options = array(); public $options = array();
/**
* @var array the options for the underlying Bootstrap JS plugin.
* Please refer to the corresponding Bootstrap plugin Web page for possible options.
* For example, [this page](http://twitter.github.io/bootstrap/javascript.html#modals) shows
* how to use the "Modal" plugin and the supported options (e.g. "remote").
*/
public $pluginOptions = array();
/**
* @var array the event handlers for the underlying Bootstrap JS plugin.
* Please refer to the corresponding Bootstrap plugin Web page for possible events.
* For example, [this page](http://twitter.github.io/bootstrap/javascript.html#modals) shows
* how to use the "Modal" plugin and the supported events (e.g. "shown").
*/
public $pluginEvents = array();
/** /**
* Initializes the widget. * Initializes the widget.
* This method will register the bootstrap asset bundle. If you override this method,
* make sure you call the parent implementation first.
*/ */
public function init() public function init()
{ {
// ensure bundle parent::init();
$this->registerBundle(static::$responsive); if (!isset($this->options['id'])) {
$this->options['id'] = $this->getId();
}
} }
/** /**
* Registers plugin events with the API. * Registers a specific Bootstrap plugin and the related events
* @param string $selector the CSS selector. * @param string $name the name of the Bootstrap plugin
* @param string[] $events the JavaScript event configuration (name=>handler).
* @return boolean whether the events were registered.
* @todo To be discussed
*/ */
protected function registerEvents($selector, $events = array()) protected function registerPlugin($name)
{ {
if (empty($events)) $id = $this->options['id'];
return; $view = $this->getView();
$script = ''; $bundle = static::$responsive ? 'yii/bootstrap-responsive' : 'yii/bootstrap';
foreach ($events as $name => $handler) { $view->registerAssetBundle($bundle);
$handler = ($handler instanceof JsExpression)
? $handler
: new JsExpression($handler);
$script .= ";jQuery('{$selector}').on('{$name}', {$handler});"; if ($this->pluginOptions !== false) {
} $options = empty($this->pluginOptions) ? '' : Json::encode($this->pluginOptions);
if (!empty($script)) $js = "jQuery('#$id').$name($options);";
$this->view->registerJs($script); $view->registerJs($js);
} }
/** if (!empty($this->pluginEvents)) {
* Registers a specific Bootstrap plugin using the given selector and options. $js = array();
* foreach ($this->pluginEvents as $event => $handler) {
* @param string $name the name of the javascript widget to initialize $js[] = "jQuery('#$id').on('$event', $handler);";
* @param array $options the Javascript options for the plugin
*/
public function registerPlugin($name, $options = array())
{
$selector = '#' . ArrayHelper::getValue($this->options, 'id');
$options = !empty($options) ? Json::encode($options) : '';
$script = ";jQuery('{$selector}').{$name}({$options});";
$this->view->registerJs($script);
} }
$view->registerJs(implode("\n", $js));
/**
* Registers bootstrap bundle
* @param bool $responsive
*/
public function registerBundle($responsive = false)
{
$bundle = $responsive ? 'yii/bootstrap-responsive' : 'yii/bootstrap';
$this->view->registerAssetBundle($bundle);
} }
/**
* Adds a new class to options. If the class key does not exists, it will create one, if it exists it will append
* the value and also makes sure the uniqueness of them.
*
* @param string $class
* @return array
*/
protected function addClassName($class)
{
if (isset($this->options['class'])) {
if (!is_array($this->options['class']))
$this->options['class'] = explode(' ', $this->options['class']);
$this->options['class'][] = $class;
$this->options['class'] = array_unique($this->options['class']);
$this->options['class'] = implode(' ', $this->options['class']);
} else
$this->options['class'] = $class;
return $this->options;
} }
/** /**
* Sets the default value for an item if not set. * Adds a CSS class to the specified options.
* @param string $key the name of the item. * This method will ensure that the CSS class is unique and the "class" option is properly formatted.
* @param mixed $value the default value. * @param array $options the options to be modified.
* @return array * @param string $class the CSS class to be added
*/ */
protected function defaultOption($key, $value) protected function addCssClass(&$options, $class)
{ {
if (!isset($this->options[$key])) if (isset($options['class'])) {
$this->options[$key] = $value; $classes = preg_split('/\s+/', $options['class'] . ' ' . $class, -1, PREG_SPLIT_NO_EMPTY);
return $this->options; $options['class'] = implode(' ', array_unique($classes));
} else {
$options['class'] = $class;
}
} }
} }

14
yii/composer.json

@ -64,10 +64,14 @@
"source": "https://github.com/yiisoft/yii2" "source": "https://github.com/yiisoft/yii2"
}, },
"require": { "require": {
"php": ">=5.3.0", "php": ">=5.3.11",
"michelf/php-markdown": "1.3", "ext-mbstring": "*",
"twig/twig": "1.12.*", "lib-pcre": "*"
"smarty/smarty": "3.1.*", },
"ezyang/htmlpurifier": "v4.5.0" "suggest": {
"michelf/php-markdown": "Required for Markdown helper.",
"twig/twig": "Required for TwigViewRenderer.",
"smarty/smarty": "Required for SmartyViewRenderer.",
"ezyang/htmlpurifier": "Required for Purifier helper."
} }
} }

32
yii/requirements/YiiRequirementChecker.php

@ -16,7 +16,7 @@ if (version_compare(PHP_VERSION, '4.3', '<')) {
* *
* Example: * Example:
* *
* ~~~ * ~~~php
* require_once('path/to/YiiRequirementChecker.php'); * require_once('path/to/YiiRequirementChecker.php');
* $requirementsChecker = new YiiRequirementChecker(); * $requirementsChecker = new YiiRequirementChecker();
* $requirements = array( * $requirements = array(
@ -62,7 +62,7 @@ class YiiRequirementChecker
* If a string, it is treated as the path of the file, which contains the requirements; * If a string, it is treated as the path of the file, which contains the requirements;
* @return YiiRequirementChecker self instance. * @return YiiRequirementChecker self instance.
*/ */
function check($requirements) public function check($requirements)
{ {
if (is_string($requirements)) { if (is_string($requirements)) {
$requirements = require($requirements); $requirements = require($requirements);
@ -132,7 +132,7 @@ class YiiRequirementChecker
* ) * )
* </code> * </code>
*/ */
function getResult() public function getResult()
{ {
if (isset($this->result)) { if (isset($this->result)) {
return $this->result; return $this->result;
@ -145,7 +145,7 @@ class YiiRequirementChecker
* Renders the requirements check result. * Renders the requirements check result.
* The output will vary depending is a script running from web or from console. * The output will vary depending is a script running from web or from console.
*/ */
function render() public function render()
{ {
if (!isset($this->result)) { if (!isset($this->result)) {
$this->usageError('Nothing to render!'); $this->usageError('Nothing to render!');
@ -166,7 +166,7 @@ class YiiRequirementChecker
* @param string $compare comparison operator, by default '>=' * @param string $compare comparison operator, by default '>='
* @return boolean if PHP extension version matches. * @return boolean if PHP extension version matches.
*/ */
function checkPhpExtensionVersion($extensionName, $version, $compare = '>=') public function checkPhpExtensionVersion($extensionName, $version, $compare = '>=')
{ {
if (!extension_loaded($extensionName)) { if (!extension_loaded($extensionName)) {
return false; return false;
@ -183,7 +183,7 @@ class YiiRequirementChecker
* @param string $name configuration option name. * @param string $name configuration option name.
* @return boolean option is on. * @return boolean option is on.
*/ */
function checkPhpIniOn($name) public function checkPhpIniOn($name)
{ {
$value = ini_get($name); $value = ini_get($name);
if (empty($value)) { if (empty($value)) {
@ -197,7 +197,7 @@ class YiiRequirementChecker
* @param string $name configuration option name. * @param string $name configuration option name.
* @return boolean option is off. * @return boolean option is off.
*/ */
function checkPhpIniOff($name) public function checkPhpIniOff($name)
{ {
$value = ini_get($name); $value = ini_get($name);
if (empty($value)) { if (empty($value)) {
@ -214,7 +214,7 @@ class YiiRequirementChecker
* @param string $compare comparison operator, by default '>='. * @param string $compare comparison operator, by default '>='.
* @return boolean comparison result. * @return boolean comparison result.
*/ */
function compareByteSize($a, $b, $compare = '>=') public function compareByteSize($a, $b, $compare = '>=')
{ {
$compareExpression = '(' . $this->getByteSize($a) . $compare . $this->getByteSize($b) . ')'; $compareExpression = '(' . $this->getByteSize($a) . $compare . $this->getByteSize($b) . ')';
return $this->evaluateExpression($compareExpression); return $this->evaluateExpression($compareExpression);
@ -226,7 +226,7 @@ class YiiRequirementChecker
* @param string $verboseSize verbose size representation. * @param string $verboseSize verbose size representation.
* @return integer actual size in bytes. * @return integer actual size in bytes.
*/ */
function getByteSize($verboseSize) public function getByteSize($verboseSize)
{ {
if (empty($verboseSize)) { if (empty($verboseSize)) {
return 0; return 0;
@ -265,7 +265,7 @@ class YiiRequirementChecker
* @param string|null $max verbose file size maximum required value, pass null to skip maximum check. * @param string|null $max verbose file size maximum required value, pass null to skip maximum check.
* @return boolean success. * @return boolean success.
*/ */
function checkUploadMaxFileSize($min = null, $max = null) public function checkUploadMaxFileSize($min = null, $max = null)
{ {
$postMaxSize = ini_get('post_max_size'); $postMaxSize = ini_get('post_max_size');
$uploadMaxFileSize = ini_get('upload_max_filesize'); $uploadMaxFileSize = ini_get('upload_max_filesize');
@ -292,7 +292,7 @@ class YiiRequirementChecker
* @param boolean $_return_ whether the rendering result should be returned as a string * @param boolean $_return_ whether the rendering result should be returned as a string
* @return string the rendering result. Null if the rendering result is not required. * @return string the rendering result. Null if the rendering result is not required.
*/ */
function renderViewFile($_viewFile_, $_data_ = null, $_return_ = false) public function renderViewFile($_viewFile_, $_data_ = null, $_return_ = false)
{ {
// we use special variable names here to avoid conflict when extracting data // we use special variable names here to avoid conflict when extracting data
if (is_array($_data_)) { if (is_array($_data_)) {
@ -316,7 +316,7 @@ class YiiRequirementChecker
* @param int $requirementKey requirement key in the list. * @param int $requirementKey requirement key in the list.
* @return array normalized requirement. * @return array normalized requirement.
*/ */
function normalizeRequirement($requirement, $requirementKey = 0) public function normalizeRequirement($requirement, $requirementKey = 0)
{ {
if (!is_array($requirement)) { if (!is_array($requirement)) {
$this->usageError('Requirement must be an array!'); $this->usageError('Requirement must be an array!');
@ -354,7 +354,7 @@ class YiiRequirementChecker
* This method will then terminate the execution of the current application. * This method will then terminate the execution of the current application.
* @param string $message the error message * @param string $message the error message
*/ */
function usageError($message) public function usageError($message)
{ {
echo "Error: $message\n\n"; echo "Error: $message\n\n";
exit(1); exit(1);
@ -365,7 +365,7 @@ class YiiRequirementChecker
* @param string $expression a PHP expression to be evaluated. * @param string $expression a PHP expression to be evaluated.
* @return mixed the expression result. * @return mixed the expression result.
*/ */
function evaluateExpression($expression) public function evaluateExpression($expression)
{ {
return eval('return ' . $expression . ';'); return eval('return ' . $expression . ';');
} }
@ -374,7 +374,7 @@ class YiiRequirementChecker
* Returns the server information. * Returns the server information.
* @return string server information. * @return string server information.
*/ */
function getServerInfo() public function getServerInfo()
{ {
$info = isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : ''; $info = isset($_SERVER['SERVER_SOFTWARE']) ? $_SERVER['SERVER_SOFTWARE'] : '';
return $info; return $info;
@ -384,7 +384,7 @@ class YiiRequirementChecker
* Returns the now date if possible in string representation. * Returns the now date if possible in string representation.
* @return string now date. * @return string now date.
*/ */
function getNowDate() public function getNowDate()
{ {
$nowDate = @strftime('%Y-%m-%d %H:%M', time()); $nowDate = @strftime('%Y-%m-%d %H:%M', time());
return $nowDate; return $nowDate;

11
yii/requirements/requirements.php

@ -1,7 +1,9 @@
<?php <?php
/** /**
* This is the Yii core requirements for the [[YiiRequirementChecker]] instance. * These are the Yii core requirements for the [[YiiRequirementChecker]] instance.
* These requirements are mandatory for any Yii application. * These requirements are mandatory for any Yii application.
*
* @var $this YiiRequirementChecker
*/ */
return array( return array(
array( array(
@ -36,4 +38,11 @@ return array(
'by' => '<a href="http://www.php.net/manual/en/book.mbstring.php">Multibyte string</a> processing', 'by' => '<a href="http://www.php.net/manual/en/book.mbstring.php">Multibyte string</a> processing',
'memo' => 'Required for multibyte encoding string processing.' 'memo' => 'Required for multibyte encoding string processing.'
), ),
array(
'name' => 'Intl extension',
'mandatory' => false,
'condition' => $this->checkPhpExtensionVersion('intl', '1.0.2'),
'by' => '<a href="http://www.php.net/manual/en/book.intl.php">Internationalization</a> support',
'memo' => 'PHP Intl extension 1.0.2 or higher is required when you want to use <abbr title="Internationalized domain names">IDN</abbr>-feature of EmailValidator or UrlValidator.'
),
); );

20
yii/requirements/views/web/index.php

@ -8,7 +8,7 @@
<head> <head>
<meta charset="utf-8"/> <meta charset="utf-8"/>
<title>Yii Application Requirement Checker</title> <title>Yii Application Requirement Checker</title>
<?php $this->renderViewFile(dirname(__FILE__).DIRECTORY_SEPARATOR.'css.php'); ?> <?php $this->renderViewFile(dirname(__FILE__) . '/css.php'); ?>
</head> </head>
<body> <body>
<div class="container"> <div class="container">
@ -25,14 +25,26 @@
It checks if the server is running the right version of PHP, It checks if the server is running the right version of PHP,
if appropriate PHP extensions have been loaded, and if php.ini file settings are correct. if appropriate PHP extensions have been loaded, and if php.ini file settings are correct.
</p> </p>
<p>
There are two kinds of requirements being checked. Mandatory requirements are those that have to be met
to allow Yii to work as expected. There are also some optional requirements beeing checked which will
show you a warning when they do not meet. You can use Yii framework without them but some specific
functionality may be not available in this case.
</p>
<h3>Conclusion</h3> <h3>Conclusion</h3>
<?php if ($summary['errors'] > 0): ?> <?php if ($summary['errors'] > 0): ?>
<strong class="text-error">Unfortunately your server configuration does not satisfy the requirements by this application.</strong> <div class="alert alert-error">
<strong>Unfortunately your server configuration does not satisfy the requirements by this application.<br>Please refer to the table below for detailed explanation.</strong>
</div>
<?php elseif ($summary['warnings'] > 0): ?> <?php elseif ($summary['warnings'] > 0): ?>
<strong class="text-warning">Your server configuration satisfies the minimum requirements by this application. Please pay attention to the warnings listed below if your application will use the corresponding features.</strong> <div class="alert alert-info">
<strong>Your server configuration satisfies the minimum requirements by this application.<br>Please pay attention to the warnings listed below and check if your application will use the corresponding features.</strong>
</div>
<?php else: ?> <?php else: ?>
<strong class="text-success">Congratulations! Your server configuration satisfies all requirements.</strong> <div class="alert alert-success">
<strong>Congratulations! Your server configuration satisfies all requirements.</strong>
</div>
<?php endif; ?> <?php endif; ?>
<h3>Details</h3> <h3>Details</h3>

3
yii/web/Request.php

@ -533,7 +533,8 @@ class Request extends \yii\base\Request
*/ */
public function getIsSecureConnection() public function getIsSecureConnection()
{ {
return !empty($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off'); return isset($_SERVER['HTTPS']) && strcasecmp($_SERVER['HTTPS'], 'off')
|| isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO']==='https';
} }
/** /**

11
yii/web/Response.php

@ -15,6 +15,7 @@ use yii\helpers\StringHelper;
/** /**
* @author Qiang Xue <qiang.xue@gmail.com> * @author Qiang Xue <qiang.xue@gmail.com>
* @author Carsten Brandt <mail@cebe.cc>
* @since 2.0 * @since 2.0
*/ */
class Response extends \yii\base\Response class Response extends \yii\base\Response
@ -50,7 +51,7 @@ class Response extends \yii\base\Response
if (isset($_SERVER['HTTP_RANGE'])) { if (isset($_SERVER['HTTP_RANGE'])) {
// client sent us a multibyte range, can not hold this one for now // client sent us a multibyte range, can not hold this one for now
if (strpos(',', $_SERVER['HTTP_RANGE']) !== false) { if (strpos($_SERVER['HTTP_RANGE'],',') !== false) {
header("Content-Range: bytes $contentStart-$contentEnd/$fileSize"); header("Content-Range: bytes $contentStart-$contentEnd/$fileSize");
throw new HttpException(416, 'Requested Range Not Satisfiable'); throw new HttpException(416, 'Requested Range Not Satisfiable');
} }
@ -63,14 +64,18 @@ class Response extends \yii\base\Response
} else { } else {
$range = explode('-', $range); $range = explode('-', $range);
$contentStart = $range[0]; $contentStart = $range[0];
$contentEnd = (isset($range[1]) && is_numeric($range[1])) ? $range[1] : $fileSize - 1;
// check if the last-byte-pos presents in header
if ((isset($range[1]) && is_numeric($range[1]))) {
$contentEnd = $range[1];
}
} }
/* Check the range and make sure it's treated according to the specs. /* Check the range and make sure it's treated according to the specs.
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html * http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
*/ */
// End bytes can not be larger than $end. // End bytes can not be larger than $end.
$contentEnd = ($contentEnd > $fileSize) ? $fileSize : $contentEnd; $contentEnd = ($contentEnd > $fileSize) ? $fileSize -1 : $contentEnd;
// Validate the requested range and return an error if it's not correct. // Validate the requested range and return an error if it's not correct.
$wrongContentStart = ($contentStart > $contentEnd || $contentStart > $fileSize - 1 || $contentStart < 0); $wrongContentStart = ($contentStart > $contentEnd || $contentStart > $fileSize - 1 || $contentStart < 0);

Loading…
Cancel
Save