mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge branch '4.0' into 4
This commit is contained in:
commit
e4b936c7c7
@ -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
|
||||
|
||||
|
@ -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 ~ /\.. {
|
||||
|
@ -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,
|
||||
];
|
||||
}
|
||||
```
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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>');
|
||||
|
@ -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>";
|
||||
}
|
||||
|
@ -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'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user