diff --git a/_build/test/Tests/Controllers/Resources/ResourceManagerControllerGetInstanceTest.php b/_build/test/Tests/Controllers/Resources/ResourceManagerControllerGetInstanceTest.php new file mode 100644 index 00000000000..7fbf52815cd --- /dev/null +++ b/_build/test/Tests/Controllers/Resources/ResourceManagerControllerGetInstanceTest.php @@ -0,0 +1,222 @@ + */ + private $createdResourceIds = []; + + + /** + * @before + */ + public function setUpFixtures() + { + parent::setUpFixtures(); + require_once MODX_MANAGER_PATH . 'controllers/default/resource/resource.class.php'; + require_once MODX_MANAGER_PATH . 'controllers/default/resource/data.class.php'; + require_once MODX_MANAGER_PATH . 'controllers/default/resource/create.class.php'; + require_once MODX_MANAGER_PATH . 'controllers/default/resource/weblink/data.class.php'; + } + + + /** + * @after + */ + public function tearDownFixtures() + { + foreach ($this->createdResourceIds as $id) { + $resource = $this->modx->getObject(modResource::class, $id); + if ($resource) { + $resource->remove(); + } + } + $this->createdResourceIds = []; + $_REQUEST = []; + $_GET = []; + parent::tearDownFixtures(); + } + + + private function createResourceWithClassKey(string $classKey, string $objectClass = modDocument::class): int + { + $resource = $this->modx->newObject($objectClass); + $resource->fromArray([ + 'pagetitle' => '15080-getinstance-' . uniqid('', true), + 'alias' => '15080-gi-' . uniqid(), + 'parent' => 0, + 'template' => 0, + 'context_key' => 'web', + 'class_key' => $classKey, + 'published' => 0, + ]); + $this->assertTrue($resource->save(), 'Failed to save test resource'); + if ($resource->get('class_key') !== $classKey) { + $resource->set('class_key', $classKey); + $this->assertTrue($resource->save(), 'Failed to force class_key on test resource'); + } + $id = (int)$resource->get('id'); + $this->createdResourceIds[] = $id; + + return $id; + } + + + private function getDataController(array $request) + { + $_REQUEST = $request; + $_GET = $request; + + return \ResourceManagerController::getInstance($this->modx, 'ResourceDataManagerController', [ + 'namespace' => 'core', + 'namespace_path' => MODX_MANAGER_PATH, + 'action' => 'Resource/Data', + ]); + } + + + public function testDocumentDataManagerControllerAlias() + { + $this->assertTrue(class_exists('DocumentDataManagerController')); + $this->assertTrue(is_a('DocumentDataManagerController', \ResourceDataManagerController::class, true)); + } + + + public function testGetInstanceWithShortClassKeyInDatabase() + { + $id = $this->createResourceWithClassKey('modDocument'); + $stored = $this->modx->getObject(modResource::class, $id); + $this->assertSame('modDocument', $stored->get('class_key')); + + $controller = $this->getDataController([ + 'id' => (string)$id, + 'a' => 'resource/data', + ]); + + $this->assertInstanceOf(\ResourceDataManagerController::class, $controller); + $this->assertSame(modDocument::class, $controller->resourceClass); + + $stored = $this->modx->getObject(modResource::class, $id); + $this->assertSame( + 'modDocument', + $stored->get('class_key'), + 'Overview must not rewrite short modDocument on GET' + ); + } + + + public function testGetInstanceWithShortModResourceClassKeyInDatabase() + { + $id = $this->createResourceWithClassKey('modResource'); + $controller = $this->getDataController([ + 'id' => (string)$id, + 'a' => 'resource/data', + ]); + + $this->assertInstanceOf(\ResourceDataManagerController::class, $controller); + $this->assertSame(modDocument::class, $controller->resourceClass); + + $stored = $this->modx->getObject(modResource::class, $id); + $this->assertSame(modDocument::class, $stored->get('class_key')); + } + + + public function testOverviewLoadsResourceWithShortClassKey() + { + $id = $this->createResourceWithClassKey('modDocument'); + $controller = $this->getDataController([ + 'id' => (string)$id, + 'a' => 'resource/data', + ]); + $controller->setProperties(['id' => $id]); + + $loaded = $this->modx->getObject(modResource::class, $id); + $this->assertNotNull($loaded); + $this->assertSame('modDocument', $loaded->get('class_key')); + + // Mirror ResourceDataManagerController::process load path + $fromProcessPath = $this->modx->getObject(modResource::class, $id); + $this->assertNotNull($fromProcessPath); + $this->assertSame($id, (int)$fromProcessPath->get('id')); + } + + + public function testGetInstanceWithFqcnClassKeyRequest() + { + $controller = $this->getDataController([ + 'a' => 'resource/data', + 'class_key' => modDocument::class, + ]); + + $this->assertInstanceOf(\ResourceDataManagerController::class, $controller); + $this->assertSame(modDocument::class, $controller->resourceClass); + } + + + public function testGetInstanceWithShortClassKeyRequest() + { + $controller = $this->getDataController([ + 'a' => 'resource/data', + 'class_key' => 'modDocument', + ]); + + $this->assertInstanceOf(\ResourceDataManagerController::class, $controller); + $this->assertSame(modDocument::class, $controller->resourceClass); + } + + + public function testGetInstanceCreateWithFqcnClassKeyRequest() + { + $_REQUEST = [ + 'a' => 'resource/create', + 'class_key' => modDocument::class, + 'parent' => 1, + 'context_key' => 'web', + ]; + $_GET = $_REQUEST; + + $controller = \ResourceManagerController::getInstance($this->modx, 'ResourceCreateManagerController', [ + 'namespace' => 'core', + 'namespace_path' => MODX_MANAGER_PATH, + 'action' => 'Resource/Create', + ]); + + $this->assertInstanceOf(\ResourceCreateManagerController::class, $controller); + $this->assertSame(modDocument::class, $controller->resourceClass); + } + + + public function testGetInstanceKeepsWeblinkDerivative() + { + $id = $this->createResourceWithClassKey(modWebLink::class, modWebLink::class); + + $controller = $this->getDataController([ + 'id' => (string)$id, + 'a' => 'resource/data', + ]); + + $this->assertInstanceOf(\WebLinkDataManagerController::class, $controller); + $this->assertSame(modWebLink::class, $controller->resourceClass); + } + + + public function testGetInstanceWeblinkClassKeyRequest() + { + $controller = $this->getDataController([ + 'a' => 'resource/data', + 'class_key' => modWebLink::class, + ]); + + $this->assertInstanceOf(\WebLinkDataManagerController::class, $controller); + $this->assertSame(modWebLink::class, $controller->resourceClass); + } +} diff --git a/manager/controllers/default/resource/data.class.php b/manager/controllers/default/resource/data.class.php index e1674222509..fe7f146c161 100644 --- a/manager/controllers/default/resource/data.class.php +++ b/manager/controllers/default/resource/data.class.php @@ -81,7 +81,8 @@ public function process(array $scriptProperties = []) $placeholders = []; $id = (int)$this->scriptProperties['id']; - if (!$this->resource = $this->modx->getObject($this->resourceClass, $id)) { + // Load via modResource so legacy short class_key values (e.g. modDocument) still resolve under STI + if (!$this->resource = $this->modx->getObject(modResource::class, $id)) { return $this->modx->lexicon('resource_err_nfs', ['id' => $this->scriptProperties['id']]); } @@ -196,3 +197,5 @@ public function loadRichTextEditor() { } } + +class_alias(ResourceDataManagerController::class, 'DocumentDataManagerController'); diff --git a/manager/controllers/default/resource/resource.class.php b/manager/controllers/default/resource/resource.class.php index b9452a00bb4..f2a8d533975 100644 --- a/manager/controllers/default/resource/resource.class.php +++ b/manager/controllers/default/resource/resource.class.php @@ -60,6 +60,18 @@ abstract class ResourceManagerController extends modManagerController public $canCreateRoot = true; + /** + * Legacy and FQCN class keys that resolve to the core document controllers, + * not a derivative resource type (weblink, symlink, static, custom). + */ + private const CORE_DOCUMENT_CLASS_KEYS = [ + modDocument::class, + modResource::class, + 'modDocument', + 'modResource', + ]; + + /** * Return the appropriate Resource controller class based on the class_key request parameter * @@ -76,19 +88,28 @@ public static function getInstance(modX $modx, $className, array $config = []) $resourceClass = modDocument::class; $isDerivative = false; if (!empty($_REQUEST['class_key'])) { - $isDerivative = true; - $resourceClass = in_array($_REQUEST['class_key'], [modDocument::class, modResource::class]) ? modDocument::class - : $_REQUEST['class_key']; - if ($resourceClass == modResource::class) $resourceClass = modDocument::class; + if (in_array($_REQUEST['class_key'], self::CORE_DOCUMENT_CLASS_KEYS, true)) { + $resourceClass = modDocument::class; + } else { + $isDerivative = true; + $resourceClass = $_REQUEST['class_key']; + } } elseif (!empty($_REQUEST['id']) && $_REQUEST['id'] != 'undefined' && strlen($_REQUEST['id']) === strlen((int)$_REQUEST['id'])) { /** @var modResource $resource */ $resource = $modx->getObject(modResource::class, ['id' => $_REQUEST['id']]); - if ($resource && !in_array($resource->get('class_key'), [modDocument::class, modResource::class])) { - $isDerivative = true; - $resourceClass = $resource->get('class_key'); - } elseif ($resource && $resource->get('class_key') == modResource::class) { /* fix improper class key */ - $resource->set('class_key', modDocument::class); - $resource->save(); + if ($resource) { + $classKey = $resource->get('class_key'); + if (in_array($classKey, self::CORE_DOCUMENT_CLASS_KEYS, true)) { + /* Fix improper class_key values that are not real derivatives */ + if (in_array($classKey, [modResource::class, 'modResource'], true)) { + $resource->set('class_key', modDocument::class); + $resource->save(); + } + $resourceClass = modDocument::class; + } else { + $isDerivative = true; + $resourceClass = $classKey; + } } }