Merge branch 'master' into 2.3

Conflicts:
	lib/Cake/Model/CakeSchema.php
	lib/Cake/Test/Case/Model/Datasource/Database/MysqlTest.php
This commit is contained in:
mark_story 2012-12-13 20:15:36 -05:00
commit 6f5ff4d7dd
14 changed files with 194 additions and 38 deletions

View file

@ -585,7 +585,8 @@ class ConsoleOptionParser {
$option = $this->_options[$name]; $option = $this->_options[$name];
$isBoolean = $option->isBoolean(); $isBoolean = $option->isBoolean();
$nextValue = $this->_nextToken(); $nextValue = $this->_nextToken();
if (!$isBoolean && !empty($nextValue) && !$this->_optionExists($nextValue)) { $emptyNextValue = (empty($nextValue) && $nextValue !== '0');
if (!$isBoolean && !$emptyNextValue && !$this->_optionExists($nextValue)) {
array_shift($this->_tokens); array_shift($this->_tokens);
$value = $nextValue; $value = $nextValue;
} elseif ($isBoolean) { } elseif ($isBoolean) {

View file

@ -580,13 +580,17 @@ class CakeSchema extends Object {
if (is_array($values)) { if (is_array($values)) {
foreach ($values as $key => $val) { foreach ($values as $key => $val) {
if (is_array($val)) { if (is_array($val)) {
$vals[] = "'{$key}' => array('" . implode("', '", $val) . "')"; $vals[] = "'{$key}' => array(" . implode(", ", $this->_values($val)) . ")";
} elseif (!is_numeric($key)) { } else {
$val = var_export($val, true); $val = var_export($val, true);
if ($val === 'NULL') { if ($val === 'NULL') {
$val = 'null'; $val = 'null';
} }
if (!is_numeric($key)) {
$vals[] = "'{$key}' => {$val}"; $vals[] = "'{$key}' => {$val}";
} else {
$vals[] = "{$val}";
}
} }
} }
} }

View file

@ -466,6 +466,12 @@ class Mysql extends DboSource {
$col[] = $idx->Column_name; $col[] = $idx->Column_name;
$index[$idx->Key_name]['column'] = $col; $index[$idx->Key_name]['column'] = $col;
} }
if (!empty($idx->Sub_part)) {
if (!isset($index[$idx->Key_name]['length'])) {
$index[$idx->Key_name]['length'] = array();
}
$index[$idx->Key_name]['length'][$idx->Column_name] = $idx->Sub_part;
}
} }
// @codingStandardsIgnoreEnd // @codingStandardsIgnoreEnd
$indexes->closeCursor(); $indexes->closeCursor();
@ -566,6 +572,55 @@ class Mysql extends DboSource {
return array(); return array();
} }
/**
* Format indexes for create table
*
* @param array $indexes An array of indexes to generate SQL from
* @param string $table Optional table name, not used
* @return array An array of SQL statements for indexes
* @see DboSource::buildIndex()
*/
public function buildIndex($indexes, $table = null) {
$join = array();
foreach ($indexes as $name => $value) {
$out = '';
if ($name === 'PRIMARY') {
$out .= 'PRIMARY ';
$name = null;
} else {
if (!empty($value['unique'])) {
$out .= 'UNIQUE ';
}
$name = $this->startQuote . $name . $this->endQuote;
}
// length attribute only used for MySQL datasource, for TEXT/BLOB index columns
$out .= 'KEY ' . $name . ' (';
if (is_array($value['column'])) {
if (isset($value['length'])) {
$vals = array();
foreach ($value['column'] as $column) {
$name = $this->name($column);
if (isset($value['length'])) {
$name .= $this->_buildIndexSubPart($value['length'], $column);
}
$vals[] = $name;
}
$out .= implode(', ', $vals);
} else {
$out .= implode(', ', array_map(array(&$this, 'name'), $value['column']));
}
} else {
$out .= $this->name($value['column']);
if (isset($value['length'])) {
$out .= $this->_buildIndexSubPart($value['length'], $value['column']);
}
}
$out .= ')';
$join[] = $out;
}
return $join;
}
/** /**
* Generate MySQL index alteration statements for a table. * Generate MySQL index alteration statements for a table.
* *
@ -595,6 +650,23 @@ class Mysql extends DboSource {
return $alter; return $alter;
} }
/**
* Format length for text indexes
*
* @param array $lengths An array of lengths for a single index
* @param string $column The column for which to generate the index length
* @return string Formatted length part of an index field
*/
protected function _buildIndexSubPart($lengths, $column) {
if (is_null($lengths)) {
return '';
}
if (!isset($lengths[$column])) {
return '';
}
return '(' . $lengths[$column] . ')';
}
/** /**
* Returns an detailed array of sources (tables) in the database. * Returns an detailed array of sources (tables) in the database.
* *

View file

@ -1574,7 +1574,9 @@ class Model extends Object implements CakeEventListener {
* *
* @param string $name Name of the table field * @param string $name Name of the table field
* @param mixed $value Value of the field * @param mixed $value Value of the field
* @param array $validate See $options param in Model::save(). Does not respect 'fieldList' key if passed * @param boolean|array $validate Either a boolean, or an array.
* If a boolean, indicates whether or not to validate before saving.
* If an array, allows control of 'validate' and 'callbacks' options.
* @return boolean See Model::save() * @return boolean See Model::save()
* @see Model::save() * @see Model::save()
* @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-savefield-string-fieldname-string-fieldvalue-validate-false * @link http://book.cakephp.org/2.0/en/models/saving-your-data.html#model-savefield-string-fieldname-string-fieldvalue-validate-false

View file

@ -42,11 +42,7 @@ class MailTransport extends AbstractTransport {
$headers = $this->_headersToString($headers, $eol); $headers = $this->_headersToString($headers, $eol);
$message = implode($eol, $email->message()); $message = implode($eol, $email->message());
$params = null;
if (!ini_get('safe_mode')) {
$params = isset($this->_config['additionalParameters']) ? $this->_config['additionalParameters'] : null; $params = isset($this->_config['additionalParameters']) ? $this->_config['additionalParameters'] : null;
}
$this->_mail($to, $email->subject(), $message, $headers, $params); $this->_mail($to, $email->subject(), $message, $headers, $params);
return array('headers' => $headers, 'message' => $message); return array('headers' => $headers, 'message' => $message);
} }
@ -58,16 +54,20 @@ class MailTransport extends AbstractTransport {
* @param string $subject email's subject * @param string $subject email's subject
* @param string $message email's body * @param string $message email's body
* @param string $headers email's custom headers * @param string $headers email's custom headers
* @param string $params additional params for sending email * @param string $params additional params for sending email, will be ignored when in safe_mode
* @throws SocketException if mail could not be sent * @throws SocketException if mail could not be sent
* @return void * @return void
*/ */
protected function _mail($to, $subject, $message, $headers, $params = null) { protected function _mail($to, $subject, $message, $headers, $params = null) {
if (ini_get('safe_mode')) {
//@codingStandardsIgnoreStart //@codingStandardsIgnoreStart
if (!@mail($to, $subject, $message, $headers, $params)) { if (!@mail($to, $subject, $message, $headers)) {
throw new SocketException(__d('cake_dev', 'Could not send email.')); throw new SocketException(__d('cake_dev', 'Could not send email.'));
} }
} elseif (!@mail($to, $subject, $message, $headers, $params)) {
//@codingStandardsIgnoreEnd //@codingStandardsIgnoreEnd
throw new SocketException(__d('cake_dev', 'Could not send email.'));
}
} }
} }

View file

@ -11,8 +11,7 @@
* @since CakePHP(tm) v 1.3 * @since CakePHP(tm) v 1.3
* @license MIT License (http://www.opensource.org/licenses/mit-license.php) * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
*/ */
App::uses('Hash', 'Utility');
App::uses('Set', 'Utility');
/** /**
* A single Route used by the Router to connect requests to * A single Route used by the Router to connect requests to

View file

@ -266,19 +266,29 @@ class Router {
* $options offers four 'special' keys. `pass`, `named`, `persist` and `routeClass` * $options offers four 'special' keys. `pass`, `named`, `persist` and `routeClass`
* have special meaning in the $options array. * have special meaning in the $options array.
* *
* `pass` is used to define which of the routed parameters should be shifted into the pass array. Adding a * - `pass` is used to define which of the routed parameters should be shifted into the pass array. Adding a
* parameter to pass will remove it from the regular route array. Ex. `'pass' => array('slug')` * parameter to pass will remove it from the regular route array. Ex. `'pass' => array('slug')`
* * - `persist` is used to define which route parameters should be automatically included when generating
* `persist` is used to define which route parameters should be automatically included when generating
* new urls. You can override persistent parameters by redefining them in a url or remove them by * new urls. You can override persistent parameters by redefining them in a url or remove them by
* setting the parameter to `false`. Ex. `'persist' => array('lang')` * setting the parameter to `false`. Ex. `'persist' => array('lang')`
* * - `routeClass` is used to extend and change how individual routes parse requests and handle reverse routing,
* `routeClass` is used to extend and change how individual routes parse requests and handle reverse routing,
* via a custom routing class. Ex. `'routeClass' => 'SlugRoute'` * via a custom routing class. Ex. `'routeClass' => 'SlugRoute'`
* * - `named` is used to configure named parameters at the route level. This key uses the same options
* `named` is used to configure named parameters at the route level. This key uses the same options
* as Router::connectNamed() * as Router::connectNamed()
* *
* You can also add additional conditions for matching routes to the $defaults array.
* The following conditions can be used:
*
* - `[type]` Only match requests for specific content types.
* - `[method]` Only match requests with specific HTTP verbs.
* - `[server]` Only match when $_SERVER['SERVER_NAME'] matches the given value.
*
* Example of using the `[method]` condition:
*
* `Router::connect('/tasks', array('controller' => 'tasks', 'action' => 'index', '[method]' => 'GET'));`
*
* The above route will only be matched for GET requests. POST requests will fail to match this route.
*
* @param string $route A string describing the template of the route * @param string $route A string describing the template of the route
* @param array $defaults An array describing the default route parameters. These parameters will be used by default * @param array $defaults An array describing the default route parameters. These parameters will be used by default
* and can supply routing parameters that are not dynamic. See above. * and can supply routing parameters that are not dynamic. See above.

View file

@ -78,6 +78,18 @@ class ConsoleOptionParserTest extends CakeTestCase {
$this->assertEquals(array('test' => 'value', 'help' => false), $result[0], 'Long parameter did not parse out'); $this->assertEquals(array('test' => 'value', 'help' => false), $result[0], 'Long parameter did not parse out');
} }
/**
* test adding an option with a zero value
*
* @return void
*/
public function testAddOptionZero() {
$parser = new ConsoleOptionParser('test', false);
$parser->addOption('count', array());
$result = $parser->parse(array('--count', '0'));
$this->assertEquals(array('count' => '0', 'help' => false), $result[0], 'Zero parameter did not parse out');
}
/** /**
* test addOption with an object. * test addOption with an object.
* *

View file

@ -330,6 +330,26 @@ class MysqlTest extends CakeTestCase {
$result = $this->Dbo->index('with_fulltext', false); $result = $this->Dbo->index('with_fulltext', false);
$this->Dbo->rawQuery('DROP TABLE ' . $name); $this->Dbo->rawQuery('DROP TABLE ' . $name);
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
$name = $this->Dbo->fullTableName('with_text_index');
$this->Dbo->rawQuery('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, text_field text, primary key(id), KEY `text_index` ( `text_field`(20) ));');
$expected = array(
'PRIMARY' => array('column' => 'id', 'unique' => 1),
'text_index' => array('column' => 'text_field', 'unique' => 0, 'length' => array('text_field' => 20)),
);
$result = $this->Dbo->index('with_text_index', false);
$this->Dbo->rawQuery('DROP TABLE ' . $name);
$this->assertEquals($expected, $result);
$name = $this->Dbo->fullTableName('with_compound_text_index');
$this->Dbo->rawQuery('CREATE TABLE ' . $name . ' (id int(11) AUTO_INCREMENT, text_field1 text, text_field2 text, primary key(id), KEY `text_index` ( `text_field1`(20), `text_field2`(20) ));');
$expected = array(
'PRIMARY' => array('column' => 'id', 'unique' => 1),
'text_index' => array('column' => array('text_field1', 'text_field2'), 'unique' => 0, 'length' => array('text_field1' => 20, 'text_field2' => 20)),
);
$result = $this->Dbo->index('with_compound_text_index', false);
$this->Dbo->rawQuery('DROP TABLE ' . $name);
$this->assertEquals($expected, $result);
} }
/** /**
@ -2963,6 +2983,19 @@ class MysqlTest extends CakeTestCase {
); );
$result = $this->Dbo->buildIndex($data); $result = $this->Dbo->buildIndex($data);
$expected = array('FULLTEXT KEY `MyFtIndex` (`name`, `description`)'); $expected = array('FULLTEXT KEY `MyFtIndex` (`name`, `description`)');
$data = array(
'MyTextIndex' => array('column' => 'text_field', 'length' => array('text_field' => 20))
);
$result = $this->Dbo->buildIndex($data);
$expected = array('KEY `MyTextIndex` (`text_field`(20))');
$this->assertEquals($expected, $result);
$data = array(
'MyMultiTextIndex' => array('column' => array('text_field1', 'text_field2'), 'length' => array('text_field1' => 20, 'text_field2' => 20))
);
$result = $this->Dbo->buildIndex($data);
$expected = array('KEY `MyMultiTextIndex` (`text_field1`(20), `text_field2`(20))');
$this->assertEquals($expected, $result); $this->assertEquals($expected, $result);
} }

View file

@ -84,7 +84,13 @@ class DebuggerTest extends CakeTestCase {
$this->assertTrue(is_array($result)); $this->assertTrue(is_array($result));
$this->assertEquals(4, count($result)); $this->assertEquals(4, count($result));
$pattern = '/<code><span style\="color\: \#\d+">.*?&lt;\?php/'; $pattern = '/<code>.*?<span style\="color\: \#\d+">.*?&lt;\?php/';
$this->assertRegExp($pattern, $result[0]);
$result = Debugger::excerpt(__FILE__, 10, 2);
$this->assertEquals(5, count($result));
$pattern = '/<span style\="color\: \#\d{6}">\*<\/span>/';
$this->assertRegExp($pattern, $result[0]); $this->assertRegExp($pattern, $result[0]);
$return = Debugger::excerpt('[internal]', 2, 2); $return = Debugger::excerpt('[internal]', 2, 2);

View file

@ -6164,6 +6164,10 @@ class FormHelperTest extends CakeTestCase {
'/div' '/div'
); );
$this->assertTags($result, $expected); $this->assertTags($result, $expected);
$this->Form->request->data['Model']['upload'] = 'no data should be set in value';
$result = $this->Form->file('Model.upload');
$this->assertTags($result, array('input' => array('type' => 'file', 'name' => 'data[Model][upload]', 'id' => 'ModelUpload')));
} }
/** /**

View file

@ -396,12 +396,14 @@ class Debugger {
return array(); return array();
} }
$data = file_get_contents($file); $data = file_get_contents($file);
if (!empty($data) && strpos($data, "\n") !== false) { if (empty($data)) {
return $lines;
}
if (strpos($data, "\n") !== false) {
$data = explode("\n", $data); $data = explode("\n", $data);
} }
if (!isset($data[$line])) {
if (empty($data) || !isset($data[$line])) { return $lines;
return;
} }
for ($i = $line - ($context + 1); $i < $line + $context; $i++) { for ($i = $line - ($context + 1); $i < $line + $context; $i++) {
if (!isset($data[$i])) { if (!isset($data[$i])) {
@ -425,13 +427,23 @@ class Debugger {
* @return string * @return string
*/ */
protected static function _highlight($str) { protected static function _highlight($str) {
static $supportHighlight = null; if (function_exists('hphp_log')) {
if (!$supportHighlight && function_exists('hphp_log')) {
$supportHighlight = false;
return htmlentities($str); return htmlentities($str);
} }
$supportHighlight = true; $added = false;
return highlight_string($str, true); if (strpos($str, '<?php') === false) {
$added = true;
$str = "<?php \n" . $str;
}
$highlight = highlight_string($str, true);
if ($added) {
$highlight = str_replace(
'&lt;?php&nbsp;<br />',
'',
$highlight
);
}
return $highlight;
} }
/** /**

View file

@ -1433,7 +1433,7 @@ class FormHelper extends AppHelper {
$showEmpty = $this->_extractOption('empty', $attributes); $showEmpty = $this->_extractOption('empty', $attributes);
if ($showEmpty) { if ($showEmpty) {
$showEmpty = ($showEmpty === true) ? __('empty') : $showEmpty; $showEmpty = ($showEmpty === true) ? __d('cake', 'empty') : $showEmpty;
$options = array('' => $showEmpty) + $options; $options = array('' => $showEmpty) + $options;
} }
unset($attributes['empty']); unset($attributes['empty']);
@ -1628,7 +1628,8 @@ class FormHelper extends AppHelper {
$this->_secure($secure, array_merge($field, array($suffix))); $this->_secure($secure, array_merge($field, array($suffix)));
} }
return $this->Html->useTag('file', $options['name'], array_diff_key($options, array('name' => ''))); $exclude = array('name' => '', 'value' => '');
return $this->Html->useTag('file', $options['name'], array_diff_key($options, $exclude));
} }
/** /**

View file

@ -744,7 +744,7 @@ class HtmlHelper extends AppHelper {
'text' => $startText 'text' => $startText
); );
} }
$startText += array('url' => '/', 'text' => __('Home')); $startText += array('url' => '/', 'text' => __d('cake', 'Home'));
list($url, $text) = array($startText['url'], $startText['text']); list($url, $text) = array($startText['url'], $startText['text']);
unset($startText['url'], $startText['text']); unset($startText['url'], $startText['text']);
array_unshift($crumbs, array($text, $url, $startText)); array_unshift($crumbs, array($text, $url, $startText));