Merge 3.1 into 3.2

Conflicts:
	admin/javascript/LeftAndMain.js
	control/HTTPRequest.php
	docs/en/00_Getting_Started/00_Server_Requirements.md
This commit is contained in:
Daniel Hensby 2016-04-26 00:09:33 +01:00
commit a0812f987a
No known key found for this signature in database
GPG Key ID: E38EC566FE29EB66
9 changed files with 124 additions and 19 deletions

View File

@ -176,7 +176,7 @@ jQuery.noConflict();
var msg = (xhr.getResponseHeader('X-Status')) ? xhr.getResponseHeader('X-Status') : xhr.statusText,
reathenticate = xhr.getResponseHeader('X-Reauthenticate'),
msgType = (xhr.status < 200 || xhr.status > 399) ? 'bad' : 'good',
ignoredMessages = ['OK'];
ignoredMessages = ['OK', 'success'];
// Enable reauthenticate dialog if requested
if(reathenticate) {
@ -911,8 +911,11 @@ jQuery.noConflict();
sessionStates = sessionData ? JSON.parse(sessionData) : false;
this.find('.cms-tabset, .ss-tabset').each(function() {
var index, tabset = $(this), tabsetId = tabset.attr('id'), tab,
forcedTab = tabset.find('.ss-tabs-force-active');
var index,
tabset = $(this),
tabsetId = tabset.attr('id'),
tab,
forcedTab = tabset.children('ul').children('li.ss-tabs-force-active');
if(!tabset.data('tabs')){
return; // don't act on uninit'ed controls
@ -921,18 +924,18 @@ jQuery.noConflict();
// The tabs may have changed, notify the widget that it should update its internal state.
tabset.tabs('refresh');
// Make sure the intended tab is selected.
// Make sure the intended tab is selected. Only force the tab on the correct tabset though
if(forcedTab.length) {
index = forcedTab.index();
index = forcedTab.first().index();
} else if(overrideStates && overrideStates[tabsetId]) {
tab = tabset.find(overrideStates[tabsetId].tabSelector);
if(tab.length){
index = tab.index();
}
} else if(sessionStates) {
$.each(sessionStates, function(i, sessionState) {
if(tabset.is('#' + sessionState.id)){
index = sessionState.selected;
$.each(sessionStates, function(i, state) {
if(tabsetId == state.id){
index = state.selected;
}
});
}

View File

@ -671,7 +671,7 @@ class SS_HTTPRequest implements ArrayAccess {
}
if ($headerOverrideIP) {
return $headerOverrideIP;
return $this->getIPFromHeaderValue($headerOverrideIP);
} elseif(isset($_SERVER['REMOTE_ADDR'])) {
return $_SERVER['REMOTE_ADDR'];
} else {
@ -679,6 +679,28 @@ class SS_HTTPRequest implements ArrayAccess {
}
}
/**
* Extract an IP address from a header value that has been obtained. Accepts single IP or comma separated string of
* IPs
*
* @param string $headerValue The value from a trusted header
* @return string The IP address
*/
protected function getIPFromHeaderValue($headerValue) {
if (strpos($headerValue, ',') !== false) {
//sometimes the IP from a load balancer could be "x.x.x.x, y.y.y.y, z.z.z.z" so we need to find the most
// likely candidate
$ips = explode(',', $headerValue);
foreach ($ips as $ip) {
$ip = trim($ip);
if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE)) {
return $ip;
}
}
}
return $headerValue;
}
/**
* Returns all mimetypes from the HTTP "Accept" header
* as an array.

View File

@ -67,9 +67,21 @@ class ErrorControlChain {
$this->error = (bool)$error;
}
/**
* Sets whether errors are suppressed or not
* Notes:
* - Errors cannot be suppressed if not handling errors.
* - Errors cannot be un-suppressed if original mode dis-allowed visible errors
*
* @param bool $suppression
*/
public function setSuppression($suppression) {
$this->suppression = (bool)$suppression;
if ($this->handleFatalErrors) ini_set('display_errors', !$suppression);
// Don't modify errors unless handling fatal errors, and if errors were
// originally allowed to be displayed.
if ($this->handleFatalErrors && $this->originalDisplayErrors) {
ini_set('display_errors', !$suppression);
}
}
/**
@ -167,7 +179,7 @@ class ErrorControlChain {
$this->handleFatalErrors = true;
$this->originalDisplayErrors = ini_get('display_errors');
ini_set('display_errors', !$this->suppression);
$this->setSuppression($this->suppression);
$this->step();
}

View File

@ -194,8 +194,8 @@ class ParameterConfirmationToken {
$_SERVER['HTTP_HOST'],
// SilverStripe base
self::$alternateBaseURL !== null ? self::$alternateBaseURL : BASE_URL,
// And URL
$url
// And URL including base script (eg: if it's index.php/page/url/)
(defined('BASE_SCRIPT_URL') ? '/' . BASE_SCRIPT_URL : '') . $url,
));
// Join together with protocol into our current absolute URL, avoiding duplicated "/" characters

View File

@ -10,10 +10,10 @@
*
* <code>
* $parser = new CSVParser('myfile.csv');
* $parser->mapColumns(
* $parser->mapColumns(array(
* 'first name' => 'FirstName'
* 'lastname' => 'Surname',
* 'last name' => 'Surname'
* 'last name' => 'Surname',
* ));
* foreach($parser as $row) {
* // $row is a map of column name => column value

View File

@ -8,7 +8,7 @@ Our web-based [PHP installer](installation/) can check if you meet the requireme
## Web server software requirements
* PHP 5.3.3+
* PHP 5.3.3+, <7
* We recommend using a PHP accelerator or opcode cache, such as [xcache](http://xcache.lighttpd.net/) or [WinCache](http://www.iis.net/download/wincacheforphp).
* Allocate at least 48MB of memory to each PHP process. (SilverStripe can be resource hungry for some intensive operations.)
* Required modules: dom, gd2, fileinfo, hash, iconv, mbstring, mysqli (or other database driver), session, simplexml, tokenizer, xml.
@ -23,7 +23,7 @@ Our web-based [PHP installer](installation/) can check if you meet the requireme
* MySQL 5.0+
* PostgreSQL 8.3+ (requires ["postgresql" module](http://silverstripe.org/postgresql-module))
* SQL Server 2008+ (requires ["mssql" module](http://silverstripe.org/microsoft-sql-server-database/))
* Support for `[Oracle](http://www.silverstripe.org/oracle-database-module/)` and [SQLite](http://silverstripe.org/sqlite-database/) is not commercially supported, but is under development by our open source community.
* Support for [Oracle](http://www.silverstripe.org/oracle-database-module/) and [SQLite](http://silverstripe.org/sqlite-database/) is not commercially supported, but is under development by our open source community.
* One of the following web server products:
* Apache 2.0+ with mod_rewrite and "AllowOverride All" set
* IIS 7+
@ -34,6 +34,11 @@ Our web-based [PHP installer](installation/) can check if you meet the requireme
* Microsoft Windows XP SP3, Vista, Windows 7, Server 2008, Server 2008 R2
* Mac OS X 10.4+
### Why doesn't SilverStripe 3 work with PHP 7?
Unfortunately, SilverStripe has classes named the same as PHP reserved words, such as "Int" and "Float". This means that
we are unable to make SilverStripe 3 support PHP 7 without breaking backward compatibility. SilverStripe 4 will work
with PHP 7 and will be released in 2016. Until then, we recommend that you use PHP 5.6.
## Web server hardware requirements
Hardware requirements vary widely depending on the traffic to your website, the complexity of its logic (i.e., PHP), and

View File

@ -30,7 +30,7 @@ directly calling methods that they shouldn't.
'cmsrestrictedaction' => 'CMS_ACCESS_CMSMain',
// complexaction can only be accessed if $this->canComplexAction() returns true.
'complexaction' '->canComplexAction'
'complexaction' => '->canComplexAction',
// complexactioncheck can only be accessed if $this->canComplexAction("MyRestrictedAction", false, 42) is true.
'complexactioncheck' => '->canComplexAction("MyRestrictedAction", false, 42)',
@ -200,4 +200,4 @@ execution. This behavior can be used to implement permission checks.
## API Documentation
* [api:Controller]
* [api:Controller]

View File

@ -254,4 +254,23 @@ class HTTPRequestTest extends SapphireTest {
$this->assertEquals('home?test=1', $req->getURL(true));
$this->assertEquals('home', $req->getURL());
}
public function testGetIPFromHeaderValue() {
$req = new SS_HTTPRequest('GET', '/');
$reflectionMethod = new ReflectionMethod($req, 'getIPFromHeaderValue');
$reflectionMethod->setAccessible(true);
$headers = array(
'80.79.208.21, 149.126.76.1, 10.51.0.68' => '80.79.208.21',
'52.19.19.103, 10.51.0.49' => '52.19.19.103',
'10.51.0.49, 52.19.19.103' => '52.19.19.103',
'10.51.0.49' => '10.51.0.49',
'127.0.0.1, 10.51.0.49' => '127.0.0.1',
);
foreach ($headers as $header => $ip) {
$this->assertEquals($ip, $reflectionMethod->invoke($req, $header));
}
}
}

View File

@ -63,7 +63,11 @@ require_once '$classpath';
class ErrorControlChainTest extends SapphireTest {
protected $displayErrors = null;
function setUp() {
$this->displayErrors = (bool)ini_get('display_errors');
// Check we can run PHP at all
$null = is_writeable('/dev/null') ? '/dev/null' : 'NUL';
exec("php -v 2> $null", $out, $rv);
@ -76,8 +80,48 @@ class ErrorControlChainTest extends SapphireTest {
parent::setUp();
}
public function tearDown() {
if($this->displayErrors !== null) {
ini_set('display_errors', $this->displayErrors);
$this->displayErrors = null;
}
parent::tearDown(); // TODO: Change the autogenerated stub
}
function testErrorSuppression() {
// Errors disabled by default
ini_set('display_errors', false);
$chain = new ErrorControlChain();
$whenNotSuppressed = null;
$whenSuppressed = null;
$chain->then(function($chain) use(&$whenNotSuppressed, &$whenSuppressed) {
$chain->setSuppression(true);
$whenSuppressed = ini_get('display_errors');
$chain->setSuppression(false);
$whenNotSuppressed = ini_get('display_errors');
})->execute();
// Disabled errors never un-disable
$this->assertFalse((bool)$whenNotSuppressed);
$this->assertFalse((bool)$whenSuppressed);
// Errors enabled by default
ini_set('display_errors', true);
$chain = new ErrorControlChain();
$whenNotSuppressed = null;
$whenSuppressed = null;
$chain->then(function($chain) use(&$whenNotSuppressed, &$whenSuppressed) {
$chain->setSuppression(true);
$whenSuppressed = ini_get('display_errors');
$chain->setSuppression(false);
$whenNotSuppressed = ini_get('display_errors');
})->execute();
// Errors can be suppressed an un-suppressed when initially enabled
$this->assertTrue((bool)$whenNotSuppressed);
$this->assertFalse((bool)$whenSuppressed);
// Fatal error
$chain = new ErrorControlChainTest_Chain();