From 369653b5dfcc3e0ed08a49a68b74b1d3da92aa96 Mon Sep 17 00:00:00 2001 From: Raissa North Date: Fri, 15 Dec 2017 16:45:46 +1300 Subject: [PATCH 1/9] FIX Ensure last GridField column when non sortable has its title displayed --- src/Forms/GridField/GridFieldSortableHeader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Forms/GridField/GridFieldSortableHeader.php b/src/Forms/GridField/GridFieldSortableHeader.php index 0dd66a64c..f4eb9344b 100644 --- a/src/Forms/GridField/GridFieldSortableHeader.php +++ b/src/Forms/GridField/GridFieldSortableHeader.php @@ -196,7 +196,7 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM '' ); } else { - $field = new LiteralField($fieldName, ''); + $field = new LiteralField($fieldName, '' . $title . ''); } } else { $field = new LiteralField($fieldName, '' . $title . ''); From ce07e4781e8fbb5d46b2ff4d9d9115fb37be36dc Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Thu, 14 Dec 2017 16:49:20 +1300 Subject: [PATCH 2/9] BUG Do database migrations before default records Fixes #7703 --- src/ORM/DatabaseAdmin.php | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) diff --git a/src/ORM/DatabaseAdmin.php b/src/ORM/DatabaseAdmin.php index 945b787e8..4d87bc98c 100644 --- a/src/ORM/DatabaseAdmin.php +++ b/src/ORM/DatabaseAdmin.php @@ -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 "
  • $dataClass
  • \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 "
  • $dataClass
  • \n"; + } + } + + DataObject::singleton($dataClass)->requireDefaultRecords(); + } + } + if (!$quiet && !Director::is_cli()) { echo ""; } From f885101a1b7d6a439b51615e566d4fc1879d1017 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Thu, 21 Dec 2017 09:50:14 +1300 Subject: [PATCH 3/9] BUG Fix basic auth in PHP-CGI Fixes #7717 --- src/Control/HTTPRequestBuilder.php | 20 +++++++++------- tests/php/Control/HTTPRequestBuilderTest.php | 25 +++++++++++++++++++- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/Control/HTTPRequestBuilder.php b/src/Control/HTTPRequestBuilder.php index 378ea3b10..07c5753b4 100644 --- a/src/Control/HTTPRequestBuilder.php +++ b/src/Control/HTTPRequestBuilder.php @@ -99,19 +99,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+(?.*)$/i', $authHeader, $matches)) { + list($name, $password) = explode(':', base64_decode($matches['token'])); $headers['PHP_AUTH_USER'] = $name; $headers['PHP_AUTH_PW'] = $password; } diff --git a/tests/php/Control/HTTPRequestBuilderTest.php b/tests/php/Control/HTTPRequestBuilderTest.php index 4d5063c54..23a6c0821 100644 --- a/tests/php/Control/HTTPRequestBuilderTest.php +++ b/tests/php/Control/HTTPRequestBuilderTest.php @@ -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' + ); } } From fb5476cb7092b3cdbfee9fb171c6a51ad1802b40 Mon Sep 17 00:00:00 2001 From: Andrew Aitken-Fincham Date: Wed, 3 Jan 2018 12:01:18 +0000 Subject: [PATCH 4/9] check for UPLOAD_ERR_NO_FILE on FileField::saveinto --- src/Forms/FileField.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Forms/FileField.php b/src/Forms/FileField.php index 44e104b85..41372d3af 100644 --- a/src/Forms/FileField.php +++ b/src/Forms/FileField.php @@ -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; } From 164edd7df3c38756147dea88df9165d9f705b5be Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Mon, 8 Jan 2018 14:10:11 +1300 Subject: [PATCH 5/9] DOCS Correct docs on polymorphic dataobject usage Fixes #7732 --- docs/en/02_Developer_Guides/00_Model/02_Relations.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/02_Developer_Guides/00_Model/02_Relations.md b/docs/en/02_Developer_Guides/00_Model/02_Relations.md index 36661aa98..c6933e2a7 100644 --- a/docs/en/02_Developer_Guides/00_Model/02_Relations.md +++ b/docs/en/02_Developer_Guides/00_Model/02_Relations.md @@ -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 "``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, ]; } ``` From de6afd4405386101c0ebd291ff25aa6bcf6b96de Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Thu, 11 Jan 2018 14:08:29 +0000 Subject: [PATCH 6/9] TEST Cant reliably use Injector to replace Extensions --- tests/php/Core/ObjectTest.php | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/tests/php/Core/ObjectTest.php b/tests/php/Core/ObjectTest.php index 46a4c4619..886a5cd0a 100644 --- a/tests/php/Core/ObjectTest.php +++ b/tests/php/Core/ObjectTest.php @@ -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)); + } } From 2b868b09f53b8bb42a92704892498f034b5efdd7 Mon Sep 17 00:00:00 2001 From: Damian Mooyman Date: Fri, 12 Jan 2018 09:25:16 +1300 Subject: [PATCH 7/9] DOCS Fix try_files index.php reference Fixes https://github.com/silverstripe/silverstripe-cms/issues/2067#issuecomment-356879060 --- .../01_Installation/How_To/Configure_Nginx.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/en/00_Getting_Started/01_Installation/How_To/Configure_Nginx.md b/docs/en/00_Getting_Started/01_Installation/How_To/Configure_Nginx.md index fdd45c4c3..8d3051d45 100644 --- a/docs/en/00_Getting_Started/01_Installation/How_To/Configure_Nginx.md +++ b/docs/en/00_Getting_Started/01_Installation/How_To/Configure_Nginx.md @@ -41,7 +41,7 @@ But enough of the disclaimer, on to the actual configuration — typically in `n deny all; } sendfile on; - try_files $uri index.php?$query_string; + try_files $uri /index.php?$query_string; } location ~ /framework/.*(main|rpc|tiny_mce_gzip)\.php$ { From fb1be0619f0e247749f6a9a9f8d02c26ed3aed6e Mon Sep 17 00:00:00 2001 From: Ingo Schommer Date: Fri, 12 Jan 2018 10:24:13 +1300 Subject: [PATCH 8/9] Removed version numbers from OS req docs Too icky to keep updated as version numbers change. It's really just a matter of what PHP version supports which OS, from a SilverStripe side we don't have any constraints there. Well, technically Windows 3.11 won't work because the filesystem will fall over, but otherwise LAMP/WAMP is pretty forgiving. --- docs/en/00_Getting_Started/00_Server_Requirements.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/en/00_Getting_Started/00_Server_Requirements.md b/docs/en/00_Getting_Started/00_Server_Requirements.md index c1ba6b938..710c62c7c 100644 --- a/docs/en/00_Getting_Started/00_Server_Requirements.md +++ b/docs/en/00_Getting_Started/00_Server_Requirements.md @@ -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 From 7d66342496698f7ab329e38c0a980ede851c105e Mon Sep 17 00:00:00 2001 From: Daniel Hensby Date: Thu, 11 Jan 2018 14:12:06 +0000 Subject: [PATCH 9/9] FIX Allow extension instances to be overridden by injector --- src/Core/ClassInfo.php | 1 + src/Core/Extensible.php | 32 ++++++++++++++++++++++---------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/src/Core/ClassInfo.php b/src/Core/ClassInfo.php index 12e707d82..3029b200f 100644 --- a/src/Core/ClassInfo.php +++ b/src/Core/ClassInfo.php @@ -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) diff --git a/src/Core/Extensible.php b/src/Core/Extensible.php index e4bb1be1b..8dc523bf5 100644 --- a/src/Core/Extensible.php +++ b/src/Core/Extensible.php @@ -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); } } }