diff --git a/src/ClientDataToEntityConverter.php b/src/ClientDataToEntityConverter.php index 18e16bb10785099226d3e9723824e09debe9a601..b722f6530a2af2f9d66c383266f94a2361f638e7 100644 --- a/src/ClientDataToEntityConverter.php +++ b/src/ClientDataToEntityConverter.php @@ -60,7 +60,11 @@ class ClientDataToEntityConverter { throw new ConstraintViolationException(new EntityConstraintViolationList($entity, iterator_to_array($e->getConstraintViolationList()))); } - $updated_entity_form_fields = $this->setEntityFields($entity, $entity_form_fields); + // The current user may not have access any other fields on the entity or + // this function may have been called to only update the layout. + $updated_entity_form_fields = \count($entity_form_fields) !== 0 ? + $this->setEntityFields($entity, $entity_form_fields) : + []; $original_entity_violations = $entity->validate(); // Validation happens using the server-side representation, but the // error message should use the client-side representation received in diff --git a/src/Controller/ApiLayoutController.php b/src/Controller/ApiLayoutController.php index 8f4a22f0a88141f93b19e54109996199c3fc3df5..7aaa2328adb7a45fd9269aa449df923e4a3d86cb 100644 --- a/src/Controller/ApiLayoutController.php +++ b/src/Controller/ApiLayoutController.php @@ -37,7 +37,6 @@ use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; */ final class ApiLayoutController { - use ClientServerConversionTrait; use EntityFormTrait; const ENTITY_DUPLICATE_SUFFIX = ' (Copy)'; @@ -380,7 +379,12 @@ final class ApiLayoutController { // 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'], + // If we are not auto-saving there is no reason to convert the + // 'entity_form_fields'. This can cause access issue for just viewing the + // preview. This runs the conversion as if the user had no access to edit + // the entity fields which is all the that is necessary when not + // auto-saving. + 'entity_form_fields' => $updateAutoSave ? $body['entity_form_fields'] : [], ], $entity, validate: FALSE); // Store the auto-save entry. if ($updateAutoSave) { diff --git a/tests/src/Kernel/ApiLayoutControllerGetTest.php b/tests/src/Kernel/ApiLayoutControllerGetTest.php index 3d85ae20df3da9392d174cf9b4e5c43de061d381..2c9a2201ab6f584167e75b825381407b087c55c1 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; @@ -376,6 +377,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',