Merge branch '4.0' into 4

This commit is contained in:
Daniel Hensby 2018-01-12 14:40:33 +00:00
commit e4b936c7c7
No known key found for this signature in database
GPG Key ID: B00D1E9767F0B06E
11 changed files with 116 additions and 44 deletions

View File

@ -45,8 +45,8 @@ Our web-based [PHP installer](installation/) can check if you meet the requireme
* We recommend enabling content compression (for example with mod_deflate) to speed up the delivery of HTML, CSS, and JavaScript.
* One of the following operating systems:
* Linux/Unix/BSD
* Microsoft Windows XP SP3, Vista, Windows 7, Server 2008, Server 2008 R2
* Mac OS X 10.4+
* Windows
* Mac OS X
## Web server hardware requirements

View File

@ -42,7 +42,7 @@ you are running your site configuration with a separate `public/` webroot folder
deny all;
}
sendfile on;
try_files $uri index.php?$query_string;
try_files $uri /index.php?$query_string;
}
location ~ /\.. {

View File

@ -69,7 +69,7 @@ This is useful where there could be many use cases for a particular data structu
An additional column is created called "`<relationship-name>`Class", which along
with the ID column identifies the object.
To specify that a has_one relation is polymorphic set the type to 'DataObject'.
To specify that a has_one relation is polymorphic set the type to [api:SilverStripe\ORM\DataObject]
Ideally, the associated has_many (or belongs_to) should be specified with dot notation.
```php
@ -94,7 +94,7 @@ class Fan extends DataObject
// Generates columns FanOfID and FanOfClass
private static $has_one = [
"FanOf" => "DataObject"
"FanOf" => DataObject::class,
];
}
```

View File

@ -103,19 +103,23 @@ class HTTPRequestBuilder
$headers['Content-Length'] = $server['CONTENT_LENGTH'];
}
// Enable HTTP Basic authentication workaround for PHP running in CGI mode with Apache
// Depending on server configuration the auth header may be in HTTP_AUTHORIZATION or
// REDIRECT_HTTP_AUTHORIZATION
$authHeader = null;
if (isset($headers['Authorization'])) {
$authHeader = $headers['Authorization'];
} elseif (isset($server['REDIRECT_HTTP_AUTHORIZATION'])) {
$authHeader = $server['REDIRECT_HTTP_AUTHORIZATION'];
}
// Ensure basic auth is available via headers
if (isset($server['PHP_AUTH_USER']) && isset($server['PHP_AUTH_PW'])) {
// Shift PHP_AUTH_* into headers so they are available via request
$headers['PHP_AUTH_USER'] = $server['PHP_AUTH_USER'];
$headers['PHP_AUTH_PW'] = $server['PHP_AUTH_PW'];
} elseif (!empty($headers['Authorization']) && preg_match('/Basic\s+(.*)$/i', $headers['Authorization'], $matches)) {
// Enable HTTP Basic authentication workaround for PHP running in CGI mode with Apache
// Depending on server configuration the auth header may be in HTTP_AUTHORIZATION or
// REDIRECT_HTTP_AUTHORIZATION
//
// The follow rewrite rule must be in the sites .htaccess file to enable this workaround
// RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
list($name, $password) = explode(':', base64_decode($matches[1]));
} elseif ($authHeader && preg_match('/Basic\s+(?<token>.*)$/i', $authHeader, $matches)) {
list($name, $password) = explode(':', base64_decode($matches['token']));
$headers['PHP_AUTH_USER'] = $name;
$headers['PHP_AUTH_PW'] = $password;
}

View File

@ -208,6 +208,7 @@ class ClassInfo
* eg: self::class_name('dataobJEct'); //returns 'DataObject'
*
* @param string|object $nameOrObject The classname or object you want to normalise
* @throws \ReflectionException
* @return string The normalised class name
*/
public static function class_name($nameOrObject)

View File

@ -495,17 +495,22 @@ trait Extensible
/**
* Get an extension instance attached to this object by name.
*
* @uses hasExtension()
*
* @param string $extension
* @return Extension
* @return Extension|null
*/
public function getExtensionInstance($extension)
{
$instances = $this->getExtensionInstances();
return isset($instances[$extension])
? $instances[$extension]
: null;
if (array_key_exists($extension, $instances)) {
return $instances[$extension];
}
// in case Injector has been used to replace an extension
foreach ($instances as $instance) {
if (is_a($instance, $extension)) {
return $instance;
}
}
return null;
}
/**
@ -524,8 +529,7 @@ trait Extensible
*/
public function hasExtension($extension)
{
$instances = $this->getExtensionInstances();
return isset($instances[$extension]);
return (bool) $this->getExtensionInstance($extension);
}
/**
@ -553,8 +557,16 @@ trait Extensible
if ($extensions) {
foreach ($extensions as $extension) {
$instance = Injector::inst()->get($extension);
$this->extension_instances[get_class($instance)] = $instance;
$name = $extension;
// Allow service names of the form "%$ServiceName"
if (substr($name, 0, 2) == '%$') {
$name = substr($name, 2);
}
$name = trim(strtok($name, '('));
if (class_exists($name)) {
$name = ClassInfo::class_name($name);
}
$this->extension_instances[$name] = Injector::inst()->get($extension);
}
}
}

View File

@ -128,7 +128,7 @@ class FileField extends FormField implements FileHandleField
*/
public function saveInto(DataObjectInterface $record)
{
if (!isset($_FILES[$this->name])) {
if (!isset($_FILES[$this->name]['error']) || $_FILES[$this->name]['error'] == UPLOAD_ERR_NO_FILE) {
return;
}

View File

@ -196,7 +196,7 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM
'<button type="button" name="showFilter" title="Open search and filter" class="btn btn-secondary font-icon-search btn--no-text btn--icon-large grid-field__filter-open"></button>'
);
} else {
$field = new LiteralField($fieldName, '<span class="non-sortable"></span>');
$field = new LiteralField($fieldName, '<span class="non-sortable">' . $title . '</span>');
}
} else {
$field = new LiteralField($fieldName, '<span class="non-sortable">' . $title . '</span>');

View File

@ -318,22 +318,6 @@ class DatabaseAdmin extends Controller
}
}
foreach ($dataClasses as $dataClass) {
// Check if class exists before trying to instantiate - this sidesteps any manifest weirdness
// Test_ indicates that it's the data class is part of testing system
if (strpos($dataClass, 'Test_') === false && class_exists($dataClass)) {
if (!$quiet) {
if (Director::is_cli()) {
echo " * $dataClass\n";
} else {
echo "<li>$dataClass</li>\n";
}
}
singleton($dataClass)->requireDefaultRecords();
}
}
// Remap obsolete class names
$schema = DataObject::getSchema();
foreach ($this->config()->classname_value_remapping as $oldClassName => $newClassName) {
@ -353,7 +337,7 @@ class DatabaseAdmin extends Controller
$updateQueries = [sprintf($updateQuery, '')];
// Remap versioned table ClassName values as well
$class = singleton($newClassName);
$class = DataObject::singleton($newClassName);
if ($class->has_extension(Versioned::class)) {
if ($class->hasStages()) {
$updateQueries[] = sprintf($updateQuery, '_Live');
@ -367,6 +351,23 @@ class DatabaseAdmin extends Controller
}
}
// Require all default records
foreach ($dataClasses as $dataClass) {
// Check if class exists before trying to instantiate - this sidesteps any manifest weirdness
// Test_ indicates that it's the data class is part of testing system
if (strpos($dataClass, 'Test_') === false && class_exists($dataClass)) {
if (!$quiet) {
if (Director::is_cli()) {
echo " * $dataClass\n";
} else {
echo "<li>$dataClass</li>\n";
}
}
DataObject::singleton($dataClass)->requireDefaultRecords();
}
}
if (!$quiet && !Director::is_cli()) {
echo "</ul>";
}

View File

@ -52,7 +52,6 @@ class HTTPRequestBuilderTest extends SapphireTest
];
$this->assertEquals($headers, HTTPRequestBuilder::extractRequestHeaders($request));
$request = [
'PHP_AUTH_USER' => 'admin',
'PHP_AUTH_PW' => 'password',
@ -62,5 +61,29 @@ class HTTPRequestBuilderTest extends SapphireTest
'PHP_AUTH_PW' => 'password',
];
$this->assertEquals($headers, HTTPRequestBuilder::extractRequestHeaders($request));
$request = [
'REDIRECT_HTTP_AUTHORIZATION' => 'Basic YWRtaW46cGFzc3dvcmQ=',
];
$headers = [
'PHP_AUTH_USER' => 'admin',
'PHP_AUTH_PW' => 'password',
];
$this->assertEquals($headers, HTTPRequestBuilder::extractRequestHeaders($request));
$request = [
'HTTP_AUTHORIZATION' => 'Basic YWRtaW46cGFzc3dvcmQ=',
'REDIRECT_HTTP_AUTHORIZATION' => 'Basic dXNlcjphdXRo=',
];
$headers = [
'PHP_AUTH_USER' => 'admin',
'PHP_AUTH_PW' => 'password',
'Authorization' => 'Basic YWRtaW46cGFzc3dvcmQ=',
];
$this->assertEquals(
$headers,
HTTPRequestBuilder::extractRequestHeaders($request),
'Prefer HTTP_AUTHORIZATION over REDIRECT_HTTP_AUTHORIZATION'
);
}
}

View File

@ -195,11 +195,13 @@ class ObjectTest extends SapphireTest
);
$inst = new ExtensionTest();
$extensions = $inst->getExtensionInstances();
$this->assertEquals(count($extensions), 2);
$this->assertCount(2, $extensions);
$this->assertArrayHasKey(ExtendTest1::class, $extensions);
$this->assertInstanceOf(
ExtendTest1::class,
$extensions[ExtendTest1::class]
);
$this->assertArrayHasKey(ExtendTest2::class, $extensions);
$this->assertInstanceOf(
ExtendTest2::class,
$extensions[ExtendTest2::class]
@ -507,4 +509,33 @@ class ObjectTest extends SapphireTest
ClassInfo::parse_class_spec('\Test\MyClass')
);
}
public function testInjectedExtensions()
{
$mockExtension = $this->createMock(TestExtension::class);
$mockClass = get_class($mockExtension);
$object = new ExtensionTest2();
// sanity check
$this->assertNotEquals(TestExtension::class, $mockClass);
$this->assertTrue($object->hasExtension(TestExtension::class));
$this->assertFalse($object->hasExtension($mockClass));
$this->assertCount(1, $object->getExtensionInstances());
$this->assertInstanceOf(TestExtension::class, $object->getExtensionInstance(TestExtension::class));
$this->assertNotInstanceOf($mockClass, $object->getExtensionInstance(TestExtension::class));
Injector::inst()->registerService($mockExtension, TestExtension::class);
$object = new ExtensionTest2();
$this->assertTrue($object->hasExtension(TestExtension::class));
$this->assertTrue($object->hasExtension($mockClass));
$this->assertCount(1, $object->getExtensionInstances());
$this->assertInstanceOf(TestExtension::class, $object->getExtensionInstance(TestExtension::class));
$this->assertInstanceOf($mockClass, $object->getExtensionInstance(TestExtension::class));
$this->assertInstanceOf(TestExtension::class, $object->getExtensionInstance($mockClass));
$this->assertInstanceOf($mockClass, $object->getExtensionInstance($mockClass));
}
}