Merge branch '2.0' into 2.0-request

Conflicts:
	cake/libs/view/helper.php
	cake/tests/cases/libs/controller/components/request_handler.test.php
This commit is contained in:
mark_story 2010-06-30 22:54:28 -04:00
commit 8b847cffef
17 changed files with 255 additions and 53 deletions

View file

@ -166,7 +166,7 @@
Configure::write('Session.cookie', 'CAKEPHP');
/**
* Session time out time (in seconds).
* Session time out time (in minutes).
* Actual value depends on 'Security.level' setting.
*/
Configure::write('Session.timeout', '120');
@ -187,9 +187,9 @@
* in 'Session.timeout' is multiplied according to the settings here.
* Valid values:
*
* 'high' Session timeout in 'Session.timeout' x 10
* 'medium' Session timeout in 'Session.timeout' x 5040
* 'low' Session timeout in 'Session.timeout' x 2628000
* 'high' Session timeout in 'Session.timeout' x 10
* 'medium' Session timeout in 'Session.timeout' x 100
* 'low' Session timeout in 'Session.timeout' x 300
*
* CakePHP session IDs are also regenerated between requests if
* 'Security.level' is set to 'high'.

View file

@ -29,7 +29,6 @@
* Development Mode:
* 1: Errors and warnings shown, model caches refreshed, flash messages halted.
* 2: As in 1, but also with full debug messages and SQL output.
* 3: As in 2, but also with full controller dump.
*
* In production mode, flash messages redirect after a time interval.
* In development mode, you need to click the flash message to continue.
@ -72,6 +71,9 @@
/**
* Uncomment the define below to use CakePHP prefix routes.
*
* The value of the define determines the names of the routes
* and their associated controller actions:
*
* Set to an array of prefixes you want to use in your application. Use for
* admin or other prefixed routes.
*
@ -80,6 +82,8 @@
* Enables:
* `admin_index()` and `/admin/controller/index`
* `manager_index()` and `/manager/controller/index`
*
* [Note Routing.admin is deprecated in 1.3. Use Routing.prefixes instead]
*/
//Configure::write('Routing.prefixes', array('admin'));
@ -154,11 +158,16 @@
/**
* The name of CakePHP's session cookie.
*
* Note the guidelines for Session names states: "The session name references
* the session id in cookies and URLs. It should contain only alphanumeric
* characters."
* @link http://php.net/session_name
*/
Configure::write('Session.cookie', 'CAKEPHP');
/**
* Session time out time (in seconds).
* Session time out time (in minutes).
* Actual value depends on 'Security.level' setting.
*/
Configure::write('Session.timeout', '120');
@ -179,9 +188,9 @@
* in 'Session.timeout' is multiplied according to the settings here.
* Valid values:
*
* 'high' Session timeout in 'Session.timeout' x 10
* 'medium' Session timeout in 'Session.timeout' x 100
* 'low' Session timeout in 'Session.timeout' x 300
* 'high' Session timeout in 'Session.timeout' x 10
* 'medium' Session timeout in 'Session.timeout' x 100
* 'low' Session timeout in 'Session.timeout' x 300
*
* CakePHP session IDs are also regenerated between requests if
* 'Security.level' is set to 'high'.
@ -198,6 +207,15 @@
*/
Configure::write('Security.cipherSeed', '76859309657453542496749683645');
/**
* Apply timestamps with the last modified time to static assets (js, css, images).
* Will append a querystring parameter containing the time the file was modified. This is
* useful for invalidating browser caches.
*
* Set to `true` to apply timestamps, when debug = 0, or set to 'force' to always enable
* timestamping.
*/
//Configure::write('Asset.timestamp', true);
/**
* Compress CSS output by removing comments, whitespace, repeating tags, etc.
* This requires a/var/cache directory to be writable by the web server for caching.
@ -228,11 +246,6 @@
*/
//date_default_timezone_set('UTC');
/**
* If you are on PHP 5.3 uncomment this line and correct your server timezone
* to fix the date & time related errors.
*/
//date_default_timezone_set('UTC');
/**
*
* Cache Engine Configuration

View file

@ -345,20 +345,15 @@ class Dispatcher {
if ($parts[0] === 'theme') {
$themeName = $parts[1];
unset($parts[0], $parts[1]);
$fileFragment = implode('/', $parts);
$viewPaths = App::path('views');
foreach ($viewPaths as $viewPath) {
$path = $viewPath . 'themed' . DS . $themeName . DS . 'webroot' . DS;
if (file_exists($path . $fileFragment)) {
$assetFile = $path . $fileFragment;
break;
}
$fileFragment = implode(DS, $parts);
$path = App::themePath($themeName) . 'webroot' . DS;
if (file_exists($path . $fileFragment)) {
$assetFile = $path . $fileFragment;
}
} else {
$plugin = $parts[0];
unset($parts[0]);
$fileFragment = implode('/', $parts);
$fileFragment = implode(DS, $parts);
$pluginWebroot = App::pluginPath($plugin) . 'webroot' . DS;
if (file_exists($pluginWebroot . $fileFragment)) {
$assetFile = $pluginWebroot . $fileFragment;

View file

@ -98,6 +98,14 @@ class CakeSession extends Object {
*/
public $sessionTime = false;
/**
* The number of seconds to set for session.cookie_lifetime. 0 means
* at browser close.
*
* @var integer
*/
var $cookieLifeTime = false;
/**
* Keeps track of keys to watch for writes on
*
@ -125,7 +133,7 @@ class CakeSession extends Object {
/**
* Session timeout multiplier factor
*
* @var ineteger
* @var integer
* @access public
*/
public $timeout = null;
@ -454,7 +462,12 @@ class CakeSession extends Object {
if ($iniSet && ($this->security === 'high' || $this->security === 'medium')) {
ini_set('session.referer_check', $this->host);
}
$this->cookieLifeTime = Configure::read('Session.timeout') * Security::inactiveMins();
if ($this->security == 'high') {
$this->cookieLifeTime = 0;
} else {
$this->cookieLifeTime = Configure::read('Session.timeout') * (Security::inactiveMins() * 60);
}
switch (Configure::read('Session.save')) {
case 'cake':
@ -582,7 +595,7 @@ class CakeSession extends Object {
if (time() > ($time - (Security::inactiveMins() * Configure::read('Session.timeout')) + 2) || $check < 1) {
$this->renew();
$this->write('Config.timeout', Security::inactiveMins());
$this->write('Config.timeout', 10);
}
}
$this->valid = true;
@ -594,7 +607,7 @@ class CakeSession extends Object {
} else {
$this->write('Config.userAgent', $this->_userAgent);
$this->write('Config.time', $this->sessionTime);
$this->write('Config.timeout', Security::inactiveMins());
$this->write('Config.timeout', 10);
$this->valid = true;
$this->__setError(1, 'Session is valid');
}

View file

@ -668,7 +668,7 @@ class App extends Object {
/**
* Get the path that a plugin is on. Searches through the defined plugin paths.
*
* @param string $plugin CamelCased plugin name to find the path of.
* @param string $plugin CamelCased/lower_cased plugin name to find the path of.
* @return string full path to the plugin.
*/
public static function pluginPath($plugin) {
@ -682,6 +682,23 @@ class App extends Object {
return $_this->plugins[0] . $pluginDir . DS;
}
/**
* Find the path that a theme is on. Search through the defined theme paths.
*
* @param string $theme lower_cased theme name to find the path of.
* @return string full path to the theme.
*/
public static function themePath($theme) {
$_this = App::getInstance();
$themeDir = 'themed' . DS . Inflector::underscore($theme);
for ($i = 0, $length = count($_this->views); $i < $length; $i++) {
if (is_dir($_this->views[$i] . $themeDir)) {
return $_this->views[$i] . $themeDir . DS ;
}
}
return $_this->views[0] . $themeDir . DS;
}
/**
* Returns a key/value list of all paths where core libs are found.
* Passing $type only returns the values for a given value of $key.
@ -723,7 +740,11 @@ class App extends Object {
}
/**
* Returns an index of objects of the given type, with the physical path to each object.
* Returns an array of objects of the given type.
*
* Example usage:
*
* `App::objects('plugin');` returns `array('DebugKit', 'Blog', 'User');`
*
* @param string $type Type of object, i.e. 'model', 'controller', 'helper', or 'plugin'
* @param mixed $path Optional Scan only the path given. If null, paths for the chosen

View file

@ -609,7 +609,6 @@ class RequestHandlerComponent extends Object {
* like 'application/x-shockwave'.
* @param array $options If $type is a friendly type name that is associated with
* more than one type of content, $index is used to select which content-type to use.
*
* @return boolean Returns false if the friendly type name given in $type does
* not exist in the type map, or if the Content-type header has
* already been set by this method.
@ -618,9 +617,6 @@ class RequestHandlerComponent extends Object {
*/
function respondAs($type, $options = array()) {
$this->__initializeTypes();
if ($this->__responseTypeSet != null) {
return false;
}
if (!array_key_exists($type, $this->__requestContent) && strpos($type, '/') === false) {
return false;
}
@ -657,10 +653,10 @@ class RequestHandlerComponent extends Object {
$header .= '; charset=' . $options['charset'];
}
if (!empty($options['attachment'])) {
header("Content-Disposition: attachment; filename=\"{$options['attachment']}\"");
$this->_header("Content-Disposition: attachment; filename=\"{$options['attachment']}\"");
}
if (Configure::read() < 2 && !defined('CAKEPHP_SHELL')) {
@header($header);
$this->_header($header);
}
$this->__responseTypeSet = $cType;
return true;
@ -668,6 +664,16 @@ class RequestHandlerComponent extends Object {
return false;
}
/**
* Wrapper for header() so calls can be easily tested.
*
* @param string $header The header to be sent.
* @return void
*/
protected function _header($header) {
header($header);
}
/**
* Returns the current response type (Content-type header), or null if none has been set
*

View file

@ -53,7 +53,10 @@ class DboSource extends DataSource {
public $alias = 'AS ';
/**
* Caches result from query parsing operations
* Caches result from query parsing operations. Cached results for both DboSource::name() and
* DboSource::conditions() will be stored here. Method caching uses `crc32()` which is
* fast but can collisions more easily than other hashing algorithms. If you have problems
* with collisions, set DboSource::$cacheMethods to false.
*
* @var array
* @access public
@ -497,7 +500,12 @@ class DboSource extends DataSource {
* Returns a quoted name of $data for use in an SQL statement.
* Strips fields out of SQL functions before quoting.
*
* @param string $data
* Results of this method are stored in a memory cache. This improves performance, but
* because the method uses a simple hashing algorithm it can infrequently have collisions.
* Setting DboSource::$cacheMethods to false will disable the memory cache.
*
* @param mixed $data Either a string with a column to quote. An array of columns to quote or an
* object from DboSource::expression() or DboSource::identifier()
* @return string SQL field
*/
public function name($data) {
@ -2008,6 +2016,10 @@ class DboSource extends DataSource {
* conditions are provided those conditions will be parsed and quoted. If a boolean
* is given it will be integer cast as condition. Null will return 1 = 1.
*
* Results of this method are stored in a memory cache. This improves performance, but
* because the method uses a simple hashing algorithm it can infrequently have collisions.
* Setting DboSource::$cacheMethods to false will disable the memory cache.
*
* @param mixed $conditions Array or string of conditions, or any value.
* @param boolean $quoteValues If true, values should be quoted
* @param boolean $where If true, "WHERE " will be prepended to the return value

View file

@ -241,7 +241,22 @@ class Helper extends Object {
);
if (strpos($path, '?') === false && $timestampEnabled) {
$filepath = preg_replace('/^' . preg_quote($this->request->webroot, '/') . '/', '', $path);
$path .= '?' . @filemtime(WWW_ROOT . str_replace('/', DS, $filepath));
$webrootPath = WWW_ROOT . str_replace('/', DS, $filepath);
if (file_exists($webrootPath)) {
return $path . '?' . @filemtime($webrootPath);
}
$segments = explode('/', ltrim($filepath, '/'));
if ($segments[0] === 'theme') {
$theme = $segments[1];
unset($segments[0], $segments[1]);
$themePath = App::themePath($theme) . 'webroot' . DS . implode(DS, $segments);
return $path . '?' . @filemtime($themePath);
} else {
$plugin = $segments[0];
unset($segments[0]);
$pluginPath = App::pluginPath($plugin) . 'webroot' . DS . implode(DS, $segments);
return $path . '?' . @filemtime($pluginPath);
}
}
return $path;
}

View file

@ -584,6 +584,13 @@ class FormHelper extends AppHelper {
* ));
* }}}
*
* In addition to fields control, inputs() allows you to use a few additional options.
*
* - `fieldset` Set to false to disable the fieldset. If a string is supplied it will be used as
* the classname for the fieldset element.
* - `legend` Set to false to disable the legend for the generated input set. Or supply a string
* to customize the legend text.
*
* @param mixed $fields An array of fields to generate inputs for, or null.
* @param array $blacklist a simple array of fields to not create inputs for.
* @return string Completed form inputs.

View file

@ -593,6 +593,7 @@ class HtmlHelper extends AppHelper {
if (!empty($this->_crumbs)) {
$result = '';
$crumbCount = count($this->_crumbs);
$ulOptions = $options;
foreach ($this->_crumbs as $which => $crumb) {
$options = array();
if (empty($crumb[1])) {
@ -607,7 +608,7 @@ class HtmlHelper extends AppHelper {
}
$result .= $this->tag('li', $elementContent, $options);
}
return $this->tag('ul', $result, $options);
return $this->tag('ul', $result, $ulOptions);
} else {
return null;
}

View file

@ -620,9 +620,9 @@ class View extends Object {
($count == 1 && !empty($this->association)) ||
($count == 1 && $this->model != $this->entityPath) ||
($count == 2 && !empty($this->fieldSuffix)) ||
is_numeric($path[0])
is_numeric($path[0]) && !empty($assoc)
) {
array_unshift($path,$assoc);
array_unshift($path, $assoc);
}
return Set::filter($path);
}

View file

@ -460,6 +460,26 @@ class AppImportTest extends CakeTestCase {
App::build();
}
/**
* test that pluginPath can find paths for plugins.
*
* @return void
*/
function testThemePath() {
App::build(array(
'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS)
));
$path = App::themePath('test_theme');
$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS;
$this->assertEqual($path, $expected);
$path = App::themePath('TestTheme');
$expected = TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS . 'themed' . DS . 'test_theme' . DS;
$this->assertEqual($path, $expected);
App::build();
}
/**
* testClassLoading method
*

View file

@ -299,6 +299,51 @@ class RequestHandlerComponentTest extends CakeTestCase {
$this->assertEqual($this->Controller->viewPath, 'request_handler_test' . DS . 'js');
}
/**
* test that respondAs works as expected.
*
* @return void
*/
function testRespondAs() {
$debug = Configure::read('debug');
Configure::write('debug', 0);
$RequestHandler = $this->getMock('RequestHandlerComponent', array('_header'));
$RequestHandler->expects($this->at(0))->method('_header')
->with('Content-type: application/json');
$RequestHandler->expects($this->at(1))->method('_header')
->with('Content-type: text/xml');
$result = $RequestHandler->respondAs('json');
$this->assertTrue($result);
$result = $RequestHandler->respondAs('text/xml');
$this->assertTrue($result);
Configure::write('debug', $debug);
}
/**
* test that attachment headers work with respondAs
*
* @return void
*/
function testRespondAsWithAttachment() {
$debug = Configure::read('debug');
Configure::write('debug', 0);
$RequestHandler = $this->getMock('RequestHandlerComponent', array('_header'));
$RequestHandler->expects($this->at(0))->method('_header')
->with('Content-Disposition: attachment; filename="myfile.xml"');
$RequestHandler->expects($this->at(1))->method('_header')
->with('Content-type: application/xml');
$result = $RequestHandler->respondAs('xml', array('attachment' => 'myfile.xml'));
$this->assertTrue($result);
Configure::write('debug', $debug);
}
/**
* test that calling renderAs() more than once continues to work.
*
@ -559,9 +604,8 @@ class RequestHandlerComponentTest extends CakeTestCase {
'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views'. DS)
), true);
$this->Controller->RequestHandler = new NoStopRequestHandler($this);
$this->Controller->RequestHandler->request = $this->Controller->request;
$this->Controller->RequestHandler->expectOnce('_stop');
$this->Controller->RequestHandler = $this->getMock('RequestHandlerComponent', array('_stop'));
$this->Controller->RequestHandler->expects($this->once())->method('_stop');
ob_start();
$this->Controller->RequestHandler->beforeRedirect(

View file

@ -352,25 +352,30 @@ class SessionComponentTest extends CakeTestCase {
Configure::write('Security.level', 'low');
$Session = new SessionComponent();
$Session->write('Test', 'some value');
$this->assertEqual($_SESSION['Config']['timeout'], Security::inactiveMins());
$this->assertEqual($Session->sessionTime, mktime() + (300 * Configure::read('Session.timeout')));
$this->assertEqual($_SESSION['Config']['timeout'], 10);
$this->assertEqual($_SESSION['Config']['time'], $Session->sessionTime);
$this->assertEqual($Session->time, mktime());
$this->assertEqual($_SESSION['Config']['time'], $Session->time + (Security::inactiveMins() * Configure::read('Session.timeout')));
$this->assertEqual($_SESSION['Config']['time'], $Session->time + (Security::inactiveMins() * Configure::read('Session.timeout')));
session_destroy();
Configure::write('Security.level', 'medium');
$Session = new SessionComponent();
$Session->write('Test', 'some value');
$this->assertEqual($_SESSION['Config']['timeout'], Security::inactiveMins());
$this->assertEqual($Session->sessionTime, mktime() + (100 * Configure::read('Session.timeout')));
$this->assertEqual($_SESSION['Config']['timeout'], 10);
$this->assertEqual($_SESSION['Config']['time'], $Session->sessionTime);
$this->assertEqual($Session->time, mktime());
$this->assertEqual($_SESSION['Config']['time'], $Session->time + (Security::inactiveMins() * Configure::read('Session.timeout')));
$this->assertEqual($_SESSION['Config']['time'], $Session->time + (Security::inactiveMins() * Configure::read('Session.timeout')));
session_destroy();
Configure::write('Security.level', 'high');
$Session = new SessionComponent();
$Session->write('Test', 'some value');
$this->assertEqual($_SESSION['Config']['timeout'], Security::inactiveMins());
$this->assertEqual($Session->sessionTime, mktime() + (10 * Configure::read('Session.timeout')));
$this->assertEqual($_SESSION['Config']['timeout'], 10);
$this->assertEqual($_SESSION['Config']['time'], $Session->sessionTime);
$this->assertEqual($Session->time, mktime());
$this->assertEqual($_SESSION['Config']['time'], $Session->time + (Security::inactiveMins() * Configure::read('Session.timeout')));

View file

@ -492,6 +492,35 @@ class HelperTest extends CakeTestCase {
Configure::write('Asset.timestamp', $_timestamp);
}
/**
* test assetTimestamp with plugins and themes
*
* @return void
*/
function testAssetTimestampPluginsAndThemes() {
$_timestamp = Configure::read('Asset.timestamp');
Configure::write('Asset.timestamp', 'force');
App::build(array(
'plugins' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'plugins' . DS),
'views' => array(TEST_CAKE_CORE_INCLUDE_PATH . 'tests' . DS . 'test_app' . DS . 'views' . DS),
));
$result = $this->Helper->assetTimestamp('/test_plugin/css/test_plugin_asset.css');
$this->assertPattern('#/test_plugin/css/test_plugin_asset.css\?[0-9]+$#', $result, 'Missing timestamp plugin');
$result = $this->Helper->assetTimestamp('/test_plugin/css/i_dont_exist.css');
$this->assertPattern('#/test_plugin/css/i_dont_exist.css\?$#', $result, 'No error on missing file');
$result = $this->Helper->assetTimestamp('/theme/test_theme/js/theme.js');
$this->assertPattern('#/theme/test_theme/js/theme.js\?[0-9]+$#', $result, 'Missing timestamp theme');
$result = $this->Helper->assetTimestamp('/theme/test_theme/js/non_existant.js');
$this->assertPattern('#/theme/test_theme/js/non_existant.js\?$#', $result, 'No error on missing file');
App::build();
Configure::write('Asset.timestamp', $_timestamp);
}
/**
* testFieldsWithSameName method
*

View file

@ -2034,6 +2034,19 @@ class FormHelperTest extends CakeTestCase {
}
}
/**
* test input name with leading integer, ensure attributes are generated correctly.
*
* @return void
*/
function testInputWithLeadingInteger() {
$result = $this->Form->text('0.Node.title');
$expected = array(
'input' => array('name' => 'data[0][Node][title]', 'id' => '0NodeTitle', 'type' => 'text')
);
$this->assertTags($result, $expected);
}
/**
* test form->input() with select type inputs.
*

View file

@ -896,6 +896,14 @@ class ViewTest extends CakeTestCase {
$View->association = 'Comment';
$View->field = 'user_id';
$this->assertEqual($View->entity(), array('Comment', 'user_id'));
$View->model = 0;
$View->association = null;
$View->field = 'Node';
$View->fieldSuffix = 'title';
$View->entityPath = '0.Node.title';
$expected = array(0, 'Node', 'title');
$this->assertEqual($View->entity(), $expected);
}
/**