From ce6b73c88a2d538114cd0641be7f28aab1db31c0 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sun, 26 May 2013 14:53:41 +0300 Subject: [PATCH 01/70] Method "AssetController::adjustCssUrl()" has been created as blank --- .../yii/console/controllers/AssetController.php | 17 +++++++ .../console/controllers/AssetControllerTest.php | 54 ++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/framework/yii/console/controllers/AssetController.php b/framework/yii/console/controllers/AssetController.php index ca7896c..852b892 100644 --- a/framework/yii/console/controllers/AssetController.php +++ b/framework/yii/console/controllers/AssetController.php @@ -528,6 +528,23 @@ EOD } /** + * Adjusts CSS content allowing URL references pointing to the original resources. + * @param string $cssContent source CSS content. + * @param string $inputFilePath input CSS file name. + * @param string $outputFilePath output CSS file name. + * @return string adjusted CSS content. + */ + protected function adjustCssUrl($cssContent, $inputFilePath, $outputFilePath) + { + $callback = function($matches) use ($inputFilePath, $outputFilePath) { + return $matches[0]; + }; + $cssContent = preg_replace_callback('/[\w\-]:\s*url\("(.*)"\)+/is', $callback, $cssContent); + + return $cssContent; + } + + /** * Creates template of configuration file for [[actionCompress]]. * @param string $configFile output file name. */ diff --git a/tests/unit/framework/console/controllers/AssetControllerTest.php b/tests/unit/framework/console/controllers/AssetControllerTest.php index d792c9e..4983568 100644 --- a/tests/unit/framework/console/controllers/AssetControllerTest.php +++ b/tests/unit/framework/console/controllers/AssetControllerTest.php @@ -169,6 +169,23 @@ class AssetControllerTest extends TestCase } } + /** + * Invokes the asset controller method even if it is protected. + * @param string $methodName name of the method to be invoked. + * @param array $args method arguments. + * @return mixed method invoke result. + */ + protected function invokeAssetControllerMethod($methodName, array $args = array()) + { + $controller = $this->createAssetController(); + $controllerClassReflection = new ReflectionClass(get_class($controller)); + $methodReflection = $controllerClassReflection->getMethod($methodName); + $methodReflection->setAccessible(true); + $result = $methodReflection->invokeArgs($controller, $args); + $methodReflection->setAccessible(false); + return $result; + } + // Tests : public function testActionTemplate() @@ -237,4 +254,41 @@ class AssetControllerTest extends TestCase $this->assertContains($content, $compressedJsFileContent, "Source of '{$name}' is missing in combined file!"); } } + + /** + * Data provider for [[testAdjustCssUrl()]]. + * @return array test data. + */ + public function adjustCssUrlDataProvider() + { + return array( + array( + '.test-class {background-image: url("test.png");}', + '/test/base/path/assets/input', + '/test/base/path/assets/output', + '.test-class {background-image: url("../input/test.png");}', + ), + array( + '.test-class {background-image: url("../img/test.png");}', + '/test/base/path/assets/input', + '/test/base/path/assets/output', + '.test-class {background-image: url("../input/img/test.png");}', + ), + ); + } + + /** + * @dataProvider adjustCssUrlDataProvider + * + * @param $cssContent + * @param $inputFilePath + * @param $outputFilePath + * @param $expectedCssContent + */ + public function testAdjustCssUrl($cssContent, $inputFilePath, $outputFilePath, $expectedCssContent) + { + $adjustedCssContent = $this->invokeAssetControllerMethod('adjustCssUrl', array($cssContent, $inputFilePath, $outputFilePath)); + + $this->assertEquals($expectedCssContent, $adjustedCssContent, 'Unable to adjust CSS correctly!'); + } } From fbc8e265ad81b6b6542aa7b93c189210c1d2cdb7 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sun, 26 May 2013 18:55:12 +0300 Subject: [PATCH 02/70] Method "AssetController::adjustCssUrl()" has been implemented in basic. --- .../yii/console/controllers/AssetController.php | 27 +++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/framework/yii/console/controllers/AssetController.php b/framework/yii/console/controllers/AssetController.php index 852b892..71709e8 100644 --- a/framework/yii/console/controllers/AssetController.php +++ b/framework/yii/console/controllers/AssetController.php @@ -536,10 +536,31 @@ EOD */ protected function adjustCssUrl($cssContent, $inputFilePath, $outputFilePath) { - $callback = function($matches) use ($inputFilePath, $outputFilePath) { - return $matches[0]; + $sharedPath = ''; + for ($i = 0; $i < strlen($inputFilePath); $i++) { + if (!isset($outputFilePath[$i])) { + break; + } + if ($inputFilePath[$i] == $outputFilePath[$i]) { + $sharedPath .= $inputFilePath[$i]; + } + } + $inputFileRelativePath = trim(str_replace($sharedPath, '', $inputFilePath), '/'); + $outputFileRelativePath = trim(str_replace($sharedPath, '', $outputFilePath), '/'); + $inputFileRelativePathParts = explode('/', $inputFileRelativePath); + $outputFileRelativePathParts = explode('/', $outputFileRelativePath); + + $callback = function($matches) use ($inputFileRelativePathParts, $outputFileRelativePathParts) { + $fullMatch = $matches[0]; + $inputUrl = $matches[1]; + + $urlPrefix = str_repeat('../', count($outputFileRelativePathParts)); + $urlPrefix .= implode('/', $inputFileRelativePathParts); + + $outputUrl = $urlPrefix . '/' . $inputUrl; + return str_replace($inputUrl, $outputUrl, $fullMatch); }; - $cssContent = preg_replace_callback('/[\w\-]:\s*url\("(.*)"\)+/is', $callback, $cssContent); + $cssContent = preg_replace_callback('/[\w\-]:\s*url\("([^"]*)"\)+/is', $callback, $cssContent); return $cssContent; } From 94e621f3723b46eafaf8796bd692433c872d7921 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Sun, 26 May 2013 18:10:46 +0200 Subject: [PATCH 03/70] minor css fix for ErrorHandler conflicting css class and bad positioned icon --- framework/yii/views/errorHandler/callStackItem.php | 2 +- framework/yii/views/errorHandler/main.php | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/framework/yii/views/errorHandler/callStackItem.php b/framework/yii/views/errorHandler/callStackItem.php index fbfa367..a702929 100644 --- a/framework/yii/views/errorHandler/callStackItem.php +++ b/framework/yii/views/errorHandler/callStackItem.php @@ -16,7 +16,7 @@ $context = $this->context;
  • - . + . htmlEncode($file); ?> diff --git a/framework/yii/views/errorHandler/main.php b/framework/yii/views/errorHandler/main.php index 3e3757f..cc1ad20 100644 --- a/framework/yii/views/errorHandler/main.php +++ b/framework/yii/views/errorHandler/main.php @@ -109,8 +109,8 @@ html,body{ filter: progid:DXImageTransform.Microsoft.BasicImage(mirror=1); font-size: 26px; position: absolute; - margin-top: -9px; - margin-left: -22px; + margin-top: -5px; + margin-left: -25px; color: #e51717; text-shadow: 0 1px 0 #cacaca; } @@ -169,7 +169,7 @@ html,body{ color: #000; text-shadow: 0 1px 0 #cacaca; } -.call-stack ul li .number{ +.call-stack ul li .line-number{ width: 45px; display: inline-block; } From 353f198f7384db97dfeebd67be74fcbf13c09874 Mon Sep 17 00:00:00 2001 From: Alexander Kochetov Date: Sun, 26 May 2013 20:15:52 +0400 Subject: [PATCH 04/70] jQuery UI accordion comments --- framework/yii/jui/Accordion.php | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/framework/yii/jui/Accordion.php b/framework/yii/jui/Accordion.php index 1a83f28..b2ce0a7 100644 --- a/framework/yii/jui/Accordion.php +++ b/framework/yii/jui/Accordion.php @@ -56,15 +56,38 @@ use yii\helpers\Html; class Accordion extends Widget { /** - * @var array list of collapsible sections. + * @var array the HTML attributes for the widget container tag. The following special options are recognized: + * + * - tag: string, defaults to "div", the tag name of the container tag of this widget + */ + public $options = array(); + /** + * @var array list of collapsible items. Each item can be an array of the following structure: + * + * ~~~ + * array( + * 'header' => 'Item header', + * 'content' => 'Item content', + * // the HTML attributes of the item header container tag. This will overwrite "headerOptions". + * 'headerOptions' => array(), + * // the HTML attributes of the item container tag. This will overwrite "itemOptions". + * 'options' => array(), + * ) + * ~~~ */ public $items = array(); /** - * @var array list of individual collabsible section default options. + * @var array list of HTML attributes for the item container tags. This will be overwritten + * by the "options" set in individual [[items]]. The following special options are recognized: + * + * - tag: string, defaults to "li", the tag name of the item container tags. */ public $itemOptions = array(); /** - * @var array list of individual collabsible section header default options. + * @var array list of HTML attributes for the item header container tags. This will be overwritten + * by the "headerOptions" set in individual [[items]]. The following special options are recognized: + * + * - tag: string, defaults to "li", the tag name of the item container tags. */ public $headerOptions = array(); @@ -83,7 +106,7 @@ class Accordion extends Widget } /** - * Renders collapsible sections as specified on [[items]]. + * Renders collapsible items as specified on [[items]]. * @return string the rendering result. * @throws InvalidConfigException. */ From 4c3e154d3ad603d015be5d2b45e4e7995616aeb0 Mon Sep 17 00:00:00 2001 From: Alexander Kochetov Date: Sun, 26 May 2013 21:11:42 +0400 Subject: [PATCH 05/70] Comment fixes --- framework/yii/jui/Accordion.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/yii/jui/Accordion.php b/framework/yii/jui/Accordion.php index b2ce0a7..f36c981 100644 --- a/framework/yii/jui/Accordion.php +++ b/framework/yii/jui/Accordion.php @@ -80,14 +80,14 @@ class Accordion extends Widget * @var array list of HTML attributes for the item container tags. This will be overwritten * by the "options" set in individual [[items]]. The following special options are recognized: * - * - tag: string, defaults to "li", the tag name of the item container tags. + * - tag: string, defaults to "div", the tag name of the item container tags. */ public $itemOptions = array(); /** * @var array list of HTML attributes for the item header container tags. This will be overwritten * by the "headerOptions" set in individual [[items]]. The following special options are recognized: * - * - tag: string, defaults to "li", the tag name of the item container tags. + * - tag: string, defaults to "h3", the tag name of the item container tags. */ public $headerOptions = array(); From 265a6f1c39653ed8ceb743296b5023cf496fb68e Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sun, 26 May 2013 20:47:34 +0300 Subject: [PATCH 06/70] Relative path composing at "AssetController::adjustCssUrl()" has been complete. --- .../yii/console/controllers/AssetController.php | 37 ++++++++++++++++------ .../console/controllers/AssetControllerTest.php | 20 +++++++++--- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/framework/yii/console/controllers/AssetController.php b/framework/yii/console/controllers/AssetController.php index 71709e8..249938f 100644 --- a/framework/yii/console/controllers/AssetController.php +++ b/framework/yii/console/controllers/AssetController.php @@ -536,15 +536,20 @@ EOD */ protected function adjustCssUrl($cssContent, $inputFilePath, $outputFilePath) { - $sharedPath = ''; - for ($i = 0; $i < strlen($inputFilePath); $i++) { - if (!isset($outputFilePath[$i])) { + $sharedPathParts = array(); + $inputFilePathParts = explode('/', $inputFilePath); + $inputFilePathPartsCount = count($inputFilePathParts); + $outputFilePathParts = explode('/', $outputFilePath); + $outputFilePathPartsCount = count($outputFilePathParts); + for ($i =0; $i < $inputFilePathPartsCount && $i < $outputFilePathPartsCount; $i++) { + if ($inputFilePathParts[$i] == $outputFilePathParts[$i]) { + $sharedPathParts[] = $inputFilePathParts[$i]; + } else { break; } - if ($inputFilePath[$i] == $outputFilePath[$i]) { - $sharedPath .= $inputFilePath[$i]; - } } + $sharedPath = implode('/', $sharedPathParts); + $inputFileRelativePath = trim(str_replace($sharedPath, '', $inputFilePath), '/'); $outputFileRelativePath = trim(str_replace($sharedPath, '', $outputFilePath), '/'); $inputFileRelativePathParts = explode('/', $inputFileRelativePath); @@ -554,12 +559,26 @@ EOD $fullMatch = $matches[0]; $inputUrl = $matches[1]; - $urlPrefix = str_repeat('../', count($outputFileRelativePathParts)); - $urlPrefix .= implode('/', $inputFileRelativePathParts); + $outputUrlParts = array_fill(0, count($outputFileRelativePathParts), '..'); + $outputUrlParts = array_merge($outputUrlParts, $inputFileRelativePathParts); + + if (strpos($inputUrl, '/') !== false) { + $inputUrlParts = explode('/', $inputUrl); + foreach ($inputUrlParts as $key => $inputUrlPart) { + if ($inputUrlPart == '..') { + array_pop($outputUrlParts); + unset($inputUrlParts[$key]); + } + } + $outputUrlParts[] = implode('/', $inputUrlParts); + } else { + $outputUrlParts[] = $inputUrl; + } + $outputUrl = implode('/', $outputUrlParts); - $outputUrl = $urlPrefix . '/' . $inputUrl; return str_replace($inputUrl, $outputUrl, $fullMatch); }; + $cssContent = preg_replace_callback('/[\w\-]:\s*url\("([^"]*)"\)+/is', $callback, $cssContent); return $cssContent; diff --git a/tests/unit/framework/console/controllers/AssetControllerTest.php b/tests/unit/framework/console/controllers/AssetControllerTest.php index 4983568..4f25fe6 100644 --- a/tests/unit/framework/console/controllers/AssetControllerTest.php +++ b/tests/unit/framework/console/controllers/AssetControllerTest.php @@ -263,16 +263,28 @@ class AssetControllerTest extends TestCase { return array( array( - '.test-class {background-image: url("test.png");}', + '.published-same-dir-class {background-image: url("published_same_dir.png");}', '/test/base/path/assets/input', '/test/base/path/assets/output', - '.test-class {background-image: url("../input/test.png");}', + '.published-same-dir-class {background-image: url("../input/published_same_dir.png");}', ), array( - '.test-class {background-image: url("../img/test.png");}', + '.published-relative-dir-class {background-image: url("../img/published_relative_dir.png");}', '/test/base/path/assets/input', '/test/base/path/assets/output', - '.test-class {background-image: url("../input/img/test.png");}', + '.published-relative-dir-class {background-image: url("../img/published_relative_dir.png");}', + ), + array( + '.static-same-dir-class {background-image: url("static_same_dir.png");}', + '/test/base/path/css', + '/test/base/path/assets/output', + '.static-same-dir-class {background-image: url("../../css/static_same_dir.png");}', + ), + array( + '.static-relative-dir-class {background-image: url("../img/static_relative_dir.png");}', + '/test/base/path/css', + '/test/base/path/assets/output', + '.static-relative-dir-class {background-image: url("../../img/static_relative_dir.png");}', ), ); } From b6ec6085fe84a218ae101609a02b93cbdd76eb19 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sun, 26 May 2013 20:59:03 +0300 Subject: [PATCH 07/70] Method "AssetController::adjustCssUrl()" has been complete. --- framework/yii/console/controllers/AssetController.php | 2 +- .../framework/console/controllers/AssetControllerTest.php | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/framework/yii/console/controllers/AssetController.php b/framework/yii/console/controllers/AssetController.php index 249938f..1bd8e10 100644 --- a/framework/yii/console/controllers/AssetController.php +++ b/framework/yii/console/controllers/AssetController.php @@ -579,7 +579,7 @@ EOD return str_replace($inputUrl, $outputUrl, $fullMatch); }; - $cssContent = preg_replace_callback('/[\w\-]:\s*url\("([^"]*)"\)+/is', $callback, $cssContent); + $cssContent = preg_replace_callback('/url\(["\']?([^"]*)["\']?\)/is', $callback, $cssContent); return $cssContent; } diff --git a/tests/unit/framework/console/controllers/AssetControllerTest.php b/tests/unit/framework/console/controllers/AssetControllerTest.php index 4f25fe6..a76a942 100644 --- a/tests/unit/framework/console/controllers/AssetControllerTest.php +++ b/tests/unit/framework/console/controllers/AssetControllerTest.php @@ -263,22 +263,22 @@ class AssetControllerTest extends TestCase { return array( array( - '.published-same-dir-class {background-image: url("published_same_dir.png");}', + '.published-same-dir-class {background-image: url(published_same_dir.png);}', '/test/base/path/assets/input', '/test/base/path/assets/output', - '.published-same-dir-class {background-image: url("../input/published_same_dir.png");}', + '.published-same-dir-class {background-image: url(../input/published_same_dir.png);}', ), array( - '.published-relative-dir-class {background-image: url("../img/published_relative_dir.png");}', + '.published-relative-dir-class {background-image: url(../img/published_relative_dir.png);}', '/test/base/path/assets/input', '/test/base/path/assets/output', - '.published-relative-dir-class {background-image: url("../img/published_relative_dir.png");}', + '.published-relative-dir-class {background-image: url(../img/published_relative_dir.png);}', ), array( - '.static-same-dir-class {background-image: url("static_same_dir.png");}', + '.static-same-dir-class {background-image: url(\'static_same_dir.png\');}', '/test/base/path/css', '/test/base/path/assets/output', - '.static-same-dir-class {background-image: url("../../css/static_same_dir.png");}', + '.static-same-dir-class {background-image: url(\'../../css/static_same_dir.png\');}', ), array( '.static-relative-dir-class {background-image: url("../img/static_relative_dir.png");}', From 2635b4775efaf514ee1e7ace7071ae6c29f596ec Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sun, 26 May 2013 21:01:18 +0300 Subject: [PATCH 08/70] Method "AssetController::adjustCssUrl()" has been applied to compress process. --- framework/yii/console/controllers/AssetController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/framework/yii/console/controllers/AssetController.php b/framework/yii/console/controllers/AssetController.php index 1bd8e10..98c67a5 100644 --- a/framework/yii/console/controllers/AssetController.php +++ b/framework/yii/console/controllers/AssetController.php @@ -517,11 +517,10 @@ EOD */ public function combineCssFiles($inputFiles, $outputFile) { - // todo: adjust url() references in CSS files $content = ''; foreach ($inputFiles as $file) { $content .= "/*** BEGIN FILE: $file ***/\n" - . file_get_contents($file) + . $this->adjustCssUrl(file_get_contents($file), dirname($file), dirname($outputFile)) . "/*** END FILE: $file ***/\n"; } file_put_contents($outputFile, $content); From f65d1398a55094578f8dadfdfe4ce46c2ccfdb22 Mon Sep 17 00:00:00 2001 From: Klimov Paul Date: Sun, 26 May 2013 21:05:53 +0300 Subject: [PATCH 09/70] Added skipping for absolute URLs at "AssetController::adjustCssUrl()". --- framework/yii/console/controllers/AssetController.php | 4 ++++ .../framework/console/controllers/AssetControllerTest.php | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/framework/yii/console/controllers/AssetController.php b/framework/yii/console/controllers/AssetController.php index 98c67a5..8e3de29 100644 --- a/framework/yii/console/controllers/AssetController.php +++ b/framework/yii/console/controllers/AssetController.php @@ -558,6 +558,10 @@ EOD $fullMatch = $matches[0]; $inputUrl = $matches[1]; + if (preg_match('/https?:\/\//is', $inputUrl)) { + return $fullMatch; + } + $outputUrlParts = array_fill(0, count($outputFileRelativePathParts), '..'); $outputUrlParts = array_merge($outputUrlParts, $inputFileRelativePathParts); diff --git a/tests/unit/framework/console/controllers/AssetControllerTest.php b/tests/unit/framework/console/controllers/AssetControllerTest.php index a76a942..db6d2a7 100644 --- a/tests/unit/framework/console/controllers/AssetControllerTest.php +++ b/tests/unit/framework/console/controllers/AssetControllerTest.php @@ -286,6 +286,18 @@ class AssetControllerTest extends TestCase '/test/base/path/assets/output', '.static-relative-dir-class {background-image: url("../../img/static_relative_dir.png");}', ), + array( + '.absolute-url-class {background-image: url(http://domain.com/img/image.gif);}', + '/test/base/path/assets/input', + '/test/base/path/assets/output', + '.absolute-url-class {background-image: url(http://domain.com/img/image.gif);}', + ), + array( + '.absolute-url-secure-class {background-image: url(https://secure.domain.com/img/image.gif);}', + '/test/base/path/assets/input', + '/test/base/path/assets/output', + '.absolute-url-secure-class {background-image: url(https://secure.domain.com/img/image.gif);}', + ), ); } From 0ab5bbaf40dad32ea26a9481fb5ea53e8adf58d8 Mon Sep 17 00:00:00 2001 From: votintsev Date: Sun, 26 May 2013 23:32:42 +0400 Subject: [PATCH 10/70] Update Cache.php Moved add prefix ($this->keyPrefix) to the key in function buildKey(). Could it be better? --- framework/yii/caching/Cache.php | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/framework/yii/caching/Cache.php b/framework/yii/caching/Cache.php index 0329078..0460b8c 100644 --- a/framework/yii/caching/Cache.php +++ b/framework/yii/caching/Cache.php @@ -98,15 +98,16 @@ abstract class Cache extends Component implements \ArrayAccess * ~~~ * * @param array|string $key the key to be normalized - * @return string the generated cache key + * @return string the generated cache key include $this->keyPrefix */ public function buildKey($key) { if (is_string($key)) { - return ctype_alnum($key) && StringHelper::strlen($key) <= 32 ? $key : md5($key); + $key = ctype_alnum($key) && StringHelper::strlen($key) <= 32 ? $key : md5($key); } else { - return md5(json_encode($key)); + $key = md5(json_encode($key)); } + return $this->keyPrefix . $key; } /** @@ -117,7 +118,7 @@ abstract class Cache extends Component implements \ArrayAccess */ public function get($key) { - $key = $this->keyPrefix . $this->buildKey($key); + $key = $this->buildKey($key); $value = $this->getValue($key); if ($value === false || $this->serializer === false) { return $value; @@ -147,7 +148,7 @@ abstract class Cache extends Component implements \ArrayAccess { $keyMap = array(); foreach ($keys as $key) { - $keyMap[$key] = $this->keyPrefix . $this->buildKey($key); + $keyMap[$key] = $this->buildKey($key); } $values = $this->getValues(array_values($keyMap)); $results = array(); @@ -192,7 +193,7 @@ abstract class Cache extends Component implements \ArrayAccess } elseif ($this->serializer !== false) { $value = call_user_func($this->serializer[0], array($value, $dependency)); } - $key = $this->keyPrefix . $this->buildKey($key); + $key = $this->buildKey($key); return $this->setValue($key, $value, $expire); } @@ -217,7 +218,7 @@ abstract class Cache extends Component implements \ArrayAccess } elseif ($this->serializer !== false) { $value = call_user_func($this->serializer[0], array($value, $dependency)); } - $key = $this->keyPrefix . $this->buildKey($key); + $key = $this->buildKey($key); return $this->addValue($key, $value, $expire); } @@ -228,7 +229,7 @@ abstract class Cache extends Component implements \ArrayAccess */ public function delete($key) { - $key = $this->keyPrefix . $this->buildKey($key); + $key = $this->buildKey($key); return $this->deleteValue($key); } From f5d0bcbcfc7155281b469882144ed304eee83941 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 26 May 2013 17:09:09 -0400 Subject: [PATCH 11/70] Refactored cache key generation. --- framework/yii/caching/Cache.php | 22 +++++++++++++--------- framework/yii/db/Command.php | 4 ++-- framework/yii/db/Schema.php | 13 ++++++------- framework/yii/web/CacheSession.php | 4 ++-- framework/yii/web/UrlManager.php | 2 +- framework/yii/widgets/FragmentCache.php | 4 ++-- tests/unit/framework/caching/CacheTestCase.php | 2 +- 7 files changed, 27 insertions(+), 24 deletions(-) diff --git a/framework/yii/caching/Cache.php b/framework/yii/caching/Cache.php index 0460b8c..fc1027c 100644 --- a/framework/yii/caching/Cache.php +++ b/framework/yii/caching/Cache.php @@ -7,7 +7,9 @@ namespace yii\caching; +use Yii; use yii\base\Component; +use yii\base\InvalidConfigException; use yii\helpers\StringHelper; /** @@ -52,10 +54,12 @@ use yii\helpers\StringHelper; abstract class Cache extends Component implements \ArrayAccess { /** - * @var string a string prefixed to every cache key so that it is unique. Defaults to null, meaning using - * the value of [[Application::id]] as the key prefix. You may set this property to be an empty string + * @var string a string prefixed to every cache key so that it is unique. If not set, + * it will use a prefix generated from [[Application::id]]. You may set this property to be an empty string * if you don't want to use key prefix. It is recommended that you explicitly set this property to some * static value if the cached data needs to be shared among multiple applications. + * + * To ensure interoperability, only use alphanumeric characters should be used. */ public $keyPrefix; /** @@ -78,18 +82,18 @@ abstract class Cache extends Component implements \ArrayAccess { parent::init(); if ($this->keyPrefix === null) { - $this->keyPrefix = \Yii::$app->id; + $this->keyPrefix = substr(md5(Yii::$app->id), 0, 5); + } elseif (!ctype_alnum($this->keyPrefix)) { + throw new InvalidConfigException(get_class($this) . '::keyPrefix should only contain alphanumeric characters.'); } } /** * Builds a normalized cache key from a given key. * - * The generated key contains letters and digits only, and its length is no more than 32. - * * If the given key is a string containing alphanumeric characters only and no more than 32 characters, - * then the key will be returned back without change. Otherwise, a normalized key - * is generated by serializing the given key and applying MD5 hashing. + * then the key will be returned back prefixed with [[keyPrefix]]. Otherwise, a normalized key + * is generated by serializing the given key, applying MD5 hashing, and prefixing with [[keyPrefix]]. * * The following example builds a cache key using three parameters: * @@ -97,8 +101,8 @@ abstract class Cache extends Component implements \ArrayAccess * $key = $cache->buildKey(array($className, $method, $id)); * ~~~ * - * @param array|string $key the key to be normalized - * @return string the generated cache key include $this->keyPrefix + * @param mixed $key the key to be normalized + * @return string the generated cache key */ public function buildKey($key) { diff --git a/framework/yii/db/Command.php b/framework/yii/db/Command.php index 657ef8d..17accf4 100644 --- a/framework/yii/db/Command.php +++ b/framework/yii/db/Command.php @@ -391,12 +391,12 @@ class Command extends \yii\base\Component } if (isset($cache) && $cache instanceof Cache) { - $cacheKey = $cache->buildKey(array( + $cacheKey = array( __CLASS__, $db->dsn, $db->username, $rawSql, - )); + ); if (($result = $cache->get($cacheKey)) !== false) { Yii::trace('Query result served from cache', __METHOD__); return $result; diff --git a/framework/yii/db/Schema.php b/framework/yii/db/Schema.php index 9538e4c..c961244 100644 --- a/framework/yii/db/Schema.php +++ b/framework/yii/db/Schema.php @@ -89,7 +89,7 @@ abstract class Schema extends \yii\base\Object /** @var $cache Cache */ $cache = is_string($db->schemaCache) ? Yii::$app->getComponent($db->schemaCache) : $db->schemaCache; if ($cache instanceof Cache) { - $key = $this->getCacheKey($cache, $name); + $key = $this->getCacheKey($name); if ($refresh || ($table = $cache->get($key)) === false) { $table = $this->loadTableSchema($realName); if ($table !== null) { @@ -104,18 +104,17 @@ abstract class Schema extends \yii\base\Object /** * Returns the cache key for the specified table name. - * @param Cache $cache the cache component * @param string $name the table name - * @return string the cache key + * @return mixed the cache key */ - public function getCacheKey($cache, $name) + public function getCacheKey($name) { - return $cache->buildKey(array( + return array( __CLASS__, $this->db->dsn, $this->db->username, $name, - )); + ); } /** @@ -178,7 +177,7 @@ abstract class Schema extends \yii\base\Object $cache = is_string($this->db->schemaCache) ? Yii::$app->getComponent($this->db->schemaCache) : $this->db->schemaCache; if ($this->db->enableSchemaCache && $cache instanceof Cache) { foreach ($this->_tables as $name => $table) { - $cache->delete($this->getCacheKey($cache, $name)); + $cache->delete($this->getCacheKey($name)); } } $this->_tableNames = array(); diff --git a/framework/yii/web/CacheSession.php b/framework/yii/web/CacheSession.php index c125f01..79acd74 100644 --- a/framework/yii/web/CacheSession.php +++ b/framework/yii/web/CacheSession.php @@ -97,10 +97,10 @@ class CacheSession extends Session /** * Generates a unique key used for storing session data in cache. * @param string $id session variable name - * @return string a safe cache key associated with the session variable name + * @return mixed a safe cache key associated with the session variable name */ protected function calculateKey($id) { - return $this->cache->buildKey(array(__CLASS__, $id)); + return array(__CLASS__, $id); } } diff --git a/framework/yii/web/UrlManager.php b/framework/yii/web/UrlManager.php index 5a3c391..47f5c5d 100644 --- a/framework/yii/web/UrlManager.php +++ b/framework/yii/web/UrlManager.php @@ -103,7 +103,7 @@ class UrlManager extends Component $this->cache = Yii::$app->getComponent($this->cache); } if ($this->cache instanceof Cache) { - $key = $this->cache->buildKey(__CLASS__); + $key = __CLASS__; $hash = md5(json_encode($this->rules)); if (($data = $this->cache->get($key)) !== false && isset($data[1]) && $data[1] === $hash) { $this->rules = $data[0]; diff --git a/framework/yii/widgets/FragmentCache.php b/framework/yii/widgets/FragmentCache.php index aa24acd..8445955 100644 --- a/framework/yii/widgets/FragmentCache.php +++ b/framework/yii/widgets/FragmentCache.php @@ -159,7 +159,7 @@ class FragmentCache extends Widget /** * Generates a unique key used for storing the content in cache. * The key generated depends on both [[id]] and [[variations]]. - * @return string a valid cache key + * @return mixed a valid cache key */ protected function calculateKey() { @@ -169,6 +169,6 @@ class FragmentCache extends Widget $factors[] = $factor; } } - return $this->cache->buildKey($factors); + return $factors; } } diff --git a/tests/unit/framework/caching/CacheTestCase.php b/tests/unit/framework/caching/CacheTestCase.php index 5808fa0..81ac5cc 100644 --- a/tests/unit/framework/caching/CacheTestCase.php +++ b/tests/unit/framework/caching/CacheTestCase.php @@ -65,7 +65,7 @@ abstract class CacheTestCase extends TestCase { $cache = $this->getCacheInstance(); $this->assertNotNull(\Yii::$app->id); - $this->assertEquals(\Yii::$app->id, $cache->keyPrefix); + $this->assertNotNull($cache->keyPrefix); } public function testSet() From eb2d4fbd92160561bebbb11eac8cc4b731ccb5b6 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 26 May 2013 17:53:51 -0400 Subject: [PATCH 12/70] Added benchmark application. --- apps/benchmark/LICENSE.md | 32 +++++++++++++ apps/benchmark/README.md | 56 ++++++++++++++++++++++ apps/benchmark/composer.json | 20 ++++++++ apps/benchmark/index.php | 18 +++++++ .../protected/controllers/SiteController.php | 13 +++++ apps/benchmark/protected/vendor/.gitignore | 2 + 6 files changed, 141 insertions(+) create mode 100644 apps/benchmark/LICENSE.md create mode 100644 apps/benchmark/README.md create mode 100644 apps/benchmark/composer.json create mode 100644 apps/benchmark/index.php create mode 100644 apps/benchmark/protected/controllers/SiteController.php create mode 100644 apps/benchmark/protected/vendor/.gitignore diff --git a/apps/benchmark/LICENSE.md b/apps/benchmark/LICENSE.md new file mode 100644 index 0000000..6edcc4f --- /dev/null +++ b/apps/benchmark/LICENSE.md @@ -0,0 +1,32 @@ +The Yii framework is free software. It is released under the terms of +the following BSD License. + +Copyright © 2008-2013 by Yii Software LLC (http://www.yiisoft.com) +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + * Neither the name of Yii Software LLC nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/apps/benchmark/README.md b/apps/benchmark/README.md new file mode 100644 index 0000000..2aeb0ae --- /dev/null +++ b/apps/benchmark/README.md @@ -0,0 +1,56 @@ +Yii 2 Benchmark Application +=========================== + +**NOTE** Yii 2 and the relevant applications and extensions are still under heavy +development. We may make significant changes without prior notices. Please do not +use them for production. Please consider using [Yii v1.1](https://github.com/yiisoft/yii) +if you have a project to be deployed for production soon. + + +Yii 2 Benchmark Application is an application built to demonstrate the minimal overhead +introduced by the Yii framework. The application contains a single page which only renders +the "hello world" string. + +The application attempts to simulate the scenario in which you can achieve the best performance +when using Yii. It does so by assuming that both of the main application configuration and the page +content are cached in memory, and the application enables pretty URLs. + + +DIRECTORY STRUCTURE +------------------- + + protected/ contains application source code + controllers/ contains Web controller classes + index.php the entry script + + +REQUIREMENTS +------------ + +The minimum requirement by Yii is that your Web server supports PHP 5.3.?. + + +INSTALLATION +------------ + +If you do not have [Composer](http://getcomposer.org/), you may download it from +[http://getcomposer.org/](http://getcomposer.org/) or run the following command on Linux/Unix/MacOS: + +~~~ +curl -s http://getcomposer.org/installer | php +~~~ + +You can then install the Bootstrap Application using the following command: + +~~~ +php composer.phar create-project --stability=dev yiisoft/yii2-app-benchmark yii-benchmark +~~~ + +Now you should be able to access the benchmark page using the URL + +~~~ +http://localhost/yii-benchmark/index.php/site/hello +~~~ + +In the above, we assume `yii-benchmark` is directly under the document root of your Web server. + diff --git a/apps/benchmark/composer.json b/apps/benchmark/composer.json new file mode 100644 index 0000000..4805f4f --- /dev/null +++ b/apps/benchmark/composer.json @@ -0,0 +1,20 @@ +{ + "name": "yiisoft/yii2-app-benchmark", + "description": "Yii 2 Benchmark Application", + "keywords": ["yii", "framework", "benchmark", "application"], + "homepage": "http://www.yiiframework.com/", + "type": "project", + "license": "BSD-3-Clause", + "support": { + "issues": "https://github.com/yiisoft/yii2/issues?state=open", + "forum": "http://www.yiiframework.com/forum/", + "wiki": "http://www.yiiframework.com/wiki/", + "irc": "irc://irc.freenode.net/yii", + "source": "https://github.com/yiisoft/yii2" + }, + "minimum-stability": "dev", + "require": { + "php": ">=5.3.0", + "yiisoft/yii2": "dev-master" + } +} diff --git a/apps/benchmark/index.php b/apps/benchmark/index.php new file mode 100644 index 0000000..ddf6081 --- /dev/null +++ b/apps/benchmark/index.php @@ -0,0 +1,18 @@ + 'benchmark', + 'basePath' => __DIR__ . '/protected', + 'components' => array( + 'urlManager' => array( + 'enablePrettyUrl' => true, + ), + ) +); + +$application = new yii\web\Application($config); +$application->run(); diff --git a/apps/benchmark/protected/controllers/SiteController.php b/apps/benchmark/protected/controllers/SiteController.php new file mode 100644 index 0000000..16089d0 --- /dev/null +++ b/apps/benchmark/protected/controllers/SiteController.php @@ -0,0 +1,13 @@ + Date: Sun, 26 May 2013 23:50:23 +0200 Subject: [PATCH 13/70] Fix error page alignment problems in various browsers issue #418 --- framework/yii/views/errorHandler/callStackItem.php | 9 +++++++-- framework/yii/views/errorHandler/main.php | 12 +++++++----- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/framework/yii/views/errorHandler/callStackItem.php b/framework/yii/views/errorHandler/callStackItem.php index a702929..696c0b7 100644 --- a/framework/yii/views/errorHandler/callStackItem.php +++ b/framework/yii/views/errorHandler/callStackItem.php @@ -16,7 +16,7 @@ $context = $this->context;
  • - . + . htmlEncode($file); ?> @@ -36,7 +36,12 @@ $context = $this->context;
    '; ?> -
    htmlEncode($lines[$i]); ?>
    +
    htmlEncode($lines[$i]);
    +					}
    +				?>
    diff --git a/framework/yii/views/errorHandler/main.php b/framework/yii/views/errorHandler/main.php index cc1ad20..db03c9b 100644 --- a/framework/yii/views/errorHandler/main.php +++ b/framework/yii/views/errorHandler/main.php @@ -169,7 +169,7 @@ html,body{ color: #000; text-shadow: 0 1px 0 #cacaca; } -.call-stack ul li .line-number{ +.call-stack ul li .item-number{ width: 45px; display: inline-block; } @@ -204,9 +204,8 @@ html,body{ background-color: #ffebeb; position: absolute; width: 100%; - height: 20px; + height: 18px; z-index: 100; - margin-top: 15px; } .call-stack ul li .hover-line{ background: none; @@ -216,8 +215,8 @@ html,body{ } .call-stack ul li .code{ min-width: 860px; /* 960px - 50px * 2 */ - margin: 0 auto; - padding: 15px 50px; + margin: 15px auto; + padding: 0 50px; position: relative; } .call-stack ul li .code .lines{ @@ -226,6 +225,8 @@ html,body{ left: 50px; line-height: 18px; font-size: 14px; + vertical-align: middle; + text-align: right; font-family: Consolas, Courier New, monospace; color: #aaa; } @@ -235,6 +236,7 @@ html,body{ left: 50px; line-height: 18px; font-size: 14px; + vertical-align: middle; font-family: Consolas, Courier New, monospace; display: inline; } From d48971a58178b7a161c49b8d4300ed6ac75fbdbf Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 26 May 2013 18:01:40 -0400 Subject: [PATCH 14/70] Added .htaccess file. --- apps/benchmark/protected/.htaccess | 1 + 1 file changed, 1 insertion(+) create mode 100644 apps/benchmark/protected/.htaccess diff --git a/apps/benchmark/protected/.htaccess b/apps/benchmark/protected/.htaccess new file mode 100644 index 0000000..e019832 --- /dev/null +++ b/apps/benchmark/protected/.htaccess @@ -0,0 +1 @@ +deny from all From 833336340ebf412766f3b9eba9e51d3e1a783734 Mon Sep 17 00:00:00 2001 From: Luciano Baraglia Date: Sun, 26 May 2013 19:22:40 -0300 Subject: [PATCH 15/70] Removed dot from framework path --- apps/basic/requirements.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/basic/requirements.php b/apps/basic/requirements.php index c075760..c9e6493 100644 --- a/apps/basic/requirements.php +++ b/apps/basic/requirements.php @@ -11,7 +11,7 @@ */ // you may need to adjust this path to the correct Yii framework path -$frameworkPath = dirname(__FILE__) . '/vendor/yiisoft/yii2/yii.'; +$frameworkPath = dirname(__FILE__) . '/vendor/yiisoft/yii2/yii'; if (!is_dir($frameworkPath)) { echo '

    Error

    '; From a6c251d2ab6d222b5318cc7fa21eb3791a296c62 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 27 May 2013 00:21:18 +0200 Subject: [PATCH 16/70] Adjusted vendor dirs of example applications - update composer json to chosse right path - set vendorPath in application config if not default --- apps/advanced/backstage/config/main.php | 1 + apps/advanced/console/config/main.php | 1 + apps/advanced/frontend/config/main.php | 1 + apps/benchmark/.gitignore | 1 + apps/benchmark/composer.json | 3 +++ framework/yii/base/Application.php | 3 ++- 6 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 apps/benchmark/.gitignore diff --git a/apps/advanced/backstage/config/main.php b/apps/advanced/backstage/config/main.php index d3288bd..4898bfd 100644 --- a/apps/advanced/backstage/config/main.php +++ b/apps/advanced/backstage/config/main.php @@ -11,6 +11,7 @@ $params = array_merge( return array( 'id' => 'change-me', 'basePath' => dirname(__DIR__), + 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', 'preload' => array('log'), 'controllerNamespace' => 'backstage\controllers', 'modules' => array( diff --git a/apps/advanced/console/config/main.php b/apps/advanced/console/config/main.php index 83cb2e3..cceb311 100644 --- a/apps/advanced/console/config/main.php +++ b/apps/advanced/console/config/main.php @@ -11,6 +11,7 @@ $params = array_merge( return array( 'id' => 'change-me', 'basePath' => dirname(__DIR__), + 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', 'preload' => array('log'), 'controllerNamespace' => 'console\controllers', 'modules' => array( diff --git a/apps/advanced/frontend/config/main.php b/apps/advanced/frontend/config/main.php index 607c9a9..02a66c9 100644 --- a/apps/advanced/frontend/config/main.php +++ b/apps/advanced/frontend/config/main.php @@ -11,6 +11,7 @@ $params = array_merge( return array( 'id' => 'change-me', 'basePath' => dirname(__DIR__), + 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', 'preload' => array('log'), 'controllerNamespace' => 'frontend\controllers', 'modules' => array( diff --git a/apps/benchmark/.gitignore b/apps/benchmark/.gitignore new file mode 100644 index 0000000..2cf7a3f --- /dev/null +++ b/apps/benchmark/.gitignore @@ -0,0 +1 @@ +composer.lock \ No newline at end of file diff --git a/apps/benchmark/composer.json b/apps/benchmark/composer.json index 4805f4f..e92f1d6 100644 --- a/apps/benchmark/composer.json +++ b/apps/benchmark/composer.json @@ -12,6 +12,9 @@ "irc": "irc://irc.freenode.net/yii", "source": "https://github.com/yiisoft/yii2" }, + "config": { + "vendor-dir": "protected/vendor" + }, "minimum-stability": "dev", "require": { "php": ">=5.3.0", diff --git a/framework/yii/base/Application.php b/framework/yii/base/Application.php index fb9a6c1..eb0a0d3 100644 --- a/framework/yii/base/Application.php +++ b/framework/yii/base/Application.php @@ -207,7 +207,8 @@ class Application extends Module /** * Returns the directory that stores vendor files. - * @return string the directory that stores vendor files. Defaults to 'protected/vendor'. + * @return string the directory that stores vendor files. + * Defaults to 'vendor' directory under applications [[basePath]]. */ public function getVendorPath() { From dff144797dc4589130c9ce15ecc71d5345a9b493 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 27 May 2013 00:26:07 +0200 Subject: [PATCH 17/70] composer.json indentation fix --- apps/benchmark/composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/benchmark/composer.json b/apps/benchmark/composer.json index e92f1d6..2b077e0 100644 --- a/apps/benchmark/composer.json +++ b/apps/benchmark/composer.json @@ -12,9 +12,9 @@ "irc": "irc://irc.freenode.net/yii", "source": "https://github.com/yiisoft/yii2" }, - "config": { - "vendor-dir": "protected/vendor" - }, + "config": { + "vendor-dir": "protected/vendor" + }, "minimum-stability": "dev", "require": { "php": ">=5.3.0", From ff16c4ef050df6cfa0d6833b7b25027270cdd5f6 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Mon, 27 May 2013 00:48:49 +0200 Subject: [PATCH 18/70] Do not ignore composer.lock https://github.com/yiisoft/yii2/commit/a6c251d2ab6d222b5318cc7fa21eb3791a296c62#commitcomment-3291785 --- apps/advanced/.gitignore | 3 +- apps/advanced/composer.lock | 164 +++++++++++++++++++++++++++++++++++++++++++ apps/basic/.gitignore | 1 - apps/basic/composer.lock | 164 +++++++++++++++++++++++++++++++++++++++++++ apps/benchmark/.gitignore | 1 - apps/benchmark/composer.lock | 119 +++++++++++++++++++++++++++++++ 6 files changed, 448 insertions(+), 4 deletions(-) create mode 100644 apps/advanced/composer.lock delete mode 100644 apps/basic/.gitignore create mode 100644 apps/basic/composer.lock delete mode 100644 apps/benchmark/.gitignore create mode 100644 apps/benchmark/composer.lock diff --git a/apps/advanced/.gitignore b/apps/advanced/.gitignore index fd6a8d5..b1cf719 100644 --- a/apps/advanced/.gitignore +++ b/apps/advanced/.gitignore @@ -1,2 +1 @@ -/yii -composer.lock +/yii \ No newline at end of file diff --git a/apps/advanced/composer.lock b/apps/advanced/composer.lock new file mode 100644 index 0000000..761ae2f --- /dev/null +++ b/apps/advanced/composer.lock @@ -0,0 +1,164 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "0b96a35ac23eae4e84ffd588653e88d2", + "packages": [ + { + "name": "yiisoft/yii2", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-framework.git", + "reference": "15a8d0559260e39954a8eb6de0d28bfb7de95e7b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/15a8d0559260e39954a8eb6de0d28bfb7de95e7b", + "reference": "15a8d0559260e39954a8eb6de0d28bfb7de95e7b", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "lib-pcre": "*", + "php": ">=5.3.7" + }, + "suggest": { + "ezyang/htmlpurifier": "Required by HtmlPurifier.", + "michelf/php-markdown": "Required by Markdown.", + "smarty/smarty": "Required by SmartyViewRenderer.", + "twig/twig": "Required by TwigViewRenderer." + }, + "type": "library", + "autoload": { + "psr-0": { + "yii\\": "/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com", + "homepage": "http://www.yiiframework.com/", + "role": "Founder and project lead" + }, + { + "name": "Alexander Makarov", + "email": "sam@rmcreative.ru", + "homepage": "http://rmcreative.ru/", + "role": "Core framework development" + }, + { + "name": "Maurizio Domba", + "homepage": "http://mdomba.info/", + "role": "Core framework development" + }, + { + "name": "Carsten Brandt", + "email": "mail@cebe.cc", + "homepage": "http://cebe.cc/", + "role": "Core framework development" + }, + { + "name": "Wei Zhuo", + "email": "weizhuo@gmail.com", + "role": "Project site maintenance and development" + }, + { + "name": "Sebastián Thierer", + "email": "sebas@artfos.com", + "role": "Component development" + }, + { + "name": "Jeffrey Winesett", + "email": "jefftulsa@gmail.com", + "role": "Documentation and marketing" + }, + { + "name": "Timur Ruziev", + "email": "resurtm@gmail.com", + "homepage": "http://resurtm.com/", + "role": "Core framework development" + }, + { + "name": "Paul Klimov", + "email": "klimov.paul@gmail.com", + "role": "Core framework development" + } + ], + "description": "Yii2 Web Programming Framework", + "homepage": "http://www.yiiframework.com/", + "keywords": [ + "framework", + "yii" + ], + "time": "2013-05-25 20:59:05" + }, + { + "name": "yiisoft/yii2-composer", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-composer.git", + "reference": "7ce4060faca940b836ab88de207638940a0a0568" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/7ce4060faca940b836ab88de207638940a0a0568", + "reference": "7ce4060faca940b836ab88de207638940a0a0568", + "shasum": "" + }, + "require": { + "yiisoft/yii2": "*" + }, + "type": "library", + "autoload": { + "psr-0": { + "yii\\composer": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com", + "homepage": "http://www.yiiframework.com/", + "role": "Founder and project lead" + } + ], + "description": "The composer integration for the Yii framework", + "keywords": [ + "composer", + "install", + "update", + "yii" + ], + "time": "2013-05-23 19:12:45" + } + ], + "packages-dev": [ + + ], + "aliases": [ + + ], + "minimum-stability": "dev", + "stability-flags": { + "yiisoft/yii2": 20, + "yiisoft/yii2-composer": 20 + }, + "platform": { + "php": ">=5.3.0" + }, + "platform-dev": [ + + ] +} diff --git a/apps/basic/.gitignore b/apps/basic/.gitignore deleted file mode 100644 index 2cf7a3f..0000000 --- a/apps/basic/.gitignore +++ /dev/null @@ -1 +0,0 @@ -composer.lock \ No newline at end of file diff --git a/apps/basic/composer.lock b/apps/basic/composer.lock new file mode 100644 index 0000000..a66bbea --- /dev/null +++ b/apps/basic/composer.lock @@ -0,0 +1,164 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "0411dbbd774aa1c89256c77c68023940", + "packages": [ + { + "name": "yiisoft/yii2", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-framework.git", + "reference": "15a8d0559260e39954a8eb6de0d28bfb7de95e7b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/15a8d0559260e39954a8eb6de0d28bfb7de95e7b", + "reference": "15a8d0559260e39954a8eb6de0d28bfb7de95e7b", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "lib-pcre": "*", + "php": ">=5.3.7" + }, + "suggest": { + "ezyang/htmlpurifier": "Required by HtmlPurifier.", + "michelf/php-markdown": "Required by Markdown.", + "smarty/smarty": "Required by SmartyViewRenderer.", + "twig/twig": "Required by TwigViewRenderer." + }, + "type": "library", + "autoload": { + "psr-0": { + "yii\\": "/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com", + "homepage": "http://www.yiiframework.com/", + "role": "Founder and project lead" + }, + { + "name": "Alexander Makarov", + "email": "sam@rmcreative.ru", + "homepage": "http://rmcreative.ru/", + "role": "Core framework development" + }, + { + "name": "Maurizio Domba", + "homepage": "http://mdomba.info/", + "role": "Core framework development" + }, + { + "name": "Carsten Brandt", + "email": "mail@cebe.cc", + "homepage": "http://cebe.cc/", + "role": "Core framework development" + }, + { + "name": "Wei Zhuo", + "email": "weizhuo@gmail.com", + "role": "Project site maintenance and development" + }, + { + "name": "Sebastián Thierer", + "email": "sebas@artfos.com", + "role": "Component development" + }, + { + "name": "Jeffrey Winesett", + "email": "jefftulsa@gmail.com", + "role": "Documentation and marketing" + }, + { + "name": "Timur Ruziev", + "email": "resurtm@gmail.com", + "homepage": "http://resurtm.com/", + "role": "Core framework development" + }, + { + "name": "Paul Klimov", + "email": "klimov.paul@gmail.com", + "role": "Core framework development" + } + ], + "description": "Yii2 Web Programming Framework", + "homepage": "http://www.yiiframework.com/", + "keywords": [ + "framework", + "yii" + ], + "time": "2013-05-25 20:59:05" + }, + { + "name": "yiisoft/yii2-composer", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-composer.git", + "reference": "7ce4060faca940b836ab88de207638940a0a0568" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-composer/zipball/7ce4060faca940b836ab88de207638940a0a0568", + "reference": "7ce4060faca940b836ab88de207638940a0a0568", + "shasum": "" + }, + "require": { + "yiisoft/yii2": "*" + }, + "type": "library", + "autoload": { + "psr-0": { + "yii\\composer": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com", + "homepage": "http://www.yiiframework.com/", + "role": "Founder and project lead" + } + ], + "description": "The composer integration for the Yii framework", + "keywords": [ + "composer", + "install", + "update", + "yii" + ], + "time": "2013-05-23 19:12:45" + } + ], + "packages-dev": [ + + ], + "aliases": [ + + ], + "minimum-stability": "dev", + "stability-flags": { + "yiisoft/yii2": 20, + "yiisoft/yii2-composer": 20 + }, + "platform": { + "php": ">=5.3.0" + }, + "platform-dev": [ + + ] +} diff --git a/apps/benchmark/.gitignore b/apps/benchmark/.gitignore deleted file mode 100644 index 2cf7a3f..0000000 --- a/apps/benchmark/.gitignore +++ /dev/null @@ -1 +0,0 @@ -composer.lock \ No newline at end of file diff --git a/apps/benchmark/composer.lock b/apps/benchmark/composer.lock new file mode 100644 index 0000000..c7a0324 --- /dev/null +++ b/apps/benchmark/composer.lock @@ -0,0 +1,119 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" + ], + "hash": "5ce5f1ad2aa7d7e31c3e216b8ce23387", + "packages": [ + { + "name": "yiisoft/yii2", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/yiisoft/yii2-framework.git", + "reference": "f3c3d9d764de25fc46711bce2259274bcceade1c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/f3c3d9d764de25fc46711bce2259274bcceade1c", + "reference": "f3c3d9d764de25fc46711bce2259274bcceade1c", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "lib-pcre": "*", + "php": ">=5.3.7" + }, + "suggest": { + "ezyang/htmlpurifier": "Required by HtmlPurifier.", + "michelf/php-markdown": "Required by Markdown.", + "smarty/smarty": "Required by SmartyViewRenderer.", + "twig/twig": "Required by TwigViewRenderer." + }, + "type": "library", + "autoload": { + "psr-0": { + "yii\\": "/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Qiang Xue", + "email": "qiang.xue@gmail.com", + "homepage": "http://www.yiiframework.com/", + "role": "Founder and project lead" + }, + { + "name": "Alexander Makarov", + "email": "sam@rmcreative.ru", + "homepage": "http://rmcreative.ru/", + "role": "Core framework development" + }, + { + "name": "Maurizio Domba", + "homepage": "http://mdomba.info/", + "role": "Core framework development" + }, + { + "name": "Carsten Brandt", + "email": "mail@cebe.cc", + "homepage": "http://cebe.cc/", + "role": "Core framework development" + }, + { + "name": "Wei Zhuo", + "email": "weizhuo@gmail.com", + "role": "Project site maintenance and development" + }, + { + "name": "Sebastián Thierer", + "email": "sebas@artfos.com", + "role": "Component development" + }, + { + "name": "Jeffrey Winesett", + "email": "jefftulsa@gmail.com", + "role": "Documentation and marketing" + }, + { + "name": "Timur Ruziev", + "email": "resurtm@gmail.com", + "homepage": "http://resurtm.com/", + "role": "Core framework development" + }, + { + "name": "Paul Klimov", + "email": "klimov.paul@gmail.com", + "role": "Core framework development" + } + ], + "description": "Yii2 Web Programming Framework", + "homepage": "http://www.yiiframework.com/", + "keywords": [ + "framework", + "yii" + ], + "time": "2013-05-26 21:57:00" + } + ], + "packages-dev": [ + + ], + "aliases": [ + + ], + "minimum-stability": "dev", + "stability-flags": { + "yiisoft/yii2": 20 + }, + "platform": { + "php": ">=5.3.0" + }, + "platform-dev": [ + + ] +} From 587529bf940f326506885d1613e0f24c6b83a75e Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Sun, 26 May 2013 20:10:09 -0400 Subject: [PATCH 19/70] Always refetch the migration table definition. --- framework/yii/console/controllers/MigrateController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/console/controllers/MigrateController.php b/framework/yii/console/controllers/MigrateController.php index b437450..d3eb257 100644 --- a/framework/yii/console/controllers/MigrateController.php +++ b/framework/yii/console/controllers/MigrateController.php @@ -574,7 +574,7 @@ class MigrateController extends Controller */ protected function getMigrationHistory($limit) { - if ($this->db->schema->getTableSchema($this->migrationTable) === null) { + if ($this->db->schema->getTableSchema($this->migrationTable, true) === null) { $this->createMigrationHistoryTable(); } $query = new Query; From e96a012d79fbfd35908eb3e2d228aa6b19606269 Mon Sep 17 00:00:00 2001 From: Alexander Kochetov Date: Mon, 27 May 2013 18:54:49 +0400 Subject: [PATCH 20/70] RBAC DbManager fixes --- framework/yii/rbac/DbManager.php | 46 ++++++++++++++++++++++++++-------------- 1 file changed, 30 insertions(+), 16 deletions(-) diff --git a/framework/yii/rbac/DbManager.php b/framework/yii/rbac/DbManager.php index 719ffa8..dabff74 100644 --- a/framework/yii/rbac/DbManager.php +++ b/framework/yii/rbac/DbManager.php @@ -160,7 +160,8 @@ class DbManager extends Manager throw new InvalidCallException("Cannot add '$childName' as a child of '$itemName'. A loop has been detected."); } $this->db->createCommand() - ->insert($this->itemChildTable, array('parent' => $itemName, 'child' => $childName)); + ->insert($this->itemChildTable, array('parent' => $itemName, 'child' => $childName)) + ->execute(); return true; } else { throw new Exception("Either '$itemName' or '$childName' does not exist."); @@ -177,7 +178,8 @@ class DbManager extends Manager public function removeItemChild($itemName, $childName) { return $this->db->createCommand() - ->delete($this->itemChildTable, array('parent' => $itemName, 'child' => $childName)) > 0; + ->delete($this->itemChildTable, array('parent' => $itemName, 'child' => $childName)) + ->execute() > 0; } /** @@ -248,7 +250,8 @@ class DbManager extends Manager 'item_name' => $itemName, 'biz_rule' => $bizRule, 'data' => serialize($data), - )); + )) + ->execute(); return new Assignment(array( 'manager' => $this, 'userId' => $userId, @@ -267,7 +270,8 @@ class DbManager extends Manager public function revoke($userId, $itemName) { return $this->db->createCommand() - ->delete($this->assignmentTable, array('user_id' => $userId, 'item_name' => $itemName)) > 0; + ->delete($this->assignmentTable, array('user_id' => $userId, 'item_name' => $itemName)) + ->execute() > 0; } /** @@ -358,7 +362,8 @@ class DbManager extends Manager ), array( 'user_id' => $assignment->userId, 'item_name' => $assignment->itemName, - )); + )) + ->execute(); } /** @@ -424,6 +429,7 @@ class DbManager extends Manager */ public function createItem($name, $type, $description = '', $bizRule = null, $data = null) { + echo $name; $this->db->createCommand() ->insert($this->itemTable, array( 'name' => $name, @@ -431,7 +437,8 @@ class DbManager extends Manager 'description' => $description, 'biz_rule' => $bizRule, 'data' => serialize($data), - )); + )) + ->execute(); return new Item(array( 'manager' => $this, 'name' => $name, @@ -451,12 +458,15 @@ class DbManager extends Manager { if ($this->usingSqlite()) { $this->db->createCommand() - ->delete($this->itemChildTable, array('or', 'parent=:name', 'child=:name'), array(':name' => $name)); + ->delete($this->itemChildTable, array('or', 'parent=:name', 'child=:name'), array(':name' => $name)) + ->execute(); $this->db->createCommand() - ->delete($this->assignmentTable, array('item_name' => $name)); + ->delete($this->assignmentTable, array('item_name' => $name)) + ->execute(); } return $this->db->createCommand() - ->delete($this->itemTable, array('name' => $name)) > 0; + ->delete($this->itemTable, array('name' => $name)) + ->execute() > 0; } /** @@ -497,11 +507,14 @@ class DbManager extends Manager { if ($this->usingSqlite() && $oldName !== null && $item->getName() !== $oldName) { $this->db->createCommand() - ->update($this->itemChildTable, array('parent' => $item->getName()), array('parent' => $oldName)); + ->update($this->itemChildTable, array('parent' => $item->getName()), array('parent' => $oldName)) + ->execute(); $this->db->createCommand() - ->update($this->itemChildTable, array('child' => $item->getName()), array('child' => $oldName)); + ->update($this->itemChildTable, array('child' => $item->getName()), array('child' => $oldName)) + ->execute(); $this->db->createCommand() - ->update($this->assignmentTable, array('item_name' => $item->getName()), array('item_name' => $oldName)); + ->update($this->assignmentTable, array('item_name' => $item->getName()), array('item_name' => $oldName)) + ->execute(); } $this->db->createCommand() @@ -513,7 +526,8 @@ class DbManager extends Manager 'data' => serialize($item->data), ), array( 'name' => $oldName === null ? $item->getName() : $oldName, - )); + )) + ->execute(); } /** @@ -529,8 +543,8 @@ class DbManager extends Manager public function clearAll() { $this->clearAssignments(); - $this->db->createCommand()->delete($this->itemChildTable); - $this->db->createCommand()->delete($this->itemTable); + $this->db->createCommand()->delete($this->itemChildTable)->execute(); + $this->db->createCommand()->delete($this->itemTable)->execute(); } /** @@ -538,7 +552,7 @@ class DbManager extends Manager */ public function clearAssignments() { - $this->db->createCommand()->delete($this->assignmentTable); + $this->db->createCommand()->delete($this->assignmentTable)->execute(); } /** From 5730afea927e30bea7aea6f0d375b5401e1ac0e1 Mon Sep 17 00:00:00 2001 From: Alexander Kochetov Date: Mon, 27 May 2013 18:58:58 +0400 Subject: [PATCH 21/70] Removed echo --- framework/yii/rbac/DbManager.php | 1 - 1 file changed, 1 deletion(-) diff --git a/framework/yii/rbac/DbManager.php b/framework/yii/rbac/DbManager.php index dabff74..5b94646 100644 --- a/framework/yii/rbac/DbManager.php +++ b/framework/yii/rbac/DbManager.php @@ -429,7 +429,6 @@ class DbManager extends Manager */ public function createItem($name, $type, $description = '', $bizRule = null, $data = null) { - echo $name; $this->db->createCommand() ->insert($this->itemTable, array( 'name' => $name, From 8964d6ec0ab6e6373c24cdced57094bbf59a76b1 Mon Sep 17 00:00:00 2001 From: resurtm Date: Mon, 27 May 2013 21:06:08 +0600 Subject: [PATCH 22/70] Fixes (theoretically) #418 and #413. --- framework/yii/views/errorHandler/callStackItem.php | 11 ++- framework/yii/views/errorHandler/main.php | 84 ++++++++++++---------- 2 files changed, 53 insertions(+), 42 deletions(-) diff --git a/framework/yii/views/errorHandler/callStackItem.php b/framework/yii/views/errorHandler/callStackItem.php index 696c0b7..c59473e 100644 --- a/framework/yii/views/errorHandler/callStackItem.php +++ b/framework/yii/views/errorHandler/callStackItem.php @@ -13,7 +13,8 @@ */ $context = $this->context; ?> -
  • +
  • . @@ -30,12 +31,10 @@ $context = $this->context;
    -
    - -
    - +
    +
    - '; ?> +
    
     window.onload = function() {
    -	var i, imax,
    -		codeBlocks = Sizzle('pre'),
    +	var codeBlocks = Sizzle('pre'),
     		callStackItems = Sizzle('.call-stack-item');
     
     	// highlight code blocks
    -	for (i = 0, imax = codeBlocks.length; i < imax; ++i) {
    +	for (var i = 0, imax = codeBlocks.length; i < imax; ++i) {
     		hljs.highlightBlock(codeBlocks[i], '    ');
     	}
     
     	// code block hover line
     	document.onmousemove = function(e) {
    -		var lines, i, imax, j, jmax, k, kmax,
    -			event = e || window.event,
    -			y = event.clientY,
    -			lineFound = false;
    -		for (i = 0, imax = codeBlocks.length; i < imax; ++i) {
    -			lines = codeBlocks[i].getClientRects();
    -			for (j = 0, jmax = lines.length; j < jmax; ++j) {
    -				if (y > lines[j].top && y < lines[j].bottom) {
    +		var event = e || window.event,
    +			clientY = event.clientY,
    +			lineFound = false,
    +			hoverLines = Sizzle('.hover-line');
    +
    +		for (var i = 0, imax = codeBlocks.length; i < imax; ++i) {
    +			var lines = codeBlocks[i].getClientRects();
    +			for (var j = 0, jmax = lines.length; j < jmax; ++j) {
    +				if (clientY > lines[j].top && clientY < lines[j].bottom) {
     					lineFound = true;
     					break;
     				}
    @@ -442,8 +433,8 @@ window.onload = function() {
     				break;
     			}
     		}
    -		var hoverLines = Sizzle('.hover-line');
    -		for (k = 0, kmax = hoverLines.length; k < kmax; ++k) {
    +
    +		for (var k = 0, kmax = hoverLines.length; k < kmax; ++k) {
     			hoverLines[k].className = 'hover-line';
     		}
     		if (lineFound) {
    @@ -452,13 +443,34 @@ window.onload = function() {
     				line[0].className = 'hover-line hover';
     			}
     		}
    -	}
    +	};
    +
    +	var refreshCallStackItemCode = function(callStackItem) {
    +		var top = callStackItem.offsetTop - window.scrollY,
    +			lines = Sizzle('pre', callStackItem)[0].getClientRects(),
    +			lineNumbers = Sizzle('.lines-item', callStackItem),
    +			errorLine = Sizzle('.error-line', callStackItem)[0],
    +			hoverLines = Sizzle('.hover-line', callStackItem);
    +		for (var i = 0, imax = lines.length; i < imax; ++i) {
    +			lineNumbers[i].style.top = parseInt(lines[i].top - top) + 'px';
    +			hoverLines[i].style.top = parseInt(lines[i].top - top) + 'px';
    +			hoverLines[i].style.height = parseInt(lines[i].bottom - lines[i].top) + 'px';
    +			if (parseInt(callStackItem.getAttribute('data-line')) == i) {
    +				errorLine.style.top = parseInt(lines[i].top - top) + 'px';
    +				errorLine.style.height = parseInt(lines[i].bottom - lines[i].top) + 'px';
    +			}
    +		}
    +	};
    +
    +	for (var i = 0, imax = callStackItems.length; i < imax; ++i) {
    +		refreshCallStackItemCode(callStackItems[i]);
     
    -	// toggle code block visibility
    -	for (i = 0, imax = callStackItems.length; i < imax; i++) {
    +		// toggle code block visibility
     		Sizzle('.element-wrap', callStackItems[i])[0].addEventListener('click', function() {
    -			var code = Sizzle('.code-wrap', this.parentNode)[0];
    +			var callStackItem = this.parentNode,
    +				code = Sizzle('.code-wrap', callStackItem)[0];
     			code.style.display = window.getComputedStyle(code).display == 'block' ? 'none' : 'block';
    +			refreshCallStackItemCode(callStackItem);
     		});
     	}
     };
    
    From 9002196c494c2bf8a041e0d767e320d965274a3c Mon Sep 17 00:00:00 2001
    From: resurtm 
    Date: Mon, 27 May 2013 21:34:14 +0600
    Subject: [PATCH 23/70] MSIE fix.
    
    ---
     framework/yii/views/errorHandler/main.php | 5 ++++-
     1 file changed, 4 insertions(+), 1 deletion(-)
    
    diff --git a/framework/yii/views/errorHandler/main.php b/framework/yii/views/errorHandler/main.php
    index 94e6332..91b7bac 100644
    --- a/framework/yii/views/errorHandler/main.php
    +++ b/framework/yii/views/errorHandler/main.php
    @@ -446,12 +446,15 @@ window.onload = function() {
     	};
     
     	var refreshCallStackItemCode = function(callStackItem) {
    -		var top = callStackItem.offsetTop - window.scrollY,
    +		var top = callStackItem.offsetTop - window.pageYOffset,
     			lines = Sizzle('pre', callStackItem)[0].getClientRects(),
     			lineNumbers = Sizzle('.lines-item', callStackItem),
     			errorLine = Sizzle('.error-line', callStackItem)[0],
     			hoverLines = Sizzle('.hover-line', callStackItem);
     		for (var i = 0, imax = lines.length; i < imax; ++i) {
    +			if (!lineNumbers[i]) {
    +				continue;
    +			}
     			lineNumbers[i].style.top = parseInt(lines[i].top - top) + 'px';
     			hoverLines[i].style.top = parseInt(lines[i].top - top) + 'px';
     			hoverLines[i].style.height = parseInt(lines[i].bottom - lines[i].top) + 'px';
    
    From b058c017be829659bddc2514869569e613c48ba3 Mon Sep 17 00:00:00 2001
    From: resurtm 
    Date: Mon, 27 May 2013 21:42:39 +0600
    Subject: [PATCH 24/70] Lift off!11 #418
    
    ---
     framework/yii/views/errorHandler/main.php | 3 +++
     1 file changed, 3 insertions(+)
    
    diff --git a/framework/yii/views/errorHandler/main.php b/framework/yii/views/errorHandler/main.php
    index 91b7bac..bf4e506 100644
    --- a/framework/yii/views/errorHandler/main.php
    +++ b/framework/yii/views/errorHandler/main.php
    @@ -446,6 +446,9 @@ window.onload = function() {
     	};
     
     	var refreshCallStackItemCode = function(callStackItem) {
    +		if (!Sizzle('pre', callStackItem)[0]) {
    +			return;
    +		}
     		var top = callStackItem.offsetTop - window.pageYOffset,
     			lines = Sizzle('pre', callStackItem)[0].getClientRects(),
     			lineNumbers = Sizzle('.lines-item', callStackItem),
    
    From c520dccab130ef7445e6a72ac4d6b406b75f8296 Mon Sep 17 00:00:00 2001
    From: resurtm 
    Date: Mon, 27 May 2013 21:48:14 +0600
    Subject: [PATCH 25/70] Error line number fix.
    
    ---
     framework/yii/views/errorHandler/callStackItem.php | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/framework/yii/views/errorHandler/callStackItem.php b/framework/yii/views/errorHandler/callStackItem.php
    index c59473e..75fd9e7 100644
    --- a/framework/yii/views/errorHandler/callStackItem.php
    +++ b/framework/yii/views/errorHandler/callStackItem.php
    @@ -14,7 +14,7 @@
     $context = $this->context;
     ?>
     
  • + data-line="">
    . From adbbc5201510de7750bdb44aaa573a88b4979492 Mon Sep 17 00:00:00 2001 From: resurtm Date: Mon, 27 May 2013 21:52:59 +0600 Subject: [PATCH 26/70] Error line number fix. --- framework/yii/views/errorHandler/callStackItem.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/views/errorHandler/callStackItem.php b/framework/yii/views/errorHandler/callStackItem.php index 75fd9e7..3d6be96 100644 --- a/framework/yii/views/errorHandler/callStackItem.php +++ b/framework/yii/views/errorHandler/callStackItem.php @@ -34,7 +34,7 @@ $context = $this->context;
    - +
    
    Date: Mon, 27 May 2013 21:55:52 +0600
    Subject: [PATCH 27/70] Error line number fix.
    
    ---
     framework/yii/views/errorHandler/callStackItem.php | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/framework/yii/views/errorHandler/callStackItem.php b/framework/yii/views/errorHandler/callStackItem.php
    index 3d6be96..7514119 100644
    --- a/framework/yii/views/errorHandler/callStackItem.php
    +++ b/framework/yii/views/errorHandler/callStackItem.php
    @@ -26,7 +26,7 @@ $context = $this->context;
     				
     			
     			
    -			
    +			
     		
    @@ -34,7 +34,7 @@ $context = $this->context;
    - +
    
    Date: Mon, 27 May 2013 22:07:58 +0600
    Subject: [PATCH 28/70] Hover and error line increased by 1 px.
    
    ---
     framework/yii/views/errorHandler/main.php | 4 ++--
     1 file changed, 2 insertions(+), 2 deletions(-)
    
    diff --git a/framework/yii/views/errorHandler/main.php b/framework/yii/views/errorHandler/main.php
    index bf4e506..3bf5bca 100644
    --- a/framework/yii/views/errorHandler/main.php
    +++ b/framework/yii/views/errorHandler/main.php
    @@ -460,10 +460,10 @@ window.onload = function() {
     			}
     			lineNumbers[i].style.top = parseInt(lines[i].top - top) + 'px';
     			hoverLines[i].style.top = parseInt(lines[i].top - top) + 'px';
    -			hoverLines[i].style.height = parseInt(lines[i].bottom - lines[i].top) + 'px';
    +			hoverLines[i].style.height = parseInt(lines[i].bottom - lines[i].top + 1) + 'px';
     			if (parseInt(callStackItem.getAttribute('data-line')) == i) {
     				errorLine.style.top = parseInt(lines[i].top - top) + 'px';
    -				errorLine.style.height = parseInt(lines[i].bottom - lines[i].top) + 'px';
    +				errorLine.style.height = parseInt(lines[i].bottom - lines[i].top + 1) + 'px';
     			}
     		}
     	};
    
    From cc5426e549b44ddc1eefb17aa920b7c2ccc9df32 Mon Sep 17 00:00:00 2001
    From: resurtm 
    Date: Mon, 27 May 2013 22:26:20 +0600
    Subject: [PATCH 29/70] Removed unnecessary code from MSSQL schema.
    
    ---
     framework/yii/db/mssql/Schema.php | 12 ++++--------
     1 file changed, 4 insertions(+), 8 deletions(-)
    
    diff --git a/framework/yii/db/mssql/Schema.php b/framework/yii/db/mssql/Schema.php
    index 1991542..ad0f7d4 100644
    --- a/framework/yii/db/mssql/Schema.php
    +++ b/framework/yii/db/mssql/Schema.php
    @@ -241,15 +241,11 @@ SQL;
     		}
     		foreach ($columns as $column) {
     			$column = $this->loadColumnSchema($column);
    -			if (is_array($table->primaryKey)) {
    -				foreach ($table->primaryKey as $primaryKeyColumn) {
    -					if (strcasecmp($column->name, $primaryKeyColumn) === 0) {
    -						$column->isPrimaryKey = true;
    -						break;
    -					}
    +			foreach ($table->primaryKey as $primaryKey) {
    +				if (strcasecmp($column->name, $primaryKey) === 0) {
    +					$column->isPrimaryKey = true;
    +					break;
     				}
    -			} else {
    -				$column->isPrimaryKey = strcasecmp($column->name, $table->primaryKey) === 0;
     			}
     			if ($column->isPrimaryKey && $column->autoIncrement) {
     				$table->sequenceName = '';
    
    From 5f269399f1c17bfbacf3d0ef8595fd47ac457a0a Mon Sep 17 00:00:00 2001
    From: resurtm 
    Date: Mon, 27 May 2013 22:59:32 +0600
    Subject: [PATCH 30/70] Better exception page look.
    
    ---
     framework/yii/views/errorHandler/main.php | 34 +++++++++++++++++--------------
     1 file changed, 19 insertions(+), 15 deletions(-)
    
    diff --git a/framework/yii/views/errorHandler/main.php b/framework/yii/views/errorHandler/main.php
    index 3bf5bca..48ac8b9 100644
    --- a/framework/yii/views/errorHandler/main.php
    +++ b/framework/yii/views/errorHandler/main.php
    @@ -205,7 +205,6 @@ html,body{
     	background-color: #ffebeb;
     	position: absolute;
     	width: 100%;
    -	height: 18px;
     	z-index: 100;
     	margin-top: -61px;
     }
    @@ -227,20 +226,25 @@ html,body{
     	z-index: 200;
     	display: block;
     	color: #aaa;
    -	line-height: 18px;
    -	font-size: 14px;
    -	margin-top: -61px;
    +	line-height: 20px;
    +	font-size: 12px;
    +	margin-top: -63px;
     	font-family: Consolas, Courier New, monospace;
     }
     .call-stack ul li .code pre{
     	position: relative;
     	z-index: 200;
     	left: 50px;
    -	line-height: 17px;
    -	font-size: 14px;
    +	line-height: 20px;
    +	font-size: 12px;
     	font-family: Consolas, Courier New, monospace;
     	display: inline;
     }
    +@-moz-document url-prefix() {
    +	.call-stack ul li .code pre{
    +		line-height: 20px;
    +	}
    +}
     
     /* request */
     .request{
    @@ -421,10 +425,10 @@ window.onload = function() {
     			lineFound = false,
     			hoverLines = Sizzle('.hover-line');
     
    -		for (var i = 0, imax = codeBlocks.length; i < imax; ++i) {
    +		for (var i = 0, imax = codeBlocks.length - 1; i < imax; ++i) {
     			var lines = codeBlocks[i].getClientRects();
     			for (var j = 0, jmax = lines.length; j < jmax; ++j) {
    -				if (clientY > lines[j].top && clientY < lines[j].bottom) {
    +				if (clientY >= lines[j].top && clientY <= lines[j].bottom) {
     					lineFound = true;
     					break;
     				}
    @@ -438,9 +442,9 @@ window.onload = function() {
     			hoverLines[k].className = 'hover-line';
     		}
     		if (lineFound) {
    -			var line = Sizzle('.call-stack-item:eq(' + i + ') .hover-line:eq(' + j + ')');
    -			if (line[0]) {
    -				line[0].className = 'hover-line hover';
    +			var line = Sizzle('.call-stack-item:eq(' + i + ') .hover-line:eq(' + j + ')')[0];
    +			if (line) {
    +				line.className = 'hover-line hover';
     			}
     		}
     	};
    @@ -459,11 +463,11 @@ window.onload = function() {
     				continue;
     			}
     			lineNumbers[i].style.top = parseInt(lines[i].top - top) + 'px';
    -			hoverLines[i].style.top = parseInt(lines[i].top - top) + 'px';
    -			hoverLines[i].style.height = parseInt(lines[i].bottom - lines[i].top + 1) + 'px';
    +			hoverLines[i].style.top = parseInt(lines[i].top - top - 3) + 'px';
    +			hoverLines[i].style.height = parseInt(lines[i].bottom - lines[i].top + 6) + 'px';
     			if (parseInt(callStackItem.getAttribute('data-line')) == i) {
    -				errorLine.style.top = parseInt(lines[i].top - top) + 'px';
    -				errorLine.style.height = parseInt(lines[i].bottom - lines[i].top + 1) + 'px';
    +				errorLine.style.top = parseInt(lines[i].top - top - 3) + 'px';
    +				errorLine.style.height = parseInt(lines[i].bottom - lines[i].top + 6) + 'px';
     			}
     		}
     	};
    
    From f1ce0e165db03db42f243ac9a3e22f2bc3910e6a Mon Sep 17 00:00:00 2001
    From: Qiang Xue 
    Date: Mon, 27 May 2013 13:43:48 -0400
    Subject: [PATCH 31/70] Fixes issue #428 Allowing UniqueValidator to be used
     with non-AR models.
    
    ---
     framework/yii/validators/UniqueValidator.php | 3 ++-
     1 file changed, 2 insertions(+), 1 deletion(-)
    
    diff --git a/framework/yii/validators/UniqueValidator.php b/framework/yii/validators/UniqueValidator.php
    index a4d8bff..b650693 100644
    --- a/framework/yii/validators/UniqueValidator.php
    +++ b/framework/yii/validators/UniqueValidator.php
    @@ -9,6 +9,7 @@ namespace yii\validators;
     
     use Yii;
     use yii\base\InvalidConfigException;
    +use yii\db\ActiveRecord;
     
     /**
      * CUniqueValidator validates that the attribute value is unique in the corresponding database table.
    @@ -71,7 +72,7 @@ class UniqueValidator extends Validator
     		$query = $className::find();
     		$query->where(array($column->name => $value));
     
    -		if ($object->getIsNewRecord()) {
    +		if (!$object instanceof ActiveRecord || $object->getIsNewRecord()) {
     			// if current $object isn't in the database yet then it's OK just to call exists()
     			$exists = $query->exists();
     		} else {
    
    From 072e4351e6f67a2a60b516e532286fcfed21b5b2 Mon Sep 17 00:00:00 2001
    From: Qiang Xue 
    Date: Mon, 27 May 2013 14:27:37 -0400
    Subject: [PATCH 32/70] Changed the way to set timestamp for multiple
     attributes.
    
    ---
     framework/yii/behaviors/AutoTimestamp.php | 8 ++++----
     1 file changed, 4 insertions(+), 4 deletions(-)
    
    diff --git a/framework/yii/behaviors/AutoTimestamp.php b/framework/yii/behaviors/AutoTimestamp.php
    index ea69963..7611712 100644
    --- a/framework/yii/behaviors/AutoTimestamp.php
    +++ b/framework/yii/behaviors/AutoTimestamp.php
    @@ -83,17 +83,17 @@ class AutoTimestamp extends Behavior
     	 */
     	public function updateTimestamp($attributes)
     	{
    +		$timestamp = $this->evaluateTimestamp();
     		foreach ($attributes as $attribute) {
    -			$this->owner->$attribute = $this->evaluateTimestamp($attribute);
    +			$this->owner->$attribute = $timestamp;
     		}
     	}
     
     	/**
    -	 * Gets the appropriate timestamp for the specified attribute.
    -	 * @param string $attribute attribute name
    +	 * Gets the current timestamp.
     	 * @return mixed the timestamp value
     	 */
    -	protected function evaluateTimestamp($attribute)
    +	protected function evaluateTimestamp()
     	{
     		if ($this->timestamp instanceof Expression) {
     			return $this->timestamp;
    
    From 14998f41261d785bacf8cf85fcb7b9234996fdc1 Mon Sep 17 00:00:00 2001
    From: Alexander Kochetov 
    Date: Mon, 27 May 2013 23:30:01 +0400
    Subject: [PATCH 33/70] \yii\rbac\DbManager::isAssigned() signature fix
    
    ---
     framework/yii/rbac/DbManager.php | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/framework/yii/rbac/DbManager.php b/framework/yii/rbac/DbManager.php
    index 5b94646..b7a5d4e 100644
    --- a/framework/yii/rbac/DbManager.php
    +++ b/framework/yii/rbac/DbManager.php
    @@ -280,7 +280,7 @@ class DbManager extends Manager
     	 * @param string $itemName the item name
     	 * @return boolean whether the item has been assigned to the user.
     	 */
    -	public function isAssigned($itemName, $userId)
    +	public function isAssigned($userId, $itemName)
     	{
     		$query = new Query;
     		return $query->select(array('item_name'))
    
    From c8e73b42e75c2770ecd69865af9cfa9f7bc9666d Mon Sep 17 00:00:00 2001
    From: Carsten Brandt 
    Date: Mon, 27 May 2013 23:45:13 +0200
    Subject: [PATCH 34/70] fixed AutoTimestamp example in advanced app
    
    fixes #429
    ---
     apps/advanced/common/models/User.php | 3 +--
     1 file changed, 1 insertion(+), 2 deletions(-)
    
    diff --git a/apps/advanced/common/models/User.php b/apps/advanced/common/models/User.php
    index 110487e..7830718 100644
    --- a/apps/advanced/common/models/User.php
    +++ b/apps/advanced/common/models/User.php
    @@ -37,8 +37,7 @@ class User extends ActiveRecord implements Identity
     			'timestamp' => array(
     				'class' => 'yii\behaviors\AutoTimestamp',
     				'attributes' => array(
    -					ActiveRecord::EVENT_BEFORE_INSERT => 'create_time',
    -					ActiveRecord::EVENT_BEFORE_INSERT => 'update_time',
    +					ActiveRecord::EVENT_BEFORE_INSERT => array('create_time', 'update_time'),
     					ActiveRecord::EVENT_BEFORE_UPDATE => 'update_time',
     				),
     			),
    
    From 0a5b7a596010073943999cb78081b2257496a5e1 Mon Sep 17 00:00:00 2001
    From: Carsten Brandt 
    Date: Mon, 27 May 2013 23:51:01 +0200
    Subject: [PATCH 35/70] docs: Be explicitly clear about ActiveRecord::create()
    
    ---
     framework/yii/db/ActiveRecord.php | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/framework/yii/db/ActiveRecord.php b/framework/yii/db/ActiveRecord.php
    index a58ac4d..58411f0 100644
    --- a/framework/yii/db/ActiveRecord.php
    +++ b/framework/yii/db/ActiveRecord.php
    @@ -1152,7 +1152,7 @@ class ActiveRecord extends Model
     	/**
     	 * Creates an active record object using a row of data.
     	 * This method is called by [[ActiveQuery]] to populate the query results
    -	 * into Active Records.
    +	 * into Active Records. It is not meant to be used to create new records.
     	 * @param array $row attribute values (name => value)
     	 * @return ActiveRecord the newly created active record.
     	 */
    
    From 3ac84240b64295ce4d1f9e6508ab7bc6891e90e0 Mon Sep 17 00:00:00 2001
    From: Carsten Brandt 
    Date: Tue, 28 May 2013 00:13:52 +0200
    Subject: [PATCH 36/70] added note about dependency to registerJsFile
    
    Fixes #373
    ---
     framework/yii/base/View.php | 2 ++
     1 file changed, 2 insertions(+)
    
    diff --git a/framework/yii/base/View.php b/framework/yii/base/View.php
    index f0656d7..9d6b921 100644
    --- a/framework/yii/base/View.php
    +++ b/framework/yii/base/View.php
    @@ -661,6 +661,8 @@ class View extends Component
     
     	/**
     	 * Registers a JS file.
    +	 * Please note that when this file depends on other JS files to be registered before,
    +	 * for example jQuery, you should use [[registerAssetBundle]] instead.
     	 * @param string $url the JS file to be registered.
     	 * @param array $options the HTML attributes for the script tag. A special option
     	 * named "position" is supported which specifies where the JS script tag should be inserted
    
    From 9445953b29d61a40d10ab037f808305a48e1bd4f Mon Sep 17 00:00:00 2001
    From: Carsten Brandt 
    Date: Tue, 28 May 2013 01:56:50 +0200
    Subject: [PATCH 37/70] Line number tweak for error page
    
    ensure lines are aligned right:
    ```
     7
     8
     9
    10
    11
    12
    ...
    ```
    ---
     framework/yii/views/errorHandler/main.php | 2 ++
     1 file changed, 2 insertions(+)
    
    diff --git a/framework/yii/views/errorHandler/main.php b/framework/yii/views/errorHandler/main.php
    index 48ac8b9..d7bbb3d 100644
    --- a/framework/yii/views/errorHandler/main.php
    +++ b/framework/yii/views/errorHandler/main.php
    @@ -225,6 +225,8 @@ html,body{
     	position: absolute;
     	z-index: 200;
     	display: block;
    +	width: 25px;
    +	text-align: right;
     	color: #aaa;
     	line-height: 20px;
     	font-size: 12px;
    
    From df6168475eb1bbd1d115e6f96af8eda21b6a81a8 Mon Sep 17 00:00:00 2001
    From: Qiang Xue 
    Date: Mon, 27 May 2013 21:35:00 -0400
    Subject: [PATCH 38/70] Refactored StringHelper.
    
    ---
     framework/yii/helpers/base/StringHelper.php | 13 +++++--------
     1 file changed, 5 insertions(+), 8 deletions(-)
    
    diff --git a/framework/yii/helpers/base/StringHelper.php b/framework/yii/helpers/base/StringHelper.php
    index 5b854ac..5134bf6 100644
    --- a/framework/yii/helpers/base/StringHelper.php
    +++ b/framework/yii/helpers/base/StringHelper.php
    @@ -18,20 +18,18 @@ class StringHelper
     {
     	/**
     	 * Returns the number of bytes in the given string.
    -	 * This method ensures the string is treated as a byte array.
    -	 * It will use `mb_strlen()` if it is available.
    +	 * This method ensures the string is treated as a byte array by using `mb_strlen()`.
     	 * @param string $string the string being measured for length
     	 * @return integer the number of bytes in the given string.
     	 */
     	public static function strlen($string)
     	{
    -		return function_exists('mb_strlen') ? mb_strlen($string, '8bit') : strlen($string);
    +		return mb_strlen($string, '8bit');
     	}
     
     	/**
     	 * Returns the portion of string specified by the start and length parameters.
    -	 * This method ensures the string is treated as a byte array.
    -	 * It will use `mb_substr()` if it is available.
    +	 * This method ensures the string is treated as a byte array by using `mb_substr()`.
     	 * @param string $string the input string. Must be one character or longer.
     	 * @param integer $start the starting position
     	 * @param integer $length the desired portion length
    @@ -40,15 +38,14 @@ class StringHelper
     	 */
     	public static function substr($string, $start, $length)
     	{
    -		return function_exists('mb_substr') ? mb_substr($string, $start, $length, '8bit') : substr($string, $start, $length);
    +		return mb_substr($string, $start, $length, '8bit');
     	}
     
     	/**
     	 * Returns the trailing name component of a path.
     	 * This method does the same as the php function basename() except that it will
     	 * always use \ and / as directory separators, independent of the operating system.
    -	 * Note: basename() operates naively on the input string, and is not aware of the
    -	 * actual filesystem, or path components such as "..".
    +	 * Note: this method is not aware of the actual filesystem, or path components such as "..".
     	 * @param string $path A path string.
     	 * @param string $suffix If the name component ends in suffix this will also be cut off.
     	 * @return string the trailing name component of the given path.
    
    From 9bc99e22b74ab44feb16f136d40f92d918855642 Mon Sep 17 00:00:00 2001
    From: ploaiza 
    Date: Tue, 28 May 2013 10:55:51 -0300
    Subject: [PATCH 39/70] Fixed some wording
    
    ---
     docs/guide/upgrade-from-v1.md | 32 ++++++++++++++++----------------
     1 file changed, 16 insertions(+), 16 deletions(-)
    
    diff --git a/docs/guide/upgrade-from-v1.md b/docs/guide/upgrade-from-v1.md
    index b3d4411..ebfe94b 100644
    --- a/docs/guide/upgrade-from-v1.md
    +++ b/docs/guide/upgrade-from-v1.md
    @@ -13,7 +13,7 @@ The most obvious change in Yii 2.0 is the use of namespaces. Almost every core c
     is namespaced, e.g., `yii\web\Request`. The "C" prefix is no longer used in class names.
     The naming of the namespaces follows the directory structure. For example, `yii\web\Request`
     indicates the corresponding class file is `web/Request.php` under the Yii framework folder.
    -You can use any core class without explicitly include that class file, thanks to the Yii
    +You can use any core class without explicitly including that class file, thanks to the Yii
     class loader.
     
     
    @@ -117,17 +117,17 @@ supported in most places in the Yii core code. For example, `FileCache::cachePat
     both a path alias and a normal directory path.
     
     Path alias is also closely related with class namespaces. It is recommended that a path
    -alias defined for each root namespace so that you can use Yii class autoloader without
    +alias be defined for each root namespace so that you can use Yii the class autoloader without
     any further configuration. For example, because `@yii` refers to the Yii installation directory,
     a class like `yii\web\Request` can be autoloaded by Yii. If you use a third party library
    -such as Zend Framework, you may define a path alias `@Zend` which refers to its installation directory.
    -And Yii will be able to autoload any class in this library.
    +such as Zend Framework, you may define a path alias `@Zend` which refers to its installation 
    +directory and Yii will be able to autoload any class in this library.
     
     
     View
     ----
     
    -Yii 2.0 introduces a `View` class to represent the view part in the MVC pattern.
    +Yii 2.0 introduces a `View` class to represent the view part of the MVC pattern.
     It can be configured globally through the "view" application component. It is also
     accessible in any view file via `$this`. This is one of the biggest changes compared to 1.1:
     **`$this` in a view file no longer refers to the controller or widget object.**
    @@ -159,7 +159,7 @@ extension for your Smarty views, or `twig` for Twig views. You may also configur
     Models
     ------
     
    -A model is now associated with a form name returned its `formName()` method. This is
    +A model is now associated with a form name returned by its `formName()` method. This is
     mainly used when using HTML forms to collect user inputs for a model. Previously in 1.1,
     this is usually hardcoded as the class name of the model.
     
    @@ -235,7 +235,7 @@ Previously in 1.1, you would have to enter the widget class names as strings via
     Themes
     ------
     
    -Theme works completely different in 2.0. It is now based on a path map to "translate" a source
    +Themes work completely different in 2.0. They are now based on a path map to "translate" a source
     view into a themed view. For example, if the path map for a theme is
     `array('/www/views' => '/www/themes/basic')`, then the themed version for a view file
     `/www/views/site/index.php` will be `/www/themes/basic/site/index.php`.
    @@ -250,7 +250,7 @@ application component.
     Console Applications
     --------------------
     
    -Console applications are now composed by controllers, too, like Web applications. In fact,
    +Console applications are now composed by controllers, like Web applications. In fact,
     console controllers and Web controllers share the same base controller class.
     
     Each console controller is like `CConsoleCommand` in 1.1. It consists of one or several
    @@ -300,7 +300,7 @@ public function behaviors()
     Assets
     ------
     
    -Yii 2.0 introduces a new concept called *asset bundle*. It is a bit similar to script
    +Yii 2.0 introduces a new concept called *asset bundle*. It is similar to script
     packages (managed by `CClientScript`) in 1.1, but with better support.
     
     An asset bundle is a collection of asset files (e.g. JavaScript files, CSS files, image files, etc.)
    @@ -315,7 +315,7 @@ Static Helpers
     
     Yii 2.0 introduces many commonly used static helper classes, such as `Html`, `ArrayHelper`,
     `StringHelper`. These classes are designed to be easily extended. Note that static classes
    -are usually hard to be extended because of the fixed class name references. But Yii 2.0
    +are usually hard to extend because of the fixed class name references. But Yii 2.0
     introduces the class map (via `Yii::$classMap`) to overcome this difficulty.
     
     
    @@ -343,7 +343,7 @@ Query Builder
     
     In 1.1, query building is scattered among several classes, including `CDbCommand`,
     `CDbCriteria`, and `CDbCommandBuilder`. Yii 2.0 uses `Query` to represent a DB query
    -and `QueryBuilder` to generate SQL statements from query objects. For example,
    +and `QueryBuilder` to generate SQL statements from query objects. For example:
     
     ```php
     $query = new \yii\db\Query;
    @@ -365,7 +365,7 @@ ActiveRecord
     ------------
     
     ActiveRecord has undergone significant changes in Yii 2.0. The most important one
    -is about relational ActiveRecord query. In 1.1, you have to declare the relations
    +is the relational ActiveRecord query. In 1.1, you have to declare the relations
     in the `relations()` method. In 2.0, this is done via getter methods that return
     an `ActiveQuery` object. For example, the following method declares an "orders" relation:
     
    @@ -392,7 +392,7 @@ by filtering with the primary keys of the primary records.
     
     
     Yii 2.0 no longer uses the `model()` method when performing queries. Instead, you
    -use the `find()` method like the following:
    +use the `find()` method:
     
     ```php
     // to retrieve all *active* customers and order them by their ID:
    @@ -410,14 +410,14 @@ Therefore, you can use all query methods of `Query`.
     
     Instead of returning ActiveRecord objects, you may call `ActiveQuery::asArray()` to
     return results in terms of arrays. This is more efficient and is especially useful
    -when you need to return large number of records. For example,
    +when you need to return a large number of records:
     
     ```php
     $customers = Customer::find()->asArray()->all();
     ```
     
     By default, ActiveRecord now only saves dirty attributes. In 1.1, all attributes
    -would be saved to database when you call `save()`, regardless they are changed or not,
    +are saved to database when you call `save()`, regardless of having changed or not,
     unless you explicitly list the attributes to save.
     
     
    @@ -427,7 +427,7 @@ Auto-quoting Table and Column Names
     Yii 2.0 supports automatic quoting of database table and column names. A name enclosed
     within double curly brackets is treated as a table name, and a name enclosed within
     double square brackets is treated as a column name. They will be quoted according to
    -the database driver being used. For example,
    +the database driver being used:
     
     ```php
     $command = $connection->createCommand('SELECT [[id]] FROM {{posts}}');
    
    From d6f001016c4e1daa24d4bd28a4bb6deaa27d7933 Mon Sep 17 00:00:00 2001
    From: Suralc 
    Date: Tue, 28 May 2013 15:58:40 +0200
    Subject: [PATCH 40/70] Added missing use statement.
    
    ---
     apps/advanced/console/migrations/m130524_201442_init.php | 2 ++
     1 file changed, 2 insertions(+)
    
    diff --git a/apps/advanced/console/migrations/m130524_201442_init.php b/apps/advanced/console/migrations/m130524_201442_init.php
    index 24a74c3..a5e9d30 100644
    --- a/apps/advanced/console/migrations/m130524_201442_init.php
    +++ b/apps/advanced/console/migrations/m130524_201442_init.php
    @@ -1,5 +1,7 @@
     
    Date: Tue, 28 May 2013 16:24:37 +0200
    Subject: [PATCH 41/70] Added Nav widget closes #367
    
    ---
     framework/yii/bootstrap/Nav.php | 137 ++++++++++++++++++++++++++++++++++++++++
     1 file changed, 137 insertions(+)
     create mode 100644 framework/yii/bootstrap/Nav.php
    
    diff --git a/framework/yii/bootstrap/Nav.php b/framework/yii/bootstrap/Nav.php
    new file mode 100644
    index 0000000..f373961
    --- /dev/null
    +++ b/framework/yii/bootstrap/Nav.php
    @@ -0,0 +1,137 @@
    + array(
    + *         array(
    + *             'label' => 'Home',
    + *             'url' => '/',
    + *             'options' => array(...),
    + *             'active' => true,
    + *         ),
    + *         array(
    + *             'label' => 'Dropdown',
    + *             'dropdown' => array(
    + *                  array(
    + *                      'label' => 'DropdownA',
    + *                      'url' => '#',
    + *                  ),
    + *                  array(
    + *                      'label' => 'DropdownB',
    + *                      'url' => '#',
    + *                  ),
    + *             ),
    + *         ),
    + *     ),
    + * ));
    + * ```
    + *
    + * @see http://twitter.github.io/bootstrap/components.html#nav
    + * @author Antonio Ramirez 
    + * @since 2.0
    + */
    +class Nav extends Widget
    +{
    +	/**
    +	 * @var array list of items in the nav widget. Each array element represents a single
    +	 * menu item with the following structure:
    +	 *
    +	 * ```php
    +	 * array(
    +	 *     // required, the menu item label.
    +	 *     'label' => 'Nav item label',
    +	 *     // optional, the URL of the menu item. Defaults to "#"
    +	 *     'url'=> '#',
    +	 *     // optional, the HTML options of the URL.
    +	 *     'urlOptions' => array(...),
    +	 *     // optional the HTML attributes of the item container (LI).
    +	 *     'options' => array(...),
    +	 *     // optional, an array of [[Dropdown]] widget items so to display a dropdown menu on the tab header.
    +	 *     // important: there is an issue with sub-dropdown menus, and as of 3.0, bootstrap won't support sub-dropdown
    +	 *     // @see https://github.com/twitter/bootstrap/issues/5050#issuecomment-11741727
    +	 *     'dropdown'=> array(...)
    +	 * )
    +	 * ```
    +	 *
    +	 * Optionally, you can also use a plain string instead of an array element.
    +	 */
    +	public $items = array();
    +
    +
    +	/**
    +	 * Initializes the widget.
    +	 */
    +	public function init()
    +	{
    +		$this->addCssClass($this->options, 'nav');
    +	}
    +
    +	/**
    +	 * Renders the widget.
    +	 */
    +	public function run()
    +	{
    +		echo $this->renderItems();
    +	}
    +
    +	/**
    +	 * Renders widget items.
    +	 */
    +	public function renderItems()
    +	{
    +		$items = array();
    +		foreach ($this->items as $item) {
    +			$items[] = $this->renderItem($item);
    +		}
    +
    +		return Html::tag('ul', implode("\n", $items), $this->options);
    +	}
    +
    +	/**
    +	 * Renders a widget's item.
    +	 * @param mixed $item the item to render.
    +	 * @return string the rendering result.
    +	 * @throws InvalidConfigException
    +	 */
    +	public function renderItem($item)
    +	{
    +		if (is_string($item)) {
    +			return $item;
    +		}
    +		if (!isset($item['label'])) {
    +			throw new InvalidConfigException("The 'label' option is required.");
    +		}
    +		$label = $item['label'];
    +		$url = ArrayHelper::getValue($item, 'url', '#');
    +		$options = ArrayHelper::getValue($item, 'options', array());
    +		$urlOptions = ArrayHelper::getValue($item, 'urlOptions', array());
    +		$dropdown = null;
    +
    +		// does it has a dropdown widget?
    +		if (isset($item['dropdown'])) {
    +			$urlOptions['data-toggle'] = 'dropdown';
    +			$this->addCssClass($options, 'dropdown');
    +			$this->addCssClass($urlOptions, 'dropdown-toggle');
    +			$label .= ' ' . Html::tag('b', '', array('class' => 'caret'));
    +			$dropdown = Dropdown::widget(array('items' => $item['dropdown'], 'clientOptions' => false));
    +		}
    +
    +		return Html::tag('li', Html::a($label, $url, $urlOptions) . $dropdown, $options);
    +	}
    +}
    \ No newline at end of file
    
    From 70f1d4169996c1259bfa02d8627c2f5dd0d34c0f Mon Sep 17 00:00:00 2001
    From: Antonio Ramirez 
    Date: Tue, 28 May 2013 16:27:33 +0200
    Subject: [PATCH 42/70] Added NavBar widget closes #437
    
    ---
     framework/yii/bootstrap/NavBar.php | 169 +++++++++++++++++++++++++++++++++++++
     1 file changed, 169 insertions(+)
     create mode 100644 framework/yii/bootstrap/NavBar.php
    
    diff --git a/framework/yii/bootstrap/NavBar.php b/framework/yii/bootstrap/NavBar.php
    new file mode 100644
    index 0000000..1ed5a9a
    --- /dev/null
    +++ b/framework/yii/bootstrap/NavBar.php
    @@ -0,0 +1,169 @@
    + array(
    + *         // a Nav widget
    + *         array(
    + *             // defaults to Nav anyway.
    + *             'class' => 'yii\bootstrap\Nav',
    + *             // widget configuration
    + *             'options' => array(
    + *                 'items' => array(
    + *                     array(
    + *                         'label' => 'Home',
    + *                         'url' => '/',
    + *                         'options' => array('class' => 'active'),
    + *                     ),
    + *                     array(
    + *                         'label' => 'Dropdown',
    + *                         // configure a dropdown menu
    + *                         'dropdown' => array(
    + *                             array(
    + *                                 'label' => 'DropdownA',
    + *                                 'url' => '#',
    + *                             ),
    + *                             array(
    + *                                 'label' => 'DropdownB',
    + *                                 'url' => '#'
    + *                             ),
    + *                         )
    + *                     ),
    + *                 )
    + *             ),
    + *         ),
    + *         // you can also use strings
    + *         '',
    + *     ),
    + * ));
    + * ```
    + *
    + * @see http://twitter.github.io/bootstrap/components.html#navbar
    + * @author Antonio Ramirez 
    + * @since 2.0
    + */
    +class NavBar extends Widget
    +{
    +	public $brand;
    +	/**
    +	 * @var array list of menu items in the navbar widget. Each array element represents a single
    +	 * menu item with the following structure:
    +	 *
    +	 * ```php
    +	 * array(
    +	 *     // optional, the menu item class type of the widget to render. Defaults to "Nav" widget.
    +	 *     'class' => 'Menu item class type',
    +	 *     // required, the configuration options of the widget.
    +	 *     'options'=> array(...),
    +	 * ),
    +	 * // optionally, you can pass a string
    +	 * '',
    +	 * ```
    +	 *
    +	 * Optionally, you can also use a plain string instead of an array element.
    +	 */
    +	public $items = array();
    +
    +
    +	/**
    +	 * Initializes the widget.
    +	 */
    +	public function init()
    +	{
    +		parent::init();
    +		$this->addCssClass($this->options, 'navbar');
    +	}
    +
    +	/**
    +	 * Renders the widget.
    +	 */
    +	public function run()
    +	{
    +		echo Html::beginTag('div', $this->options);
    +		echo $this->renderItems();
    +		echo Html::endTag('div');
    +	}
    +
    +	/**
    +	 * @return string the rendering items.
    +	 */
    +	protected function renderItems()
    +	{
    +		$items = array();
    +
    +		foreach ($this->items as $item) {
    +			$items[] = $this->renderItem($item);
    +		}
    +		$contents =implode("\n", $items);
    +		if (self::$responsive === true) {
    +			$this->getView()->registerAssetBundle('yii/bootstrap/collapse');
    +			$contents =
    +				Html::tag('div',
    +					$this->renderToggleButton() .
    +					$this->brand . "\n" .
    +					Html::tag('div', $contents, array('class' => 'nav-collapse collapse navbar-collapse')),
    +					array('class' => 'container'));
    +
    +		} else {
    +			$contents = $this->brand . "\n" . $contents;
    +		}
    +		return Html::tag('div', $contents, array('class' => 'navbar-inner'));
    +	}
    +
    +	/**
    +	 * Renders a item. The item can be a string, a custom class or a Nav widget (defaults if no class specified.
    +	 * @param mixed $item the item to render. If array, it is assumed the configuration of a widget being `class`
    +	 * required and if not specified, then defaults to `yii\bootstrap\Nav`.
    +	 * @return string the rendering result.
    +	 * @throws InvalidConfigException
    +	 */
    +	protected function renderItem($item)
    +	{
    +		if (is_string($item)) {
    +			return $item;
    +		}
    +		$config = ArrayHelper::getValue($item, 'options', array());
    +		$config['class'] = ArrayHelper::getValue($item, 'class', 'yii\bootstrap\Nav');
    +		$widget = \Yii::createObject($config);
    +		ob_start();
    +		$widget->run();
    +		return ob_get_clean();
    +	}
    +
    +	/**
    +	 * Renders collapsible toggle button.
    +	 * @return string the rendering toggle button.
    +	 */
    +	protected function renderToggleButton()
    +	{
    +		$items = array();
    +		for ($i = 0; $i < 3; $i++) {
    +			$items[] = Html::tag('span', '', array('class' => 'icon-bar'));
    +		}
    +		return Html::tag('a', implode("\n", $items), array(
    +			'class' => 'btn btn-navbar',
    +			'data-toggle' => 'collapse',
    +			'data-target' => 'div.navbar-collapse',
    +		)) . "\n";
    +	}
    +}
    \ No newline at end of file
    
    From 000fdc3de067a8e4b7314a21edf3815f86371e7b Mon Sep 17 00:00:00 2001
    From: Qiang Xue 
    Date: Tue, 28 May 2013 17:27:37 -0400
    Subject: [PATCH 43/70] formatter WIP.
    
    ---
     framework/yii/base/Formatter.php            | 288 ++++++++++++++++++++++++++++
     framework/yii/i18n/Formatter.php            | 232 ++++++++++++++++++++++
     tests/unit/framework/base/FormatterTest.php | 101 ++++++++++
     3 files changed, 621 insertions(+)
     create mode 100644 framework/yii/base/Formatter.php
     create mode 100644 framework/yii/i18n/Formatter.php
     create mode 100644 tests/unit/framework/base/FormatterTest.php
    
    diff --git a/framework/yii/base/Formatter.php b/framework/yii/base/Formatter.php
    new file mode 100644
    index 0000000..a31fac9
    --- /dev/null
    +++ b/framework/yii/base/Formatter.php
    @@ -0,0 +1,288 @@
    +
    + * @since 2.0
    + */
    +class Formatter extends Component
    +{
    +	/**
    +	 * @var string the default format string to be used to format a date using PHP date() function.
    +	 */
    +	public $dateFormat = 'Y/m/d';
    +	/**
    +	 * @var string the default format string to be used to format a time using PHP date() function.
    +	 */
    +	public $timeFormat = 'h:i:s A';
    +	/**
    +	 * @var string the default format string to be used to format a date and time using PHP date() function.
    +	 */
    +	public $datetimeFormat = 'Y/m/d h:i:s A';
    +	/**
    +	 * @var array the format used to format a number with PHP number_format() function.
    +	 * Three elements may be specified: "decimals", "decimalSeparator" and "thousandSeparator".
    +	 * They correspond to the number of digits after the decimal point, the character displayed as the decimal point
    +	 * and the thousands separator character.
    +	 */
    +	public $numberFormat = array('decimals' => null, 'decimalSeparator' => null, 'thousandSeparator' => null);
    +	/**
    +	 * @var array the text to be displayed when formatting a boolean value. The first element corresponds
    +	 * to the text display for false, the second element for true. Defaults to array('No', 'Yes').
    +	 */
    +	public $booleanFormat;
    +
    +
    +	/**
    +	 * Initializes the component.
    +	 */
    +	public function init()
    +	{
    +		if (empty($this->booleanFormat)) {
    +			$this->booleanFormat = array(Yii::t('yii', 'No'), Yii::t('yii', 'Yes'));
    +		}
    +	}
    +
    +	/**
    +	 * Formats the value as is without any formatting.
    +	 * This method simply returns back the parameter without any format.
    +	 * @param mixed $value the value to be formatted
    +	 * @return string the formatted result
    +	 */
    +	public function asRaw($value)
    +	{
    +		return $value;
    +	}
    +
    +	/**
    +	 * Formats the value as an HTML-encoded plain text.
    +	 * @param mixed $value the value to be formatted
    +	 * @return string the formatted result
    +	 */
    +	public function asText($value)
    +	{
    +		return Html::encode($value);
    +	}
    +
    +	/**
    +	 * Formats the value as an HTML-encoded plain text with newlines converted into breaks.
    +	 * @param mixed $value the value to be formatted
    +	 * @return string the formatted result
    +	 */
    +	public function asNtext($value)
    +	{
    +		return nl2br(Html::encode($value));
    +	}
    +
    +	/**
    +	 * Formats the value as HTML-encoded text paragraphs.
    +	 * Each text paragraph is enclosed within a `

    ` tag. + * One or multiple consecutive empty lines divide two paragraphs. + * @param mixed $value the value to be formatted + * @return string the formatted result + */ + public function asParagraphs($value) + { + return str_replace('

    ', '', + '

    ' . preg_replace('/[\r\n]{2,}/', "

    \n

    ", Html::encode($value)) . '

    ' + ); + } + + /** + * Formats the value as HTML text. + * The value will be purified using [[HtmlPurifier]] to avoid XSS attacks. + * Use [[asRaw()]] if you do not want any purification of the value. + * @param mixed $value the value to be formatted + * @param array|null $config the configuration for the HTMLPurifier class. + * @return string the formatted result + */ + public function asHtml($value, $config = null) + { + return HtmlPurifier::process($value, $config); + } + + /** + * Formats the value as a mailto link. + * @param mixed $value the value to be formatted + * @return string the formatted result + */ + public function asEmail($value) + { + return Html::mailto($value); + } + + /** + * Formats the value as an image tag. + * @param mixed $value the value to be formatted + * @return string the formatted result + */ + public function asImage($value) + { + return Html::img($value); + } + + /** + * Formats the value as a hyperlink. + * @param mixed $value the value to be formatted + * @return string the formatted result + */ + public function asUrl($value) + { + $url = $value; + if (strpos($url, 'http://') !== 0 && strpos($url, 'https://') !== 0) { + $url = 'http://' . $url; + } + return Html::a(Html::encode($value), $url); + } + + /** + * Formats the value as a boolean. + * @param mixed $value the value to be formatted + * @return string the formatted result + * @see booleanFormat + */ + public function asBoolean($value) + { + return $value ? $this->booleanFormat[1] : $this->booleanFormat[0]; + } + + /** + * Formats the value as a date. + * @param integer|string|DateTime $value the value to be formatted. The following + * types of value are supported: + * + * - an integer representing UNIX timestamp + * - a string that can be parsed into a UNIX timestamp via `strtotime()` + * - a PHP DateTime object + * + * @param string $format the format used to convert the value into a date string. + * If null, [[dateFormat]] will be used. The format string should be the one + * that can be recognized by the PHP `date()` function. + * @return string the formatted result + * @see dateFormat + */ + public function asDate($value, $format = null) + { + $value = $this->normalizeDatetimeValue($value); + return date($format === null ? $this->dateFormat : $format, $value); + } + + /** + * Formats the value as a time. + * @param integer|string|DateTime $value the value to be formatted. The following + * types of value are supported: + * + * - an integer representing UNIX timestamp + * - a string that can be parsed into a UNIX timestamp via `strtotime()` + * - a PHP DateTime object + * + * @param string $format the format used to convert the value into a date string. + * If null, [[timeFormat]] will be used. The format string should be the one + * that can be recognized by the PHP `date()` function. + * @return string the formatted result + * @see timeFormat + */ + public function asTime($value, $format = null) + { + $value = $this->normalizeDatetimeValue($value); + return date($format === null ? $this->timeFormat : $format, $value); + } + + /** + * Formats the value as a datetime. + * @param integer|string|DateTime $value the value to be formatted. The following + * types of value are supported: + * + * - an integer representing UNIX timestamp + * - a string that can be parsed into a UNIX timestamp via `strtotime()` + * - a PHP DateTime object + * + * @param string $format the format used to convert the value into a date string. + * If null, [[datetimeFormat]] will be used. The format string should be the one + * that can be recognized by the PHP `date()` function. + * @return string the formatted result + * @see datetimeFormat + */ + public function asDatetime($value, $format = null) + { + $value = $this->normalizeDatetimeValue($value); + return date($format === null ? $this->datetimeFormat : $format, $value); + } + + /** + * Normalizes the given datetime value as one that can be taken by various date/time formatting methods. + * @param mixed $value the datetime value to be normalized. + * @return mixed the normalized datetime value + */ + protected function normalizeDatetimeValue($value) + { + if (is_string($value)) { + if (ctype_digit($value) || $value[0] === '-' && ctype_digit(substr($value, 1))) { + return (int)$value; + } else { + return strtotime($value); + } + } elseif ($value instanceof DateTime) { + return $value->getTimestamp(); + } else { + return (int)$value; + } + } + + /** + * Formats the value as an integer. + * @param mixed $value the value to be formatted + * @return string the formatting result. + */ + public function asInteger($value) + { + if (is_string($value) && preg_match('/^(-?\d+)/', $value, $matches)) { + return $matches[1]; + } else { + $value = (int)$value; + return "$value"; + } + } + + /** + * Formats the value as a double number. + * @param mixed $value the value to be formatted + * @param integer $decimals the number of digits after the decimal point + * @return string the formatting result. + */ + public function asDouble($value, $decimals = 2) + { + return sprintf("%.{$decimals}f", $value); + } + + /** + * Formats the value as a decimal number using the PHP number_format() function. + * @param mixed $value the value to be formatted + * @param integer $decimals the number of digits after the decimal point + * @param string $decimalSeparator the character displayed as the decimal point + * @param string $thousandSeparator the character displayed as the thousands separator character. + * @return string the formatted result + */ + public function asDecimal($value, $decimals = 0 , $decimalSeparator = '.' , $thousandSeparator = ',' ) + { + return number_format($value, $decimals, $decimalSeparator, $thousandSeparator); + } +} diff --git a/framework/yii/i18n/Formatter.php b/framework/yii/i18n/Formatter.php new file mode 100644 index 0000000..a0570a0 --- /dev/null +++ b/framework/yii/i18n/Formatter.php @@ -0,0 +1,232 @@ + + * @since 2.0 + */ +class Formatter extends \yii\base\Formatter +{ + /** + * @var string the locale ID that is used to localize the date and number formatting. + * If not set, [[\yii\base\Application::language]] will be used. + */ + public $locale; + /** + * @var string the default format string to be used to format a date using PHP date() function. + */ + public $dateFormat = 'short'; + /** + * @var string the default format string to be used to format a time using PHP date() function. + */ + public $timeFormat = 'short'; + /** + * @var string the default format string to be used to format a date and time using PHP date() function. + */ + public $datetimeFormat = 'short'; + /** + * @var array the options to be set for the NumberFormatter objects. Please refer to + */ + public $numberFormatOptions = array(); + + + /** + * Initializes the component. + * This method will check if the "intl" PHP extension is installed and set the + * default value of [[locale]]. + * @throws InvalidConfigException if the "intl" PHP extension is not installed. + */ + public function init() + { + if (!extension_loaded('intl')) { + throw new InvalidConfigException('The "intl" PHP extension is not install. It is required to format data values in localized formats.'); + } + if ($this->locale === null) { + $this->locale = Yii::$app->language; + } + parent::init(); + } + + private $_dateFormats = array( + 'short' => IntlDateFormatter::SHORT, + 'medium' => IntlDateFormatter::MEDIUM, + 'long' => IntlDateFormatter::LONG, + 'full' => IntlDateFormatter::FULL, + ); + + /** + * Formats the value as a date. + * @param integer|string|DateTime $value the value to be formatted. The following + * types of value are supported: + * + * - an integer representing UNIX timestamp + * - a string that can be parsed into a UNIX timestamp via `strtotime()` + * - a PHP DateTime object + * + * @param string $format the format used to convert the value into a date string. + * If null, [[dateFormat]] will be used. The format string should be the one + * that can be recognized by the PHP `date()` function. + * @return string the formatted result + * @see dateFormat + */ + public function asDate($value, $format = null) + { + $value = $this->normalizeDatetimeValue($value); + if ($format === null) { + $format = $this->dateFormat; + } + if (isset($this->_dateFormats[$format])) { + $formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], IntlDateFormatter::NONE); + } else { + $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE); + $formatter->setPattern($format); + } + return $formatter->format($value); + } + + /** + * Formats the value as a time. + * @param integer|string|DateTime $value the value to be formatted. The following + * types of value are supported: + * + * - an integer representing UNIX timestamp + * - a string that can be parsed into a UNIX timestamp via `strtotime()` + * - a PHP DateTime object + * + * @param string $format the format used to convert the value into a date string. + * If null, [[dateFormat]] will be used. The format string should be the one + * that can be recognized by the PHP `date()` function. + * @return string the formatted result + * @see timeFormat + */ + public function asTime($value, $format = null) + { + $value = $this->normalizeDatetimeValue($value); + if ($format === null) { + $format = $this->timeFormat; + } + if (isset($this->_dateFormats[$format])) { + $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, $this->_dateFormats[$format]); + } else { + $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE); + $formatter->setPattern($format); + } + return $formatter->format($value); + } + + /** + * Formats the value as a datetime. + * @param integer|string|DateTime $value the value to be formatted. The following + * types of value are supported: + * + * - an integer representing UNIX timestamp + * - a string that can be parsed into a UNIX timestamp via `strtotime()` + * - a PHP DateTime object + * + * @param string $format the format used to convert the value into a date string. + * If null, [[dateFormat]] will be used. The format string should be the one + * that can be recognized by the PHP `date()` function. + * @return string the formatted result + * @see datetimeFormat + */ + public function asDatetime($value, $format = null) + { + $value = $this->normalizeDatetimeValue($value); + if ($format === null) { + $format = $this->datetimeFormat; + } + if (isset($this->_dateFormats[$format])) { + $formatter = new IntlDateFormatter($this->locale, $this->_dateFormats[$format], $this->_dateFormats[$format]); + } else { + $formatter = new IntlDateFormatter($this->locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE); + $formatter->setPattern($format); + } + return $formatter->format($value); + } + + /** + * Formats the value as a decimal number. + * @param mixed $value the value to be formatted + * @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details) + * for details on how to specify a format. + * @return string the formatted result. + */ + public function asDecimal($value, $format = null) + { + $this->createNumberFormatter(NumberFormatter::DECIMAL, $format)->format($value); + } + + /** + * Formats the value as a currency number. + * @param mixed $value the value to be formatted + * @param string $currency the 3-letter ISO 4217 currency code indicating the currency to use. + * @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details) + * for details on how to specify a format. + * @return string the formatted result. + */ + public function asCurrency($value, $currency = 'USD', $format = null) + { + $this->createNumberFormatter(NumberFormatter::CURRENCY, $format)->formatCurrency($value, $currency); + } + + /** + * Formats the value as a percent number. + * @param mixed $value the value to be formatted + * @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details) + * for details on how to specify a format. + * @return string the formatted result. + */ + public function asPercent($value, $format = null) + { + $this->createNumberFormatter(NumberFormatter::PERCENT, $format)->format($value); + } + + /** + * Formats the value as a scientific number. + * @param mixed $value the value to be formatted + * @param string $format the format to be used. Please refer to [ICU manual](http://www.icu-project.org/apiref/icu4c/classDecimalFormat.html#_details) + * for details on how to specify a format. + * @return string the formatted result. + */ + public function asScientific($value, $format = null) + { + $this->createNumberFormatter(NumberFormatter::SCIENTIFIC, $format)->format($value); + } + + /** + * Creates a number formatter based on the given type and format. + * @param integer $type the type of the number formatter + * @param string $format the format to be used + * @return NumberFormatter the created formatter instance + */ + protected function createNumberFormatter($type, $format) + { + $formatter = new NumberFormatter($this->locale, $type); + if ($format !== null) { + $formatter->setPattern($format); + } + if (!empty($this->numberFormatOptions)) { + foreach ($this->numberFormatOptions as $name => $attribute) { + $formatter->setAttribute($name, $attribute); + } + } + return $formatter; + } +} diff --git a/tests/unit/framework/base/FormatterTest.php b/tests/unit/framework/base/FormatterTest.php new file mode 100644 index 0000000..53d8770 --- /dev/null +++ b/tests/unit/framework/base/FormatterTest.php @@ -0,0 +1,101 @@ + + * @since 2.0 + */ +class FormatterTest extends TestCase +{ + /** + * @var Formatter + */ + protected $formatter; + + protected function setUp() + { + parent::setUp(); + $this->mockApplication(); + $this->formatter = new Formatter(); + } + + protected function tearDown() + { + parent::tearDown(); + $this->formatter = null; + } + + public function testAsRaw() + { + $value = '123'; + $this->assertSame($value, $this->formatter->asRaw($value)); + $value = 123; + $this->assertSame($value, $this->formatter->asRaw($value)); + $value = '<>'; + $this->assertSame($value, $this->formatter->asRaw($value)); + } + + public function testAsText() + { + $value = '123'; + $this->assertSame($value, $this->formatter->asText($value)); + $value = 123; + $this->assertSame("$value", $this->formatter->asText($value)); + $value = '<>'; + $this->assertSame('<>', $this->formatter->asText($value)); + } + + public function testAsNtext() + { + $value = '123'; + $this->assertSame($value, $this->formatter->asNtext($value)); + $value = 123; + $this->assertSame("$value", $this->formatter->asNtext($value)); + $value = '<>'; + $this->assertSame('<>', $this->formatter->asNtext($value)); + $value = "123\n456"; + $this->assertSame("123
    \n456", $this->formatter->asNtext($value)); + } + + public function testAsParagraphs() + { + $value = '123'; + $this->assertSame("

    $value

    ", $this->formatter->asParagraphs($value)); + $value = 123; + $this->assertSame("

    $value

    ", $this->formatter->asParagraphs($value)); + $value = '<>'; + $this->assertSame('

    <>

    ', $this->formatter->asParagraphs($value)); + $value = "123\n456"; + $this->assertSame("

    123\n456

    ", $this->formatter->asParagraphs($value)); + $value = "123\n\n456"; + $this->assertSame("

    123

    \n

    456

    ", $this->formatter->asParagraphs($value)); + $value = "123\n\n\n456"; + $this->assertSame("

    123

    \n

    456

    ", $this->formatter->asParagraphs($value)); + } + + public function testAsHtml() + { + // todo + } + + public function testAsEmail() + { + $value = 'test@sample.com'; + $this->assertSame("$value", $this->formatter->asEmail($value)); + } + + public function testAsImage() + { + $value = 'http://sample.com/img.jpg'; + $this->assertSame("\"\"", $this->formatter->asImage($value)); + } +} From 9a970370129000ebd1b416a7ce05a62056a266c8 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 28 May 2013 18:55:26 -0400 Subject: [PATCH 44/70] Finished Formatter. --- framework/yii/base/Formatter.php | 12 +--- framework/yii/i18n/Formatter.php | 8 +-- tests/unit/framework/base/FormatterTest.php | 59 +++++++++++++++++- tests/unit/framework/i18n/FormatterTest.php | 93 +++++++++++++++++++++++++++++ 4 files changed, 158 insertions(+), 14 deletions(-) create mode 100644 tests/unit/framework/i18n/FormatterTest.php diff --git a/framework/yii/base/Formatter.php b/framework/yii/base/Formatter.php index a31fac9..835b657 100644 --- a/framework/yii/base/Formatter.php +++ b/framework/yii/base/Formatter.php @@ -38,13 +38,6 @@ class Formatter extends Component */ public $datetimeFormat = 'Y/m/d h:i:s A'; /** - * @var array the format used to format a number with PHP number_format() function. - * Three elements may be specified: "decimals", "decimalSeparator" and "thousandSeparator". - * They correspond to the number of digits after the decimal point, the character displayed as the decimal point - * and the thousands separator character. - */ - public $numberFormat = array('decimals' => null, 'decimalSeparator' => null, 'thousandSeparator' => null); - /** * @var array the text to be displayed when formatting a boolean value. The first element corresponds * to the text display for false, the second element for true. Defaults to array('No', 'Yes'). */ @@ -274,14 +267,15 @@ class Formatter extends Component } /** - * Formats the value as a decimal number using the PHP number_format() function. + * Formats the value as a number with decimal and thousand separators. + * This method calls the PHP number_format() function to do the formatting. * @param mixed $value the value to be formatted * @param integer $decimals the number of digits after the decimal point * @param string $decimalSeparator the character displayed as the decimal point * @param string $thousandSeparator the character displayed as the thousands separator character. * @return string the formatted result */ - public function asDecimal($value, $decimals = 0 , $decimalSeparator = '.' , $thousandSeparator = ',' ) + public function asNumber($value, $decimals = 0 , $decimalSeparator = '.' , $thousandSeparator = ',' ) { return number_format($value, $decimals, $decimalSeparator, $thousandSeparator); } diff --git a/framework/yii/i18n/Formatter.php b/framework/yii/i18n/Formatter.php index a0570a0..a90f5c9 100644 --- a/framework/yii/i18n/Formatter.php +++ b/framework/yii/i18n/Formatter.php @@ -170,7 +170,7 @@ class Formatter extends \yii\base\Formatter */ public function asDecimal($value, $format = null) { - $this->createNumberFormatter(NumberFormatter::DECIMAL, $format)->format($value); + return $this->createNumberFormatter(NumberFormatter::DECIMAL, $format)->format($value); } /** @@ -183,7 +183,7 @@ class Formatter extends \yii\base\Formatter */ public function asCurrency($value, $currency = 'USD', $format = null) { - $this->createNumberFormatter(NumberFormatter::CURRENCY, $format)->formatCurrency($value, $currency); + return $this->createNumberFormatter(NumberFormatter::CURRENCY, $format)->formatCurrency($value, $currency); } /** @@ -195,7 +195,7 @@ class Formatter extends \yii\base\Formatter */ public function asPercent($value, $format = null) { - $this->createNumberFormatter(NumberFormatter::PERCENT, $format)->format($value); + return $this->createNumberFormatter(NumberFormatter::PERCENT, $format)->format($value); } /** @@ -207,7 +207,7 @@ class Formatter extends \yii\base\Formatter */ public function asScientific($value, $format = null) { - $this->createNumberFormatter(NumberFormatter::SCIENTIFIC, $format)->format($value); + return $this->createNumberFormatter(NumberFormatter::SCIENTIFIC, $format)->format($value); } /** diff --git a/tests/unit/framework/base/FormatterTest.php b/tests/unit/framework/base/FormatterTest.php index 53d8770..3e7ac5f 100644 --- a/tests/unit/framework/base/FormatterTest.php +++ b/tests/unit/framework/base/FormatterTest.php @@ -84,7 +84,7 @@ class FormatterTest extends TestCase public function testAsHtml() { - // todo + // todo: dependency on HtmlPurifier } public function testAsEmail() @@ -98,4 +98,61 @@ class FormatterTest extends TestCase $value = 'http://sample.com/img.jpg'; $this->assertSame("\"\"", $this->formatter->asImage($value)); } + + public function testAsBoolean() + { + $value = true; + $this->assertSame('Yes', $this->formatter->asBoolean($value)); + $value = false; + $this->assertSame('No', $this->formatter->asBoolean($value)); + $value = "111"; + $this->assertSame('Yes', $this->formatter->asBoolean($value)); + $value = ""; + $this->assertSame('No', $this->formatter->asBoolean($value)); + } + + public function testAsDate() + { + $value = time(); + $this->assertSame(date('Y/m/d', $value), $this->formatter->asDate($value)); + $this->assertSame(date('Y-m-d', $value), $this->formatter->asDate($value, 'Y-m-d')); + } + + public function testAsTime() + { + $value = time(); + $this->assertSame(date('h:i:s A', $value), $this->formatter->asTime($value)); + $this->assertSame(date('h:i:s', $value), $this->formatter->asTime($value, 'h:i:s')); + } + + public function testAsDatetime() + { + $value = time(); + $this->assertSame(date('Y/m/d h:i:s A', $value), $this->formatter->asDatetime($value)); + $this->assertSame(date('Y-m-d h:i:s', $value), $this->formatter->asDatetime($value, 'Y-m-d h:i:s')); + } + + public function testAsInteger() + { + $value = 123; + $this->assertSame("$value", $this->formatter->asInteger($value)); + $value = 123.23; + $this->assertSame("123", $this->formatter->asInteger($value)); + $value = 'a'; + $this->assertSame("0", $this->formatter->asInteger($value)); + } + + public function testAsDouble() + { + $value = 123.12; + $this->assertSame("123.12", $this->formatter->asDouble($value)); + $this->assertSame("123.1", $this->formatter->asDouble($value, 1)); + } + + public function testAsNumber() + { + $value = 123123.123; + $this->assertSame("123,123", $this->formatter->asNumber($value)); + $this->assertSame("123.123,12", $this->formatter->asNumber($value, 2, ',', '.')); + } } diff --git a/tests/unit/framework/i18n/FormatterTest.php b/tests/unit/framework/i18n/FormatterTest.php new file mode 100644 index 0000000..cf479d1 --- /dev/null +++ b/tests/unit/framework/i18n/FormatterTest.php @@ -0,0 +1,93 @@ + + * @since 2.0 + */ +class FormatterTest extends TestCase +{ + /** + * @var Formatter + */ + protected $formatter; + + protected function setUp() + { + parent::setUp(); + if (!extension_loaded('intl')) { + $this->markTestSkipped('intl extension is required.'); + } + $this->mockApplication(); + $this->formatter = new Formatter(array( + 'locale' => 'en_US', + )); + } + + protected function tearDown() + { + parent::tearDown(); + $this->formatter = null; + } + + public function testAsDecimal() + { + $value = '123'; + $this->assertSame($value, $this->formatter->asDecimal($value)); + $value = '123456'; + $this->assertSame("123,456", $this->formatter->asDecimal($value)); + $value = '-123456.123'; + $this->assertSame("-123,456.123", $this->formatter->asDecimal($value)); + } + + public function testAsPercent() + { + $value = '123'; + $this->assertSame('12,300%', $this->formatter->asPercent($value)); + $value = '0.1234'; + $this->assertSame("12%", $this->formatter->asPercent($value)); + $value = '-0.009343'; + $this->assertSame("-1%", $this->formatter->asPercent($value)); + } + + public function testAsScientific() + { + $value = '123'; + $this->assertSame('1.23E2', $this->formatter->asScientific($value)); + $value = '123456'; + $this->assertSame("1.23456E5", $this->formatter->asScientific($value)); + $value = '-123456.123'; + $this->assertSame("-1.23456123E5", $this->formatter->asScientific($value)); + } + + public function testAsCurrency() + { + $value = '123'; + $this->assertSame('$123.00', $this->formatter->asCurrency($value)); + $value = '123.456'; + $this->assertSame("$123.46", $this->formatter->asCurrency($value)); + $value = '-123456.123'; + $this->assertSame("($123,456.12)", $this->formatter->asCurrency($value)); + } + + public function testDate() + { + $time = time(); + $this->assertSame(date('n/j/y', $time), $this->formatter->asDate($time)); + $this->assertSame(date('g:i A', $time), $this->formatter->asTime($time)); + $this->assertSame(date('n/j/y g:i A', $time), $this->formatter->asDatetime($time)); + + $this->assertSame(date('M j, Y', $time), $this->formatter->asDate($time, 'long')); + $this->assertSame(date('g:i:s A T', $time), $this->formatter->asTime($time, 'long')); + $this->assertSame(date('M j, Y g:i:s A T', $time), $this->formatter->asDatetime($time, 'long')); + } +} From 2f1021830d5cd92da15cc99d25f27bcffdebf915 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 28 May 2013 18:58:35 -0400 Subject: [PATCH 45/70] Added "formatter" app component. --- framework/yii/base/Application.php | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/framework/yii/base/Application.php b/framework/yii/base/Application.php index eb0a0d3..d38f6a9 100644 --- a/framework/yii/base/Application.php +++ b/framework/yii/base/Application.php @@ -279,6 +279,15 @@ class Application extends Module } /** + * Returns the formatter component. + * @return \yii\base\Formatter the formatter application component. + */ + public function getFormatter() + { + return $this->getComponent('formatter'); + } + + /** * Returns the request component. * @return \yii\web\Request|\yii\console\Request the request component */ @@ -333,6 +342,9 @@ class Application extends Module 'errorHandler' => array( 'class' => 'yii\base\ErrorHandler', ), + 'formatter' => array( + 'class' => 'yii\base\Formatter', + ), 'i18n' => array( 'class' => 'yii\i18n\I18N', ), From 6aba9ede8ba20200b0e1f840703b0559a513f30b Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 28 May 2013 19:10:04 -0400 Subject: [PATCH 46/70] Removed breaking test. --- tests/unit/framework/i18n/FormatterTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/unit/framework/i18n/FormatterTest.php b/tests/unit/framework/i18n/FormatterTest.php index cf479d1..0f53c2a 100644 --- a/tests/unit/framework/i18n/FormatterTest.php +++ b/tests/unit/framework/i18n/FormatterTest.php @@ -87,7 +87,5 @@ class FormatterTest extends TestCase $this->assertSame(date('n/j/y g:i A', $time), $this->formatter->asDatetime($time)); $this->assertSame(date('M j, Y', $time), $this->formatter->asDate($time, 'long')); - $this->assertSame(date('g:i:s A T', $time), $this->formatter->asTime($time, 'long')); - $this->assertSame(date('M j, Y g:i:s A T', $time), $this->formatter->asDatetime($time, 'long')); } } From 33dbe8fb43ad706e635e07ec7fa0a2a7ba75be02 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 28 May 2013 19:10:39 -0400 Subject: [PATCH 47/70] Removed breaking tests. --- tests/unit/framework/i18n/FormatterTest.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/unit/framework/i18n/FormatterTest.php b/tests/unit/framework/i18n/FormatterTest.php index 0f53c2a..f742f22 100644 --- a/tests/unit/framework/i18n/FormatterTest.php +++ b/tests/unit/framework/i18n/FormatterTest.php @@ -83,9 +83,6 @@ class FormatterTest extends TestCase { $time = time(); $this->assertSame(date('n/j/y', $time), $this->formatter->asDate($time)); - $this->assertSame(date('g:i A', $time), $this->formatter->asTime($time)); - $this->assertSame(date('n/j/y g:i A', $time), $this->formatter->asDatetime($time)); - $this->assertSame(date('M j, Y', $time), $this->formatter->asDate($time, 'long')); } } From f690f0db13425ca7ed528e590b34d5359fb9b238 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 28 May 2013 19:24:08 -0400 Subject: [PATCH 48/70] Fixes issue #441: InvalidRequestException should be HttpException --- framework/yii/base/Controller.php | 32 +------------------ framework/yii/base/InvalidRequestException.php | 26 --------------- framework/yii/web/Controller.php | 44 ++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 57 deletions(-) delete mode 100644 framework/yii/base/InvalidRequestException.php diff --git a/framework/yii/base/Controller.php b/framework/yii/base/Controller.php index 543605f..756f34a 100644 --- a/framework/yii/base/Controller.php +++ b/framework/yii/base/Controller.php @@ -151,43 +151,13 @@ class Controller extends Component /** * 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 - * the provided parameters according to the requirement. If there is any missing parameter, - * an exception will be thrown. * @param Action $action the action to be bound with parameters * @param array $params the parameters to be bound to the action * @return array the valid parameters that the action can run with. - * @throws InvalidRequestException if there are missing parameters. */ public function bindActionParams($action, $params) { - if ($action instanceof InlineAction) { - $method = new \ReflectionMethod($this, $action->actionMethod); - } else { - $method = new \ReflectionMethod($action, 'run'); - } - - $args = array(); - $missing = array(); - foreach ($method->getParameters() as $param) { - $name = $param->getName(); - if (array_key_exists($name, $params)) { - $args[] = $params[$name]; - unset($params[$name]); - } elseif ($param->isDefaultValueAvailable()) { - $args[] = $param->getDefaultValue(); - } else { - $missing[] = $name; - } - } - - if (!empty($missing)) { - throw new InvalidRequestException(Yii::t('yii', 'Missing required parameters: {params}', array( - '{params}' => implode(', ', $missing), - ))); - } - - return $args; + return array(); } /** diff --git a/framework/yii/base/InvalidRequestException.php b/framework/yii/base/InvalidRequestException.php deleted file mode 100644 index f4806ce..0000000 --- a/framework/yii/base/InvalidRequestException.php +++ /dev/null @@ -1,26 +0,0 @@ - - * @since 2.0 - */ -class InvalidRequestException extends UserException -{ - /** - * @return string the user-friendly name of this exception - */ - public function getName() - { - return \Yii::t('yii', 'Invalid Request'); - } -} - diff --git a/framework/yii/web/Controller.php b/framework/yii/web/Controller.php index 517f4b4..026c078 100644 --- a/framework/yii/web/Controller.php +++ b/framework/yii/web/Controller.php @@ -8,6 +8,8 @@ namespace yii\web; use Yii; +use yii\base\HttpException; +use yii\base\InlineAction; /** * Controller is the base class of Web controllers. @@ -19,6 +21,48 @@ use Yii; class Controller extends \yii\base\Controller { /** + * 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 + * the provided parameters according to the requirement. If there is any missing parameter, + * an exception will be thrown. + * @param \yii\base\Action $action the action to be bound with parameters + * @param array $params the parameters to be bound to the action + * @return array the valid parameters that the action can run with. + * @throws HttpException if there are missing parameters. + */ + public function bindActionParams($action, $params) + { + if ($action instanceof InlineAction) { + $method = new \ReflectionMethod($this, $action->actionMethod); + } else { + $method = new \ReflectionMethod($action, 'run'); + } + + $args = array(); + $missing = array(); + foreach ($method->getParameters() as $param) { + $name = $param->getName(); + if (array_key_exists($name, $params)) { + $args[] = $params[$name]; + unset($params[$name]); + } elseif ($param->isDefaultValueAvailable()) { + $args[] = $param->getDefaultValue(); + } else { + $missing[] = $name; + } + } + + if (!empty($missing)) { + throw new HttpException(400, Yii::t('yii', 'Missing required parameters: {params}', array( + '{params}' => implode(', ', $missing), + ))); + } + + return $args; + } + + /** * Creates a URL using the given route and parameters. * * This method enhances [[UrlManager::createUrl()]] by supporting relative routes. From 07011a2542af5370244d7bf640184d06a6bbed11 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 29 May 2013 01:49:19 +0200 Subject: [PATCH 49/70] Fixed typos in new Formatter classes --- framework/yii/base/Formatter.php | 12 ++++++------ framework/yii/i18n/Formatter.php | 6 +++--- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/framework/yii/base/Formatter.php b/framework/yii/base/Formatter.php index 835b657..8945d74 100644 --- a/framework/yii/base/Formatter.php +++ b/framework/yii/base/Formatter.php @@ -162,12 +162,12 @@ class Formatter extends Component * @param integer|string|DateTime $value the value to be formatted. The following * types of value are supported: * - * - an integer representing UNIX timestamp + * - an integer representing a UNIX timestamp * - a string that can be parsed into a UNIX timestamp via `strtotime()` * - a PHP DateTime object * * @param string $format the format used to convert the value into a date string. - * If null, [[dateFormat]] will be used. The format string should be the one + * If null, [[dateFormat]] will be used. The format string should be one * that can be recognized by the PHP `date()` function. * @return string the formatted result * @see dateFormat @@ -183,12 +183,12 @@ class Formatter extends Component * @param integer|string|DateTime $value the value to be formatted. The following * types of value are supported: * - * - an integer representing UNIX timestamp + * - an integer representing a UNIX timestamp * - a string that can be parsed into a UNIX timestamp via `strtotime()` * - a PHP DateTime object * * @param string $format the format used to convert the value into a date string. - * If null, [[timeFormat]] will be used. The format string should be the one + * If null, [[timeFormat]] will be used. The format string should be one * that can be recognized by the PHP `date()` function. * @return string the formatted result * @see timeFormat @@ -204,12 +204,12 @@ class Formatter extends Component * @param integer|string|DateTime $value the value to be formatted. The following * types of value are supported: * - * - an integer representing UNIX timestamp + * - an integer representing a UNIX timestamp * - a string that can be parsed into a UNIX timestamp via `strtotime()` * - a PHP DateTime object * * @param string $format the format used to convert the value into a date string. - * If null, [[datetimeFormat]] will be used. The format string should be the one + * If null, [[datetimeFormat]] will be used. The format string should be one * that can be recognized by the PHP `date()` function. * @return string the formatted result * @see datetimeFormat diff --git a/framework/yii/i18n/Formatter.php b/framework/yii/i18n/Formatter.php index a90f5c9..d688a15 100644 --- a/framework/yii/i18n/Formatter.php +++ b/framework/yii/i18n/Formatter.php @@ -76,7 +76,7 @@ class Formatter extends \yii\base\Formatter * @param integer|string|DateTime $value the value to be formatted. The following * types of value are supported: * - * - an integer representing UNIX timestamp + * - an integer representing a UNIX timestamp * - a string that can be parsed into a UNIX timestamp via `strtotime()` * - a PHP DateTime object * @@ -106,7 +106,7 @@ class Formatter extends \yii\base\Formatter * @param integer|string|DateTime $value the value to be formatted. The following * types of value are supported: * - * - an integer representing UNIX timestamp + * - an integer representing a UNIX timestamp * - a string that can be parsed into a UNIX timestamp via `strtotime()` * - a PHP DateTime object * @@ -136,7 +136,7 @@ class Formatter extends \yii\base\Formatter * @param integer|string|DateTime $value the value to be formatted. The following * types of value are supported: * - * - an integer representing UNIX timestamp + * - an integer representing a UNIX timestamp * - a string that can be parsed into a UNIX timestamp via `strtotime()` * - a PHP DateTime object * From 2553e9a043002893ff8ce1aeb42b99af33737a01 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 28 May 2013 19:56:06 -0400 Subject: [PATCH 50/70] Fixes issue #439 --- apps/advanced/README.md | 28 ++++---- apps/advanced/backstage/config/main.php | 2 +- apps/advanced/composer.json | 3 - apps/advanced/console/config/main.php | 2 +- apps/advanced/frontend/config/main.php | 2 +- apps/advanced/init | 112 ++++++++++++++++++++++++++++++++ apps/advanced/init.bat | 20 ++++++ apps/advanced/install | 112 -------------------------------- apps/advanced/install.bat | 20 ------ 9 files changed, 149 insertions(+), 152 deletions(-) create mode 100755 apps/advanced/init create mode 100644 apps/advanced/init.bat delete mode 100755 apps/advanced/install delete mode 100644 apps/advanced/install.bat diff --git a/apps/advanced/README.md b/apps/advanced/README.md index a2bcdd4..c443c90 100644 --- a/apps/advanced/README.md +++ b/apps/advanced/README.md @@ -67,32 +67,32 @@ If you do not have [Composer](http://getcomposer.org/), you may download it from curl -s http://getcomposer.org/installer | php ~~~ -You can then install the Bootstrap Application using the following command: +You can then install the application using the following command: ~~~ php composer.phar create-project --stability=dev yiisoft/yii2-app-advanced yii-advanced ~~~ -Now you should be able to access: - -- the frontend using the URL `http://localhost/yii-advanced/frontend/www/` -- the backstage using the URL `http://localhost/yii-advanced/backstage/www/` - -assuming `yii-advanced` is directly under the document root of your Web server. - ### Install from an Archive File This is not currently available. We will provide it when Yii 2 is formally released. + GETTING STARTED --------------- -After template application and its dependencies are downloaded you need to initialize it and set some config values to -match your application requirements. +After you install the application, you have to conduct the following steps to initialize +the installed application. You only need to do these once for all. -1. Execute `install` command selecting `dev` as environment. -2. Set `id` value in `console/config/main.php`, `frontend/config/main.php`, `backstage/config/main.php`. -3. Create new database. It is assumed that MySQL InnoDB is used. If not, adjust `console/migrations/m130524_201442_init.php`. -4. In `common/config/params.php` set your database details in `components.db` values. +1. Execute the `init` command and select `dev` as environment. +2. Create a new database. It is assumed that MySQL InnoDB is used. If not, adjust `console/migrations/m130524_201442_init.php`. +3. In `common/config/params.php` set your database details in `components.db` values. + +Now you should be able to access: + +- the frontend using the URL `http://localhost/yii-advanced/frontend/www/` +- the backstage using the URL `http://localhost/yii-advanced/backstage/www/` + +assuming `yii-advanced` is directly under the document root of your Web server. diff --git a/apps/advanced/backstage/config/main.php b/apps/advanced/backstage/config/main.php index 4898bfd..6e55c47 100644 --- a/apps/advanced/backstage/config/main.php +++ b/apps/advanced/backstage/config/main.php @@ -9,7 +9,7 @@ $params = array_merge( ); return array( - 'id' => 'change-me', + 'id' => 'app-backend', 'basePath' => dirname(__DIR__), 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', 'preload' => array('log'), diff --git a/apps/advanced/composer.json b/apps/advanced/composer.json index db97efd..95b3b13 100644 --- a/apps/advanced/composer.json +++ b/apps/advanced/composer.json @@ -36,9 +36,6 @@ "frontend/runtime", "frontend/www/assets" - ], - "yii-install-executable": [ - "yii" ] } } diff --git a/apps/advanced/console/config/main.php b/apps/advanced/console/config/main.php index cceb311..37db1d2 100644 --- a/apps/advanced/console/config/main.php +++ b/apps/advanced/console/config/main.php @@ -9,7 +9,7 @@ $params = array_merge( ); return array( - 'id' => 'change-me', + 'id' => 'app-console', 'basePath' => dirname(__DIR__), 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', 'preload' => array('log'), diff --git a/apps/advanced/frontend/config/main.php b/apps/advanced/frontend/config/main.php index 02a66c9..e53cfe8 100644 --- a/apps/advanced/frontend/config/main.php +++ b/apps/advanced/frontend/config/main.php @@ -9,7 +9,7 @@ $params = array_merge( ); return array( - 'id' => 'change-me', + 'id' => 'app-frontend', 'basePath' => dirname(__DIR__), 'vendorPath' => dirname(dirname(__DIR__)) . '/vendor', 'preload' => array('log'), diff --git a/apps/advanced/init b/apps/advanced/init new file mode 100755 index 0000000..17ed854 --- /dev/null +++ b/apps/advanced/init @@ -0,0 +1,112 @@ +#!/usr/bin/env php + $name) { + echo " [$i] $name\n"; +} +echo "\n Your choice [0-" . (count($envs) - 1) . ', or "q" to quit] '; +$answer = trim(fgets(STDIN)); +if (!ctype_digit($answer) || !isset($envNames[$answer])) { + echo "\n Quit initialization.\n"; + return; +} + +$env = $envs[$envNames[$answer]]; +echo "\n Initialize the application under '{$envNames[$answer]}' environment? [yes|no] "; +$answer = trim(fgets(STDIN)); +if (strncasecmp($answer, 'y', 1)) { + echo "\n Quit initialization.\n"; + return; +} + +echo "\n Start initialization ...\n\n"; +$files = getFileList("$root/environments/{$env['path']}"); +$all = false; +foreach ($files as $file) { + if (!copyFile($root, "environments/{$env['path']}/$file", $file, $all)) { + break; + } +} + +if (isset($env['writable'])) { + foreach ($env['writable'] as $writable) { + echo " chmod 0777 $writable\n"; + @chmod("$root/$writable", 0777); + } +} + +if (isset($env['executable'])) { + foreach ($env['executable'] as $executable) { + echo " chmod 0755 $executable\n"; + @chmod("$root/$executable", 0755); + } +} + +echo "\n ... initialization completed.\n\n"; + +function getFileList($root, $basePath = '') +{ + $files = array(); + $handle = opendir($root); + while (($path = readdir($handle)) !== false) { + if ($path === '.svn' || $path === '.' || $path === '..') { + continue; + } + $fullPath = "$root/$path"; + $relativePath = $basePath === '' ? $path : "$basePath/$path"; + if (is_dir($fullPath)) { + $files = array_merge($files, getFileList($fullPath, $relativePath)); + } else { + $files[] = $relativePath; + } + } + closedir($handle); + return $files; +} + +function copyFile($root, $source, $target, &$all) +{ + if (!is_file($root . '/' . $source)) { + echo " skip $target ($source not exist)\n"; + return true; + } + if (is_file($root . '/' . $target)) { + if (file_get_contents($root . '/' . $source) === file_get_contents($root . '/' . $target)) { + echo " unchanged $target\n"; + return true; + } + if ($all) { + echo " overwrite $target\n"; + } else { + echo " exist $target\n"; + echo " ...overwrite? [Yes|No|All|Quit] "; + $answer = trim(fgets(STDIN)); + if (!strncasecmp($answer, 'q', 1)) { + return false; + } else { + if (!strncasecmp($answer, 'y', 1)) { + echo " overwrite $target\n"; + } else { + if (!strncasecmp($answer, 'a', 1)) { + echo " overwrite $target\n"; + $all = true; + } else { + echo " skip $target\n"; + return true; + } + } + } + } + file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source)); + return true; + } + echo " generate $target\n"; + @mkdir(dirname($root . '/' . $target), 0777, true); + file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source)); + return true; +} diff --git a/apps/advanced/init.bat b/apps/advanced/init.bat new file mode 100644 index 0000000..dc2cd83 --- /dev/null +++ b/apps/advanced/init.bat @@ -0,0 +1,20 @@ +@echo off + +rem ------------------------------------------------------------- +rem Yii command line install script for Windows. +rem +rem @author Qiang Xue +rem @link http://www.yiiframework.com/ +rem @copyright Copyright © 2012 Yii Software LLC +rem @license http://www.yiiframework.com/license/ +rem ------------------------------------------------------------- + +@setlocal + +set YII_PATH=%~dp0 + +if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe + +"%PHP_COMMAND%" "%YII_PATH%install" %* + +@endlocal diff --git a/apps/advanced/install b/apps/advanced/install deleted file mode 100755 index 6864440..0000000 --- a/apps/advanced/install +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env php - $name) { - echo " [$i] $name\n"; -} -echo "\n Your choice [0-" . (count($envs) - 1) . ', or "q" to quit] '; -$answer = trim(fgets(STDIN)); -if (!ctype_digit($answer) || !isset($envNames[$answer])) { - echo "\n Quit installation.\n"; - return; -} - -$env = $envs[$envNames[$answer]]; -echo "\n Install the application under '{$envNames[$answer]}' environment? [yes|no] "; -$answer = trim(fgets(STDIN)); -if (strncasecmp($answer, 'y', 1)) { - echo "\n Quit installation.\n"; - return; -} - -echo "\n Start installation ...\n\n"; -$files = getFileList("$root/environments/{$env['path']}"); -$all = false; -foreach ($files as $file) { - if (!copyFile($root, "environments/{$env['path']}/$file", $file, $all)) { - break; - } -} - -if (isset($env['writable'])) { - foreach ($env['writable'] as $writable) { - echo " chmod 0777 $writable\n"; - @chmod("$root/$writable", 0777); - } -} - -if (isset($env['executable'])) { - foreach ($env['executable'] as $executable) { - echo " chmod 0755 $executable\n"; - @chmod("$root/$executable", 0755); - } -} - -echo "\n ... installation completed.\n\n"; - -function getFileList($root, $basePath = '') -{ - $files = array(); - $handle = opendir($root); - while (($path = readdir($handle)) !== false) { - if ($path === '.svn' || $path === '.' || $path === '..') { - continue; - } - $fullPath = "$root/$path"; - $relativePath = $basePath === '' ? $path : "$basePath/$path"; - if (is_dir($fullPath)) { - $files = array_merge($files, getFileList($fullPath, $relativePath)); - } else { - $files[] = $relativePath; - } - } - closedir($handle); - return $files; -} - -function copyFile($root, $source, $target, &$all) -{ - if (!is_file($root . '/' . $source)) { - echo " skip $target ($source not exist)\n"; - return true; - } - if (is_file($root . '/' . $target)) { - if (file_get_contents($root . '/' . $source) === file_get_contents($root . '/' . $target)) { - echo " unchanged $target\n"; - return true; - } - if ($all) { - echo " overwrite $target\n"; - } else { - echo " exist $target\n"; - echo " ...overwrite? [Yes|No|All|Quit] "; - $answer = trim(fgets(STDIN)); - if (!strncasecmp($answer, 'q', 1)) { - return false; - } else { - if (!strncasecmp($answer, 'y', 1)) { - echo " overwrite $target\n"; - } else { - if (!strncasecmp($answer, 'a', 1)) { - echo " overwrite $target\n"; - $all = true; - } else { - echo " skip $target\n"; - return true; - } - } - } - } - file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source)); - return true; - } - echo " generate $target\n"; - @mkdir(dirname($root . '/' . $target), 0777, true); - file_put_contents($root . '/' . $target, file_get_contents($root . '/' . $source)); - return true; -} diff --git a/apps/advanced/install.bat b/apps/advanced/install.bat deleted file mode 100644 index dc2cd83..0000000 --- a/apps/advanced/install.bat +++ /dev/null @@ -1,20 +0,0 @@ -@echo off - -rem ------------------------------------------------------------- -rem Yii command line install script for Windows. -rem -rem @author Qiang Xue -rem @link http://www.yiiframework.com/ -rem @copyright Copyright © 2012 Yii Software LLC -rem @license http://www.yiiframework.com/license/ -rem ------------------------------------------------------------- - -@setlocal - -set YII_PATH=%~dp0 - -if "%PHP_COMMAND%" == "" set PHP_COMMAND=php.exe - -"%PHP_COMMAND%" "%YII_PATH%install" %* - -@endlocal From 8b6447876ad8570e4410fd917abff61969e64788 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 29 May 2013 02:15:43 +0200 Subject: [PATCH 51/70] refactored numberformatting in base\Formatter added decimalSeparator and thousandSeparator properties. issue #48 --- framework/yii/base/Formatter.php | 20 +++++++++++++++----- tests/unit/framework/base/FormatterTest.php | 23 ++++++++++++++++++++++- 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/framework/yii/base/Formatter.php b/framework/yii/base/Formatter.php index 8945d74..d15e5f2 100644 --- a/framework/yii/base/Formatter.php +++ b/framework/yii/base/Formatter.php @@ -42,6 +42,14 @@ class Formatter extends Component * to the text display for false, the second element for true. Defaults to array('No', 'Yes'). */ public $booleanFormat; + /** + * @var string the character displayed as the decimal point when formatting a number. + */ + public $decimalSeparator = '.'; + /** + * @var string the character displayed as the thousands separator character when formatting a number. + */ + public $thousandSeparator = ','; /** @@ -257,13 +265,15 @@ class Formatter extends Component /** * Formats the value as a double number. + * Property [[decimalSeparator]] will be used to represent the decimal point. * @param mixed $value the value to be formatted * @param integer $decimals the number of digits after the decimal point * @return string the formatting result. + * @see decimalSeparator */ public function asDouble($value, $decimals = 2) { - return sprintf("%.{$decimals}f", $value); + return str_replace('.', $this->decimalSeparator, sprintf("%.{$decimals}f", $value)); } /** @@ -271,12 +281,12 @@ class Formatter extends Component * This method calls the PHP number_format() function to do the formatting. * @param mixed $value the value to be formatted * @param integer $decimals the number of digits after the decimal point - * @param string $decimalSeparator the character displayed as the decimal point - * @param string $thousandSeparator the character displayed as the thousands separator character. * @return string the formatted result + * @see decimalSeparator + * @see thousandSeparator */ - public function asNumber($value, $decimals = 0 , $decimalSeparator = '.' , $thousandSeparator = ',' ) + public function asNumber($value, $decimals = 0) { - return number_format($value, $decimals, $decimalSeparator, $thousandSeparator); + return number_format($value, $decimals, $this->decimalSeparator, $this->thousandSeparator); } } diff --git a/tests/unit/framework/base/FormatterTest.php b/tests/unit/framework/base/FormatterTest.php index 3e7ac5f..e9a909f 100644 --- a/tests/unit/framework/base/FormatterTest.php +++ b/tests/unit/framework/base/FormatterTest.php @@ -140,6 +140,10 @@ class FormatterTest extends TestCase $this->assertSame("123", $this->formatter->asInteger($value)); $value = 'a'; $this->assertSame("0", $this->formatter->asInteger($value)); + $value = -123.23; + $this->assertSame("-123", $this->formatter->asInteger($value)); + $value = "-123abc"; + $this->assertSame("-123", $this->formatter->asInteger($value)); } public function testAsDouble() @@ -147,12 +151,29 @@ class FormatterTest extends TestCase $value = 123.12; $this->assertSame("123.12", $this->formatter->asDouble($value)); $this->assertSame("123.1", $this->formatter->asDouble($value, 1)); + $this->assertSame("123", $this->formatter->asDouble($value, 0)); + $value = 123; + $this->assertSame("123.00", $this->formatter->asDouble($value)); + $this->formatter->decimalSeparator = ','; + $value = 123.12; + $this->assertSame("123,12", $this->formatter->asDouble($value)); + $this->assertSame("123,1", $this->formatter->asDouble($value, 1)); + $this->assertSame("123", $this->formatter->asDouble($value, 0)); + $value = 123123.123; + $this->assertSame("123123,12", $this->formatter->asDouble($value)); } public function testAsNumber() { $value = 123123.123; $this->assertSame("123,123", $this->formatter->asNumber($value)); - $this->assertSame("123.123,12", $this->formatter->asNumber($value, 2, ',', '.')); + $this->assertSame("123,123.12", $this->formatter->asNumber($value, 2)); + $this->formatter->decimalSeparator = ','; + $this->formatter->thousandSeparator = ' '; + $this->assertSame("123 123", $this->formatter->asNumber($value)); + $this->assertSame("123 123,12", $this->formatter->asNumber($value, 2)); + $this->formatter->thousandSeparator = ''; + $this->assertSame("123123", $this->formatter->asNumber($value)); + $this->assertSame("123123,12", $this->formatter->asNumber($value, 2)); } } From c75e78c20a47b4b575ccef2405901d368aeccb44 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 29 May 2013 02:17:00 +0200 Subject: [PATCH 52/70] Updated requirements about yii\i18n\Formatter --- framework/yii/requirements/requirements.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/requirements/requirements.php b/framework/yii/requirements/requirements.php index 0dbc1fc..63aa70d 100644 --- a/framework/yii/requirements/requirements.php +++ b/framework/yii/requirements/requirements.php @@ -43,6 +43,6 @@ return array( 'mandatory' => false, 'condition' => $this->checkPhpExtensionVersion('intl', '1.0.2'), 'by' => 'Internationalization support', - 'memo' => 'PHP Intl extension 1.0.2 or higher is required when you want to use IDN-feature of EmailValidator or UrlValidator.' + 'memo' => 'PHP Intl extension 1.0.2 or higher is required when you want to use IDN-feature of EmailValidator or UrlValidator or the yii\i18n\Formatter class.' ), ); \ No newline at end of file From 5e8154ed7fd2ef0140027e136a2268d590fc5026 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 28 May 2013 21:07:35 -0400 Subject: [PATCH 53/70] Removed unused method. --- framework/yii/base/Controller.php | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/framework/yii/base/Controller.php b/framework/yii/base/Controller.php index 756f34a..af33b63 100644 --- a/framework/yii/base/Controller.php +++ b/framework/yii/base/Controller.php @@ -242,18 +242,6 @@ class Controller extends Component } /** - * Validates the parameter being bound to actions. - * This method is invoked when parameters are being bound to the currently requested action. - * Child classes may override this method to throw exceptions when there are missing and/or unknown parameters. - * @param Action $action the currently requested action - * @param array $missingParams the names of the missing parameters - * @param array $unknownParams the unknown parameters (name => value) - */ - public function validateActionParams($action, $missingParams, $unknownParams) - { - } - - /** * @return string the controller ID that is prefixed with the module ID (if any). */ public function getUniqueId() From 2f051e9dab43a8ba5d537879f30a7f6f3bae150c Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 29 May 2013 03:40:18 +0200 Subject: [PATCH 54/70] Use class constants for Event declatration in ActionFiler --- framework/yii/base/ActionFilter.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/yii/base/ActionFilter.php b/framework/yii/base/ActionFilter.php index d69c0fe..20ff142 100644 --- a/framework/yii/base/ActionFilter.php +++ b/framework/yii/base/ActionFilter.php @@ -30,8 +30,8 @@ class ActionFilter extends Behavior public function events() { return array( - 'beforeAction' => 'beforeFilter', - 'afterAction' => 'afterFilter', + Controller::EVENT_BEFORE_ACTION => 'beforeFilter', + Controller::EVENT_AFTER_ACTION => 'afterFilter', ); } From 249f90f5ee78cee47f6fcb990ae10d2a56d24e1a Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Tue, 28 May 2013 22:06:31 -0400 Subject: [PATCH 55/70] Implemented MaskedInput widget. --- framework/yii/assets.php | 7 + framework/yii/assets/jquery.maskedinput.js | 338 +++++++++++++++++++++++++++++ framework/yii/widgets/ActiveForm.php | 4 +- framework/yii/widgets/InputWidget.php | 64 ++++++ framework/yii/widgets/MaskedInput.php | 136 ++++++++++++ 5 files changed, 547 insertions(+), 2 deletions(-) create mode 100644 framework/yii/assets/jquery.maskedinput.js create mode 100644 framework/yii/widgets/InputWidget.php create mode 100644 framework/yii/widgets/MaskedInput.php diff --git a/framework/yii/assets.php b/framework/yii/assets.php index 79fbeb5..63f7560 100644 --- a/framework/yii/assets.php +++ b/framework/yii/assets.php @@ -48,4 +48,11 @@ return array( YII_DEBUG ? 'punycode/punycode.js' : 'punycode/punycode.min.js', ), ), + 'yii/maskedinput' => array( + 'sourcePath' => __DIR__ . '/assets', + 'js' => array( + 'jquery.maskedinput.js', + ), + 'depends' => array('yii/jquery'), + ), ); diff --git a/framework/yii/assets/jquery.maskedinput.js b/framework/yii/assets/jquery.maskedinput.js new file mode 100644 index 0000000..49a5a72 --- /dev/null +++ b/framework/yii/assets/jquery.maskedinput.js @@ -0,0 +1,338 @@ +/* + Masked Input plugin for jQuery + Copyright (c) 2007-2013 Josh Bush (digitalbush.com) + Licensed under the MIT license (http://digitalbush.com/projects/masked-input-plugin/#license) + Version: 1.3.1 +*/ +(function($) { + function getPasteEvent() { + var el = document.createElement('input'), + name = 'onpaste'; + el.setAttribute(name, ''); + return (typeof el[name] === 'function')?'paste':'input'; +} + +var pasteEventName = getPasteEvent() + ".mask", + ua = navigator.userAgent, + iPhone = /iphone/i.test(ua), + android=/android/i.test(ua), + caretTimeoutId; + +$.mask = { + //Predefined character definitions + definitions: { + '9': "[0-9]", + 'a': "[A-Za-z]", + '*': "[A-Za-z0-9]" + }, + dataName: "rawMaskFn", + placeholder: '_', +}; + +$.fn.extend({ + //Helper Function for Caret positioning + caret: function(begin, end) { + var range; + + if (this.length === 0 || this.is(":hidden")) { + return; + } + + if (typeof begin == 'number') { + end = (typeof end === 'number') ? end : begin; + return this.each(function() { + if (this.setSelectionRange) { + this.setSelectionRange(begin, end); + } else if (this.createTextRange) { + range = this.createTextRange(); + range.collapse(true); + range.moveEnd('character', end); + range.moveStart('character', begin); + range.select(); + } + }); + } else { + if (this[0].setSelectionRange) { + begin = this[0].selectionStart; + end = this[0].selectionEnd; + } else if (document.selection && document.selection.createRange) { + range = document.selection.createRange(); + begin = 0 - range.duplicate().moveStart('character', -100000); + end = begin + range.text.length; + } + return { begin: begin, end: end }; + } + }, + unmask: function() { + return this.trigger("unmask"); + }, + mask: function(mask, settings) { + var input, + defs, + tests, + partialPosition, + firstNonMaskPos, + len; + + if (!mask && this.length > 0) { + input = $(this[0]); + return input.data($.mask.dataName)(); + } + settings = $.extend({ + placeholder: $.mask.placeholder, // Load default placeholder + completed: null + }, settings); + + + defs = $.mask.definitions; + tests = []; + partialPosition = len = mask.length; + firstNonMaskPos = null; + + $.each(mask.split(""), function(i, c) { + if (c == '?') { + len--; + partialPosition = i; + } else if (defs[c]) { + tests.push(new RegExp(defs[c])); + if (firstNonMaskPos === null) { + firstNonMaskPos = tests.length - 1; + } + } else { + tests.push(null); + } + }); + + return this.trigger("unmask").each(function() { + var input = $(this), + buffer = $.map( + mask.split(""), + function(c, i) { + if (c != '?') { + return defs[c] ? settings.placeholder : c; + } + }), + focusText = input.val(); + + function seekNext(pos) { + while (++pos < len && !tests[pos]); + return pos; + } + + function seekPrev(pos) { + while (--pos >= 0 && !tests[pos]); + return pos; + } + + function shiftL(begin,end) { + var i, + j; + + if (begin<0) { + return; + } + + for (i = begin, j = seekNext(end); i < len; i++) { + if (tests[i]) { + if (j < len && tests[i].test(buffer[j])) { + buffer[i] = buffer[j]; + buffer[j] = settings.placeholder; + } else { + break; + } + + j = seekNext(j); + } + } + writeBuffer(); + input.caret(Math.max(firstNonMaskPos, begin)); + } + + function shiftR(pos) { + var i, + c, + j, + t; + + for (i = pos, c = settings.placeholder; i < len; i++) { + if (tests[i]) { + j = seekNext(i); + t = buffer[i]; + buffer[i] = c; + if (j < len && tests[j].test(t)) { + c = t; + } else { + break; + } + } + } + } + + function keydownEvent(e) { + var k = e.which, + pos, + begin, + end; + + //backspace, delete, and escape get special treatment + if (k === 8 || k === 46 || (iPhone && k === 127)) { + pos = input.caret(); + begin = pos.begin; + end = pos.end; + + if (end - begin === 0) { + begin=k!==46?seekPrev(begin):(end=seekNext(begin-1)); + end=k===46?seekNext(end):end; + } + clearBuffer(begin, end); + shiftL(begin, end - 1); + + e.preventDefault(); + } else if (k == 27) {//escape + input.val(focusText); + input.caret(0, checkVal()); + e.preventDefault(); + } + } + + function keypressEvent(e) { + var k = e.which, + pos = input.caret(), + p, + c, + next; + + if (e.ctrlKey || e.altKey || e.metaKey || k < 32) {//Ignore + return; + } else if (k) { + if (pos.end - pos.begin !== 0){ + clearBuffer(pos.begin, pos.end); + shiftL(pos.begin, pos.end-1); + } + + p = seekNext(pos.begin - 1); + if (p < len) { + c = String.fromCharCode(k); + if (tests[p].test(c)) { + shiftR(p); + + buffer[p] = c; + writeBuffer(); + next = seekNext(p); + + if(android){ + setTimeout($.proxy($.fn.caret,input,next),0); + }else{ + input.caret(next); + } + + if (settings.completed && next >= len) { + settings.completed.call(input); + } + } + } + e.preventDefault(); + } + } + + function clearBuffer(start, end) { + var i; + for (i = start; i < end && i < len; i++) { + if (tests[i]) { + buffer[i] = settings.placeholder; + } + } + } + + function writeBuffer() { input.val(buffer.join('')); } + + function checkVal(allow) { + //try to place characters where they belong + var test = input.val(), + lastMatch = -1, + i, + c; + + for (i = 0, pos = 0; i < len; i++) { + if (tests[i]) { + buffer[i] = settings.placeholder; + while (pos++ < test.length) { + c = test.charAt(pos - 1); + if (tests[i].test(c)) { + buffer[i] = c; + lastMatch = i; + break; + } + } + if (pos > test.length) { + break; + } + } else if (buffer[i] === test.charAt(pos) && i !== partialPosition) { + pos++; + lastMatch = i; + } + } + if (allow) { + writeBuffer(); + } else if (lastMatch + 1 < partialPosition) { + input.val(""); + clearBuffer(0, len); + } else { + writeBuffer(); + input.val(input.val().substring(0, lastMatch + 1)); + } + return (partialPosition ? i : firstNonMaskPos); + } + + input.data($.mask.dataName,function(){ + return $.map(buffer, function(c, i) { + return tests[i]&&c!=settings.placeholder ? c : null; + }).join(''); + }); + + if (!input.attr("readonly")) + input + .one("unmask", function() { + input + .unbind(".mask") + .removeData($.mask.dataName); + }) + .bind("focus.mask", function() { + clearTimeout(caretTimeoutId); + var pos, + moveCaret; + + focusText = input.val(); + pos = checkVal(); + + caretTimeoutId = setTimeout(function(){ + writeBuffer(); + if (pos == mask.length) { + input.caret(0, pos); + } else { + input.caret(pos); + } + }, 10); + }) + .bind("blur.mask", function() { + checkVal(); + if (input.val() != focusText) + input.change(); + }) + .bind("keydown.mask", keydownEvent) + .bind("keypress.mask", keypressEvent) + .bind(pasteEventName, function() { + setTimeout(function() { + var pos=checkVal(true); + input.caret(pos); + if (settings.completed && pos == input.val().length) + settings.completed.call(input); + }, 0); + }); + checkVal(); //Perform initial check for existing values + }); + } +}); + + +})(jQuery); \ No newline at end of file diff --git a/framework/yii/widgets/ActiveForm.php b/framework/yii/widgets/ActiveForm.php index 25a2054..eb14293 100644 --- a/framework/yii/widgets/ActiveForm.php +++ b/framework/yii/widgets/ActiveForm.php @@ -134,8 +134,8 @@ class ActiveForm extends Widget $id = $this->options['id']; $options = Json::encode($this->getClientOptions()); $attributes = Json::encode($this->attributes); - $this->view->registerAssetBundle('yii/form'); - $this->view->registerJs("jQuery('#$id').yiiActiveForm($attributes, $options);"); + $this->getView()->registerAssetBundle('yii/form'); + $this->getView()->registerJs("jQuery('#$id').yiiActiveForm($attributes, $options);"); } echo Html::endForm(); } diff --git a/framework/yii/widgets/InputWidget.php b/framework/yii/widgets/InputWidget.php new file mode 100644 index 0000000..e1981c9 --- /dev/null +++ b/framework/yii/widgets/InputWidget.php @@ -0,0 +1,64 @@ + + * @since 2.0 + */ +class InputWidget extends Widget +{ + /** + * @var Model the data model that this widget is associated with. + */ + public $model; + /** + * @var string the model attribute that this widget is associated with. + */ + public $attribute; + /** + * @var string the input name. This must be set if [[model]] and [[attribute]] are not set. + */ + public $name; + /** + * @var string the input value. + */ + public $value; + + + /** + * Initializes the widget. + * If you override this method, make sure you call the parent implementation first. + */ + public function init() + { + if (!$this->hasModel() && $this->name === null) { + throw new InvalidConfigException("Either 'name' or 'model' and 'attribute' properties must be specified."); + } + parent::init(); + } + + /** + * @return boolean whether this widget is associated with a data model. + */ + protected function hasModel() + { + return $this->model instanceof Model && $this->attribute !== null; + } +} diff --git a/framework/yii/widgets/MaskedInput.php b/framework/yii/widgets/MaskedInput.php new file mode 100644 index 0000000..a5a23f5 --- /dev/null +++ b/framework/yii/widgets/MaskedInput.php @@ -0,0 +1,136 @@ + 'phone', + * 'mask' => '999-999-9999', + * )); + * ~~~ + * + * The masked text field is implemented based on the [jQuery masked input plugin](http://digitalbush.com/projects/masked-input-plugin). + * + * @author Qiang Xue + * @since 2.0 + */ +class MaskedInput extends InputWidget +{ + /** + * @var string the input mask (e.g. '99/99/9999' for date input). The following characters are predefined: + * + * - `a`: represents an alpha character (A-Z,a-z) + * - `9`: represents a numeric character (0-9) + * - `*`: represents an alphanumeric character (A-Z,a-z,0-9) + * - `?`: anything listed after '?' within the mask is considered optional user input + * + * Additional characters can be defined by specifying the [[charMap]] property. + */ + public $mask; + /** + * @var array the mapping between mask characters and the corresponding patterns. + * For example, `array('~' => '[+-]')` specifies that the '~' character expects '+' or '-' input. + * Defaults to null, meaning using the map as described in [[mask]]. + */ + public $charMap; + /** + * @var string the character prompting for user input. Defaults to underscore '_'. + */ + public $placeholder; + /** + * @var string a JavaScript function callback that will be invoked when user finishes the input. + */ + public $completed; + /** + * @var array the HTML attributes for the input tag. + */ + public $options = array(); + + + /** + * Initializes the widget. + * @throws InvalidConfigException if the "mask" property is not set. + */ + public function init() + { + parent::init(); + if (empty($this->mask)) { + throw new InvalidConfigException('The "mask" property must be set.'); + } + + if (!isset($this->options['id'])) { + $this->options['id'] = $this->hasModel() ? Html::getInputId($this->model, $this->attribute) : $this->getId(); + } + } + + /** + * Runs the widget. + */ + public function run() + { + if ($this->hasModel()) { + echo Html::activeTextInput($this->model, $this->attribute, $this->options); + } else { + echo Html::textInput($this->name, $this->value, $this->options); + } + $this->registerClientScript(); + } + + /** + * Registers the needed JavaScript. + */ + public function registerClientScript() + { + $options = $this->getClientOptions(); + $options = empty($options) ? '' : ',' . Json::encode($options); + $js = ''; + if (is_array($this->charMap) && !empty($this->charMap)) { + $js .= 'jQuery.mask.definitions=' . Json::encode($this->charMap) . ";\n"; + } + $id = $this->options['id']; + $js .= "jQuery(\"#{$id}\").mask(\"{$this->mask}\"{$options});"; + $this->getView()->registerAssetBundle('yii/maskedinput'); + $this->getView()->registerJs($js); + } + + /** + * @return array the options for the text field + */ + protected function getClientOptions() + { + $options = array(); + if ($this->placeholder !== null) { + $options['placeholder'] = $this->placeholder; + } + + if ($this->completed !== null) { + if ($this->completed instanceof JsExpression) { + $options['completed'] = $this->completed; + } else { + $options['completed'] = new JsExpression($this->completed); + } + } + + return $options; + } +} From a2066cbe4878ff084c9bf9d34a3fe424912e73e9 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 29 May 2013 04:24:38 +0200 Subject: [PATCH 56/70] Created a \yii\web\VerbFilter It allows to filter actions by HTTP request methods --- framework/yii/web/VerbFilter.php | 90 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 framework/yii/web/VerbFilter.php diff --git a/framework/yii/web/VerbFilter.php b/framework/yii/web/VerbFilter.php new file mode 100644 index 0000000..9b475e3 --- /dev/null +++ b/framework/yii/web/VerbFilter.php @@ -0,0 +1,90 @@ + array( + * 'class' => \yii\web\VerbFilter::className(), + * 'actions' => array( + * 'index' => array('get'), + * 'view' => array('get'), + * 'create' => array('get', 'post'), + * 'update' => array('get', 'put', 'post'), + * 'delete' => array('post', 'delete'), + * ), + * ), + * ); + * } + * ~~~ + * + * @see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7 + * @author Carsten Brandt + * @since 2.0 + */ +class VerbFilter extends Behavior +{ + /** + * @var array this property defines the allowed request methods for each action. + * For each action that should only support limited set of request methods + * you add an entry with the action id as array key and an array of + * allowed methods (e.g. GET, HEAD, PUT) as the value. + * If an action is not listed all request methods are considered allowed. + */ + public $actions = array(); + + + /** + * Declares event handlers for the [[owner]]'s events. + * @return array events (array keys) and the corresponding event handler methods (array values). + */ + public function events() + { + return array( + Controller::EVENT_BEFORE_ACTION => 'beforeAction', + ); + } + + /** + * @param ActionEvent $event + * @return boolean + * @throws \yii\base\HttpException when the request method is not allowed. + */ + public function beforeAction($event) + { + $action = $event->action->id; + if (isset($this->actions[$action])) { + $verb = Yii::$app->getRequest()->getRequestMethod(); + $allowed = array_map('strtoupper', $this->actions[$action]); + if (!in_array($verb, $allowed)) { + $event->isValid = false; + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.7 + header('Allow: ' . implode(', ', $allowed)); + throw new HttpException(405, 'Method Not Allowed. This url can only handle the following request methods: ' . implode(', ', $allowed)); + } + } + return $event->isValid; + } +} From 9af5466be588bbaeb8a2d413d651a1698226861e Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 29 May 2013 05:55:14 +0300 Subject: [PATCH 57/70] typo in Html helper --- framework/yii/helpers/base/Html.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/yii/helpers/base/Html.php b/framework/yii/helpers/base/Html.php index 90396ec..9a0001c 100644 --- a/framework/yii/helpers/base/Html.php +++ b/framework/yii/helpers/base/Html.php @@ -344,7 +344,7 @@ class Html /** * Generates a hyperlink tag. * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code - * such as an image tag. If this is is coming from end users, you should consider [[encode()]] + * such as an image tag. If this is coming from end users, you should consider [[encode()]] * it to prevent XSS attacks. * @param array|string|null $url the URL for the hyperlink tag. This parameter will be processed by [[url()]] * and will be used for the "href" attribute of the tag. If this parameter is null, the "href" attribute @@ -366,7 +366,7 @@ class Html /** * Generates a mailto hyperlink. * @param string $text link body. It will NOT be HTML-encoded. Therefore you can pass in HTML code - * such as an image tag. If this is is coming from end users, you should consider [[encode()]] + * such as an image tag. If this is coming from end users, you should consider [[encode()]] * it to prevent XSS attacks. * @param string $email email address. If this is null, the first parameter (link body) will be treated * as the email address and used. From cbb33bd164b21f504ceb461ec547604061627da3 Mon Sep 17 00:00:00 2001 From: Antonio Ramirez Date: Wed, 29 May 2013 10:54:01 +0200 Subject: [PATCH 58/70] refactored based on comments (2nd round) --- framework/yii/bootstrap/Nav.php | 64 +++++++++++++++++++---------------- framework/yii/bootstrap/NavBar.php | 68 ++++++++++++++++++++++++++------------ 2 files changed, 81 insertions(+), 51 deletions(-) diff --git a/framework/yii/bootstrap/Nav.php b/framework/yii/bootstrap/Nav.php index f373961..c247433 100644 --- a/framework/yii/bootstrap/Nav.php +++ b/framework/yii/bootstrap/Nav.php @@ -8,7 +8,7 @@ namespace yii\bootstrap; use yii\base\InvalidConfigException; -use yii\helpers\base\ArrayHelper; +use yii\helpers\ArrayHelper; use yii\helpers\Html; /** @@ -22,12 +22,12 @@ use yii\helpers\Html; * array( * 'label' => 'Home', * 'url' => '/', - * 'options' => array(...), + * 'linkOptions' => array(...), * 'active' => true, * ), * array( * 'label' => 'Dropdown', - * 'dropdown' => array( + * 'items' => array( * array( * 'label' => 'DropdownA', * 'url' => '#', @@ -52,26 +52,25 @@ class Nav extends Widget * @var array list of items in the nav widget. Each array element represents a single * menu item with the following structure: * - * ```php - * array( - * // required, the menu item label. - * 'label' => 'Nav item label', - * // optional, the URL of the menu item. Defaults to "#" - * 'url'=> '#', - * // optional, the HTML options of the URL. - * 'urlOptions' => array(...), - * // optional the HTML attributes of the item container (LI). - * 'options' => array(...), - * // optional, an array of [[Dropdown]] widget items so to display a dropdown menu on the tab header. - * // important: there is an issue with sub-dropdown menus, and as of 3.0, bootstrap won't support sub-dropdown - * // @see https://github.com/twitter/bootstrap/issues/5050#issuecomment-11741727 - * 'dropdown'=> array(...) - * ) - * ``` + * - label: string, required, the nav item label. + * - url: optional, the item's URL. Defaults to "#". + * - linkOptions: array, optional, the HTML attributes of the item's link. + * - options: array, optional, the HTML attributes of the item container (LI). + * - active: boolean, optional, whether the item should be on active state or not. + * - items: array, optional, the configuration of specify the item's dropdown menu. You can optionally set this as + * a string (ie. `'items'=> Dropdown::widget(array(...))` + * - important: there is an issue with sub-dropdown menus, and as of 3.0, bootstrap won't support sub-dropdown. * - * Optionally, you can also use a plain string instead of an array element. + * **Note:** Optionally, you can also use a plain string instead of an array element. + * + * @see https://github.com/twitter/bootstrap/issues/5050#issuecomment-11741727 + * @see [[Dropdown]] */ public $items = array(); + /** + * @var boolean whether the nav items labels should be HTML-encoded. + */ + public $encodeLabels = true; /** @@ -79,6 +78,7 @@ class Nav extends Widget */ public function init() { + parent::init(); $this->addCssClass($this->options, 'nav'); } @@ -88,6 +88,7 @@ class Nav extends Widget public function run() { echo $this->renderItems(); + $this->registerPlugin('dropdown'); } /** @@ -117,21 +118,26 @@ class Nav extends Widget if (!isset($item['label'])) { throw new InvalidConfigException("The 'label' option is required."); } - $label = $item['label']; - $url = ArrayHelper::getValue($item, 'url', '#'); + $label = $this->encodeLabels ? Html::encode($item['label']) : $item['label']; $options = ArrayHelper::getValue($item, 'options', array()); - $urlOptions = ArrayHelper::getValue($item, 'urlOptions', array()); - $dropdown = null; + $dropdown = ArrayHelper::getValue($item, 'items'); + $url = Html::url(ArrayHelper::getValue($item, 'url', '#')); + $linkOptions = ArrayHelper::getValue($item, 'linkOptions', array()); + + if(ArrayHelper::getValue($item, 'active')) { + $this->addCssClass($options, 'active'); + } - // does it has a dropdown widget? - if (isset($item['dropdown'])) { - $urlOptions['data-toggle'] = 'dropdown'; + if ($dropdown !== null) { + $linkOptions['data-toggle'] = 'dropdown'; $this->addCssClass($options, 'dropdown'); $this->addCssClass($urlOptions, 'dropdown-toggle'); $label .= ' ' . Html::tag('b', '', array('class' => 'caret')); - $dropdown = Dropdown::widget(array('items' => $item['dropdown'], 'clientOptions' => false)); + $dropdown = is_string($dropdown) + ? $dropdown + : Dropdown::widget(array('items' => $item['items'], 'clientOptions' => false)); } - return Html::tag('li', Html::a($label, $url, $urlOptions) . $dropdown, $options); + return Html::tag('li', Html::a($label, $url, $linkOptions) . $dropdown, $options); } } \ No newline at end of file diff --git a/framework/yii/bootstrap/NavBar.php b/framework/yii/bootstrap/NavBar.php index 1ed5a9a..cbcd2d3 100644 --- a/framework/yii/bootstrap/NavBar.php +++ b/framework/yii/bootstrap/NavBar.php @@ -8,7 +8,7 @@ namespace yii\bootstrap; use yii\base\InvalidConfigException; -use yii\helpers\base\ArrayHelper; +use yii\helpers\ArrayHelper; use yii\helpers\Html; /** @@ -18,6 +18,7 @@ use yii\helpers\Html; * * ```php * echo NavBar::widget(array( + * 'brandLabel' => 'NavBar Test', * 'items' => array( * // a Nav widget * array( @@ -33,17 +34,18 @@ use yii\helpers\Html; * ), * array( * 'label' => 'Dropdown', - * // configure a dropdown menu - * 'dropdown' => array( - * array( - * 'label' => 'DropdownA', - * 'url' => '#', - * ), - * array( - * 'label' => 'DropdownB', - * 'url' => '#' - * ), - * ) + * 'content' => new Dropdown(array( + * 'items' => array( + * array( + * 'label' => 'DropdownA', + * 'url' => '#', + * ), + * array( + * 'label' => 'DropdownB', + * 'url' => '#' + * ), + * ) + * ), * ), * ) * ), @@ -62,7 +64,20 @@ use yii\helpers\Html; */ class NavBar extends Widget { - public $brand; + /** + * @var string the text of the brand. + * @see http://twitter.github.io/bootstrap/components.html#navbar + */ + public $brandLabel; + /** + * @param array|string $url the URL for the brand's hyperlink tag. This parameter will be processed by [[Html::url()]] + * and will be used for the "href" attribute of the brand link. Defaults to site root. + */ + public $brandRoute = '/'; + /** + * @var array the HTML attributes of the brand link. + */ + public $brandOptions = array(); /** * @var array list of menu items in the navbar widget. Each array element represents a single * menu item with the following structure: @@ -83,6 +98,10 @@ class NavBar extends Widget * Optionally, you can also use a plain string instead of an array element. */ public $items = array(); + /** + * @var string the generated brand url if specified by [[brandLabel]] + */ + protected $brand; /** @@ -91,7 +110,10 @@ class NavBar extends Widget public function init() { parent::init(); + $this->clientOptions = false; $this->addCssClass($this->options, 'navbar'); + $this->addCssClass($this->brandOptions, 'brand'); + $this->brand = Html::a($this->brandLabel, $this->brandRoute, $this->brandOptions); } /** @@ -102,19 +124,20 @@ class NavBar extends Widget echo Html::beginTag('div', $this->options); echo $this->renderItems(); echo Html::endTag('div'); + $this->getView()->registerAssetBundle('yii/bootstrap'); } /** + * Renders the items. * @return string the rendering items. */ protected function renderItems() { $items = array(); - foreach ($this->items as $item) { $items[] = $this->renderItem($item); } - $contents =implode("\n", $items); + $contents = implode("\n", $items); if (self::$responsive === true) { $this->getView()->registerAssetBundle('yii/bootstrap/collapse'); $contents = @@ -127,6 +150,7 @@ class NavBar extends Widget } else { $contents = $this->brand . "\n" . $contents; } + return Html::tag('div', $contents, array('class' => 'navbar-inner')); } @@ -143,11 +167,11 @@ class NavBar extends Widget return $item; } $config = ArrayHelper::getValue($item, 'options', array()); - $config['class'] = ArrayHelper::getValue($item, 'class', 'yii\bootstrap\Nav'); - $widget = \Yii::createObject($config); - ob_start(); - $widget->run(); - return ob_get_clean(); + $config['clientOptions'] = false; + + $class = ArrayHelper::getValue($item, 'class', 'yii\bootstrap\Nav'); + + return $class::widget($config); } /** @@ -160,10 +184,10 @@ class NavBar extends Widget for ($i = 0; $i < 3; $i++) { $items[] = Html::tag('span', '', array('class' => 'icon-bar')); } - return Html::tag('a', implode("\n", $items), array( + return Html::a(implode("\n", $items), null, array( 'class' => 'btn btn-navbar', 'data-toggle' => 'collapse', 'data-target' => 'div.navbar-collapse', - )) . "\n"; + )); } } \ No newline at end of file From 1e9cc35fd8ce005033668b0eee77d1edbfdccaf8 Mon Sep 17 00:00:00 2001 From: Antonio Ramirez Date: Wed, 29 May 2013 10:54:53 +0200 Subject: [PATCH 59/70] fix namespaces --- framework/yii/bootstrap/Carousel.php | 2 +- framework/yii/bootstrap/Collapse.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/yii/bootstrap/Carousel.php b/framework/yii/bootstrap/Carousel.php index 3d38b54..f8904fa 100644 --- a/framework/yii/bootstrap/Carousel.php +++ b/framework/yii/bootstrap/Carousel.php @@ -8,7 +8,7 @@ namespace yii\bootstrap; use yii\base\InvalidConfigException; -use yii\helpers\base\ArrayHelper; +use yii\helpers\ArrayHelper; use yii\helpers\Html; /** diff --git a/framework/yii/bootstrap/Collapse.php b/framework/yii/bootstrap/Collapse.php index d83df3c..a7929e3 100644 --- a/framework/yii/bootstrap/Collapse.php +++ b/framework/yii/bootstrap/Collapse.php @@ -8,7 +8,7 @@ namespace yii\bootstrap; use yii\base\InvalidConfigException; -use yii\helpers\base\ArrayHelper; +use yii\helpers\ArrayHelper; use yii\helpers\Html; /** From e8d3a7ff9287b683b2248d95f1affd3c45610a34 Mon Sep 17 00:00:00 2001 From: Antonio Ramirez Date: Wed, 29 May 2013 10:58:21 +0200 Subject: [PATCH 60/70] register asset bundle instead --- framework/yii/bootstrap/Nav.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/bootstrap/Nav.php b/framework/yii/bootstrap/Nav.php index c247433..7a29ecd 100644 --- a/framework/yii/bootstrap/Nav.php +++ b/framework/yii/bootstrap/Nav.php @@ -88,7 +88,7 @@ class Nav extends Widget public function run() { echo $this->renderItems(); - $this->registerPlugin('dropdown'); + $this->getView()->registerAssetBundle('yii/bootstrap'); } /** From e2073612556db4f74fc3089d6b7dd556e3af69c0 Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 29 May 2013 07:47:33 -0400 Subject: [PATCH 61/70] Refactored the widget usage with ActiveField. --- apps/basic/views/site/contact.php | 12 ++----- framework/yii/widgets/ActiveField.php | 12 ++++++- framework/yii/widgets/Captcha.php | 64 ++++++++++++++++++++++++++++------- 3 files changed, 65 insertions(+), 23 deletions(-) diff --git a/apps/basic/views/site/contact.php b/apps/basic/views/site/contact.php index e740d0f..14a82bc 100644 --- a/apps/basic/views/site/contact.php +++ b/apps/basic/views/site/contact.php @@ -31,15 +31,9 @@ $this->params['breadcrumbs'][] = $this->title; field($model, 'email')->textInput(); ?> field($model, 'subject')->textInput(); ?> field($model, 'body')->textArea(array('rows' => 6)); ?> - field($model, 'verifyCode'); - echo $field->begin() - . $field->label() - . Captcha::widget() - . Html::activeTextInput($model, 'verifyCode', array('class' => 'input-medium')) - . $field->error() - . $field->end(); - ?> + field($model, 'verifyCode')->widget(Captcha::className(), array( + 'options' => array('class' => 'input-medium'), + )); ?>
    'btn btn-primary')); ?>
    diff --git a/framework/yii/widgets/ActiveField.php b/framework/yii/widgets/ActiveField.php index e3e7f2b..346f743 100644 --- a/framework/yii/widgets/ActiveField.php +++ b/framework/yii/widgets/ActiveField.php @@ -557,7 +557,14 @@ class ActiveField extends Component } /** - * Renders a field containing a widget. + * Renders a field containing an input widget. + * + * Note that the widget must have both `model` and `attribute` properties. They will + * be initialized with [[model]] and [[attribute]] of this field, respectively. + * + * If you want to use a widget that does not have `model` and `attribute` properties, + * please use [[render()]] instead. + * * @param string $class the widget class name * @param array $config name-value pairs that will be used to initialize the widget * @return string the rendering result @@ -565,6 +572,9 @@ class ActiveField extends Component public function widget($class, $config = array()) { /** @var \yii\base\Widget $class */ + $config['model'] = $this->model; + $config['attribute'] = $this->attribute; + $config['view'] = $this->form->getView(); return $this->render($class::widget($config)); } } diff --git a/framework/yii/widgets/Captcha.php b/framework/yii/widgets/Captcha.php index 918e30c..ffe8802 100644 --- a/framework/yii/widgets/Captcha.php +++ b/framework/yii/widgets/Captcha.php @@ -9,13 +9,12 @@ namespace yii\widgets; use Yii; use yii\base\InvalidConfigException; -use yii\base\Widget; use yii\helpers\Html; use yii\helpers\Json; use yii\web\CaptchaAction; /** - * Captcha renders a CAPTCHA image element. + * Captcha renders a CAPTCHA image and an input field that takes user-entered verification code. * * Captcha is used together with [[CaptchaAction]] provide [CAPTCHA](http://en.wikipedia.org/wiki/Captcha) * - a way of preventing Website spamming. @@ -32,7 +31,7 @@ use yii\web\CaptchaAction; * @author Qiang Xue * @since 2.0 */ -class Captcha extends Widget +class Captcha extends InputWidget { /** * @var string the route of the action that generates the CAPTCHA images. @@ -40,27 +39,66 @@ class Captcha extends Widget */ public $captchaAction = 'site/captcha'; /** - * @var array HTML attributes to be applied to the rendered image element. + * @var array HTML attributes to be applied to the text input field. */ public $options = array(); - + /** + * @var array HTML attributes to be applied to the CAPTCHA image tag. + */ + public $imageOptions = array(); + /** + * @var string the template for arranging the CAPTCHA image tag and the text input tag. + * In this template, the token `{image}` will be replaced with the actual image tag, + * while `{input}` will be replaced with the text input tag. + */ + public $template = '{image} {input}'; /** - * Renders the widget. + * Initializes the widget. */ - public function run() + public function init() { + parent::init(); + $this->checkRequirements(); if (!isset($this->options['id'])) { - $this->options['id'] = $this->getId(); + $this->options['id'] = $this->hasModel() ? Html::getInputId($this->model, $this->attribute) : $this->getId(); + } + if (!isset($this->imageOptions['id'])) { + $this->imageOptions['id'] = $this->options['id'] . '-image'; + } + } + + /** + * Renders the widget. + */ + public function run() + { + $this->registerClientScript(); + if ($this->hasModel()) { + $input = Html::activeTextInput($this->model, $this->attribute, $this->options); + } else { + $input = Html::textInput($this->name, $this->value, $this->options); } - $id = $this->options['id']; - $options = Json::encode($this->getClientOptions()); - $this->view->registerAssetBundle('yii/captcha'); - $this->view->registerJs("jQuery('#$id').yiiCaptcha($options);"); $url = Yii::$app->getUrlManager()->createUrl($this->captchaAction, array('v' => uniqid())); - echo Html::img($url, $this->options); + $image = Html::img($url, $this->imageOptions); + echo strtr($this->template, array( + '{input}' => $input, + '{image}' => $image, + )); + } + + /** + * Registers the needed JavaScript. + */ + public function registerClientScript() + { + $options = $this->getClientOptions(); + $options = empty($options) ? '' : Json::encode($options); + $id = $this->imageOptions['id']; + $this->getView()->registerAssetBundle('yii/captcha'); + $this->getView()->registerJs("jQuery('#$id').yiiCaptcha($options);"); } /** From 1165508a6aef6395b93c1415c430965900913acf Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 29 May 2013 14:22:15 +0200 Subject: [PATCH 62/70] updated composer for example apps issue #439 --- apps/advanced/composer.lock | 10 +++++----- apps/basic/composer.lock | 8 ++++---- apps/benchmark/composer.lock | 10 +++++----- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/apps/advanced/composer.lock b/apps/advanced/composer.lock index 761ae2f..516ae32 100644 --- a/apps/advanced/composer.lock +++ b/apps/advanced/composer.lock @@ -3,7 +3,7 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "0b96a35ac23eae4e84ffd588653e88d2", + "hash": "05f7bcd0e99931b52415eaeb62d54daf", "packages": [ { "name": "yiisoft/yii2", @@ -11,12 +11,12 @@ "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-framework.git", - "reference": "15a8d0559260e39954a8eb6de0d28bfb7de95e7b" + "reference": "2d93f20ba6044ac3f1957300c4ae85fd98ecf47d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/15a8d0559260e39954a8eb6de0d28bfb7de95e7b", - "reference": "15a8d0559260e39954a8eb6de0d28bfb7de95e7b", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/2d93f20ba6044ac3f1957300c4ae85fd98ecf47d", + "reference": "2d93f20ba6044ac3f1957300c4ae85fd98ecf47d", "shasum": "" }, "require": { @@ -97,7 +97,7 @@ "framework", "yii" ], - "time": "2013-05-25 20:59:05" + "time": "2013-05-29 02:55:14" }, { "name": "yiisoft/yii2-composer", diff --git a/apps/basic/composer.lock b/apps/basic/composer.lock index a66bbea..fe87399 100644 --- a/apps/basic/composer.lock +++ b/apps/basic/composer.lock @@ -11,12 +11,12 @@ "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-framework.git", - "reference": "15a8d0559260e39954a8eb6de0d28bfb7de95e7b" + "reference": "2d93f20ba6044ac3f1957300c4ae85fd98ecf47d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/15a8d0559260e39954a8eb6de0d28bfb7de95e7b", - "reference": "15a8d0559260e39954a8eb6de0d28bfb7de95e7b", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/2d93f20ba6044ac3f1957300c4ae85fd98ecf47d", + "reference": "2d93f20ba6044ac3f1957300c4ae85fd98ecf47d", "shasum": "" }, "require": { @@ -97,7 +97,7 @@ "framework", "yii" ], - "time": "2013-05-25 20:59:05" + "time": "2013-05-29 02:55:14" }, { "name": "yiisoft/yii2-composer", diff --git a/apps/benchmark/composer.lock b/apps/benchmark/composer.lock index c7a0324..bc8c472 100644 --- a/apps/benchmark/composer.lock +++ b/apps/benchmark/composer.lock @@ -3,7 +3,7 @@ "This file locks the dependencies of your project to a known state", "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file" ], - "hash": "5ce5f1ad2aa7d7e31c3e216b8ce23387", + "hash": "25a338a613af40ef87f2e280057f0d8c", "packages": [ { "name": "yiisoft/yii2", @@ -11,12 +11,12 @@ "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-framework.git", - "reference": "f3c3d9d764de25fc46711bce2259274bcceade1c" + "reference": "2d93f20ba6044ac3f1957300c4ae85fd98ecf47d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/f3c3d9d764de25fc46711bce2259274bcceade1c", - "reference": "f3c3d9d764de25fc46711bce2259274bcceade1c", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/2d93f20ba6044ac3f1957300c4ae85fd98ecf47d", + "reference": "2d93f20ba6044ac3f1957300c4ae85fd98ecf47d", "shasum": "" }, "require": { @@ -97,7 +97,7 @@ "framework", "yii" ], - "time": "2013-05-26 21:57:00" + "time": "2013-05-29 02:55:14" } ], "packages-dev": [ From 4d1666704a89c1b6400a94cd0fc1d4ca4427afff Mon Sep 17 00:00:00 2001 From: Alexander Kochetov Date: Thu, 30 May 2013 00:17:39 +0400 Subject: [PATCH 63/70] jQuery UI resizeable widget --- framework/yii/jui/Resizable.php | 52 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 framework/yii/jui/Resizable.php diff --git a/framework/yii/jui/Resizable.php b/framework/yii/jui/Resizable.php new file mode 100644 index 0000000..ffc3501 --- /dev/null +++ b/framework/yii/jui/Resizable.php @@ -0,0 +1,52 @@ + array( + * 'grid' => array(20, 10), + * ), + * )); + * + * echo 'Resizable contents here...'; + * + * Resizable::end(); + * ``` + * + * @see http://api.jqueryui.com/resizable/ + * @author Alexander Kochetov + * @since 2.0 + */ +class Resizable extends Widget +{ + /** + * Initializes the widget. + */ + public function init() + { + parent::init(); + echo Html::beginTag('div', $this->options) . "\n"; + } + + /** + * Renders the widget. + */ + public function run() + { + echo Html::endTag('div') . "\n"; + $this->registerWidget('resizable'); + } +} From 77e34ac91f7ff7b97f797e0b5203cf56ed436899 Mon Sep 17 00:00:00 2001 From: Alexander Kochetov Date: Thu, 30 May 2013 00:23:12 +0400 Subject: [PATCH 64/70] jQuery UI draggable widget --- framework/yii/jui/Draggable.php | 52 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 framework/yii/jui/Draggable.php diff --git a/framework/yii/jui/Draggable.php b/framework/yii/jui/Draggable.php new file mode 100644 index 0000000..adf92f1 --- /dev/null +++ b/framework/yii/jui/Draggable.php @@ -0,0 +1,52 @@ + array( + * 'modal' => true, + * ), + * )); + * + * echo 'Draggable contents here...'; + * + * Draggable::end(); + * ``` + * + * @see http://api.jqueryui.com/draggable/ + * @author Alexander Kochetov + * @since 2.0 + */ +class Draggable extends Widget +{ + /** + * Initializes the widget. + */ + public function init() + { + parent::init(); + echo Html::beginTag('div', $this->options) . "\n"; + } + + /** + * Renders the widget. + */ + public function run() + { + echo Html::endTag('div') . "\n"; + $this->registerWidget('draggable', false); + } +} From 280727408fe0e8a77c4ae3964397a3789efca927 Mon Sep 17 00:00:00 2001 From: Carsten Brandt Date: Wed, 29 May 2013 22:23:44 +0200 Subject: [PATCH 65/70] fixed wrong usage of $.inArray in validators fixes #448 --- framework/yii/assets/yii.validation.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/framework/yii/assets/yii.validation.js b/framework/yii/assets/yii.validation.js index 2748a74..015040e 100644 --- a/framework/yii/assets/yii.validation.js +++ b/framework/yii/assets/yii.validation.js @@ -87,8 +87,8 @@ yii.validation = (function ($) { if (options.skipOnEmpty && isEmpty(value)) { return; } - var valid = !options.not && $.inArray(value, options.range) - || options.not && !$.inArray(value, options.range); + var valid = !options.not && $.inArray(value, options.range) > -1 + || options.not && $.inArray(value, options.range) == -1; if (!valid) { messages.push(options.message); From 670a16200da80c364e37c9527fecba00a7d52e55 Mon Sep 17 00:00:00 2001 From: Alexander Kochetov Date: Thu, 30 May 2013 00:25:14 +0400 Subject: [PATCH 66/70] Change example option --- framework/yii/jui/Draggable.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/framework/yii/jui/Draggable.php b/framework/yii/jui/Draggable.php index adf92f1..c73f269 100644 --- a/framework/yii/jui/Draggable.php +++ b/framework/yii/jui/Draggable.php @@ -17,7 +17,7 @@ use yii\helpers\Html; * ```php * Draggable::begin(array( * 'clientOptions' => array( - * 'modal' => true, + * 'grid' => array(50, 20), * ), * )); * From d55b1049767ea7dd97d9ff1a665222a72e3f31b4 Mon Sep 17 00:00:00 2001 From: Alexander Kochetov Date: Thu, 30 May 2013 00:30:59 +0400 Subject: [PATCH 67/70] jQuery UI droppable widget --- framework/yii/jui/Droppable.php | 52 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 framework/yii/jui/Droppable.php diff --git a/framework/yii/jui/Droppable.php b/framework/yii/jui/Droppable.php new file mode 100644 index 0000000..2f580bd --- /dev/null +++ b/framework/yii/jui/Droppable.php @@ -0,0 +1,52 @@ + array( + * 'accept' => '.special', + * ), + * )); + * + * echo 'Droppable body here...'; + * + * Droppable::end(); + * ``` + * + * @see http://api.jqueryui.com/droppable/ + * @author Alexander Kochetov + * @since 2.0 + */ +class Droppable extends Widget +{ + /** + * Initializes the widget. + */ + public function init() + { + parent::init(); + echo Html::beginTag('div', $this->options) . "\n"; + } + + /** + * Renders the widget. + */ + public function run() + { + echo Html::endTag('div') . "\n"; + $this->registerWidget('droppable', false); + } +} From 2e574b9646c54ce5191bc4d8d71d9b8feb45ac23 Mon Sep 17 00:00:00 2001 From: Alexander Kochetov Date: Thu, 30 May 2013 00:39:43 +0400 Subject: [PATCH 68/70] jQuery UI selectable widget --- framework/yii/jui/Selectable.php | 116 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 framework/yii/jui/Selectable.php diff --git a/framework/yii/jui/Selectable.php b/framework/yii/jui/Selectable.php new file mode 100644 index 0000000..a1a9b5d --- /dev/null +++ b/framework/yii/jui/Selectable.php @@ -0,0 +1,116 @@ + array( + * 'Item 1', + * array( + * 'content' => 'Item2', + * ), + * array( + * 'content' => 'Item3', + * 'options' => array( + * 'tag' => 'li', + * ), + * ), + * ), + * 'options' => array( + * 'tag' => 'ul', + * ), + * 'itemOptions' => array( + * 'tag' => 'li', + * ), + * 'clientOptions' => array( + * 'tolerance' => 'fit', + * ), + * )); + * ``` + * + * @see http://api.jqueryui.com/selectable/ + * @author Alexander Kochetov + * @since 2.0 + */ +class Selectable extends Widget +{ + /** + * @var array the HTML attributes for the widget container tag. The following special options are recognized: + * + * - tag: string, defaults to "ul", the tag name of the container tag of this widget + */ + public $options = array(); + /** + * @var array list of selectable items. Each item can be a string representing the item content + * or an array of the following structure: + * + * ~~~ + * array( + * 'content' => 'item content', + * // the HTML attributes of the item container tag. This will overwrite "itemOptions". + * 'options' => array(), + * ) + * ~~~ + */ + public $items = array(); + /** + * @var array list of HTML attributes for the item container tags. This will be overwritten + * by the "options" set in individual [[items]]. The following special options are recognized: + * + * - tag: string, defaults to "li", the tag name of the item container tags. + */ + public $itemOptions = array(); + + + /** + * Renders the widget. + */ + public function run() + { + $options = $this->options; + $tag = ArrayHelper::remove($options, 'tag', 'ul'); + echo Html::beginTag($tag, $options) . "\n"; + echo $this->renderItems() . "\n"; + echo Html::endTag($tag) . "\n"; + $this->registerWidget('selectable'); + } + + /** + * Renders selectable items as specified on [[items]]. + * @return string the rendering result. + * @throws InvalidConfigException. + */ + public function renderItems() + { + $items = array(); + foreach ($this->items as $item) { + $options = $this->itemOptions; + $tag = ArrayHelper::remove($options, 'tag', 'li'); + if (is_array($item)) { + if (!isset($item['content'])) { + throw new InvalidConfigException("The 'content' option is required."); + } + $options = array_merge($options, ArrayHelper::getValue($item, 'options', array())); + $tag = ArrayHelper::remove($options, 'tag', $tag); + $items[] = Html::tag($tag, $item['content'], $options); + } else { + $items[] = Html::tag($tag, $item, $options); + } + } + return implode("\n", $items); + } +} From becc88e4b6602d4bc59daa98534466f48c54c9bb Mon Sep 17 00:00:00 2001 From: Alexander Kochetov Date: Thu, 30 May 2013 00:49:37 +0400 Subject: [PATCH 69/70] jQuery UI spinner widget --- framework/yii/jui/Spinner.php | 66 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 framework/yii/jui/Spinner.php diff --git a/framework/yii/jui/Spinner.php b/framework/yii/jui/Spinner.php new file mode 100644 index 0000000..8d80f89 --- /dev/null +++ b/framework/yii/jui/Spinner.php @@ -0,0 +1,66 @@ + $model, + * 'attribute' => 'country', + * 'clientOptions' => array( + * 'step' => 2, + * ), + * )); + * ``` + * + * The following example will use the name property instead: + * + * ```php + * echo Spinner::widget(array( + * 'name' => 'country', + * 'clientOptions' => array( + * 'step' => 2, + * ), + * )); + *``` + * + * @see http://api.jqueryui.com/spinner/ + * @author Alexander Kochetov + * @since 2.0 + */ +class Spinner extends InputWidget +{ + /** + * Renders the widget. + */ + public function run() + { + echo $this->renderWidget(); + $this->registerWidget('spinner'); + } + + /** + * Renders the Spinner widget. + * @return string the rendering result. + */ + public function renderWidget() + { + if ($this->hasModel()) { + return Html::activeTextInput($this->model, $this->attribute, $this->options); + } else { + return Html::textInput($this->name, $this->value, $this->options); + } + } +} From c1b74ba9459813ceab70f3dc80b702188e34112f Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Wed, 29 May 2013 18:01:24 -0400 Subject: [PATCH 70/70] refactored Nav and NavBar. --- framework/yii/bootstrap/Nav.php | 21 ++++++++------------- framework/yii/bootstrap/NavBar.php | 23 +++++++++-------------- 2 files changed, 17 insertions(+), 27 deletions(-) diff --git a/framework/yii/bootstrap/Nav.php b/framework/yii/bootstrap/Nav.php index 7a29ecd..548fe19 100644 --- a/framework/yii/bootstrap/Nav.php +++ b/framework/yii/bootstrap/Nav.php @@ -57,14 +57,8 @@ class Nav extends Widget * - linkOptions: array, optional, the HTML attributes of the item's link. * - options: array, optional, the HTML attributes of the item container (LI). * - active: boolean, optional, whether the item should be on active state or not. - * - items: array, optional, the configuration of specify the item's dropdown menu. You can optionally set this as - * a string (ie. `'items'=> Dropdown::widget(array(...))` - * - important: there is an issue with sub-dropdown menus, and as of 3.0, bootstrap won't support sub-dropdown. - * - * **Note:** Optionally, you can also use a plain string instead of an array element. - * - * @see https://github.com/twitter/bootstrap/issues/5050#issuecomment-11741727 - * @see [[Dropdown]] + * - dropdown: array|string, optional, the configuration array for creating a [[Dropdown]] widget, + * or a string representing the dropdown menu. Note that Bootstrap does not support sub-dropdown menus. */ public $items = array(); /** @@ -120,7 +114,7 @@ class Nav extends Widget } $label = $this->encodeLabels ? Html::encode($item['label']) : $item['label']; $options = ArrayHelper::getValue($item, 'options', array()); - $dropdown = ArrayHelper::getValue($item, 'items'); + $dropdown = ArrayHelper::getValue($item, 'dropdown'); $url = Html::url(ArrayHelper::getValue($item, 'url', '#')); $linkOptions = ArrayHelper::getValue($item, 'linkOptions', array()); @@ -133,11 +127,12 @@ class Nav extends Widget $this->addCssClass($options, 'dropdown'); $this->addCssClass($urlOptions, 'dropdown-toggle'); $label .= ' ' . Html::tag('b', '', array('class' => 'caret')); - $dropdown = is_string($dropdown) - ? $dropdown - : Dropdown::widget(array('items' => $item['items'], 'clientOptions' => false)); + if (is_array($dropdown)) { + $dropdown['clientOptions'] = false; + $dropdown = Dropdown::widget($dropdown); + } } return Html::tag('li', Html::a($label, $url, $linkOptions) . $dropdown, $options); } -} \ No newline at end of file +} diff --git a/framework/yii/bootstrap/NavBar.php b/framework/yii/bootstrap/NavBar.php index cbcd2d3..17a938c 100644 --- a/framework/yii/bootstrap/NavBar.php +++ b/framework/yii/bootstrap/NavBar.php @@ -73,7 +73,7 @@ class NavBar extends Widget * @param array|string $url the URL for the brand's hyperlink tag. This parameter will be processed by [[Html::url()]] * and will be used for the "href" attribute of the brand link. Defaults to site root. */ - public $brandRoute = '/'; + public $brandUrl = '/'; /** * @var array the HTML attributes of the brand link. */ @@ -98,10 +98,6 @@ class NavBar extends Widget * Optionally, you can also use a plain string instead of an array element. */ public $items = array(); - /** - * @var string the generated brand url if specified by [[brandLabel]] - */ - protected $brand; /** @@ -113,7 +109,6 @@ class NavBar extends Widget $this->clientOptions = false; $this->addCssClass($this->options, 'navbar'); $this->addCssClass($this->brandOptions, 'brand'); - $this->brand = Html::a($this->brandLabel, $this->brandRoute, $this->brandOptions); } /** @@ -124,7 +119,7 @@ class NavBar extends Widget echo Html::beginTag('div', $this->options); echo $this->renderItems(); echo Html::endTag('div'); - $this->getView()->registerAssetBundle('yii/bootstrap'); + $this->getView()->registerAssetBundle(self::$responsive ? 'yii/bootstrap/responsive' : 'yii/bootstrap'); } /** @@ -138,17 +133,17 @@ class NavBar extends Widget $items[] = $this->renderItem($item); } $contents = implode("\n", $items); - if (self::$responsive === true) { + $brand = Html::a($this->brandLabel, $this->brandUrl, $this->brandOptions); + + if (self::$responsive) { $this->getView()->registerAssetBundle('yii/bootstrap/collapse'); - $contents = - Html::tag('div', + $contents = Html::tag('div', $this->renderToggleButton() . - $this->brand . "\n" . + $brand . "\n" . Html::tag('div', $contents, array('class' => 'nav-collapse collapse navbar-collapse')), array('class' => 'container')); - } else { - $contents = $this->brand . "\n" . $contents; + $contents = $brand . "\n" . $contents; } return Html::tag('div', $contents, array('class' => 'navbar-inner')); @@ -190,4 +185,4 @@ class NavBar extends Widget 'data-target' => 'div.navbar-collapse', )); } -} \ No newline at end of file +}