diff --git a/src/Controller/ApiLayoutController.php b/src/Controller/ApiLayoutController.php index fead960229ef54f794ae22f1d6acf1315a5cf025..08c698d93bffe3a8c68da2cab9fe47f45b2b7d0c 100644 --- a/src/Controller/ApiLayoutController.php +++ b/src/Controller/ApiLayoutController.php @@ -360,16 +360,17 @@ final class ApiLayoutController { assert(isset($content)); \assert($entity instanceof FieldableEntityInterface); - $updated_entity_form_fields = $this->converter->convert([ - 'layout' => $content, - // An empty model needs to be represented as \stdClass so that it is - // correctly json encoded. But we need to convert it to an array before - // we can extract it. - 'model' => (array) $model, - 'entity_form_fields' => $body['entity_form_fields'], - ], $entity, validate: FALSE); + // Store the auto-save entry. if ($updateAutoSave) { + $updated_entity_form_fields = $this->converter->convert([ + 'layout' => $content, + // An empty model needs to be represented as \stdClass so that it is + // correctly json encoded. But we need to convert it to an array before + // we can extract it. + 'model' => (array) $model, + 'entity_form_fields' => $body['entity_form_fields'], + ], $entity, validate: FALSE); $this->autoSaveManager->save($entity, [ 'layout' => [$content], // An empty model needs to be represented as \stdClass so that it is @@ -386,6 +387,14 @@ final class ApiLayoutController { ]) + $body['entity_form_fields'], ]); } + else { + // If not saving do not call convert 'entity_form_fields' because this + // requires edit access to all fields which the user is not currently + // editing. + $item = $this->componentTreeLoader->load($entity); + assert(is_array($content['components'])); + $item->setValue($this->convertClientToServer($content['components'], (array) $model, $entity, FALSE)); + } $renderable = $this->componentTreeLoader->load($entity)->toRenderable(TRUE); if (isset($renderable[ComponentTreeStructure::ROOT_UUID])) { diff --git a/tests/src/Kernel/ApiLayoutControllerGetTest.php b/tests/src/Kernel/ApiLayoutControllerGetTest.php index e93f17599a26cb95a59d690f93cf5cc27a42b01a..52008579b442073869ab7f7df921ccf5d165ce5e 100644 --- a/tests/src/Kernel/ApiLayoutControllerGetTest.php +++ b/tests/src/Kernel/ApiLayoutControllerGetTest.php @@ -16,6 +16,7 @@ use Drupal\node\NodeInterface; use Drupal\Tests\experience_builder\Kernel\Traits\RequestTrait; use Drupal\Tests\experience_builder\TestSite\XBTestSetup; use Drupal\Tests\user\Traits\UserCreationTrait; +use Drupal\user\Entity\User; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -361,6 +362,68 @@ class ApiLayoutControllerGetTest extends ApiLayoutControllerTestBase { self::assertSame($isPublished, $json['isPublished']); } + /** + * Tests that auto_save entries with inaccessible fields do not cause errors. + * + * @covers \Drupal\experience_builder\Controller\ApiLayoutController::buildPreviewRenderable + */ + public function testInaccessibleFieldsInAutoSave(): void { + // Create a node to work with. + $node = Node::create([ + 'type' => 'article', + 'title' => 'Test Node', + ]); + $node->save(); + + // Set up the current user without access to path field. + $authenticated_role = $this->createRole(['access administration pages']); + $limited_user = $this->createUser([], NULL, FALSE, ['roles' => [$authenticated_role]]); + assert($limited_user instanceof User); + $this->setCurrentUser($limited_user); + + // Create an auto-save entry with a value for a field that the user doesn't have access to. + $autoSave = $this->container->get(AutoSaveManager::class); + assert($autoSave instanceof AutoSaveManager); + + // Create test data with a field the user doesn't have access to (path field). + $data = [ + 'layout' => [ + [ + 'nodeType' => 'region', + 'id' => 'content', + 'name' => 'Content', + 'components' => [], + ], + ], + 'model' => [], + 'entity_form_fields' => [ + 'title[0][value]' => 'Test Node', + // Path field that user doesn't have access to + 'path[0][alias]' => '/test-path', + ], + ]; + + $autoSave->save($node, $data); + + $url = Url::fromRoute('experience_builder.api.layout.get', [ + 'entity' => $node->id(), + 'entity_type' => 'node', + ]); + + // This should not throw an exception even though the auto_save data + // contains a value for path field that the user doesn't have access to. + $response = $this->request(Request::create($url->toString())); + + // Verify that the response is successful. + self::assertEquals(Response::HTTP_OK, $response->getStatusCode()); + + // Check that the response contains the correct title. + self::assertInstanceOf(JsonResponse::class, $response); + $json = json_decode($response->getContent() ?: '', TRUE); + self::assertArrayHasKey('entity_form_fields', $json); + self::assertEquals('Test Node', $json['entity_form_fields']['title[0][value]']); + } + public function testFieldException(): void { $page_type = NodeType::create([ 'type' => 'page',