diff --git a/Content/Application/ContentDataMapper/DataMapper/AuthorDataMapper.php b/Content/Application/ContentDataMapper/DataMapper/AuthorDataMapper.php index f4bf7cf7..998f484c 100644 --- a/Content/Application/ContentDataMapper/DataMapper/AuthorDataMapper.php +++ b/Content/Application/ContentDataMapper/DataMapper/AuthorDataMapper.php @@ -54,10 +54,18 @@ private function setAuthorData(AuthorInterface $dimensionContent, array $data): ); } + if (\array_key_exists('lastModified', $data)) { + $dimensionContent->setLastModified( + $data['lastModified'] && (\array_key_exists('lastModifiedEnabled', $data) && $data['lastModifiedEnabled']) + ? new \DateTime($data['lastModified']) + : null + ); + } + if (\array_key_exists('authored', $data)) { $dimensionContent->setAuthored( $data['authored'] - ? new \DateTimeImmutable($data['authored']) + ? new \DateTime($data['authored']) : null ); } diff --git a/Content/Application/ContentMerger/Merger/AuthorMerger.php b/Content/Application/ContentMerger/Merger/AuthorMerger.php index 0cccc559..eb5d586e 100644 --- a/Content/Application/ContentMerger/Merger/AuthorMerger.php +++ b/Content/Application/ContentMerger/Merger/AuthorMerger.php @@ -35,6 +35,10 @@ public function merge(object $targetObject, object $sourceObject): void $targetObject->setAuthor($author); } + if ($lastModified = $sourceObject->getLastModified()) { + $targetObject->setLastModified($lastModified); + } + if ($authored = $sourceObject->getAuthored()) { $targetObject->setAuthored($authored); } diff --git a/Content/Domain/Model/AuthorInterface.php b/Content/Domain/Model/AuthorInterface.php index 68e5d72d..9e765a7c 100644 --- a/Content/Domain/Model/AuthorInterface.php +++ b/Content/Domain/Model/AuthorInterface.php @@ -17,11 +17,17 @@ interface AuthorInterface { + public function getLastModifiedEnabled(): ?bool; + + public function getLastModified(): ?\DateTime; + + public function setLastModified(?\DateTime $lastModified): void; + public function getAuthor(): ?ContactInterface; public function setAuthor(?ContactInterface $author): void; - public function getAuthored(): ?\DateTimeImmutable; + public function getAuthored(): ?\DateTime; - public function setAuthored(?\DateTimeImmutable $authored): void; + public function setAuthored(?\DateTime $authored): void; } diff --git a/Content/Domain/Model/AuthorTrait.php b/Content/Domain/Model/AuthorTrait.php index 088a49e7..ff5d700f 100644 --- a/Content/Domain/Model/AuthorTrait.php +++ b/Content/Domain/Model/AuthorTrait.php @@ -26,10 +26,30 @@ trait AuthorTrait private $author; /** - * @var \DateTimeImmutable|null + * @var \DateTime|null */ private $authored; + /** + * @var \DateTime|null + */ + private $lastModified; + + public function getLastModifiedEnabled(): ?bool + { + return null !== $this->lastModified; + } + + public function getLastModified(): ?\DateTime + { + return $this->lastModified; + } + + public function setLastModified(?\DateTime $lastModified): void + { + $this->lastModified = $lastModified; + } + public function getAuthor(): ?ContactInterface { return $this->author; @@ -40,12 +60,12 @@ public function setAuthor(?ContactInterface $author): void $this->author = $author; } - public function getAuthored(): ?\DateTimeImmutable + public function getAuthored(): ?\DateTime { return $this->authored; } - public function setAuthored(?\DateTimeImmutable $authored): void + public function setAuthored(?\DateTime $authored): void { $this->authored = $authored; } diff --git a/Content/Domain/Model/TemplateTrait.php b/Content/Domain/Model/TemplateTrait.php index 2bfedc14..b0d8a025 100644 --- a/Content/Domain/Model/TemplateTrait.php +++ b/Content/Domain/Model/TemplateTrait.php @@ -43,6 +43,9 @@ public function getTemplateData(): array return $this->templateData; } + /** + * @param mixed[] $templateData + */ public function setTemplateData(array $templateData): void { $this->templateData = $templateData; diff --git a/Content/Infrastructure/Doctrine/MetadataLoader.php b/Content/Infrastructure/Doctrine/MetadataLoader.php index ad58ef6c..cf56377e 100644 --- a/Content/Infrastructure/Doctrine/MetadataLoader.php +++ b/Content/Infrastructure/Doctrine/MetadataLoader.php @@ -117,7 +117,8 @@ public function loadClassMetadata(LoadClassMetadataEventArgs $event): void } if ($reflection->implementsInterface(AuthorInterface::class)) { - $this->addField($metadata, 'authored', 'datetime_immutable', ['nullable' => true]); + $this->addField($metadata, 'authored', 'datetime', ['nullable' => true]); + $this->addField($metadata, 'lastModified', 'datetime', ['nullable' => true]); $this->addManyToOne($event, $metadata, 'author', ContactInterface::class, true); } diff --git a/Content/Infrastructure/Sulu/Link/ContentLinkProvider.php b/Content/Infrastructure/Sulu/Link/ContentLinkProvider.php index 7ee470e8..cf7d596f 100644 --- a/Content/Infrastructure/Sulu/Link/ContentLinkProvider.php +++ b/Content/Infrastructure/Sulu/Link/ContentLinkProvider.php @@ -89,6 +89,7 @@ public function preload(array $hrefs, $locale, $published = true): array return null; } + /** @var array{title?: string, name?: string} $data */ $data = $this->contentManager->normalize($resolvedDimensionContent); return new LinkItem( @@ -104,7 +105,10 @@ public function preload(array $hrefs, $locale, $published = true): array /** * @param B $dimensionContent - * @param mixed[] $data + * @param array{ + * title?: string, + * name?: string + * } $data */ protected function getTitle(DimensionContentInterface $dimensionContent, array $data): ?string { diff --git a/Content/Infrastructure/Sulu/Sitemap/ContentSitemapProvider.php b/Content/Infrastructure/Sulu/Sitemap/ContentSitemapProvider.php index e9010ba8..05b86649 100644 --- a/Content/Infrastructure/Sulu/Sitemap/ContentSitemapProvider.php +++ b/Content/Infrastructure/Sulu/Sitemap/ContentSitemapProvider.php @@ -168,7 +168,7 @@ public function getMaxPage($scheme, $host): int ->getQuery() ->getSingleScalarResult(); - return (int) \ceil($amount / $this->pageSize); + return (int) \ceil((int) $amount / $this->pageSize); } catch (NoResultException|NonUniqueResultException $e) { // @codeCoverageIgnore // TODO FIXME add testcase for this return 0; // @codeCoverageIgnore diff --git a/Content/Infrastructure/Sulu/Structure/ContentDocument.php b/Content/Infrastructure/Sulu/Structure/ContentDocument.php index d5fa0954..5fbd05d0 100644 --- a/Content/Infrastructure/Sulu/Structure/ContentDocument.php +++ b/Content/Infrastructure/Sulu/Structure/ContentDocument.php @@ -13,12 +13,17 @@ namespace Sulu\Bundle\ContentBundle\Content\Infrastructure\Sulu\Structure; +use Sulu\Bundle\ContactBundle\Entity\ContactInterface; +use Sulu\Bundle\ContentBundle\Content\Domain\Model\AuthorInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Model\ExcerptInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Model\SeoInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Model\TemplateInterface; use Sulu\Component\Content\Document\Behavior\ExtensionBehavior; +use Sulu\Component\Content\Document\Behavior\LocalizedAuthorBehavior; +use Sulu\Component\Persistence\Model\UserBlameInterface; +use Sulu\Component\Security\Authentication\UserInterface; -class ContentDocument implements ExtensionBehavior +class ContentDocument implements ExtensionBehavior, LocalizedAuthorBehavior { /** * @var TemplateInterface @@ -151,4 +156,79 @@ protected function createReadOnlyException(string $method): \BadMethodCallExcept ) ); } + + public function getLastModifiedEnabled(): ?bool + { + if ($this->content instanceof AuthorInterface) { + return $this->content->getLastModifiedEnabled(); + } + + return null; + } + + public function getLastModified(): ?\DateTime + { + if ($this->content instanceof AuthorInterface) { + return $this->content->getLastModified(); + } + + return null; + } + + /** + * @param \DateTime|null $lastModified + */ + public function setLastModified($lastModified): void + { + throw $this->createReadOnlyException(__METHOD__); + } + + public function getAuthored(): ?\DateTime + { + if ($this->content instanceof AuthorInterface) { + return $this->content->getAuthored(); + } + + return null; + } + + public function setAuthored($authored): void + { + throw $this->createReadOnlyException(__METHOD__); + } + + public function getAuthor(): ?ContactInterface + { + if ($this->content instanceof AuthorInterface) { + return $this->content->getAuthor(); + } + + return null; + } + + /** + * @param ContactInterface|null $contactId + */ + public function setAuthor($contactId): void + { + throw $this->createReadOnlyException(__METHOD__); + } + + public function getCreator(): ?UserInterface + { + if ($this->content instanceof UserBlameInterface) { + return $this->content->getCreator(); + } + + return null; + } + + public function getChanger(): ?UserInterface + { + if ($this->content instanceof UserBlameInterface) { + return $this->content->getChanger(); + } + + return null; + } } diff --git a/Content/Infrastructure/Sulu/Teaser/ContentTeaserProvider.php b/Content/Infrastructure/Sulu/Teaser/ContentTeaserProvider.php index bb1358a6..d88d2a67 100644 --- a/Content/Infrastructure/Sulu/Teaser/ContentTeaserProvider.php +++ b/Content/Infrastructure/Sulu/Teaser/ContentTeaserProvider.php @@ -113,6 +113,7 @@ function(ContentRichEntityInterface $contentRichEntity) use ($locale): ?Teaser { return null; } + /** @var array{title?: string|null, name?: string|null} $data */ $data = $this->contentManager->normalize($resolvedDimensionContent); return $this->createTeaser($resolvedDimensionContent, $data, $locale); @@ -125,7 +126,10 @@ function(ContentRichEntityInterface $contentRichEntity) use ($locale): ?Teaser { /** * @param B $dimensionContent - * @param array $data + * @param array{ + * title?: string|null, + * name?: string|null + * } $data */ protected function createTeaser(DimensionContentInterface $dimensionContent, array $data, string $locale): ?Teaser { @@ -139,10 +143,10 @@ protected function createTeaser(DimensionContentInterface $dimensionContent, arr $title = $this->getTitle($dimensionContent, $data); /** @var string $description */ - $description = $this->getDescription($dimensionContent, $data); // @phpstan-ignore-line + $description = $this->getDescription($dimensionContent, $data); /** @var string $moreText */ - $moreText = $this->getMoreText($dimensionContent, $data); // @phpstan-ignore-line + $moreText = $this->getMoreText($dimensionContent, $data); /** @var int $mediaId */ $mediaId = $this->getMediaId($dimensionContent, $data); @@ -162,7 +166,10 @@ protected function createTeaser(DimensionContentInterface $dimensionContent, arr /** * @param B $dimensionContent - * @param mixed[] $data + * @param array{ + * title?: string|null, + * name?: string|null + * } $data */ protected function getTitle(DimensionContentInterface $dimensionContent, array $data): ?string { diff --git a/Resources/config/forms/content_settings_author.xml b/Resources/config/forms/content_settings_author.xml index 43fb35d1..1ae9a7a1 100644 --- a/Resources/config/forms/content_settings_author.xml +++ b/Resources/config/forms/content_settings_author.xml @@ -13,6 +13,23 @@ sulu_content.author + + + sulu_content.last_modified_enabled + + + + + + + + + + + sulu_content.last_modified_date + + + sulu_content.authored_date diff --git a/Resources/translations/admin.de.json b/Resources/translations/admin.de.json index 6bc4b705..e7c632bb 100644 --- a/Resources/translations/admin.de.json +++ b/Resources/translations/admin.de.json @@ -3,6 +3,8 @@ "sulu_content.excerpt": "Auszug & Taxonomien", "sulu_content.content": "Inhalt", "sulu_content.published": "Veröffentlicht am", + "sulu_content.last_modified_enabled": "Aktualisierungsdatum aktivieren", + "sulu_content.last_modified_date": "Datum der Aktualisierung", "sulu_content.author": "Autor", "sulu_content.authored_date": "Verfasst am", "sulu_content.shadow_page": "Shadow Seite", diff --git a/Resources/translations/admin.en.json b/Resources/translations/admin.en.json index d7db66b2..b042f259 100644 --- a/Resources/translations/admin.en.json +++ b/Resources/translations/admin.en.json @@ -3,6 +3,8 @@ "sulu_content.excerpt": "Excerpt & Taxonomies", "sulu_content.content": "Content", "sulu_content.published": "Published on", + "sulu_content.last_modified_enabled": "Enable last modified date", + "sulu_content.last_modified_date": "Last modified date", "sulu_content.author": "Author", "sulu_content.authored_date": "Authored Date", "sulu_content.shadow_page": "Shadow page", diff --git a/Tests/Application/ExampleTestBundle/Repository/ExampleRepository.php b/Tests/Application/ExampleTestBundle/Repository/ExampleRepository.php index 1ec3f853..220c1151 100644 --- a/Tests/Application/ExampleTestBundle/Repository/ExampleRepository.php +++ b/Tests/Application/ExampleTestBundle/Repository/ExampleRepository.php @@ -185,7 +185,7 @@ public function countBy(array $filters = []): int $queryBuilder->select('COUNT(DISTINCT example.id)'); - return (int) $queryBuilder->getQuery()->getSingleScalarResult(); // @phpstan-ignore-line + return (int) $queryBuilder->getQuery()->getSingleScalarResult(); } /** diff --git a/Tests/Functional/Integration/ExampleControllerTest.php b/Tests/Functional/Integration/ExampleControllerTest.php index d7fd346d..89be7149 100644 --- a/Tests/Functional/Integration/ExampleControllerTest.php +++ b/Tests/Functional/Integration/ExampleControllerTest.php @@ -66,6 +66,8 @@ public function testPostPublish(): int 'excerptMedia' => null, 'author' => null, 'authored' => '2020-05-08T00:00:00+00:00', + 'lastModifiedEnabled' => true, + 'lastModified' => '2022-05-08T00:00:00+00:00', 'mainWebspace' => 'sulu-io', ]) ?: null); @@ -136,6 +138,8 @@ public function testPost(): int 'excerptMedia' => null, 'mainWebspace' => 'sulu-io', 'authored' => '2020-05-08T00:00:00+00:00', + 'lastModifiedEnabled' => false, + 'lastModified' => null, ]) ?: null); $response = $this->client->getResponse(); @@ -226,6 +230,8 @@ public function testPut(int $id): void 'excerptMedia' => null, 'authored' => '2020-06-09T00:00:00+00:00', 'mainWebspace' => 'sulu-io2', + 'lastModifiedEnabled' => true, + 'lastModified' => '2022-05-08T00:00:00+00:00', ]) ?: null); $response = $this->client->getResponse(); diff --git a/Tests/Functional/Integration/responses/example_get.json b/Tests/Functional/Integration/responses/example_get.json index 00aa7a07..12305ab0 100644 --- a/Tests/Functional/Integration/responses/example_get.json +++ b/Tests/Functional/Integration/responses/example_get.json @@ -38,5 +38,7 @@ "template": "example-2", "title": "Test Example", "url": "/my-example", - "workflowPlace": "unpublished" + "workflowPlace": "unpublished", + "lastModifiedEnabled": false, + "lastModified": null } diff --git a/Tests/Functional/Integration/responses/example_get_ghost_locale.json b/Tests/Functional/Integration/responses/example_get_ghost_locale.json index 99727b2a..b2fe3566 100644 --- a/Tests/Functional/Integration/responses/example_get_ghost_locale.json +++ b/Tests/Functional/Integration/responses/example_get_ghost_locale.json @@ -29,5 +29,7 @@ "stage": "draft", "template": null, "title": null, - "workflowPlace": null + "workflowPlace": null, + "lastModifiedEnabled": false, + "lastModified": null } diff --git a/Tests/Functional/Integration/responses/example_post.json b/Tests/Functional/Integration/responses/example_post.json index 30d31e1b..7a1aae6c 100644 --- a/Tests/Functional/Integration/responses/example_post.json +++ b/Tests/Functional/Integration/responses/example_post.json @@ -38,5 +38,7 @@ "title": "Test Example", "url": "/my-example", "workflowPlace": "unpublished", - "mainWebspace": "sulu-io" + "mainWebspace": "sulu-io", + "lastModifiedEnabled": false, + "lastModified": null } diff --git a/Tests/Functional/Integration/responses/example_post_publish.json b/Tests/Functional/Integration/responses/example_post_publish.json index 1fcfbf81..9ba5c964 100644 --- a/Tests/Functional/Integration/responses/example_post_publish.json +++ b/Tests/Functional/Integration/responses/example_post_publish.json @@ -38,5 +38,7 @@ "title": "Test Example", "url": "/my-example", "workflowPlace": "published", - "mainWebspace": "sulu-io" + "mainWebspace": "sulu-io", + "lastModifiedEnabled": true, + "lastModified": "2022-05-08T00:00:00+00:00" } diff --git a/Tests/Functional/Integration/responses/example_post_trigger_copy_locale.json b/Tests/Functional/Integration/responses/example_post_trigger_copy_locale.json index 88c9a53a..e5a39686 100644 --- a/Tests/Functional/Integration/responses/example_post_trigger_copy_locale.json +++ b/Tests/Functional/Integration/responses/example_post_trigger_copy_locale.json @@ -40,5 +40,7 @@ "template": "example-2", "title": "Test Example", "url": "/my-example", - "workflowPlace": "unpublished" + "workflowPlace": "unpublished", + "lastModifiedEnabled": false, + "lastModified": null } diff --git a/Tests/Functional/Integration/responses/example_post_trigger_unpublish.json b/Tests/Functional/Integration/responses/example_post_trigger_unpublish.json index bfa1a3cd..849a273c 100644 --- a/Tests/Functional/Integration/responses/example_post_trigger_unpublish.json +++ b/Tests/Functional/Integration/responses/example_post_trigger_unpublish.json @@ -38,5 +38,7 @@ "title": "Test Example", "url": "\/my-example", "workflowPlace": "unpublished", - "mainWebspace": "sulu-io" + "mainWebspace": "sulu-io", + "lastModifiedEnabled": true, + "lastModified": "2022-05-08T00:00:00+00:00" } diff --git a/Tests/Functional/Integration/responses/example_put.json b/Tests/Functional/Integration/responses/example_put.json index 31644c04..412d1cd4 100644 --- a/Tests/Functional/Integration/responses/example_put.json +++ b/Tests/Functional/Integration/responses/example_put.json @@ -1,6 +1,8 @@ { "author": null, "authored": "2020-06-09T00:00:00+00:00", + "lastModifiedEnabled": true, + "lastModified": "2022-05-08T00:00:00+00:00", "article": "

Test Article 2

", "availableLocales": [ "en", diff --git a/Tests/Unit/Content/Application/ContentDataMapper/DataMapper/AuthorDataMapperTest.php b/Tests/Unit/Content/Application/ContentDataMapper/DataMapper/AuthorDataMapperTest.php index 96d0de66..bfec8887 100644 --- a/Tests/Unit/Content/Application/ContentDataMapper/DataMapper/AuthorDataMapperTest.php +++ b/Tests/Unit/Content/Application/ContentDataMapper/DataMapper/AuthorDataMapperTest.php @@ -84,6 +84,8 @@ public function testMapData(): void $data = [ 'author' => 1, 'authored' => '2020-05-08T00:00:00+00:00', + 'lastModifiedEnabled' => true, + 'lastModified' => '2024-05-08T00:00:00+00:00', ]; $example = new Example(); @@ -100,8 +102,11 @@ public function testMapData(): void $this->assertSame($contact, $localizedDimensionContent->getAuthor()); $authored = $localizedDimensionContent->getAuthored(); + /** @var \DateTime $lastModified */ + $lastModified = $localizedDimensionContent->getLastModified(); $this->assertNotNull($authored); $this->assertSame('2020-05-08T00:00:00+00:00', $authored->format('c')); + $this->assertSame('2024-05-08T00:00:00+00:00', $lastModified->format('c')); } public function testMapDataNull(): void @@ -109,13 +114,15 @@ public function testMapDataNull(): void $data = [ 'author' => null, 'authored' => null, + 'lastModifiedEnabled' => false, + 'lastModified' => '2024-05-08T00:00:00+00:00', ]; $example = new Example(); $unlocalizedDimensionContent = new ExampleDimensionContent($example); $localizedDimensionContent = new ExampleDimensionContent($example); $localizedDimensionContent->setAuthor(new Contact()); - $localizedDimensionContent->setAuthored(new \DateTimeImmutable()); + $localizedDimensionContent->setAuthored(new \DateTime()); $this->contactFactory->create(Argument::cetera()) ->shouldNotBeCalled(); @@ -123,6 +130,7 @@ public function testMapDataNull(): void $authorMapper = $this->createAuthorDataMapperInstance(); $authorMapper->map($unlocalizedDimensionContent, $localizedDimensionContent, $data); + $this->assertNull($localizedDimensionContent->getLastModified()); $this->assertNull($localizedDimensionContent->getAuthor()); $this->assertNull($localizedDimensionContent->getAuthored()); } diff --git a/Tests/Unit/Content/Application/ContentDataMapper/DataMapper/WebspaceDataMapperTest.php b/Tests/Unit/Content/Application/ContentDataMapper/DataMapper/WebspaceDataMapperTest.php index 2d33b8a2..f1ec5f11 100644 --- a/Tests/Unit/Content/Application/ContentDataMapper/DataMapper/WebspaceDataMapperTest.php +++ b/Tests/Unit/Content/Application/ContentDataMapper/DataMapper/WebspaceDataMapperTest.php @@ -90,7 +90,7 @@ public function testMapDefaultWebspace(): void $webspace = new Webspace(); $webspace->setKey('default-webspace'); - $this->webspaceCollection->setWebspaces([$webspace]); + $this->webspaceCollection->setWebspaces(['default-webspace' => $webspace]); $authorMapper = $this->createWebspaceDataMapperInstance(); $authorMapper->map($unlocalizedDimensionContent, $localizedDimensionContent, $data); diff --git a/Tests/Unit/Content/Application/ContentMerger/Merger/AuthorMergerTest.php b/Tests/Unit/Content/Application/ContentMerger/Merger/AuthorMergerTest.php index 54532062..4d7b700f 100644 --- a/Tests/Unit/Content/Application/ContentMerger/Merger/AuthorMergerTest.php +++ b/Tests/Unit/Content/Application/ContentMerger/Merger/AuthorMergerTest.php @@ -62,15 +62,18 @@ public function testMergeSet(): void $merger = $this->getAuthorMergerInstance(); $contact = $this->prophesize(ContactInterface::class); - $authoredDate = new \DateTimeImmutable('2020-05-08T00:00:00+00:00'); + $authoredDate = new \DateTime('2020-05-08T00:00:00+00:00'); + $lastModifiedDate = new \DateTime('2020-05-08T00:00:00+00:00'); $source = $this->prophesize(DimensionContentInterface::class); $source->willImplement(AuthorInterface::class); + $source->getLastModified()->willReturn($lastModifiedDate)->shouldBeCalled(); $source->getAuthor()->willReturn($contact->reveal())->shouldBeCalled(); $source->getAuthored()->willReturn($authoredDate)->shouldBeCalled(); $target = $this->prophesize(DimensionContentInterface::class); $target->willImplement(AuthorInterface::class); + $target->setLastModified($lastModifiedDate)->shouldBeCalled(); $target->setAuthor($contact->reveal())->shouldBeCalled(); $target->setAuthored($authoredDate)->shouldBeCalled(); @@ -83,11 +86,13 @@ public function testMergeNotSet(): void $source = $this->prophesize(DimensionContentInterface::class); $source->willImplement(AuthorInterface::class); + $source->getLastModified()->willReturn(null)->shouldBeCalled(); $source->getAuthor()->willReturn(null)->shouldBeCalled(); $source->getAuthored()->willReturn(null)->shouldBeCalled(); $target = $this->prophesize(DimensionContentInterface::class); $target->willImplement(AuthorInterface::class); + $target->setLastModified(Argument::any())->shouldNotBeCalled(); $target->setAuthor(Argument::any())->shouldNotBeCalled(); $target->setAuthored(Argument::any())->shouldNotBeCalled(); diff --git a/Tests/Unit/Content/Application/ContentNormalizer/Normalizer/AuthorNormalizerTest.php b/Tests/Unit/Content/Application/ContentNormalizer/Normalizer/AuthorNormalizerTest.php index eab4cb36..61b61063 100644 --- a/Tests/Unit/Content/Application/ContentNormalizer/Normalizer/AuthorNormalizerTest.php +++ b/Tests/Unit/Content/Application/ContentNormalizer/Normalizer/AuthorNormalizerTest.php @@ -59,7 +59,7 @@ public function testEnhanceNotImplementAuthorInterface(): void $data = [ 'author' => 1, - 'authored' => new \DateTimeImmutable('2020-05-08T00:00:00+00:00'), + 'authored' => new \DateTime('2020-05-08T00:00:00+00:00'), ]; $this->assertSame( @@ -76,16 +76,21 @@ public function testEnhance(): void $contact = $this->prophesize(ContactInterface::class); $contact->getId()->shouldBeCalled()->willReturn(1); $object->getAuthor()->willReturn($contact->reveal()); - $authored = new \DateTimeImmutable('2020-05-08T00:00:00+00:00'); + $authored = new \DateTime('2020-05-08T00:00:00+00:00'); + $lastModified = new \DateTime('2022-05-08T00:00:00+00:00'); $data = [ 'author' => $contact->reveal(), 'authored' => $authored, + 'lastModifiedEnabled' => true, + 'lastModified' => $lastModified, ]; $expectedResult = [ 'author' => 1, 'authored' => $authored, + 'lastModifiedEnabled' => true, + 'lastModified' => $lastModified, ]; $this->assertSame( diff --git a/Tests/Unit/Content/Domain/Model/AuthorTraitTest.php b/Tests/Unit/Content/Domain/Model/AuthorTraitTest.php index f4a4425e..2ffe0cf7 100644 --- a/Tests/Unit/Content/Domain/Model/AuthorTraitTest.php +++ b/Tests/Unit/Content/Domain/Model/AuthorTraitTest.php @@ -42,9 +42,18 @@ public function testGetSetAuthor(): void public function testGetSetAuthored(): void { $model = $this->getAuthorInstance(); - $authored = new \DateTimeImmutable('2020-05-08T00:00:00+00:00'); + $authored = new \DateTime('2020-05-08T00:00:00+00:00'); $this->assertNull($model->getAuthored()); $model->setAuthored($authored); $this->assertSame($authored, $model->getAuthored()); } + + public function testGetSetLastModified(): void + { + $model = $this->getAuthorInstance(); + $lastModified = new \DateTime('2024-05-08T00:00:00+00:00'); + $model->setLastModified($lastModified); + $this->assertTrue($model->getLastModifiedEnabled()); + $this->assertSame($lastModified, $model->getLastModified()); + } } diff --git a/Tests/Unit/Content/Infrastructure/Doctrine/MetadataLoaderTest.php b/Tests/Unit/Content/Infrastructure/Doctrine/MetadataLoaderTest.php index 7eef7285..e19ed739 100644 --- a/Tests/Unit/Content/Infrastructure/Doctrine/MetadataLoaderTest.php +++ b/Tests/Unit/Content/Infrastructure/Doctrine/MetadataLoaderTest.php @@ -247,6 +247,7 @@ public function dataProvider(): \Generator [ 'author' => true, 'authored' => true, + 'lastModified' => null, ], [], [ diff --git a/Tests/Unit/Content/Infrastructure/Sulu/Page/Select/WebspaceSelectTest.php b/Tests/Unit/Content/Infrastructure/Sulu/Page/Select/WebspaceSelectTest.php index 29a4b6af..6bff0831 100644 --- a/Tests/Unit/Content/Infrastructure/Sulu/Page/Select/WebspaceSelectTest.php +++ b/Tests/Unit/Content/Infrastructure/Sulu/Page/Select/WebspaceSelectTest.php @@ -49,8 +49,8 @@ public function testGetValues(): void $webspaceB->setKey('webspace-b'); $webspaceB->setName('Webspace B'); $webspaceCollection = new WebspaceCollection([ - $webspaceA, - $webspaceB, + 'webspace-a' => $webspaceA, + 'webspace-b' => $webspaceB, ]); $this->webspaceManager->getWebspaceCollection() diff --git a/Tests/Unit/Content/Infrastructure/Sulu/Structure/ContentDocumentTest.php b/Tests/Unit/Content/Infrastructure/Sulu/Structure/ContentDocumentTest.php index 7b5fcd80..59b21f41 100644 --- a/Tests/Unit/Content/Infrastructure/Sulu/Structure/ContentDocumentTest.php +++ b/Tests/Unit/Content/Infrastructure/Sulu/Structure/ContentDocumentTest.php @@ -14,11 +14,15 @@ namespace Sulu\Bundle\ContentBundle\Tests\Unit\Content\Infrastructure\Sulu\Structure; use PHPUnit\Framework\TestCase; +use Sulu\Bundle\ContactBundle\Entity\Contact; +use Sulu\Bundle\ContentBundle\Content\Domain\Model\AuthorInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Model\ExcerptInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Model\SeoInterface; use Sulu\Bundle\ContentBundle\Content\Domain\Model\TemplateInterface; use Sulu\Bundle\ContentBundle\Content\Infrastructure\Sulu\Structure\ContentDocument; +use Sulu\Bundle\SecurityBundle\Entity\User; use Sulu\Component\Content\Compat\StructureInterface; +use Sulu\Component\Persistence\Model\UserBlameInterface; class ContentDocumentTest extends TestCase { @@ -170,4 +174,83 @@ public function testGetStructure(): void $this->assertNull($structure); } + + public function testGetLastModified(): void + { + $content = $this->prophesize(TemplateInterface::class); + $content->willImplement(AuthorInterface::class); + $content->getLastModifiedEnabled()->willReturn(true); + $authored = new \DateTime('2020-01-01'); + $lastModified = new \DateTime('2022-01-01'); + $content->getLastModified()->willReturn($lastModified); + $content->getAuthored()->willReturn($authored); + $document = $this->createContentDocument($content->reveal()); + + $this->assertTrue($document->getLastModifiedEnabled()); + $this->assertSame($lastModified, $document->getLastModified()); + $this->assertSame($authored, $document->getAuthored()); + } + + public function testSetLastModified(): void + { + $this->expectException(\BadMethodCallException::class); + $lastModified = new \DateTime('2020-01-01'); + $document = $this->createContentDocument(); + + $document->setLastModified($lastModified); + } + + public function testSetAuthored(): void + { + $this->expectException(\BadMethodCallException::class); + $authored = new \DateTime('2020-01-01'); + $document = $this->createContentDocument(); + + $document->setAuthored($authored); + } + + public function testSetAuthor(): void + { + $this->expectException(\BadMethodCallException::class); + $document = $this->createContentDocument(); + + $document->setAuthor(null); + } + + public function testGetAuthor(): void + { + $content = $this->prophesize(TemplateInterface::class); + $content->willImplement(AuthorInterface::class); + $author = new Contact(); + $content->getAuthor()->willReturn($author); + $document = $this->createContentDocument($content->reveal()); + + $this->assertSame($author, $document->getAuthor()); + } + + public function testGetUser(): void + { + $content = $this->prophesize(TemplateInterface::class); + $content->willImplement(UserBlameInterface::class); + $user = new User(); + $content->getCreator()->willReturn($user); + $content->getChanger()->willReturn($user); + $document = $this->createContentDocument($content->reveal()); + + $this->assertSame($user, $document->getCreator()); + $this->assertSame($user, $document->getChanger()); + } + + public function testLocalizedAuthorNull(): void + { + $content = $this->prophesize(TemplateInterface::class); + $document = $this->createContentDocument($content->reveal()); + + $this->assertNull($document->getLastModifiedEnabled()); + $this->assertNull($document->getLastModified()); + $this->assertNull($document->getAuthored()); + $this->assertNull($document->getAuthor()); + $this->assertNull($document->getCreator()); + $this->assertNull($document->getChanger()); + } } diff --git a/UPGRADE.md b/UPGRADE.md index 36726412..1a5b26b8 100644 --- a/UPGRADE.md +++ b/UPGRADE.md @@ -1,5 +1,9 @@ # Upgrade +```sql +ALTER TABLE test_example_dimension_contents ADD updated DATETIME DEFAULT NULL COMMENT '(DC2Type:datetime_immutable)'; +``` + ## 0.7.0 ### Route property name forced to `url` diff --git a/composer.json b/composer.json index afed83e3..c8b5b5f1 100644 --- a/composer.json +++ b/composer.json @@ -14,13 +14,13 @@ "php": "^7.2 || ^8.0", "doctrine/inflector": "^1.4.1 || ^2.0.1", "doctrine/collections": "^1.0", - "doctrine/orm": "^2.5.3", + "doctrine/orm": "^2.5.3 || ^2.8", "doctrine/persistence": "^1.3 || ^2.0", "doctrine/doctrine-bundle": "^1.10 || ^2.0", "friendsofsymfony/rest-bundle": "^2.6 || ^3.0", "massive/search-bundle": "^2.4", "ramsey/uuid": "^3.8 || ^4.0", - "sulu/sulu": "^2.4 || ^2.6@dev", + "sulu/sulu": "2.6.x-dev", "symfony/config": "^4.4 || ^5.4 || ^6.0", "symfony/dependency-injection": "^4.4 || ^5.4 || ^6.0", "symfony/event-dispatcher": "^4.4 || ^5.4 || ^6.0", @@ -54,7 +54,7 @@ "phpstan/phpstan-webmozart-assert": "^1.0", "phpunit/phpunit": "^8.5.33 || ^9.6.5", "qossmic/deptrac-shim": "^0.11.1 || ^0.23.0 || ^1.0", - "sulu/automation-bundle": "^2.0@dev", + "sulu/automation-bundle": "^2.0@dev || ^2.1", "symfony/browser-kit": "^4.4 || ^5.4 || ^6.0", "symfony/console": "^4.4 || ^5.4 || ^6.0", "symfony/dotenv": "^4.4 || ^5.4 || ^6.0", @@ -66,7 +66,8 @@ }, "conflict": { "coduo/php-matcher": "6.0.12", - "doctrine/persistence": "1.3.2" + "doctrine/persistence": "1.3.2", + "doctrine/orm": "2.10.0 || 2.14.2 || >=2.16.0" }, "config": { "sort-packages": true,