Skip to content
Browse files

Merge branch 'master' into 2.3

Conflicts:
	lib/Cake/VERSION.txt
  • Loading branch information...
2 parents 7979ee5 + 3b46cd4 commit 3c6b50953b7ae53bb6267ed4dfd9793b86b385df @markstory markstory committed
Showing with 390 additions and 49 deletions.
  1. +1 −1 lib/Cake/Console/Command/Task/ModelTask.php
  2. +30 −11 lib/Cake/Console/Shell.php
  3. +3 −2 lib/Cake/Controller/Component/SecurityComponent.php
  4. +6 −1 lib/Cake/Log/Engine/ConsoleLog.php
  5. +10 −0 lib/Cake/Model/Behavior/TranslateBehavior.php
  6. +6 −5 lib/Cake/Model/Behavior/TreeBehavior.php
  7. +1 −0 lib/Cake/Model/BehaviorCollection.php
  8. +11 −0 lib/Cake/Model/ModelBehavior.php
  9. +3 −3 lib/Cake/Network/CakeRequest.php
  10. +2 −2 lib/Cake/Routing/Dispatcher.php
  11. +2 −2 lib/Cake/Routing/Route/CakeRoute.php
  12. +33 −1 lib/Cake/Test/Case/Console/ShellTest.php
  13. +33 −0 lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php
  14. +17 −0 lib/Cake/Test/Case/Log/Engine/ConsoleLogTest.php
  15. +28 −0 lib/Cake/Test/Case/Model/Behavior/TranslateBehaviorTest.php
  16. +32 −0 lib/Cake/Test/Case/Model/Behavior/TreeBehaviorNumberTest.php
  17. +44 −0 lib/Cake/Test/Case/Model/BehaviorCollectionTest.php
  18. +19 −0 lib/Cake/Test/Case/Model/Validator/CakeValidationRuleTest.php
  19. +16 −0 lib/Cake/Test/Case/Network/CakeRequestTest.php
  20. +2 −1 lib/Cake/Test/Case/Network/Email/CakeEmailTest.php
  21. +2 −2 lib/Cake/Test/Case/Routing/Route/CakeRouteTest.php
  22. +22 −0 lib/Cake/Test/Case/Utility/XmlTest.php
  23. +16 −0 lib/Cake/Test/Case/View/Helper/HtmlHelperTest.php
  24. +8 −0 lib/Cake/TestSuite/Fixture/CakeTestFixture.php
  25. +3 −0 lib/Cake/TestSuite/Reporter/CakeBaseReporter.php
  26. +33 −13 lib/Cake/Utility/Xml.php
  27. +5 −3 lib/Cake/View/Helper.php
  28. +2 −2 lib/Cake/View/Helper/PaginatorHelper.php
View
2 lib/Cake/Console/Command/Task/ModelTask.php
@@ -414,7 +414,7 @@ public function fieldValidation($fieldName, $metaData, $primaryKey = 'id') {
for ($i = 1, $m = $defaultChoice / 2; $i < $m; $i++) {
$line = sprintf("%2d. %s", $i, $this->_validations[$i]);
$optionText .= $line . str_repeat(" ", 31 - strlen($line));
- $optionText .= sprintf("%2d. %s", $m + $i, $this->_validations[$m + $i]);
+ $optionText .= sprintf("%2d. %s\n", $m + $i, $this->_validations[$m + $i]);
}
$this->out($optionText);
$this->out(__d('cake_console', "%s - Do not do any validation on this field.", $defaultChoice));
View
41 lib/Cake/Console/Shell.php
@@ -165,23 +165,13 @@ public function __construct($stdout = null, $stderr = null, $stdin = null) {
if ($this->stdout == null) {
$this->stdout = new ConsoleOutput('php://stdout');
}
- CakeLog::config('stdout', array(
- 'engine' => 'ConsoleLog',
- 'types' => array('notice', 'info'),
- 'stream' => $this->stdout,
- ));
if ($this->stderr == null) {
$this->stderr = new ConsoleOutput('php://stderr');
}
- CakeLog::config('stderr', array(
- 'engine' => 'ConsoleLog',
- 'types' => array('emergency', 'alert', 'critical', 'error', 'warning', 'debug'),
- 'stream' => $this->stderr,
- ));
if ($this->stdin == null) {
$this->stdin = new ConsoleInput('php://stdin');
}
-
+ $this->_useLogger();
$parent = get_parent_class($this);
if ($this->tasks !== null && $this->tasks !== false) {
$this->_mergeVars(array('tasks'), $parent, true);
@@ -379,6 +369,10 @@ public function runCommand($command, $argv) {
return false;
}
+ if (!empty($this->params['quiet'])) {
+ $this->_useLogger(false);
+ }
+
$this->command = $command;
if (!empty($this->params['help'])) {
return $this->_displayHelp($command);
@@ -825,4 +819,29 @@ protected function _pluginPath($pluginName) {
return current(App::path('plugins')) . $pluginName . DS;
}
+/**
+ * Used to enable or disable logging stream output to stdout and stderr
+ * If you don't wish to see in your stdout or stderr everything that is logged
+ * through CakeLog, call this function with first param as false
+ *
+ * @param boolean $enable wheter to enable CakeLog output or not
+ * @return void
+ **/
+ protected function _useLogger($enable = true) {
+ if (!$enable) {
+ CakeLog::drop('stdout');
+ CakeLog::drop('stderr');
+ return;
+ }
+ CakeLog::config('stdout', array(
+ 'engine' => 'ConsoleLog',
+ 'types' => array('notice', 'info'),
+ 'stream' => $this->stdout,
+ ));
+ CakeLog::config('stderr', array(
+ 'engine' => 'ConsoleLog',
+ 'types' => array('emergency', 'alert', 'critical', 'error', 'warning', 'debug'),
+ 'stream' => $this->stderr,
+ ));
+ }
}
View
5 lib/Cake/Controller/Component/SecurityComponent.php
@@ -229,7 +229,7 @@ public function startup(Controller $controller) {
}
}
$this->generateToken($controller->request);
- if ($isPost) {
+ if ($isPost && is_array($controller->request->data)) {
unset($controller->request->data['_Token']);
}
}
@@ -585,12 +585,13 @@ protected function _expireTokens($tokens) {
* @param string $method Method to execute
* @param array $params Parameters to send to method
* @return mixed Controller callback method's response
+ * @throws BadRequestException When a the blackholeCallback is not callable.
*/
protected function _callback(Controller $controller, $method, $params = array()) {
if (is_callable(array($controller, $method))) {
return call_user_func_array(array(&$controller, $method), empty($params) ? null : $params);
} else {
- return null;
+ throw new BadRequestException(__d('cake_dev', 'The request has been black-holed'));
}
}
View
7 lib/Cake/Log/Engine/ConsoleLog.php
@@ -49,11 +49,16 @@ class ConsoleLog extends BaseLog {
*/
public function __construct($config = array()) {
parent::__construct($config);
+ if (DS == '\\' && !(bool)env('ANSICON')) {
+ $outputAs = ConsoleOutput::PLAIN;
+ } else {
+ $outputAs = ConsoleOutput::COLOR;
+ }
$config = Hash::merge(array(
'stream' => 'php://stderr',
'types' => null,
'scopes' => array(),
- 'outputAs' => ConsoleOutput::COLOR,
+ 'outputAs' => $outputAs,
), $this->_config);
$config = $this->config($config);
if ($config['stream'] instanceof ConsoleOutput) {
View
10 lib/Cake/Model/Behavior/TranslateBehavior.php
@@ -393,6 +393,16 @@ public function afterSave(Model $model, $created) {
$conditions = array('model' => $model->alias, 'foreign_key' => $model->id);
$RuntimeModel = $this->translateModel($model);
+ $fields = array_merge($this->settings[$model->alias], $this->runtime[$model->alias]['fields']);
+ if ($created) {
+ foreach ($fields as $field) {
+ if (!isset($tempData[$field])) {
+ //set the field value to an empty string
+ $tempData[$field] = '';
+ }
+ }
+ }
+
foreach ($tempData as $field => $value) {
unset($conditions['content']);
$conditions['field'] = $field;
View
11 lib/Cake/Model/Behavior/TreeBehavior.php
@@ -124,11 +124,13 @@ public function beforeFind(Model $Model, $query) {
*/
public function beforeDelete(Model $Model, $cascade = true) {
extract($this->settings[$Model->alias]);
- $data = current($Model->find('first', array(
+ $data = $Model->find('first', array(
'conditions' => array($Model->alias . '.' . $Model->primaryKey => $Model->id),
'fields' => array($Model->alias . '.' . $left, $Model->alias . '.' . $right),
- 'recursive' => -1)));
- $this->_deletedRow = $data;
+ 'recursive' => -1));
+ if ($data) {
+ $this->_deletedRow = current($data);
+ }
return true;
}
@@ -369,8 +371,7 @@ public function generateTreeList(Model $Model, $conditions = null, $keyPath = nu
$valuePath = array('%s%s', '{n}.tree_prefix', $valuePath);
} else {
- $valuePath[0] = '{' . (count($valuePath) - 1) . '}' . $valuePath[0];
- $valuePath[] = '{n}.tree_prefix';
+ array_unshift($valuePath, '%s' . $valuePath[0], '{n}.tree_prefix');
}
$order = $Model->alias . '.' . $left . ' asc';
$results = $Model->find('all', compact('conditions', 'fields', 'order', 'recursive'));
View
1 lib/Cake/Model/BehaviorCollection.php
@@ -285,6 +285,7 @@ public function implementedEvents() {
'Model.beforeFind' => 'trigger',
'Model.afterFind' => 'trigger',
'Model.beforeValidate' => 'trigger',
+ 'Model.afterValidate' => 'trigger',
'Model.beforeSave' => 'trigger',
'Model.afterSave' => 'trigger',
'Model.beforeDelete' => 'trigger',
View
11 lib/Cake/Model/ModelBehavior.php
@@ -147,6 +147,17 @@ public function beforeValidate(Model $model) {
}
/**
+ * afterValidate is called just after model data was validated, you can use this callback
+ * to perform any data cleanup or preparation if needed
+ *
+ * @param Model $model Model using this behavior
+ * @return mixed False will stop this event from being passed to other behaviors
+ */
+ public function afterValidate(Model $model) {
+ return true;
+ }
+
+/**
* beforeSave is called before a model is saved. Returning false from a beforeSave callback
* will abort the save operation.
*
View
6 lib/Cake/Network/CakeRequest.php
@@ -175,7 +175,8 @@ protected function _processPost() {
if (env('HTTP_X_HTTP_METHOD_OVERRIDE')) {
$this->data['_method'] = env('HTTP_X_HTTP_METHOD_OVERRIDE');
}
- if (isset($this->data['_method'])) {
+ $isArray = is_array($this->data);
+ if ($isArray && isset($this->data['_method'])) {
if (!empty($_SERVER)) {
$_SERVER['REQUEST_METHOD'] = $this->data['_method'];
} else {
@@ -183,8 +184,7 @@ protected function _processPost() {
}
unset($this->data['_method']);
}
-
- if (isset($this->data['data'])) {
+ if ($isArray && isset($this->data['data'])) {
$data = $this->data['data'];
if (count($this->data) <= 1) {
$this->data = $data;
View
4 lib/Cake/Routing/Dispatcher.php
@@ -43,7 +43,7 @@ class Dispatcher implements CakeEventListener {
/**
* Event manager, used to handle dispatcher filters
*
- * @var CakeEventMaanger
+ * @var CakeEventManager
*/
protected $_eventManager;
@@ -62,7 +62,7 @@ public function __construct($base = false) {
* Returns the CakeEventManager instance or creates one if none was
* creted. Attaches the default listeners and filters
*
- * @return CakeEventmanger
+ * @return CakeEventManager
*/
public function getEventManager() {
if (!$this->_eventManager) {
View
4 lib/Cake/Routing/Route/CakeRoute.php
@@ -497,9 +497,9 @@ protected function _writeUrl($params) {
$named = array();
foreach ($params['named'] as $key => $value) {
if (is_array($value)) {
- $flat = Hash::flatten($value, '][');
+ $flat = Hash::flatten($value, '%5D%5B');
foreach ($flat as $namedKey => $namedValue) {
- $named[] = $key . "[$namedKey]" . $separator . rawurlencode($namedValue);
+ $named[] = $key . "%5B{$namedKey}%5D" . $separator . rawurlencode($namedValue);
}
} else {
$named[] = $key . $separator . rawurlencode($value);
View
34 lib/Cake/Test/Case/Console/ShellTest.php
@@ -80,6 +80,10 @@ public function mergeVars($properties, $class, $normalize = true) {
return $this->_mergeVars($properties, $class, $normalize);
}
+ public function useLogger($enable = true) {
+ $this->_useLogger($enable);
+ }
+
}
/**
@@ -825,7 +829,7 @@ public function testFileAndConsoleLogging() {
require_once CORE_TEST_CASES . DS . 'Log' . DS . 'Engine' . DS . 'ConsoleLogTest.php';
$mock = $this->getMock('ConsoleLog', array('write'), array(
array('types' => 'error'),
- ));
+ ));
TestCakeLog::config('console', array(
'engine' => 'ConsoleLog',
'stream' => 'php://stderr',
@@ -840,4 +844,32 @@ public function testFileAndConsoleLogging() {
$this->assertContains($this->Shell->testMessage, $contents);
}
+/**
+ * Tests that _useLogger works properly
+ *
+ * @return void
+ **/
+ public function testProtectedUseLogger() {
+ CakeLog::drop('stdout');
+ CakeLog::drop('stderr');
+ $this->Shell->useLogger(true);
+ $this->assertNotEmpty(CakeLog::stream('stdout'));
+ $this->assertNotEmpty(CakeLog::stream('stderr'));
+ $this->Shell->useLogger(false);
+ $this->assertFalse(CakeLog::stream('stdout'));
+ $this->assertFalse(CakeLog::stream('stderr'));
+ }
+
+/**
+ * Test file and console and logging quiet output
+ */
+ public function testQuietLog() {
+ $output = $this->getMock('ConsoleOutput', array(), array(), '', false);
+ $error = $this->getMock('ConsoleOutput', array(), array(), '', false);
+ $in = $this->getMock('ConsoleInput', array(), array(), '', false);
+ $this->Shell = $this->getMock('ShellTestShell', array('_useLogger'), array($output, $error, $in));
+ $this->Shell->expects($this->once())->method('_useLogger')->with(false);
+ $this->Shell->runCommand('foo', array('--quiet'));
+ }
+
}
View
33 lib/Cake/Test/Case/Controller/Component/SecurityComponentTest.php
@@ -107,6 +107,20 @@ public function header($status) {
}
+class BrokenCallbackController extends Controller {
+
+ public $name = 'UncallableCallback';
+
+ public $components = array('Session', 'TestSecurity');
+
+ public function index() {
+ }
+
+ protected function _fail() {
+ }
+
+}
+
/**
* SecurityComponentTest class
*
@@ -162,6 +176,25 @@ public function tearDown() {
}
/**
+ * Test that requests are still blackholed when controller has incorrect
+ * visibility keyword in the blackhole callback
+ *
+ * @expectedException BadRequestException
+ */
+ public function testBlackholeWithBrokenCallback() {
+ $request = new CakeRequest('posts/index', false);
+ $request->addParams(array(
+ 'controller' => 'posts', 'action' => 'index')
+ );
+ $this->Controller = new BrokenCallbackController($request);
+ $this->Controller->Components->init($this->Controller);
+ $this->Controller->Security = $this->Controller->TestSecurity;
+ $this->Controller->Security->blackHoleCallback = '_fail';
+ $this->Controller->Security->startup($this->Controller);
+ $this->Controller->Security->blackHole($this->Controller, 'csrf');
+ }
+
+/**
* test that initialize can set properties.
*
* @return void
View
17 lib/Cake/Test/Case/Log/Engine/ConsoleLogTest.php
@@ -116,4 +116,21 @@ public function testCombinedLogWriting() {
$this->assertContains($message, $logOutput);
}
+/**
+ * test default value of stream 'outputAs'
+ */
+ public function testDefaultOutputAs() {
+ TestCakeLog::config('test_console_log', array(
+ 'engine' => 'TestConsoleLog',
+ ));
+ if (DS == '\\' && !(bool)env('ANSICON')) {
+ $expected = ConsoleOutput::PLAIN;
+ } else {
+ $expected = ConsoleOutput::COLOR;
+ }
+ $stream = TestCakeLog::stream('test_console_log');
+ $config = $stream->config();
+ $this->assertEquals($expected, $config['outputAs']);
+ }
+
}
View
28 lib/Cake/Test/Case/Model/Behavior/TranslateBehaviorTest.php
@@ -532,6 +532,34 @@ public function testSaveCreate() {
}
/**
+ * Test that saving only some of the translated fields allows the record to be found again.
+ *
+ * @return void
+ */
+ public function testSavePartialFields() {
+ $this->loadFixtures('Translate', 'TranslatedItem');
+
+ $TestModel = new TranslatedItem();
+ $TestModel->locale = 'spa';
+ $data = array(
+ 'slug' => 'fourth_translated',
+ 'title' => 'Leyenda #4',
+ );
+ $TestModel->create($data);
+ $TestModel->save();
+ $result = $TestModel->read();
+ $expected = array(
+ 'TranslatedItem' => array(
+ 'id' => $TestModel->id,
+ 'translated_article_id' => null,
+ 'locale' => 'spa',
+ 'content' => '',
+ ) + $data
+ );
+ $this->assertEquals($expected, $result);
+ }
+
+/**
* testSaveUpdate method
*
* @return void
View
32 lib/Cake/Test/Case/Model/Behavior/TreeBehaviorNumberTest.php
@@ -915,6 +915,18 @@ public function testDelete() {
}
/**
+ * Test deleting a record that doesn't exist.
+ *
+ * @return void
+ */
+ public function testDeleteDoesNotExist() {
+ extract($this->settings);
+ $this->Tree = new $modelClass();
+ $this->Tree->initialize(2, 2);
+ $this->Tree->delete(99999);
+ }
+
+/**
* testRemove method
*
* @return void
@@ -1270,6 +1282,26 @@ public function testGenerateTreeListWithSelfJoin() {
}
/**
+ * Test the formatting options of generateTreeList()
+ *
+ * @return void
+ */
+ public function testGenerateTreeListFormatting() {
+ extract($this->settings);
+ $this->Tree = new $modelClass();
+ $this->Tree->initialize(2, 2);
+
+ $result = $this->Tree->generateTreeList(
+ null,
+ "{n}.$modelClass.id",
+ array('%s - %s', "{n}.$modelClass.id", "{n}.$modelClass.name")
+ );
+ $this->assertEquals('1 - 1. Root', $result[1]);
+ $this->assertEquals('_2 - 1.1', $result[2]);
+ $this->assertEquals('__3 - 1.1.1', $result[3]);
+ }
+
+/**
* testArraySyntax method
*
* @return void
View
44 lib/Cake/Test/Case/Model/BehaviorCollectionTest.php
@@ -195,6 +195,29 @@ public function beforeValidate(Model $model) {
}
/**
+ * afterValidate method
+ *
+ * @param Model $model
+ * @param bool $cascade
+ * @return void
+ */
+ public function afterValidate(Model $model) {
+ $settings = $this->settings[$model->alias];
+ if (!isset($settings['afterValidate']) || $settings['afterValidate'] == 'off') {
+ return parent::afterValidate($model);
+ }
+ switch ($settings['afterValidate']) {
+ case 'on':
+ return false;
+ break;
+ case 'test':
+ $model->data = array('foo');
+ return true;
+ break;
+ }
+ }
+
+/**
* beforeDelete method
*
* @param Model $model
@@ -967,6 +990,27 @@ public function testBehaviorValidateCallback() {
}
/**
+ * testBehaviorValidateAfterCallback method
+ *
+ * @return void
+ */
+ public function testBehaviorValidateAfterCallback() {
+ $Apple = new Apple();
+
+ $Apple->Behaviors->attach('Test');
+ $this->assertSame($Apple->validates(), true);
+
+ $Apple->Behaviors->attach('Test', array('afterValidate' => 'on'));
+ $this->assertSame($Apple->validates(), true);
+ $this->assertSame($Apple->validationErrors, array());
+
+ $Apple->Behaviors->attach('Test', array('afterValidate' => 'test'));
+ $Apple->data = array('bar');
+ $Apple->validates();
+ $this->assertEquals(array('foo'), $Apple->data);
+ }
+
+/**
* testBehaviorValidateMethods method
*
* @return void
View
19 lib/Cake/Test/Case/Model/Validator/CakeValidationRuleTest.php
@@ -73,6 +73,7 @@ public function testIsValid() {
$Rule->process('fieldName', $data, $methods);
$this->assertTrue($Rule->isValid());
}
+
/**
* tests that passing custom validation methods work
*
@@ -99,6 +100,24 @@ public function testCustomMethods() {
}
/**
+ * Make sure errors are triggered when validation is missing.
+ *
+ * @expectedException PHPUnit_Framework_Error_Warning
+ * @expectedExceptionMessage Could not find validation handler totallyMissing for fieldName
+ * @return void
+ */
+ public function testCustomMethodMissingError() {
+ $def = array('rule' => array('totallyMissing'));
+ $data = array(
+ 'fieldName' => 'some data'
+ );
+ $methods = array('mytestrule' => array($this, 'myTestRule'));
+
+ $Rule = new CakeValidationRule($def);
+ $Rule->process('fieldName', $data, $methods);
+ }
+
+/**
* Test isRequired method
*
* @return void
View
16 lib/Cake/Test/Case/Network/CakeRequestTest.php
@@ -301,6 +301,22 @@ public function testPutParsing() {
}
/**
+ * test parsing json PUT data into the object.
+ *
+ * @return void
+ */
+ public function testPutParsingJSON() {
+ $_SERVER['REQUEST_METHOD'] = 'PUT';
+ $_SERVER['CONTENT_TYPE'] = 'application/json';
+
+ $request = $this->getMock('TestCakeRequest', array('_readInput'));
+ $request->expects($this->at(0))->method('_readInput')
+ ->will($this->returnValue('{Article":["title"]}'));
+ $request->reConstruct();
+ $this->assertEquals('{Article":["title"]}', $request->data);
+ }
+
+/**
* test parsing of FILES array
*
* @return void
View
3 lib/Cake/Test/Case/Network/Email/CakeEmailTest.php
@@ -1119,7 +1119,8 @@ public function testSendRenderWithImage() {
$this->CakeEmail->template('image');
$this->CakeEmail->emailFormat('html');
- $expected = '<img src="http://localhost/img/image.gif" alt="cool image" width="100" height="100" />';
+ $server = env('SERVER_NAME') ? env('SERVER_NAME') : 'localhost';
+ $expected = '<img src="http://' . $server . '/img/image.gif" alt="cool image" width="100" height="100" />';
$result = $this->CakeEmail->send();
$this->assertContains($expected, $result['message']);
}
View
4 lib/Cake/Test/Case/Routing/Route/CakeRouteTest.php
@@ -798,7 +798,7 @@ public function testMatchNamedParametersArray() {
)
);
$result = $route->match($url);
- $expected = '/posts/index/filter[0]:one/filter[model]:value';
+ $expected = '/posts/index/filter%5B0%5D:one/filter%5Bmodel%5D:value';
$this->assertEquals($expected, $result);
$url = array(
@@ -813,7 +813,7 @@ public function testMatchNamedParametersArray() {
)
);
$result = $route->match($url);
- $expected = '/posts/index/filter[0]:one/filter[model][0]:two/filter[model][order]:field';
+ $expected = '/posts/index/filter%5B0%5D:one/filter%5Bmodel%5D%5B0%5D:two/filter%5Bmodel%5D%5Border%5D:field';
$this->assertEquals($expected, $result);
}
View
22 lib/Cake/Test/Case/Utility/XmlTest.php
@@ -1039,4 +1039,26 @@ public function testAmpInText() {
$this->assertContains('mark &amp; mark', $result);
}
+/**
+ * Test that entity loading is disabled by default.
+ *
+ * @return void
+ */
+ public function testNoEntityLoading() {
+ $file = CAKE . 'VERSION.txt';
+ $xml = <<<XML
+<!DOCTYPE cakephp [
+ <!ENTITY payload SYSTEM "file://$file" >]>
+<request>
+ <xxe>&payload;</xxe>
+</request>
+XML;
+ try {
+ $result = Xml::build($xml);
+ $this->assertEquals('', (string)$result->xxe);
+ } catch (Exception $e) {
+ $this->assertTrue(true, 'A warning was raised meaning external entities were not loaded');
+ }
+ }
+
}
View
16 lib/Cake/Test/Case/View/Helper/HtmlHelperTest.php
@@ -361,7 +361,14 @@ public function testImageTag() {
$result = $this->Html->image('test.gif?one=two&three=four');
$this->assertTags($result, array('img' => array('src' => 'img/test.gif?one=two&amp;three=four', 'alt' => '')));
+ }
+/**
+ * Test that image() works with fullBase and a webroot not equal to /
+ *
+ * @return void
+ */
+ public function testImageWithFullBase() {
$result = $this->Html->image('test.gif', array('fullBase' => true));
$here = $this->Html->url('/', true);
$this->assertTags($result, array('img' => array('src' => $here . 'img/test.gif', 'alt' => '')));
@@ -369,6 +376,15 @@ public function testImageTag() {
$result = $this->Html->image('sub/test.gif', array('fullBase' => true));
$here = $this->Html->url('/', true);
$this->assertTags($result, array('img' => array('src' => $here . 'img/sub/test.gif', 'alt' => '')));
+
+ $request = $this->Html->request;
+ $request->webroot = '/myproject/';
+ $request->base = '/myproject';
+ Router::setRequestInfo($request);
+
+ $result = $this->Html->image('sub/test.gif', array('fullBase' => true));
+ $here = $this->Html->url('/', true);
+ $this->assertTags($result, array('img' => array('src' => $here . 'img/sub/test.gif', 'alt' => '')));
}
/**
View
8 lib/Cake/TestSuite/Fixture/CakeTestFixture.php
@@ -194,6 +194,14 @@ public function create($db) {
$db->execute($db->createSchema($this->Schema), array('log' => false));
$this->created[] = $db->configKeyName;
} catch (Exception $e) {
+ $msg = __d(
+ 'cake_dev',
+ 'Fixture creation for "%s" failed "%s"',
+ $this->table,
+ $e->getMessage()
+ );
+ CakeLog::error($msg);
+ trigger_error($msg, E_USER_WARNING);
return false;
}
return true;
View
3 lib/Cake/TestSuite/Reporter/CakeBaseReporter.php
@@ -201,6 +201,9 @@ public function startTest(PHPUnit_Framework_Test $test) {
*/
public function endTest(PHPUnit_Framework_Test $test, $time) {
$this->numAssertions += $test->getNumAssertions();
+ if ($test->hasFailed()) {
+ return;
+ }
$this->paintPass($test, $time);
}
View
46 lib/Cake/Utility/Xml.php
@@ -74,6 +74,8 @@ class Xml {
* ### Options
*
* - `return` Can be 'simplexml' to return object of SimpleXMLElement or 'domdocument' to return DOMDocument.
+ * - `loadEntities` Defaults to false. Set to true to enable loading of `<!ENTITY` definitions. This
+ * is disabled by default for security reasons.
* - If using array as input, you can pass `options` from Xml::fromArray.
*
* @param string|array $input XML string, a path to a file, an URL or an array
@@ -86,26 +88,18 @@ public static function build($input, $options = array()) {
$options = array('return' => (string)$options);
}
$defaults = array(
- 'return' => 'simplexml'
+ 'return' => 'simplexml',
+ 'loadEntities' => false,
);
$options = array_merge($defaults, $options);
if (is_array($input) || is_object($input)) {
return self::fromArray((array)$input, $options);
} elseif (strpos($input, '<') !== false) {
- if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
- return new SimpleXMLElement($input, LIBXML_NOCDATA);
- }
- $dom = new DOMDocument();
- $dom->loadXML($input);
- return $dom;
+ return self::_loadXml($input, $options);
} elseif (file_exists($input) || strpos($input, 'http://') === 0 || strpos($input, 'https://') === 0) {
- if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
- return new SimpleXMLElement($input, LIBXML_NOCDATA, true);
- }
- $dom = new DOMDocument();
- $dom->load($input);
- return $dom;
+ $input = file_get_contents($input);
+ return self::_loadXml($input, $options);
} elseif (!is_string($input)) {
throw new XmlException(__d('cake_dev', 'Invalid input.'));
}
@@ -113,6 +107,32 @@ public static function build($input, $options = array()) {
}
/**
+ * Parse the input data and create either a SimpleXmlElement object or a DOMDocument.
+ *
+ * @param string $input The input to load.
+ * @param array $options The options to use. See Xml::build()
+ * @return SimpleXmlElement|DOMDocument.
+ */
+ protected static function _loadXml($input, $options) {
+ $hasDisable = function_exists('libxml_disable_entity_loader');
+ $internalErrors = libxml_use_internal_errors(true);
+ if ($hasDisable && !$options['loadEntities']) {
+ libxml_disable_entity_loader(true);
+ }
+ if ($options['return'] === 'simplexml' || $options['return'] === 'simplexmlelement') {
+ $xml = new SimpleXMLElement($input, LIBXML_NOCDATA);
+ } else {
+ $xml = new DOMDocument();
+ $xml->loadXML($input);
+ }
+ if ($hasDisable && !$options['loadEntities']) {
+ libxml_disable_entity_loader(false);
+ }
+ libxml_use_internal_errors($internalErrors);
+ return $xml;
+ }
+
+/**
* Transform an array into a SimpleXMLElement
*
* ### Options
View
8 lib/Cake/View/Helper.php
@@ -313,10 +313,12 @@ public function assetUrl($path, $options = array()) {
$path = h($this->assetTimestamp($this->webroot($path)));
if (!empty($options['fullBase'])) {
- if ($path[0] == '/') {
- $path = substr($path, 1);
+ $base = $this->url('/', true);
+ $len = strlen($this->request->webroot);
+ if ($len) {
+ $base = substr($base, 0, -$len);
}
- $path = $this->url('/', true) . $path;
+ $path = $base . $path;
}
}
View
4 lib/Cake/View/Helper/PaginatorHelper.php
@@ -701,7 +701,7 @@ public function numbers($options = array()) {
if ($offset < $start - 1) {
$out .= $this->first($offset, compact('tag', 'separator', 'ellipsis', 'class'));
} else {
- $out .= $this->first($offset, compact('tag', 'separator', 'class') + array('after' => $separator));
+ $out .= $this->first($offset, compact('tag', 'separator', 'class', 'ellipsis') + array('after' => $separator));
}
}
@@ -735,7 +735,7 @@ public function numbers($options = array()) {
if ($offset <= $last && $params['pageCount'] - $end > $offset) {
$out .= $this->last($offset, compact('tag', 'separator', 'ellipsis', 'class'));
} else {
- $out .= $this->last($offset, compact('tag', 'separator', 'class') + array('before' => $separator));
+ $out .= $this->last($offset, compact('tag', 'separator', 'class', 'ellipsis') + array('before' => $separator));
}
}

0 comments on commit 3c6b509

Please sign in to comment.
Something went wrong with that request. Please try again.