diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000000..90862ff0ee4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2005-2018, Cake Software Foundation, Inc. (https://cakefoundation.org) + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 624193e46c5..00000000000 --- a/LICENSE.txt +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -CakePHP(tm) : The Rapid Development PHP Framework (https://cakephp.org) -Copyright (c) 2005-2018, Cake Software Foundation, Inc. (https://cakefoundation.org) - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/README.md b/README.md index 5dc3663d57d..f452017be48 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@

- + Software License @@ -38,7 +38,7 @@ recommend using the [app skeleton](https://github.com/cakephp/app) as a starting point. For existing applications you can run the following: ``` bash -$ composer require cakephp/cakephp:"~3.5" +$ composer require cakephp/cakephp:"~3.6" ``` ## Running Tests @@ -69,7 +69,7 @@ tests for CakePHP by doing the following: * [Slack](https://cakesf.herokuapp.com/) - Join us on Slack. * [#cakephp](https://webchat.freenode.net/?channels=#cakephp) on irc.freenode.net - Come chat with us, we have cake. -* [Forum](http://discourse.cakephp.org/) - Official CakePHP forum. +* [Forum](https://discourse.cakephp.org/) - Official CakePHP forum. * [GitHub Issues](https://github.com/cakephp/cakephp/issues) - Got issues? Please tell us! * [Roadmaps](https://github.com/cakephp/cakephp/wiki#roadmaps) - Want to contribute? Get involved! diff --git a/VERSION.txt b/VERSION.txt index 98cc05d3bfd..10007b25c52 100644 --- a/VERSION.txt +++ b/VERSION.txt @@ -16,4 +16,4 @@ // @license https://opensource.org/licenses/mit-license.php MIT License // +--------------------------------------------------------------------------------------------+ // //////////////////////////////////////////////////////////////////////////////////////////////////// -3.6.0 +3.6.1 diff --git a/src/Cache/composer.json b/src/Cache/composer.json index 3e101a04f2d..23289aa1961 100644 --- a/src/Cache/composer.json +++ b/src/Cache/composer.json @@ -23,7 +23,7 @@ }, "require": { "php": ">=5.6.0", - "cakephp/core": "^3.0.0" + "cakephp/core": "^3.6.0" }, "autoload": { "psr-4": { diff --git a/src/Controller/Component/AuthComponent.php b/src/Controller/Component/AuthComponent.php index 108a2d833ce..19806db4d73 100644 --- a/src/Controller/Component/AuthComponent.php +++ b/src/Controller/Component/AuthComponent.php @@ -439,8 +439,8 @@ protected function _loginActionRedirectUrl() */ protected function _isLoginAction(Controller $controller) { - $url = $controller->request->getRequestTarget(); - $url = Router::normalize($url); + $uri = $controller->request->getUri(); + $url = Router::normalize($uri->getPath()); $loginAction = Router::normalize($this->_config['loginAction']); return $loginAction === $url; diff --git a/src/Controller/Component/SecurityComponent.php b/src/Controller/Component/SecurityComponent.php index 4ebd65f31fa..db83f249418 100644 --- a/src/Controller/Component/SecurityComponent.php +++ b/src/Controller/Component/SecurityComponent.php @@ -22,6 +22,7 @@ use Cake\Event\Event; use Cake\Http\Exception\BadRequestException; use Cake\Http\ServerRequest; +use Cake\Routing\Router; use Cake\Utility\Hash; use Cake\Utility\Security; @@ -378,14 +379,21 @@ protected function _validToken(Controller $controller) */ protected function _hashParts(Controller $controller) { - $fieldList = $this->_fieldsList($controller->request->getData()); - $unlocked = $this->_sortedUnlocked($controller->request->getData()); + $request = $controller->getRequest(); + + // Start the session to ensure we get the correct session id. + $session = $request->getSession(); + $session->start(); + + $data = $request->getData(); + $fieldList = $this->_fieldsList($data); + $unlocked = $this->_sortedUnlocked($data); return [ - $controller->request->getRequestTarget(), + Router::url($request->getRequestTarget()), serialize($fieldList), $unlocked, - session_id(), + $session->id() ]; } diff --git a/src/Controller/Controller.php b/src/Controller/Controller.php index ebc214bf91d..8b5b299f2df 100644 --- a/src/Controller/Controller.php +++ b/src/Controller/Controller.php @@ -346,7 +346,7 @@ public function __get($name) ]; if (isset($deprecated[$name])) { $method = $deprecated[$name]; - deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->%s instead.', $name, $method)); + deprecationWarning(sprintf('Controller::$%s is deprecated. Use $this->%s() instead.', $name, $method)); return $this->{$method}(); } diff --git a/src/Core/BasePlugin.php b/src/Core/BasePlugin.php index 6a20ff972c9..bb76304a807 100644 --- a/src/Core/BasePlugin.php +++ b/src/Core/BasePlugin.php @@ -224,7 +224,7 @@ protected function checkHook($hook) */ public function routes($routes) { - $path = $this->getConfigPath() . DS . 'routes.php'; + $path = $this->getConfigPath() . 'routes.php'; if (file_exists($path)) { require $path; } @@ -235,7 +235,7 @@ public function routes($routes) */ public function bootstrap(PluginApplicationInterface $app) { - $bootstrap = $this->getConfigPath() . DS . 'bootstrap.php'; + $bootstrap = $this->getConfigPath() . 'bootstrap.php'; if (file_exists($bootstrap)) { require $bootstrap; } diff --git a/src/Core/composer.json b/src/Core/composer.json index 3d49da99ec8..3a485859cb7 100644 --- a/src/Core/composer.json +++ b/src/Core/composer.json @@ -23,7 +23,7 @@ }, "require": { "php": ">=5.6.0", - "cakephp/utility": "^3.0.0" + "cakephp/utility": "^3.6.0" }, "suggest": { "cakephp/event": "To use PluginApplicationInterface or plugin applications." diff --git a/src/Core/functions.php b/src/Core/functions.php index 35bebd1079e..d73eb06bf75 100644 --- a/src/Core/functions.php +++ b/src/Core/functions.php @@ -295,7 +295,9 @@ function deprecationWarning($message, $stackFrame = 1) $frame += ['file' => '[internal]', 'line' => '??']; $message = sprintf( - '%s - %s, line: %s', + '%s - %s, line: %s' . "\n" . + ' You can disable deprecation warnings by setting `Error.errorLevel` to' . + ' `E_ALL & ~E_USER_DEPRECATED` in your config/app.php.', $message, $frame['file'], $frame['line'] diff --git a/src/Database/composer.json b/src/Database/composer.json index 37bc777ea74..3de6513568e 100644 --- a/src/Database/composer.json +++ b/src/Database/composer.json @@ -25,9 +25,9 @@ }, "require": { "php": ">=5.6.0", - "cakephp/cache": "^3.0.0", - "cakephp/core": "^3.0.0", - "cakephp/datasource": "^3.0.0" + "cakephp/cache": "^3.6.0", + "cakephp/core": "^3.6.0", + "cakephp/datasource": "^3.6.0" }, "suggest": { "cakephp/log": "Require this if you want to use the built-in query logger" diff --git a/src/Datasource/QueryTrait.php b/src/Datasource/QueryTrait.php index c0abf4410fe..c99e83ace04 100644 --- a/src/Datasource/QueryTrait.php +++ b/src/Datasource/QueryTrait.php @@ -17,7 +17,6 @@ use BadMethodCallException; use Cake\Collection\Iterator\MapReduce; use Cake\Datasource\Exception\RecordNotFoundException; -use Cake\Datasource\ResultSetDecorator; /** * Contains the characteristics for an object that is attached to a repository and @@ -380,8 +379,8 @@ public function getMapReducers() * Registers a new formatter callback function that is to be executed when trying * to fetch the results from the database. * - * Formatting callbacks will get a first parameter, a `ResultSetDecorator`, that - * can be traversed and modified at will. + * Formatting callbacks will get a first parameter, an object implementing + * `\Cake\Collection\CollectionInterface`, that can be traversed and modified at will. * * Callbacks are required to return an iterator object, which will be used as * the return value for this query's result. Formatter functions are applied diff --git a/src/Datasource/composer.json b/src/Datasource/composer.json index 8ff1b05810a..7846db255c9 100644 --- a/src/Datasource/composer.json +++ b/src/Datasource/composer.json @@ -25,7 +25,7 @@ }, "require": { "php": ">=5.6.0", - "cakephp/core": "^3.0.0" + "cakephp/core": "^3.6.0" }, "suggest": { "cakephp/utility": "If you decide to use EntityTrait.", diff --git a/src/Error/ErrorHandler.php b/src/Error/ErrorHandler.php index 7f035e23fd2..633b35f058d 100644 --- a/src/Error/ErrorHandler.php +++ b/src/Error/ErrorHandler.php @@ -106,8 +106,6 @@ public function __construct($options = []) * * Template method of BaseErrorHandler. * - * Only when debug > 2 will a formatted error be displayed. - * * @param array $error An array of error data. * @param bool $debug Whether or not the app is in debug mode. * @return void diff --git a/src/Form/composer.json b/src/Form/composer.json index ee3cddc50a3..5e1dfdc9226 100644 --- a/src/Form/composer.json +++ b/src/Form/composer.json @@ -22,8 +22,8 @@ }, "require": { "php": ">=5.6.0", - "cakephp/event": "^3.5.0", - "cakephp/validation": "^3.5.0" + "cakephp/event": "^3.6.0", + "cakephp/validation": "^3.6.0" }, "autoload": { "psr-4": { diff --git a/src/Http/ServerRequest.php b/src/Http/ServerRequest.php index 121e02687ae..cab76391b47 100644 --- a/src/Http/ServerRequest.php +++ b/src/Http/ServerRequest.php @@ -230,7 +230,7 @@ class ServerRequest implements ArrayAccess, ServerRequestInterface 'query' => ['get' => 'getQuery()', 'set' => 'withQueryParams()'], 'params' => ['get' => 'getParam()', 'set' => 'withParam()'], 'cookies' => ['get' => 'getCookie()', 'set' => 'withCookieParams()'], - 'url' => ['get' => 'getRequestTarget()', 'set' => 'withRequestTarget()'], + 'url' => ['get' => 'getPath()', 'set' => 'withRequestTarget()'], 'base' => ['get' => 'getAttribute("base")', 'set' => 'withAttribute("base")'], 'webroot' => ['get' => 'getAttribute("webroot")', 'set' => 'withAttribute("webroot")'], 'here' => ['get' => 'getRequestTarget()', 'set' => 'withRequestTarget()'], @@ -2344,6 +2344,23 @@ public function getRequestTarget() return $target; } + /** + * Get the path of current request. + * + * @return string + * @since 3.6.1 + */ + public function getPath() + { + if ($this->requestTarget === null) { + return $this->uri->getPath(); + } + + list($path) = explode('?', $this->requestTarget); + + return $path; + } + /** * Array access read implementation * diff --git a/src/Http/Session.php b/src/Http/Session.php index 3d2126d8f80..fb1891500df 100644 --- a/src/Http/Session.php +++ b/src/Http/Session.php @@ -248,13 +248,12 @@ public function __construct(array $config = []) */ public function engine($class = null, array $options = []) { - if ($class instanceof SessionHandlerInterface) { - return $this->_engine = $class; - } - if ($class === null) { return $this->_engine; } + if ($class instanceof SessionHandlerInterface) { + return $this->setEngine($class); + } $className = App::className($class, 'Http/Session'); if (!$className) { @@ -273,6 +272,18 @@ public function engine($class = null, array $options = []) 'The chosen SessionHandler does not implement SessionHandlerInterface, it cannot be used as an engine.' ); } + + return $this->setEngine($handler); + } + + /** + * Set the engine property and update the session handler in PHP. + * + * @param \SessionHandlerInterface $handler The handler to set + * @return \SessionHandlerInterface + */ + protected function setEngine(SessionHandlerInterface $handler) + { if (!headers_sent()) { session_set_save_handler($handler, false); } diff --git a/src/ORM/composer.json b/src/ORM/composer.json index f8e207290d6..1432893969f 100644 --- a/src/ORM/composer.json +++ b/src/ORM/composer.json @@ -24,10 +24,10 @@ }, "require": { "php": ">=5.6.0", - "cakephp/collection": "^3.0.0", + "cakephp/collection": "^3.6.0", "cakephp/core": "^3.6.0", - "cakephp/datasource": "^3.1.2", - "cakephp/database": "^3.1.4", + "cakephp/datasource": "^3.6.0", + "cakephp/database": "^3.6.0", "cakephp/event": "^3.6.0", "cakephp/utility": "^3.6.0", "cakephp/validation": "^3.6.0" diff --git a/src/Routing/Router.php b/src/Routing/Router.php index de6a95161c3..cab3db2f875 100644 --- a/src/Routing/Router.php +++ b/src/Routing/Router.php @@ -633,7 +633,7 @@ public static function url($url = null, $full = false) } if (empty($url)) { - $output = isset($here) ? $here : $base . '/'; + $output = $base . (isset($here) ? $here : '/'); if ($full) { $output = static::fullBaseUrl() . $output; } diff --git a/src/View/Helper/UrlHelper.php b/src/View/Helper/UrlHelper.php index 81c3a62c2b2..72628787a35 100644 --- a/src/View/Helper/UrlHelper.php +++ b/src/View/Helper/UrlHelper.php @@ -161,6 +161,10 @@ public function assetUrl($path, array $options = []) if (is_array($path)) { return $this->build($path, !empty($options['fullBase'])); } + // data URIs only require HTML escaping + if (preg_match('/^data:[a-z]+\/[a-z]+;/', $path)) { + return h($path); + } if (strpos($path, '://') !== false || preg_match('/^[a-z]+:/i', $path)) { return ltrim($this->build($path), '/'); } diff --git a/src/View/View.php b/src/View/View.php index 0f39d044c5b..d86ae3946fc 100644 --- a/src/View/View.php +++ b/src/View/View.php @@ -56,6 +56,7 @@ * `plugins/SuperHot/Template/Posts/index.ctp`. If a theme template * is not found for the current action the default app template file is used. * + * @property \Cake\View\Helper\BreadCrumbsHelper $BreadCrumbs * @property \Cake\View\Helper\FlashHelper $Flash * @property \Cake\View\Helper\FormHelper $Form * @property \Cake\View\Helper\HtmlHelper $Html diff --git a/tests/TestCase/Controller/Component/AuthComponentTest.php b/tests/TestCase/Controller/Component/AuthComponentTest.php index dd4fc855746..cd743177974 100644 --- a/tests/TestCase/Controller/Component/AuthComponentTest.php +++ b/tests/TestCase/Controller/Component/AuthComponentTest.php @@ -830,6 +830,36 @@ public function testNoLoginRedirectForAuthenticatedUser() $this->assertNull($this->Controller->testUrl); } + /** + * testNoLoginRedirectForAuthenticatedUser method + * + * @return void + * @triggers Controller.startup $this->Controller + */ + public function testStartupLoginActionIgnoreQueryString() + { + $request = new ServerRequest([ + 'params' => [ + 'plugin' => null, + 'controller' => 'auth_test', + 'action' => 'login' + ], + 'query' => ['redirect' => '/admin/articles'], + 'url' => '/auth_test/login?redirect=%2Fadmin%2Farticles', + 'session' => $this->Auth->session + ]); + $this->Controller->request = $request; + + $this->Auth->session->clear(); + $this->Auth->setConfig('authenticate', ['Form']); + $this->Auth->setConfig('authorize', false); + $this->Auth->setConfig('loginAction', ['controller' => 'auth_test', 'action' => 'login']); + + $event = new Event('Controller.startup', $this->Controller); + $return = $this->Auth->startup($event); + $this->assertNull($return); + } + /** * Default to loginRedirect, if set, on authError. * diff --git a/tests/TestCase/Controller/Component/SecurityComponentTest.php b/tests/TestCase/Controller/Component/SecurityComponentTest.php index d5ea4f523eb..2971f31806d 100644 --- a/tests/TestCase/Controller/Component/SecurityComponentTest.php +++ b/tests/TestCase/Controller/Component/SecurityComponentTest.php @@ -21,6 +21,7 @@ use Cake\Event\Event; use Cake\Http\ServerRequest; use Cake\Http\Session; +use Cake\Routing\Router; use Cake\TestSuite\TestCase; use Cake\Utility\Security; @@ -189,7 +190,7 @@ public function tearDown() unset($this->Controller); } - public function validatePost($expectedException = null, $expectedExceptionMessage = null) + public function validatePost($expectedException = 'SecurityException', $expectedExceptionMessage = null) { try { return $this->Controller->Security->validatePost($this->Controller); @@ -744,6 +745,37 @@ public function testValidatePostSimple() $this->assertTrue($result); } + /** + * test validatePost uses full URL + * + * @return void + * @triggers Controller.startup $this->Controller + */ + public function testValidatePostSubdirectory() + { + // set the base path. + $this->Controller->request = $this->Controller->request + ->withAttribute('base', 'subdir') + ->withAttributE('webroot', 'subdir/'); + Router::pushRequest($this->Controller->request); + + $event = new Event('Controller.startup', $this->Controller); + $this->Security->startup($event); + + // Differs from testValidatePostSimple because of base url + $fields = 'cc9b6af3f33147235ae8f8037b0a71399a2425f2%3A'; + $unlocked = ''; + $debug = ''; + + $this->Controller->request = $this->Controller->request->withParsedBody([ + 'Model' => ['username' => '', 'password' => ''], + '_Token' => compact('fields', 'unlocked', 'debug') + ]); + + $result = $this->validatePost(); + $this->assertTrue($result); + } + /** * testValidatePostComplex method * diff --git a/tests/TestCase/Filesystem/FileTest.php b/tests/TestCase/Filesystem/FileTest.php index 0045fa64756..743e2579fc3 100644 --- a/tests/TestCase/Filesystem/FileTest.php +++ b/tests/TestCase/Filesystem/FileTest.php @@ -4,7 +4,7 @@ * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) * * Licensed under The MIT License - * For full copyright and license information, please see the LICENSE.txt + * For full copyright and license information, please see the LICENSE * Redistributions of files must retain the above copyright notice * * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org) @@ -66,7 +66,7 @@ public function tearDown() */ public function testBasic() { - $file = CORE_PATH . DS . 'LICENSE.txt'; + $file = CORE_PATH . DS . 'LICENSE'; $this->File = new File($file, false); @@ -78,7 +78,6 @@ public function testBasic() $expecting = [ 'dirname' => dirname($file), 'basename' => basename($file), - 'extension' => 'txt', 'filename' => 'LICENSE', 'filesize' => filesize($file), 'mime' => 'text/plain' @@ -93,8 +92,7 @@ function_exists('mime_content_type') && $this->assertEquals($expecting, $result); $result = $this->File->ext(); - $expecting = 'txt'; - $this->assertEquals($expecting, $result); + $this->assertEquals('', $result); $result = $this->File->name(); $expecting = 'LICENSE'; diff --git a/tests/TestCase/Http/ServerRequestTest.php b/tests/TestCase/Http/ServerRequestTest.php index 51755d6b383..d33da3fd686 100644 --- a/tests/TestCase/Http/ServerRequestTest.php +++ b/tests/TestCase/Http/ServerRequestTest.php @@ -211,6 +211,23 @@ public function testUrlInPath() $this->assertEquals('/jump/http://cakephp.org', $request->getRequestTarget()); } + /** + * Test getPath(). + * + * @return void + */ + public function testGetPath() + { + $request = new ServerRequest(['url' => '']); + $this->assertSame('/', $request->getPath()); + + $request = new ServerRequest(['url' => 'some/path?one=something&two=else']); + $this->assertEquals('/some/path', $request->getPath()); + + $request = $request->withRequestTarget('/foo/bar?x=y'); + $this->assertEquals('/foo/bar', $request->getPath()); + } + /** * Test addParams() method * diff --git a/tests/TestCase/Mailer/EmailTest.php b/tests/TestCase/Mailer/EmailTest.php index c9111d29fba..cd54ea1f323 100644 --- a/tests/TestCase/Mailer/EmailTest.php +++ b/tests/TestCase/Mailer/EmailTest.php @@ -879,13 +879,13 @@ public function testAttachments() $this->Email->addAttachments([CORE_PATH . 'config' . DS . 'bootstrap.php']); $this->Email->addAttachments([ 'other.txt' => CORE_PATH . 'config' . DS . 'bootstrap.php', - 'license' => CORE_PATH . 'LICENSE.txt' + 'license' => CORE_PATH . 'LICENSE' ]); $expected = [ 'basics.php' => ['file' => CAKE . 'basics.php', 'mimetype' => 'text/plain'], 'bootstrap.php' => ['file' => CORE_PATH . 'config' . DS . 'bootstrap.php', 'mimetype' => 'text/x-php'], 'other.txt' => ['file' => CORE_PATH . 'config' . DS . 'bootstrap.php', 'mimetype' => 'text/x-php'], - 'license' => ['file' => CORE_PATH . 'LICENSE.txt', 'mimetype' => 'text/plain'] + 'license' => ['file' => CORE_PATH . 'LICENSE', 'mimetype' => 'text/plain'] ]; $this->assertSame($expected, $this->Email->getAttachments()); $this->expectException(\InvalidArgumentException::class); diff --git a/tests/TestCase/Routing/RouterTest.php b/tests/TestCase/Routing/RouterTest.php index d4590d0fe14..a01b06b6287 100644 --- a/tests/TestCase/Routing/RouterTest.php +++ b/tests/TestCase/Routing/RouterTest.php @@ -61,7 +61,7 @@ public function tearDown() * * @return void */ - public function testbaseUrl() + public function testBaseUrl() { $this->assertRegExp('/^http(s)?:\/\//', Router::url('/', true)); $this->assertRegExp('/^http(s)?:\/\//', Router::url(null, true)); @@ -73,7 +73,7 @@ public function testbaseUrl() * * @return void */ - public function testfullBaseURL() + public function testFullBaseURL() { Router::fullBaseUrl('http://example.com'); $this->assertEquals('http://example.com/', Router::url('/', true)); @@ -571,14 +571,17 @@ public function testUrlGenerationWithBasePath() 'plugin' => null, 'controller' => 'subscribe', ], - 'url' => '/magazine/', + 'url' => '/subscribe', 'base' => '/magazine', 'webroot' => '/magazine/' ]); Router::pushRequest($request); $result = Router::url(); - $this->assertEquals('/magazine/', $result); + $this->assertEquals('/magazine/subscribe', $result); + + $result = Router::url([]); + $this->assertEquals('/magazine/subscribe', $result); $result = Router::url('/'); $this->assertEquals('/magazine/', $result); @@ -933,7 +936,7 @@ public function testUrlGenerationWithPrefix() ], 'webroot' => '/magazine/', 'base' => '/magazine', - 'url' => '/magazine/admin/subscriptions/edit/1', + 'url' => '/admin/subscriptions/edit/1', ]); Router::setRequestInfo($request); diff --git a/tests/TestCase/View/Helper/HtmlHelperTest.php b/tests/TestCase/View/Helper/HtmlHelperTest.php index b2d6ca28d94..b527d279f38 100644 --- a/tests/TestCase/View/Helper/HtmlHelperTest.php +++ b/tests/TestCase/View/Helper/HtmlHelperTest.php @@ -363,6 +363,31 @@ public function testImageTag() $this->assertHtml($expected, $result); } + /** + * Ensure that data URIs don't get base paths set. + * + * @return void + */ + public function testImageDataUriBaseDir() + { + $request = $this->Html->request + ->withAttribute('base', 'subdir') + ->withAttribute('webroot', 'subdir/'); + $this->Html->Url->request = $this->Html->request = $request; + Router::pushRequest($request); + + $data = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4' . + '/8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; + $result = $this->Html->image($data); + $expected = ['img' => ['src' => $data, 'alt' => '']]; + $this->assertHtml($expected, $result); + + $data = 'data:image/png;base64,'; + $result = $this->Html->image($data); + $expected = ['img' => ['src' => h($data), 'alt' => '']]; + $this->assertHtml($expected, $result); + } + /** * Test image() with query strings. * diff --git a/tests/TestCase/View/Helper/UrlHelperTest.php b/tests/TestCase/View/Helper/UrlHelperTest.php index 2a34614fbb5..97ddca66180 100644 --- a/tests/TestCase/View/Helper/UrlHelperTest.php +++ b/tests/TestCase/View/Helper/UrlHelperTest.php @@ -70,7 +70,7 @@ public function tearDown() * * @return void */ - public function testUrlConversion() + public function testBuildUrlConversion() { Router::connect('/:controller/:action/*'); @@ -104,10 +104,37 @@ public function testUrlConversion() $this->assertEquals('/posts/index?one=value&two=value&three=purple&page=1', $result); } + /** + * ensure that build factors in base paths. + * + * @return void + */ + public function testBuildBasePath() + { + Router::connect('/:controller/:action/*'); + $request = new ServerRequest([ + 'params' => [ + 'action' => 'index', + 'plugin' => null, + 'controller' => 'subscribe', + ], + 'url' => '/subscribe', + 'base' => '/magazine', + 'webroot' => '/magazine/' + ]); + Router::pushRequest($request); + + $this->assertEquals('/magazine/subscribe', $this->Helper->build()); + $this->assertEquals( + '/magazine/articles/add', + $this->Helper->build(['controller' => 'articles', 'action' => 'add']) + ); + } + /** * @return void */ - public function testUrlConversionUnescaped() + public function testBuildUrlConversionUnescaped() { $result = $this->Helper->build('/controller/action/1?one=1&two=2', ['escape' => false]); $this->assertEquals('/controller/action/1?one=1&two=2', $result); @@ -205,6 +232,30 @@ public function testAssetUrl() $this->assertEquals('dir/big%2Btall/image.jpg', $result); } + /** + * Test assetUrl and data uris + * + * @return void + */ + public function testAssetUrlDataUri() + { + $request = $this->Helper->request + ->withAttribute('base', 'subdir') + ->withAttribute('webroot', 'subdir/'); + + $this->Helper->request = $request; + Router::pushRequest($request); + + $data = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4' . + '/8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='; + $result = $this->Helper->assetUrl($data); + $this->assertSame($data, $result); + + $data = 'data:image/png;base64,'; + $result = $this->Helper->assetUrl($data); + $this->assertHtml(h($data), $result); + } + /** * Test assetUrl with no rewriting. *