Browse Source

Merge branch '2.6' into 3.0

Conflicts:
	app/Config/Schema/db_acl.php
	app/Config/Schema/db_acl.sql
	app/Console/cake.php
	lib/Cake/Console/Command/UpgradeShell.php
	lib/Cake/Console/ShellDispatcher.php
	lib/Cake/Console/Templates/skel/Console/cake.php
	lib/Cake/Core/App.php
	lib/Cake/Model/AclNode.php
	lib/Cake/Model/Datasource/Database/Mysql.php
	lib/Cake/Model/ModelValidator.php
	lib/Cake/Model/Validator/CakeValidationSet.php
	lib/Cake/Test/Case/Core/AppTest.php
	lib/Cake/TestSuite/Coverage/BaseCoverageReport.php
	lib/Cake/Utility/CakeTime.php
	lib/Cake/View/Helper/JqueryEngineHelper.php
	lib/Cake/View/Helper/MootoolsEngineHelper.php
	lib/Cake/View/Helper/PrototypeEngineHelper.php
	src/Cache/Engine/ApcEngine.php
	src/Configure/Engine/IniConfig.php
	src/Configure/Engine/PhpConfig.php
	src/Console/ConsoleOptionParser.php
	src/Network/Email/Email.php
	src/Network/Request.php
	src/View/View.php
	tests/TestCase/Network/RequestTest.php
	tests/TestCase/View/Helper/HtmlHelperTest.php
	tests/TestCase/View/ViewTest.php
ADmad 11 years ago
parent
commit
f2716c1913

+ 1 - 1
src/Auth/DigestAuthenticate.php

@@ -151,7 +151,7 @@ class DigestAuthenticate extends BasicAuthenticate {
 		}
 		$keys = $match = array();
 		$req = array('nonce' => 1, 'nc' => 1, 'cnonce' => 1, 'qop' => 1, 'username' => 1, 'uri' => 1, 'response' => 1);
-		preg_match_all('/(\w+)=([\'"]?)([a-zA-Z0-9@=.\/_-]+)\2/', $digest, $match, PREG_SET_ORDER);
+		preg_match_all('/(\w+)=([\'"]?)([a-zA-Z0-9\:\#\%@=.\/_-]+)\2/', $digest, $match, PREG_SET_ORDER);
 
 		foreach ($match as $i) {
 			$keys[$i[1]] = $i[3];

+ 12 - 7
src/Cache/Engine/ApcEngine.php

@@ -1,7 +1,5 @@
 <?php
 /**
- * APC storage engine for cache.
- *
  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  *
@@ -134,11 +132,18 @@ class ApcEngine extends CacheEngine {
 		if ($check) {
 			return true;
 		}
-		$info = apc_cache_info('user');
-		$cacheKeys = $info['cache_list'];
-		unset($info);
-		foreach ($cacheKeys as $key) {
-			if (strpos($key['info'], $this->_config['prefix']) === 0) {
+		if (class_exists('APCIterator', false)) {
+			$iterator = new APCIterator(
+				'user',
+				'/^' . preg_quote($this->settings['prefix'], '/') . '/',
+				APC_ITER_NONE
+			);
+			apc_delete($iterator);
+			return true;
+		}
+		$cache = apc_cache_info('user');
+		foreach ($cache['cache_list'] as $key) {
+			if (strpos($key['info'], $this->settings['prefix']) === 0) {
 				apc_delete($key['info']);
 			}
 		}

+ 10 - 9
src/Console/ConsoleOptionParser.php

@@ -209,7 +209,7 @@ class ConsoleOptionParser {
  * Get or set the command name for shell/task.
  *
  * @param string $text The text to set, or null if you want to read
- * @return mixed If reading, the value of the command. If setting $this will be returned
+ * @return string|$this If reading, the value of the command. If setting $this will be returned.
  */
 	public function command($text = null) {
 		if ($text !== null) {
@@ -224,7 +224,7 @@ class ConsoleOptionParser {
  *
  * @param string|array $text The text to set, or null if you want to read. If an array the
  *   text will be imploded with "\n"
- * @return mixed If reading, the value of the description. If setting $this will be returned
+ * @return string|$this If reading, the value of the description. If setting $this will be returned.
  */
 	public function description($text = null) {
 		if ($text !== null) {
@@ -242,7 +242,7 @@ class ConsoleOptionParser {
  * the options and arguments listing when help is generated.
  *
  * @param string|array $text Text when setting or null when reading. If an array the text will be imploded with "\n"
- * @return mixed If reading, the value of the epilog. If setting $this will be returned.
+ * @return string|$this If reading, the value of the epilog. If setting $this will be returned.
  */
 	public function epilog($text = null) {
 		if ($text !== null) {
@@ -275,7 +275,7 @@ class ConsoleOptionParser {
  * @param ConsoleInputOption|string $name The long name you want to the value to be parsed out as when options are parsed.
  *   Will also accept an instance of ConsoleInputOption
  * @param array $options An array of parameters that define the behavior of the option
- * @return ConsoleOptionParser this.
+ * @return $this
  */
 	public function addOption($name, $options = []) {
 		if (is_object($name) && $name instanceof ConsoleInputOption) {
@@ -326,7 +326,7 @@ class ConsoleOptionParser {
  *
  * @param ConsoleInputArgument|string $name The name of the argument. Will also accept an instance of ConsoleInputArgument
  * @param array $params Parameters for the argument, see above.
- * @return ConsoleOptionParser this.
+ * @return $this
  */
 	public function addArgument($name, $params = []) {
 		if (is_object($name) && $name instanceof ConsoleInputArgument) {
@@ -356,7 +356,7 @@ class ConsoleOptionParser {
  *
  * @param array $args Array of arguments to add.
  * @see ConsoleOptionParser::addArgument()
- * @return ConsoleOptionParser this
+ * @return $this
  */
 	public function addArguments(array $args) {
 		foreach ($args as $name => $params) {
@@ -371,7 +371,7 @@ class ConsoleOptionParser {
  *
  * @param array $options Array of options to add.
  * @see ConsoleOptionParser::addOption()
- * @return ConsoleOptionParser this
+ * @return $this
  */
 	public function addOptions(array $options) {
 		foreach ($options as $name => $params) {
@@ -393,7 +393,7 @@ class ConsoleOptionParser {
  *
  * @param ConsoleInputSubcommand|string $name Name of the subcommand. Will also accept an instance of ConsoleInputSubcommand
  * @param array $options Array of params, see above.
- * @return ConsoleOptionParser this.
+ * @return $this
  */
 	public function addSubcommand($name, $options = []) {
 		if (is_object($name) && $name instanceof ConsoleInputSubcommand) {
@@ -416,7 +416,7 @@ class ConsoleOptionParser {
  * Add multiple subcommands at once.
  *
  * @param array $commands Array of subcommands.
- * @return ConsoleOptionParser this
+ * @return $this
  */
 	public function addSubcommands(array $commands) {
 		foreach ($commands as $name => $params) {
@@ -605,6 +605,7 @@ class ConsoleOptionParser {
 			$params[$name] = $value;
 			return $params;
 		}
+		return array();
 	}
 
 /**

+ 1 - 1
src/Network/Email/Email.php

@@ -1766,7 +1766,7 @@ class Email {
 /**
  * Return the Content-Transfer Encoding value based on the set charset
  *
- * @return void
+ * @return string
  */
 	protected function _getContentTransferEncoding() {
 		$charset = strtoupper($this->charset);

+ 14 - 3
src/Network/Request.php

@@ -683,7 +683,7 @@ class Request implements \ArrayAccess {
  * This modifies the parameters available through `$request->params`.
  *
  * @param array $params Array of parameters to merge in
- * @return \Cake\Network\Request The current object, you can chain this method.
+ * @return $this The current object, you can chain this method.
  */
 	public function addParams(array $params) {
 		$this->params = array_merge($this->params, $params);
@@ -695,7 +695,7 @@ class Request implements \ArrayAccess {
  * Provides an easy way to modify, here, webroot and base.
  *
  * @param array $paths Array of paths to merge in
- * @return \Cake\Network\Request the current object, you can chain this method.
+ * @return $this The current object, you can chain this method.
  */
 	public function addPaths(array $paths) {
 		foreach (array('webroot', 'here', 'base') as $element) {
@@ -958,7 +958,7 @@ class Request implements \ArrayAccess {
  * will be created for you.
  *
  * @param string|null $name Dot separated name of the value to read/write
- * @return mixed Either the value being read, or this so you can chain consecutive writes.
+ * @return mixed|$this Either the value being read, or this so you can chain consecutive writes.
  */
 	public function data($name = null) {
 		$args = func_get_args();
@@ -1102,6 +1102,17 @@ class Request implements \ArrayAccess {
 	}
 
 /**
+ * Modify data originally from `php://input`. Useful for altering json/xml data
+ * in middleware or DispatcherFilters before it gets to RequestHandlerComponent
+ *
+ * @param string $input A string to replace original parsed data from input()
+ * @return void
+ */
+	public function setInput($input) {
+		$this->_input = $input;
+	}
+
+/**
  * Array access read implementation
  *
  * @param string $name Name of the key being accessed.

+ 1 - 1
src/Network/Response.php

@@ -583,7 +583,7 @@ class Response {
 	}
 
 /**
- * Acccessor for the location header.
+ * Accessor for the location header.
  *
  * Get/Set the Location header value.
  *

+ 23 - 1
src/Utility/Folder.php

@@ -277,7 +277,29 @@ class Folder {
  * @link http://book.cakephp.org/2.0/en/core-utility-libraries/file-folder.html#Folder::isAbsolute
  */
 	public static function isAbsolute($path) {
-		return !empty($path) && ($path[0] === '/' || preg_match('/^[A-Z]:\\\\/i', $path) || substr($path, 0, 2) === '\\\\');
+		if (empty($path)) {
+			return false;
+		}
+
+		return $path[0] === '/' ||
+			preg_match('/^[A-Z]:\\\\/i', $path) ||
+			substr($path, 0, 2) === '\\\\' ||
+			self::isRegisteredStreamWrapper($path);
+	}
+
+/**
+ * Returns true if given $path is a registered stream wrapper.
+ *
+ * @param string $path Path to check
+ * @return boolean true If path is registered stream wrapper.
+ */
+	public static function isRegisteredStreamWrapper($path) {
+		if (preg_match('/^[A-Z]+(?=:\/\/)/i', $path, $matches) &&
+			in_array($matches[0], stream_get_wrappers())
+		) {
+			return true;
+		}
+		return false;
 	}
 
 /**

+ 10 - 0
src/Utility/Hash.php

@@ -35,6 +35,7 @@ class Hash {
  * @param string|array $path The path being searched for. Either a dot
  *   separated string, or an array of path segments.
  * @param mixed $default The return value when the path does not exist
+ * @throws \InvalidArgumentException
  * @return mixed The value fetched from the array, or null.
  * @link http://book.cakephp.org/2.0/en/core-utility-libraries/hash.html#Hash::get
  */
@@ -52,8 +53,16 @@ class Hash {
 		if ($isString || is_numeric($path)) {
 			$parts = explode('.', $path);
 		} else {
+			if (!is_array($path)) {
+				throw new \InvalidArgumentException(sprintf(
+					'Invalid Parameter %s, should be dot separated path or array.',
+					$path
+				));
+			}
+
 			$parts = $path;
 		}
+
 		foreach ($parts as $key) {
 			if (is_array($data) && isset($data[$key])) {
 				$data =& $data[$key];
@@ -61,6 +70,7 @@ class Hash {
 				return $default;
 			}
 		}
+
 		return $data;
 	}
 

+ 1 - 1
src/View/Helper/HtmlHelper.php

@@ -140,7 +140,7 @@ class HtmlHelper extends Helper {
  * @param string $name Text for link
  * @param string $link URL for link (if empty it won't be a link)
  * @param string|array $options Link attributes e.g. array('id' => 'selected')
- * @return this HtmlHelper
+ * @return $this
  * @see HtmlHelper::link() for details on $options that can be used.
  * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/html.html#creating-breadcrumb-trails-with-htmlhelper
  */

+ 18 - 7
src/View/View.php

@@ -743,6 +743,15 @@ class View {
 	}
 
 /**
+ * Retrieve the current view type
+ *
+ * @return string
+ */
+	public function getCurrentType() {
+		return $this->_currentType;
+	}
+
+/**
  * Magic accessor for helpers.
  *
  * @param string $name Name of the attribute to get.
@@ -1091,25 +1100,27 @@ class View {
  * @return string
  */
 	protected function _renderElement($file, $data, $options) {
+		$current = $this->_current;
+		$restore = $this->_currentType;
+		$this->_currentType = static::TYPE_ELEMENT;
+
 		if ($options['callbacks']) {
 			$this->eventManager()->dispatch(new Event('View.beforeRender', $this, array($file)));
 		}
 
-		$current = $this->_current;
-		$restore = $this->_currentType;
-
-		$this->_currentType = static::TYPE_ELEMENT;
 		$element = $this->_render($file, array_merge($this->viewVars, $data));
 
-		$this->_currentType = $restore;
-		$this->_current = $current;
-
 		if ($options['callbacks']) {
 			$this->eventManager()->dispatch(new Event('View.afterRender', $this, array($file, $element)));
 		}
+
+		$this->_currentType = $restore;
+		$this->_current = $current;
+
 		if (isset($options['cache'])) {
 			Cache::write($this->elementCacheSettings['key'], $element, $this->elementCacheSettings['config']);
 		}
 		return $element;
 	}
+
 }

+ 23 - 0
tests/TestCase/Auth/DigestAuthenticateTest.php

@@ -260,6 +260,29 @@ DIGEST;
 	}
 
 /**
+ * Test parsing a full URI. While not part of the spec some mobile clients will do it wrong.
+ *
+ * @return void
+ */
+	public function testParseAuthDataFullUri() {
+		$digest = <<<DIGEST
+			Digest username="admin",
+			realm="192.168.0.2",
+			nonce="53a7f9b83f61b",
+			uri="http://192.168.0.2/pvcollection/sites/pull/HFD%200001.json#fragment",
+			qop=auth,
+			nc=00000001,
+			cnonce="b85ff144e496e6e18d1c73020566ea3b",
+			response="5894f5d9cd41d012bac09eeb89d2ddf2",
+			opaque="6f65e91667cf98dd13464deaf2739fde"
+DIGEST;
+
+		$expected = 'http://192.168.0.2/pvcollection/sites/pull/HFD%200001.json#fragment';
+		$result = $this->auth->parseAuthData($digest);
+		$this->assertSame($expected, $result['uri']);
+	}
+
+/**
  * test parsing digest information with email addresses
  *
  * @return void

+ 14 - 1
tests/TestCase/Network/RequestTest.php

@@ -2065,7 +2065,7 @@ class RequestTest extends TestCase {
 
 /**
  * Data provider for testing reading values with Request::param()
- * 
+ *
  * @return array
  */
 	public function paramReadingDataProvider() {
@@ -2217,6 +2217,19 @@ class RequestTest extends TestCase {
  *
  * @return void
  */
+	public function testSetInput() {
+		$request = new Request();
+
+		$request->setInput('I came from setInput');
+		$result = $request->input();
+		$this->assertEquals('I came from setInput', $result);
+	}
+
+/**
+ * Test the input() method.
+ *
+ * @return void
+ */
 	public function testInput() {
 		$request = $this->getMock('Cake\Network\Request', array('_readInput'));
 		$request->expects($this->once())->method('_readInput')

+ 3 - 0
tests/TestCase/Utility/FolderTest.php

@@ -508,6 +508,8 @@ class FolderTest extends TestCase {
 		$this->assertFalse(Folder::isAbsolute('0:\\path\\to\\file'));
 		$this->assertFalse(Folder::isAbsolute('\\path/to/file'));
 		$this->assertFalse(Folder::isAbsolute('\\path\\to\\file'));
+		$this->assertFalse(Folder::isAbsolute('notRegisteredStreamWrapper://example'));
+		$this->assertFalse(Folder::isAbsolute('://example'));
 
 		$this->assertTrue(Folder::isAbsolute('/usr/local'));
 		$this->assertTrue(Folder::isAbsolute('//path/to/file'));
@@ -515,6 +517,7 @@ class FolderTest extends TestCase {
 		$this->assertTrue(Folder::isAbsolute('C:\\path\\to\\file'));
 		$this->assertTrue(Folder::isAbsolute('d:\\path\\to\\file'));
 		$this->assertTrue(Folder::isAbsolute('\\\\vmware-host\\Shared Folders\\file'));
+		$this->assertTrue(Folder::isAbsolute('http://www.example.com'));
 	}
 
 /**

+ 10 - 0
tests/TestCase/Utility/HashTest.php

@@ -230,6 +230,16 @@ class HashTest extends TestCase {
 	}
 
 /**
+ * Test get() with an invalid path
+ *
+ * @expectedException \InvalidArgumentException
+ * @return void
+ */
+	public function testGetInvalidPath() {
+		Hash::get(array('one' => 'two'), true);
+	}
+
+/**
  * Test dimensions.
  *
  * @return void

+ 81 - 0
tests/TestCase/View/ViewTest.php

@@ -19,6 +19,8 @@ use Cake\Controller\Controller;
 use Cake\Core\App;
 use Cake\Core\Configure;
 use Cake\Core\Plugin;
+use Cake\Event\Event;
+use Cake\Event\EventListener;
 use Cake\Network\Request;
 use Cake\Routing\Router;
 use Cake\TestSuite\TestCase;
@@ -230,6 +232,61 @@ class TestObjectWithoutToString {
 }
 
 /**
+ * Class TestViewEventListener
+ *
+ * An event listener to test cakePHP events
+ */
+class TestViewEventListener implements EventListener {
+
+/**
+ * type of view before rendering has occurred
+ *
+ * @var string
+ */
+	public $beforeRenderViewType;
+
+/**
+ * type of view after rendering has occurred
+ *
+ * @var string
+ */
+	public $afterRenderViewType;
+
+/**
+ * implementedEvents method
+ *
+ * @return array
+ */
+	public function implementedEvents() {
+		return array(
+			'View.beforeRender' => 'beforeRender',
+			'View.afterRender' => 'afterRender'
+		);
+	}
+
+/**
+ * beforeRender method
+ *
+ * @param \Cake\Event\Event $event the event being sent
+ * @return void
+ */
+	public function beforeRender(Event $event) {
+		$this->beforeRenderViewType = $event->subject()->getCurrentType();
+	}
+
+/**
+ * afterRender method
+ *
+ * @param \Cake\Event\Event $event the event being sent
+ * @return void
+ */
+	public function afterRender(Event $event) {
+		$this->afterRenderViewType = $event->subject()->getCurrentType();
+	}
+
+}
+
+/**
  * ViewTest class
  *
  */
@@ -763,6 +820,30 @@ class ViewTest extends TestCase {
 	}
 
 /**
+ * Test element events
+ *
+ * @return void
+ */
+	public function testViewEvent() {
+		$View = $this->PostsController->createView();
+		$View->autoLayout = false;
+		$listener = new TestViewEventListener();
+
+		$View->eventManager()->attach($listener);
+
+		$View->render('index');
+		$this->assertEquals(View::TYPE_VIEW, $listener->beforeRenderViewType);
+		$this->assertEquals(View::TYPE_VIEW, $listener->afterRenderViewType);
+
+		$this->assertEquals($View->getCurrentType(), View::TYPE_VIEW);
+		$View->element('test_element', array(), array('callbacks' => true));
+		$this->assertEquals($View->getCurrentType(), View::TYPE_VIEW);
+
+		$this->assertEquals(View::TYPE_ELEMENT, $listener->beforeRenderViewType);
+		$this->assertEquals(View::TYPE_ELEMENT, $listener->afterRenderViewType);
+	}
+
+/**
  * Test __get allowing access to helpers.
  *
  * @return void