From c33ed4d708c0086d8513b086d08d553eaafc3c6d Mon Sep 17 00:00:00 2001 From: edalzell Date: Thu, 12 Dec 2024 15:11:24 -0800 Subject: [PATCH 01/45] add to base field --- src/Fields/Field.php | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Fields/Field.php b/src/Fields/Field.php index b7b9f4f17e9..75b765169e2 100644 --- a/src/Fields/Field.php +++ b/src/Fields/Field.php @@ -248,6 +248,11 @@ public function isFilterable() return (bool) $this->get('filterable'); } + public function isRevisable() + { + return (bool) $this->get('revisable', true); + } + public function shouldBeDuplicated() { if (is_null($this->get('duplicate'))) { @@ -269,6 +274,7 @@ public function toPublishArray() 'visibility' => $this->visibility(), 'read_only' => $this->visibility() === 'read_only', // Deprecated: Addon fieldtypes should now reference new `visibility` state. 'always_save' => $this->alwaysSave(), + 'revisable' => $this->isRevisable(), ]); } @@ -567,6 +573,13 @@ public static function commonFieldOptions(): Fields 'validate' => 'boolean', 'default' => true, ], + 'revisable' => [ + 'display' => __('Revisable'), + 'instructions' => __('statamic::messages.fields_revisable_instructions'), + 'type' => 'toggle', + 'validate' => 'boolean', + 'default' => true, + ], ])->map(fn ($field, $handle) => compact('handle', 'field'))->values()->all(); return new ConfigFields($fields); From 4aff3fa536f099366e9df9c1671eb92acdc2fa24 Mon Sep 17 00:00:00 2001 From: edalzell Date: Thu, 12 Dec 2024 15:23:44 -0800 Subject: [PATCH 02/45] tiny refactor to prep --- src/Entries/Entry.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Entries/Entry.php b/src/Entries/Entry.php index 06a18e7c0e3..3b19d1db016 100644 --- a/src/Entries/Entry.php +++ b/src/Entries/Entry.php @@ -668,12 +668,14 @@ protected function revisionKey() protected function revisionAttributes() { + $nonRevisableFields = ['updated_by', 'updated_at']; + return [ 'id' => $this->id(), 'slug' => $this->slug(), 'published' => $this->published(), 'date' => $this->collection()->dated() ? $this->date()->timestamp : null, - 'data' => $this->data()->except(['updated_by', 'updated_at'])->all(), + 'data' => $this->data()->except($nonRevisableFields)->all(), ]; } From 8817d35cb1ee80efd679ec9c9a82b9852ca187dc Mon Sep 17 00:00:00 2001 From: edalzell Date: Thu, 12 Dec 2024 15:28:26 -0800 Subject: [PATCH 03/45] update the contract and implement in entry --- src/Entries/Entry.php | 7 ++++++- src/Revisions/Revisable.php | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/Entries/Entry.php b/src/Entries/Entry.php index 3b19d1db016..5b57d84d34e 100644 --- a/src/Entries/Entry.php +++ b/src/Entries/Entry.php @@ -666,6 +666,11 @@ protected function revisionKey() ]); } + protected function nonRevisableFields(): array + { + return []; + } + protected function revisionAttributes() { $nonRevisableFields = ['updated_by', 'updated_at']; @@ -675,7 +680,7 @@ protected function revisionAttributes() 'slug' => $this->slug(), 'published' => $this->published(), 'date' => $this->collection()->dated() ? $this->date()->timestamp : null, - 'data' => $this->data()->except($nonRevisableFields)->all(), + 'data' => $this->data()->except(array_merge($nonRevisableFields, $this->nonRevisableFields()))->all(), ]; } diff --git a/src/Revisions/Revisable.php b/src/Revisions/Revisable.php index 2c1dfd0efc7..7a94eb34f3c 100644 --- a/src/Revisions/Revisable.php +++ b/src/Revisions/Revisable.php @@ -177,6 +177,8 @@ public function revisionsEnabled() return config('statamic.revisions.enabled') && Statamic::pro(); } + abstract protected function nonRevisableFields(): array; + abstract protected function revisionKey(); abstract protected function revisionAttributes(); From 42b23b7182ebd5370ecfc592715fe9ca113d0995 Mon Sep 17 00:00:00 2001 From: edalzell Date: Thu, 12 Dec 2024 15:29:11 -0800 Subject: [PATCH 04/45] better --- src/Entries/Entry.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Entries/Entry.php b/src/Entries/Entry.php index 5b57d84d34e..2f56dee2daf 100644 --- a/src/Entries/Entry.php +++ b/src/Entries/Entry.php @@ -673,14 +673,14 @@ protected function nonRevisableFields(): array protected function revisionAttributes() { - $nonRevisableFields = ['updated_by', 'updated_at']; + $nonRevisableFields = ['updated_by', 'updated_at', ...$this->nonRevisableFields()]; return [ 'id' => $this->id(), 'slug' => $this->slug(), 'published' => $this->published(), 'date' => $this->collection()->dated() ? $this->date()->timestamp : null, - 'data' => $this->data()->except(array_merge($nonRevisableFields, $this->nonRevisableFields()))->all(), + 'data' => $this->data()->except($nonRevisableFields)->all(), ]; } From cac88a783db914a4dad6edfc56daf4f03e47215d Mon Sep 17 00:00:00 2001 From: edalzell Date: Thu, 12 Dec 2024 16:16:33 -0800 Subject: [PATCH 05/45] get the non-revisable fields --- src/Entries/Entry.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Entries/Entry.php b/src/Entries/Entry.php index 2f56dee2daf..791fee7249d 100644 --- a/src/Entries/Entry.php +++ b/src/Entries/Entry.php @@ -668,7 +668,12 @@ protected function revisionKey() protected function nonRevisableFields(): array { - return []; + return $this->blueprint() + ->fields() + ->all() + ->reject(fn ($field) => $field->isRevisable()) + ->keys() + ->all(); } protected function revisionAttributes() From 59c926752f2549f0f31df22ebaf7473f2f8c9594 Mon Sep 17 00:00:00 2001 From: edalzell Date: Thu, 12 Dec 2024 16:16:49 -0800 Subject: [PATCH 06/45] save the non-revisable data --- src/Entries/Entry.php | 2 +- src/Http/Controllers/CP/Collections/EntriesController.php | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Entries/Entry.php b/src/Entries/Entry.php index 791fee7249d..3b6de95e3f1 100644 --- a/src/Entries/Entry.php +++ b/src/Entries/Entry.php @@ -701,7 +701,7 @@ public function makeFromRevision($revision) $entry ->published($attrs['published']) - ->data($attrs['data']) + ->data(array_merge($this->data()->all(), $attrs['data'])) ->slug($attrs['slug']); if ($this->collection()->dated() && ($date = Arr::get($attrs, 'date'))) { diff --git a/src/Http/Controllers/CP/Collections/EntriesController.php b/src/Http/Controllers/CP/Collections/EntriesController.php index 08921a71e1a..37067e27c32 100644 --- a/src/Http/Controllers/CP/Collections/EntriesController.php +++ b/src/Http/Controllers/CP/Collections/EntriesController.php @@ -262,7 +262,8 @@ public function update(Request $request, $collection, $entry) ->save(); // catch any changes through RevisionSaving event - $entry = $entry->fromWorkingCopy(); + // have to save in case there are non-revisable fields + $entry = tap($entry->fromWorkingCopy())->save(); } else { if (! $entry->revisionsEnabled() && User::current()->can('publish', $entry)) { $entry->published($request->published); From 83108eabadb56eb42018decd898eb2db8d43eced Mon Sep 17 00:00:00 2001 From: edalzell Date: Thu, 12 Dec 2024 16:17:39 -0800 Subject: [PATCH 07/45] tidier --- src/Entries/Entry.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Entries/Entry.php b/src/Entries/Entry.php index 3b6de95e3f1..d6ae2781968 100644 --- a/src/Entries/Entry.php +++ b/src/Entries/Entry.php @@ -701,7 +701,7 @@ public function makeFromRevision($revision) $entry ->published($attrs['published']) - ->data(array_merge($this->data()->all(), $attrs['data'])) + ->data($this->data()->merge($attrs['data'])) ->slug($attrs['slug']); if ($this->collection()->dated() && ($date = Arr::get($attrs, 'date'))) { From 4f5106a982d74e3648357e48d87e34eff6a72daa Mon Sep 17 00:00:00 2001 From: edalzell Date: Thu, 12 Dec 2024 16:19:08 -0800 Subject: [PATCH 08/45] tidy --- src/Entries/Entry.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/Entries/Entry.php b/src/Entries/Entry.php index d6ae2781968..9864a07ba36 100644 --- a/src/Entries/Entry.php +++ b/src/Entries/Entry.php @@ -678,14 +678,12 @@ protected function nonRevisableFields(): array protected function revisionAttributes() { - $nonRevisableFields = ['updated_by', 'updated_at', ...$this->nonRevisableFields()]; - return [ 'id' => $this->id(), 'slug' => $this->slug(), 'published' => $this->published(), 'date' => $this->collection()->dated() ? $this->date()->timestamp : null, - 'data' => $this->data()->except($nonRevisableFields)->all(), + 'data' => $this->data()->except(['updated_by', 'updated_at', ...$this->nonRevisableFields()])->all(), ]; } From 668de505d5f52db11ba4b1b85e08ebfb3ed5ebaf Mon Sep 17 00:00:00 2001 From: edalzell Date: Thu, 12 Dec 2024 16:23:06 -0800 Subject: [PATCH 09/45] move this so all revisable data can use it --- src/Entries/Entry.php | 10 ---------- src/Revisions/Revisable.php | 10 +++++++++- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/src/Entries/Entry.php b/src/Entries/Entry.php index 9864a07ba36..2b58343161e 100644 --- a/src/Entries/Entry.php +++ b/src/Entries/Entry.php @@ -666,16 +666,6 @@ protected function revisionKey() ]); } - protected function nonRevisableFields(): array - { - return $this->blueprint() - ->fields() - ->all() - ->reject(fn ($field) => $field->isRevisable()) - ->keys() - ->all(); - } - protected function revisionAttributes() { return [ diff --git a/src/Revisions/Revisable.php b/src/Revisions/Revisable.php index 7a94eb34f3c..a4dd7742dbf 100644 --- a/src/Revisions/Revisable.php +++ b/src/Revisions/Revisable.php @@ -177,7 +177,15 @@ public function revisionsEnabled() return config('statamic.revisions.enabled') && Statamic::pro(); } - abstract protected function nonRevisableFields(): array; + protected function nonRevisableFields(): array + { + return $this->blueprint() + ->fields() + ->all() + ->reject(fn ($field) => $field->isRevisable()) + ->keys() + ->all(); + } abstract protected function revisionKey(); From 34b3dc592038f1597e39d6d1e82a362dd0014a00 Mon Sep 17 00:00:00 2001 From: edalzell Date: Fri, 13 Dec 2024 10:24:51 -0800 Subject: [PATCH 10/45] first test --- src/Revisions/Revisable.php | 2 +- tests/Revisions/RevisableTest.php | 41 +++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 tests/Revisions/RevisableTest.php diff --git a/src/Revisions/Revisable.php b/src/Revisions/Revisable.php index a4dd7742dbf..f64bed5ecca 100644 --- a/src/Revisions/Revisable.php +++ b/src/Revisions/Revisable.php @@ -177,7 +177,7 @@ public function revisionsEnabled() return config('statamic.revisions.enabled') && Statamic::pro(); } - protected function nonRevisableFields(): array + public function nonRevisableFields(): array { return $this->blueprint() ->fields() diff --git a/tests/Revisions/RevisableTest.php b/tests/Revisions/RevisableTest.php new file mode 100644 index 00000000000..5bde76e1c07 --- /dev/null +++ b/tests/Revisions/RevisableTest.php @@ -0,0 +1,41 @@ +repo = (new RevisionRepository); + } + + #[Test] + public function it_gets_revisions_and_excludes_working_copies() + { + $blueprint = Blueprint::makeFromFields([ + 'revisable' => ['type' => 'text'], + 'non_revisable' => ['type' => 'text', 'revisable' => false], + ]); + BlueprintRepository::shouldReceive('in')->with('collections/blog')->andReturn(collect(['blog' => $blueprint])); + Collection::make('blog')->save(); + + $entry = (new Entry)->collection('blog')->id('123'); + + $this->assertEquals(['non_revisable'], $entry->nonRevisableFields()); + } +} From 9a328711ceb94de51348f36a02888f77b5289865 Mon Sep 17 00:00:00 2001 From: edalzell Date: Fri, 13 Dec 2024 10:32:14 -0800 Subject: [PATCH 11/45] Field test --- tests/Fields/FieldTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/Fields/FieldTest.php b/tests/Fields/FieldTest.php index 62c92217484..d3c0f5c05a4 100644 --- a/tests/Fields/FieldTest.php +++ b/tests/Fields/FieldTest.php @@ -342,6 +342,7 @@ public function preProcess($data) 'visibility' => 'visible', 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, 'type' => 'example', 'validate' => 'required', 'foo' => 'bar', @@ -647,4 +648,20 @@ public function it_gets_and_sets_the_form() $this->assertEquals($field, $return); $this->assertEquals($form, $field->form()); } + + #[Test] + public function it_defaults_to_revisable() + { + $field = new Field('test', ['type' => 'text']); + + $this->assertTrue($field->isRevisable()); + } + + #[Test] + public function it_gets_revisable() + { + $field = new Field('test', ['type' => 'text', 'revisable' => false]); + + $this->assertFalse($field->isRevisable()); + } } From 34e8b2399026a04b271320c55af152f0c65cc2fa Mon Sep 17 00:00:00 2001 From: edalzell Date: Fri, 13 Dec 2024 10:34:52 -0800 Subject: [PATCH 12/45] fix tests --- tests/Fields/BlueprintTest.php | 4 ++++ tests/Fields/FieldsTest.php | 4 ++++ tests/Fields/SectionTest.php | 2 ++ tests/Fields/TabTest.php | 2 ++ tests/Fieldtypes/NestedFieldsTest.php | 2 ++ tests/Fieldtypes/SetsTest.php | 3 +++ 6 files changed, 17 insertions(+) diff --git a/tests/Fields/BlueprintTest.php b/tests/Fields/BlueprintTest.php index a13c952be82..60e59d5139f 100644 --- a/tests/Fields/BlueprintTest.php +++ b/tests/Fields/BlueprintTest.php @@ -436,6 +436,7 @@ public function converts_to_array_suitable_for_rendering_fields_in_publish_compo 'visibility' => 'visible', 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, 'type' => 'text', 'validate' => 'required|min:2', 'input_type' => 'text', @@ -474,6 +475,7 @@ public function converts_to_array_suitable_for_rendering_fields_in_publish_compo 'visibility' => 'visible', 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, 'type' => 'textarea', 'placeholder' => null, 'validate' => 'min:2', @@ -563,6 +565,7 @@ public function converts_to_array_suitable_for_rendering_prefixed_conditional_fi 'visibility' => 'visible', 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, 'type' => 'text', 'input_type' => 'text', 'placeholder' => null, @@ -589,6 +592,7 @@ public function converts_to_array_suitable_for_rendering_prefixed_conditional_fi 'visibility' => 'visible', 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, 'type' => 'text', 'input_type' => 'text', 'placeholder' => null, diff --git a/tests/Fields/FieldsTest.php b/tests/Fields/FieldsTest.php index 057bc08d2a5..fddcdcd2901 100644 --- a/tests/Fields/FieldsTest.php +++ b/tests/Fields/FieldsTest.php @@ -434,6 +434,7 @@ public function converts_to_array_suitable_for_rendering_fields_in_publish_compo 'sortable' => true, 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, ], [ 'handle' => 'two', @@ -457,6 +458,7 @@ public function converts_to_array_suitable_for_rendering_fields_in_publish_compo 'sortable' => true, 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, ], ], $fields->toPublishArray()); } @@ -522,6 +524,7 @@ public function converts_to_array_suitable_for_rendering_prefixed_conditional_fi 'sortable' => true, 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, ], [ 'handle' => 'nested_deeper_two', @@ -548,6 +551,7 @@ public function converts_to_array_suitable_for_rendering_prefixed_conditional_fi 'sortable' => true, 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, ], ], $fields->toPublishArray()); } diff --git a/tests/Fields/SectionTest.php b/tests/Fields/SectionTest.php index 0e09f08b90b..0afb84b986d 100644 --- a/tests/Fields/SectionTest.php +++ b/tests/Fields/SectionTest.php @@ -125,6 +125,7 @@ public function converts_to_array_suitable_for_rendering_fields_in_publish_compo 'visibility' => 'visible', 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, 'type' => 'text', 'validate' => 'required|min:2', 'input_type' => 'text', @@ -152,6 +153,7 @@ public function converts_to_array_suitable_for_rendering_fields_in_publish_compo 'visibility' => 'visible', 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, 'type' => 'textarea', 'validate' => 'min:2', 'placeholder' => null, diff --git a/tests/Fields/TabTest.php b/tests/Fields/TabTest.php index ad0d34d3bc6..f6f1b0c98c6 100644 --- a/tests/Fields/TabTest.php +++ b/tests/Fields/TabTest.php @@ -150,6 +150,7 @@ public function converts_to_array_suitable_for_rendering_fields_in_publish_compo 'visibility' => 'visible', 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, 'type' => 'text', 'validate' => 'required|min:2', 'input_type' => 'text', @@ -177,6 +178,7 @@ public function converts_to_array_suitable_for_rendering_fields_in_publish_compo 'visibility' => 'visible', 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, 'type' => 'textarea', 'validate' => 'min:2', 'placeholder' => null, diff --git a/tests/Fieldtypes/NestedFieldsTest.php b/tests/Fieldtypes/NestedFieldsTest.php index d35817067a7..5f20838d86c 100644 --- a/tests/Fieldtypes/NestedFieldsTest.php +++ b/tests/Fieldtypes/NestedFieldsTest.php @@ -23,6 +23,7 @@ public function it_preprocesses_each_value_when_used_for_config() ->andReturn(new class extends Fieldtype { protected $component = 'assets'; + protected $configFields = [ 'max_files' => ['type' => 'integer'], 'container' => ['type' => 'plain'], @@ -84,6 +85,7 @@ public function preProcess($data) 'visibility' => 'visible', 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, 'type' => 'assets', 'max_files' => 2, 'container' => 'main', diff --git a/tests/Fieldtypes/SetsTest.php b/tests/Fieldtypes/SetsTest.php index e57b713568c..f0e29b94cd2 100644 --- a/tests/Fieldtypes/SetsTest.php +++ b/tests/Fieldtypes/SetsTest.php @@ -227,6 +227,7 @@ public function it_preprocesses_for_config_with_groups() 'visibility' => 'visible', 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, 'type' => 'text', 'input_type' => 'text', 'placeholder' => null, @@ -264,6 +265,7 @@ public function it_preprocesses_for_config_with_groups() 'visibility' => 'visible', 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, 'type' => 'text', 'input_type' => 'text', 'placeholder' => null, @@ -324,6 +326,7 @@ public function it_preprocesses_for_config_without_groups() 'visibility' => 'visible', 'replicator_preview' => true, 'duplicate' => true, + 'revisable' => true, 'type' => 'text', 'input_type' => 'text', 'placeholder' => null, From b7eefe1de196eec2578b51afe0204231286bf091 Mon Sep 17 00:00:00 2001 From: edalzell Date: Fri, 13 Dec 2024 10:41:56 -0800 Subject: [PATCH 13/45] tidy --- tests/Revisions/RevisableTest.php | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/tests/Revisions/RevisableTest.php b/tests/Revisions/RevisableTest.php index 5bde76e1c07..a12fc717987 100644 --- a/tests/Revisions/RevisableTest.php +++ b/tests/Revisions/RevisableTest.php @@ -7,7 +7,6 @@ use Statamic\Entries\Entry; use Statamic\Facades\Blueprint; use Statamic\Facades\Collection; -use Statamic\Revisions\RevisionRepository; use Tests\PreventSavingStacheItemsToDisk; use Tests\TestCase; @@ -15,17 +14,8 @@ class RevisableTest extends TestCase { use PreventSavingStacheItemsToDisk; - private $entry; - - protected function setUp(): void - { - parent::setUp(); - - // $this->repo = (new RevisionRepository); - } - #[Test] - public function it_gets_revisions_and_excludes_working_copies() + public function it_gets_non_revisable_fields() { $blueprint = Blueprint::makeFromFields([ 'revisable' => ['type' => 'text'], From bc3645abec1a5d9242ec947960874a5668ccd955 Mon Sep 17 00:00:00 2001 From: edalzell Date: Fri, 13 Dec 2024 10:47:34 -0800 Subject: [PATCH 14/45] test revision attributes --- tests/Revisions/RevisableTest.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/Revisions/RevisableTest.php b/tests/Revisions/RevisableTest.php index a12fc717987..8487aa9230c 100644 --- a/tests/Revisions/RevisableTest.php +++ b/tests/Revisions/RevisableTest.php @@ -28,4 +28,22 @@ public function it_gets_non_revisable_fields() $this->assertEquals(['non_revisable'], $entry->nonRevisableFields()); } + + #[Test] + public function it_sets_proper_revision_attributes() + { + $blueprint = Blueprint::makeFromFields([ + 'revisable' => ['type' => 'text'], + 'non_revisable' => ['type' => 'text', 'revisable' => false], + ]); + BlueprintRepository::shouldReceive('in')->with('collections/blog')->andReturn(collect(['blog' => $blueprint])); + Collection::make('blog')->save(); + + $entry = (new Entry)->collection('blog')->id('123'); + $entry + ->set('revisable', 'see me') + ->set('non_revisable', "don't see me"); + + $this->assertEquals(['revisable' => 'see me'], $entry->makeRevision()->attributes()['data']); + } } From d7cb17677ec8d95c4b8ad42b9517b2a2f581e35e Mon Sep 17 00:00:00 2001 From: edalzell Date: Fri, 13 Dec 2024 10:59:30 -0800 Subject: [PATCH 15/45] test makeFromRevision --- tests/Revisions/RevisableTest.php | 37 +++++++++++++++++++ .../__fixtures__/123/1553546421.yaml | 8 +++- 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/tests/Revisions/RevisableTest.php b/tests/Revisions/RevisableTest.php index 8487aa9230c..d14fe5ee106 100644 --- a/tests/Revisions/RevisableTest.php +++ b/tests/Revisions/RevisableTest.php @@ -7,6 +7,7 @@ use Statamic\Entries\Entry; use Statamic\Facades\Blueprint; use Statamic\Facades\Collection; +use Statamic\Revisions\RevisionRepository; use Tests\PreventSavingStacheItemsToDisk; use Tests\TestCase; @@ -14,6 +15,15 @@ class RevisableTest extends TestCase { use PreventSavingStacheItemsToDisk; + private $repo; + + protected function setUp(): void + { + parent::setUp(); + config(['statamic.revisions.path' => __DIR__.'/__fixtures__']); + $this->repo = (new RevisionRepository); + } + #[Test] public function it_gets_non_revisable_fields() { @@ -46,4 +56,31 @@ public function it_sets_proper_revision_attributes() $this->assertEquals(['revisable' => 'see me'], $entry->makeRevision()->attributes()['data']); } + + #[Test] + public function it_can_make_entry_from_revision() + { + $blueprint = Blueprint::makeFromFields([ + 'revisable' => ['type' => 'text'], + 'non_revisable' => ['type' => 'text', 'revisable' => false], + ]); + + BlueprintRepository::shouldReceive('in')->with('collections/blog')->andReturn(collect(['blog' => $blueprint])); + Collection::make('blog')->save(); + + $entry = (new Entry)->collection('blog')->id('123'); + $entry->set('revisable', 'override me')->set('non_revisable', "don't override me"); + + $revision = $this->repo->whereKey('123')->first(); + + $revisedEntry = $entry->makeFromRevision($revision); + + $this->assertEquals( + collect([ + "revisable" => "overridden", + "non_revisable" => "don't override me", + ]), + $entry->makeFromRevision($revision)->data() + ); + } } diff --git a/tests/Revisions/__fixtures__/123/1553546421.yaml b/tests/Revisions/__fixtures__/123/1553546421.yaml index 833a2599843..cb828ee7ebd 100644 --- a/tests/Revisions/__fixtures__/123/1553546421.yaml +++ b/tests/Revisions/__fixtures__/123/1553546421.yaml @@ -1,2 +1,8 @@ date: 1553546421 -attributes: {} +attributes: + id: 123 + slug: the-slug + published: true + date: 1633590000 + data: + revisable: 'overridden' From 5b26aa10f4abe43669f0a2df333960066ddfb9db Mon Sep 17 00:00:00 2001 From: edalzell Date: Fri, 13 Dec 2024 11:27:56 -0800 Subject: [PATCH 16/45] support terms --- src/Taxonomies/LocalizedTerm.php | 10 +++---- tests/Revisions/RevisableTest.php | 45 ++++++++++++++++++++++++++----- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/Taxonomies/LocalizedTerm.php b/src/Taxonomies/LocalizedTerm.php index f40b394ae00..97613a52ea3 100644 --- a/src/Taxonomies/LocalizedTerm.php +++ b/src/Taxonomies/LocalizedTerm.php @@ -244,23 +244,23 @@ protected function revisionAttributes() 'id' => $this->id(), 'slug' => $this->slug(), 'published' => $this->published(), - 'data' => $this->data()->except(['updated_by', 'updated_at'])->all(), + 'data' => $this->data()->except(['updated_by', 'updated_at', ...$this->nonRevisableFields()])->all(), ]; } public function makeFromRevision($revision) { - $entry = clone $this; + $term = clone $this; if (! $revision) { - return $entry; + return $term; } $attrs = $revision->attributes(); - return $entry + return $term ->published($attrs['published']) - ->data($attrs['data']) + ->data($this->data()->merge($attrs['data'])) ->slug($attrs['slug']); } diff --git a/tests/Revisions/RevisableTest.php b/tests/Revisions/RevisableTest.php index d14fe5ee106..2d422f0cdb9 100644 --- a/tests/Revisions/RevisableTest.php +++ b/tests/Revisions/RevisableTest.php @@ -7,7 +7,10 @@ use Statamic\Entries\Entry; use Statamic\Facades\Blueprint; use Statamic\Facades\Collection; +use Statamic\Facades\Taxonomy; use Statamic\Revisions\RevisionRepository; +use Statamic\Taxonomies\LocalizedTerm; +use Statamic\Taxonomies\Term; use Tests\PreventSavingStacheItemsToDisk; use Tests\TestCase; @@ -47,14 +50,21 @@ public function it_sets_proper_revision_attributes() 'non_revisable' => ['type' => 'text', 'revisable' => false], ]); BlueprintRepository::shouldReceive('in')->with('collections/blog')->andReturn(collect(['blog' => $blueprint])); + BlueprintRepository::shouldReceive('in')->with('taxonomies/tags')->andReturn(collect(['tags' => $blueprint])); Collection::make('blog')->save(); + Taxonomy::make('tags')->save(); - $entry = (new Entry)->collection('blog')->id('123'); - $entry + $entry = (new Entry) + ->collection('blog') + ->id('123') ->set('revisable', 'see me') ->set('non_revisable', "don't see me"); + $term = (new Term)->taxonomy('tags')->slug('foo')->data(['revisable' => 'see me', 'non_revisable' => "don't see me"]); + $localizedTerm = (new LocalizedTerm($term, 'en')); + $this->assertEquals(['revisable' => 'see me'], $entry->makeRevision()->attributes()['data']); + $this->assertEquals(['revisable' => 'see me'], $localizedTerm->makeRevision()->attributes()['data']); } #[Test] @@ -73,14 +83,37 @@ public function it_can_make_entry_from_revision() $revision = $this->repo->whereKey('123')->first(); - $revisedEntry = $entry->makeFromRevision($revision); - $this->assertEquals( collect([ - "revisable" => "overridden", - "non_revisable" => "don't override me", + 'revisable' => 'overridden', + 'non_revisable' => "don't override me", ]), $entry->makeFromRevision($revision)->data() ); } + + #[Test] + public function it_can_make_term_from_revision() + { + $blueprint = Blueprint::makeFromFields([ + 'revisable' => ['type' => 'text'], + 'non_revisable' => ['type' => 'text', 'revisable' => false], + ]); + + BlueprintRepository::shouldReceive('in')->with('taxonomies/tags')->andReturn(collect(['tags' => $blueprint])); + Taxonomy::make('tags')->save(); + + $term = (new Term)->taxonomy('tags')->slug('foo')->data(['revisable' => 'override me', 'non_revisable' => "don't override me"]); + $localizedTerm = (new LocalizedTerm($term, 'en')); + + $revision = $this->repo->whereKey('123')->first(); + + $this->assertEquals( + collect([ + 'revisable' => 'overridden', + 'non_revisable' => "don't override me", + ]), + $localizedTerm->makeFromRevision($revision)->data() + ); + } } From 9b5c6e2c4359d9567ccecba2416db15b9be820ac Mon Sep 17 00:00:00 2001 From: edalzell Date: Fri, 13 Dec 2024 11:29:58 -0800 Subject: [PATCH 17/45] term controller --- src/Http/Controllers/CP/Taxonomies/TermsController.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Http/Controllers/CP/Taxonomies/TermsController.php b/src/Http/Controllers/CP/Taxonomies/TermsController.php index 9482380aae3..56745049858 100644 --- a/src/Http/Controllers/CP/Taxonomies/TermsController.php +++ b/src/Http/Controllers/CP/Taxonomies/TermsController.php @@ -200,6 +200,9 @@ public function update(Request $request, $taxonomy, $term, $site) ->makeWorkingCopy() ->user(User::current()) ->save(); + + // have to save in case there are non-revisable fields + $term = tap($term->fromWorkingCopy())->save(); } else { if (! $term->revisionsEnabled()) { $term->published($request->published); From 06028e1f60f8d536cb06a0d312ac1693145c6c18 Mon Sep 17 00:00:00 2001 From: edalzell Date: Fri, 20 Dec 2024 14:41:10 -0800 Subject: [PATCH 18/45] entry revision tests --- tests/Feature/Entries/EntryRevisionsTest.php | 52 ++++++++++++++++++-- 1 file changed, 48 insertions(+), 4 deletions(-) diff --git a/tests/Feature/Entries/EntryRevisionsTest.php b/tests/Feature/Entries/EntryRevisionsTest.php index e50009df679..066a70834b6 100644 --- a/tests/Feature/Entries/EntryRevisionsTest.php +++ b/tests/Feature/Entries/EntryRevisionsTest.php @@ -45,7 +45,16 @@ public function it_gets_revisions() { $now = Carbon::parse('2017-02-03'); Carbon::setTestNow($now); - $this->setTestBlueprint('test', ['foo' => ['type' => 'text']]); + $this->setTestBlueprint( + 'test', + [ + 'foo' => ['type' => 'text'], + 'bar' => [ + 'type' => 'text', + 'revisable' => false, + ], + ] + ); $this->setTestRoles(['test' => ['access cp', 'publish blog entries']]); $user = User::make()->id('user-1')->assignRole('test')->save(); @@ -58,6 +67,7 @@ public function it_gets_revisions() 'blueprint' => 'test', 'title' => 'Original title', 'foo' => 'bar', + 'bar' => 'foo', ])->create(); tap($entry->makeRevision(), function ($copy) { @@ -85,6 +95,7 @@ public function it_gets_revisions() ->assertJsonPath('0.revisions.0.message', 'Revision one') ->assertJsonPath('0.revisions.0.attributes.data.title', 'Original title') ->assertJsonPath('0.revisions.0.attributes.item_url', 'http://localhost/cp/collections/blog/entries/1/revisions/'.Carbon::parse('2017-02-01')->timestamp) + ->assertJsonPath('0.revisions.0.attributes.data.bar', null) ->assertJsonPath('1.revisions.0.action', 'revision') ->assertJsonPath('1.revisions.0.message', false) @@ -102,7 +113,16 @@ public function it_publishes_an_entry() { $now = Carbon::parse('2017-02-03'); Carbon::setTestNow($now); - $this->setTestBlueprint('test', ['foo' => ['type' => 'text']]); + $this->setTestBlueprint( + 'test', + [ + 'foo' => ['type' => 'text'], + 'bar' => [ + 'type' => 'text', + 'revisable' => false, + ], + ] + ); $this->setTestRoles(['test' => ['access cp', 'publish blog entries']]); $user = User::make()->id('user-1')->assignRole('test')->save(); @@ -115,6 +135,7 @@ public function it_publishes_an_entry() 'blueprint' => 'test', 'title' => 'Title', 'foo' => 'bar', + 'bar' => 'foo' ])->create(); tap($entry->makeWorkingCopy(), function ($copy) { @@ -137,6 +158,7 @@ public function it_publishes_an_entry() 'blueprint' => 'test', 'title' => 'Title', 'foo' => 'foo modified in working copy', + 'bar' => 'foo', 'updated_at' => $now->timestamp, 'updated_by' => $user->id(), ], $entry->data()->all()); @@ -166,7 +188,16 @@ public function it_unpublishes_an_entry() { $now = Carbon::parse('2017-02-03'); Carbon::setTestNow($now); - $this->setTestBlueprint('test', ['foo' => ['type' => 'text']]); + $this->setTestBlueprint( + 'test', + [ + 'foo' => ['type' => 'text'], + 'bar' => [ + 'type' => 'text', + 'revisable' => false, + ], + ] + ); $this->setTestRoles(['test' => ['access cp', 'publish blog entries']]); $user = User::make()->id('user-1')->assignRole('test')->save(); @@ -179,6 +210,7 @@ public function it_unpublishes_an_entry() 'blueprint' => 'test', 'title' => 'Title', 'foo' => 'bar', + 'bar' => 'foo' ])->create(); $this->assertTrue($entry->published()); @@ -194,6 +226,7 @@ public function it_unpublishes_an_entry() 'blueprint' => 'test', 'title' => 'Title', 'foo' => 'bar', + 'bar' => 'foo', 'updated_at' => $now->timestamp, 'updated_by' => $user->id(), ], $entry->data()->all()); @@ -219,7 +252,16 @@ public function it_unpublishes_an_entry() #[Test] public function it_creates_a_revision() { - $this->setTestBlueprint('test', ['foo' => ['type' => 'text']]); + $this->setTestBlueprint( + 'test', + [ + 'foo' => ['type' => 'text'], + 'bar' => [ + 'type' => 'text', + 'revisable' => false, + ], + ] + ); $this->setTestRoles(['test' => ['access cp', 'edit blog entries']]); $user = User::make()->id('user-1')->assignRole('test')->save(); @@ -232,6 +274,7 @@ public function it_creates_a_revision() 'blueprint' => 'test', 'title' => 'Title', 'foo' => 'bar', + 'bar' => 'foo' ])->create(); tap($entry->makeWorkingCopy(), function ($copy) { @@ -253,6 +296,7 @@ public function it_creates_a_revision() 'blueprint' => 'test', 'title' => 'Title', 'foo' => 'bar', + 'bar' => 'foo' ], $entry->data()->all()); $this->assertFalse($entry->published()); $this->assertCount(1, $entry->revisions()); From e6543e3eb04c61bb7632646d00ab3ed30c065328 Mon Sep 17 00:00:00 2001 From: edalzell Date: Fri, 20 Dec 2024 14:46:59 -0800 Subject: [PATCH 19/45] lint --- tests/Feature/Entries/EntryRevisionsTest.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/Feature/Entries/EntryRevisionsTest.php b/tests/Feature/Entries/EntryRevisionsTest.php index 066a70834b6..5b9b60ef39f 100644 --- a/tests/Feature/Entries/EntryRevisionsTest.php +++ b/tests/Feature/Entries/EntryRevisionsTest.php @@ -135,7 +135,7 @@ public function it_publishes_an_entry() 'blueprint' => 'test', 'title' => 'Title', 'foo' => 'bar', - 'bar' => 'foo' + 'bar' => 'foo', ])->create(); tap($entry->makeWorkingCopy(), function ($copy) { @@ -210,7 +210,7 @@ public function it_unpublishes_an_entry() 'blueprint' => 'test', 'title' => 'Title', 'foo' => 'bar', - 'bar' => 'foo' + 'bar' => 'foo', ])->create(); $this->assertTrue($entry->published()); @@ -274,7 +274,7 @@ public function it_creates_a_revision() 'blueprint' => 'test', 'title' => 'Title', 'foo' => 'bar', - 'bar' => 'foo' + 'bar' => 'foo', ])->create(); tap($entry->makeWorkingCopy(), function ($copy) { @@ -296,7 +296,7 @@ public function it_creates_a_revision() 'blueprint' => 'test', 'title' => 'Title', 'foo' => 'bar', - 'bar' => 'foo' + 'bar' => 'foo', ], $entry->data()->all()); $this->assertFalse($entry->published()); $this->assertCount(1, $entry->revisions()); From 710f61fb013999c1fd2a61e75696e8e8a94c8a01 Mon Sep 17 00:00:00 2001 From: edalzell Date: Fri, 3 Jan 2025 14:32:35 -0800 Subject: [PATCH 20/45] prep for revisions --- tests/Feature/Entries/UpdateEntryTest.php | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/tests/Feature/Entries/UpdateEntryTest.php b/tests/Feature/Entries/UpdateEntryTest.php index 4531ade84d1..e7a480fe564 100644 --- a/tests/Feature/Entries/UpdateEntryTest.php +++ b/tests/Feature/Entries/UpdateEntryTest.php @@ -11,6 +11,7 @@ use Statamic\Facades\Blueprint; use Statamic\Facades\Collection; use Statamic\Facades\Entry; +use Statamic\Facades\Folder; use Statamic\Facades\Role; use Statamic\Facades\User; use Statamic\Structures\CollectionStructure; @@ -23,6 +24,24 @@ class UpdateEntryTest extends TestCase use FakesRoles; use PreventSavingStacheItemsToDisk; + public function setUp(): void + { + parent::setUp(); + + $this->dir = __DIR__.'/tmp'; + + config([ + 'statamic.revisions.path' => $this->dir, + 'statamic.revisions.enable' => true, + ]); + } + + public function tearDown(): void + { + Folder::delete($this->dir); + parent::tearDown(); + } + #[Test] public function it_denies_access_if_you_dont_have_edit_permission() { From 1f89cedad72de08fa1e28a09f8c673a59ed1028e Mon Sep 17 00:00:00 2001 From: edalzell Date: Fri, 3 Jan 2025 14:44:04 -0800 Subject: [PATCH 21/45] get base test going --- tests/Feature/Entries/UpdateEntryTest.php | 44 +++++++++++++++++++++-- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/tests/Feature/Entries/UpdateEntryTest.php b/tests/Feature/Entries/UpdateEntryTest.php index e7a480fe564..f36833bfa80 100644 --- a/tests/Feature/Entries/UpdateEntryTest.php +++ b/tests/Feature/Entries/UpdateEntryTest.php @@ -435,7 +435,45 @@ public function it_can_validate_against_published_value() #[Test] public function published_entry_gets_saved_to_working_copy() { - $this->markTestIncomplete(); + [$user, $collection] = $this->seedUserAndCollection(); + + $this->seedBlueprintFields($collection, [ + 'revisable' => ['type' => 'text'], + 'non_revisable' => ['type' => 'text', 'revisable' => false], + ]); + + $entry = EntryFactory::id('1') + ->slug('test') + ->collection('test') + ->data([ + 'title' => 'revisable test', + ])->create(); + + $this + ->actingAs($user) + ->update($entry, [ + 'revisable' => 'revise me', + 'non_revisable' => 'no revisions for you', + ]) + ->assertOk(); + + // $entry = Entry::find($entry->id()); + // $this->assertEquals('test', $entry->slug()); + // $this->assertEquals([ + // 'blueprint' => 'test', + // 'title' => 'Original title', + // 'foo' => 'bar', + // 'updated_at' => $originalTimestamp, + // ], $entry->data()); + + // $workingCopy = $entry->fromWorkingCopy(); + // $this->assertEquals('updated-slug', $workingCopy->slug()); + // $this->assertEquals([ + // 'blueprint' => 'test', + // 'title' => 'Updated title', + // 'foo' => 'updated foo', + // ], $workingCopy->data()); + } #[Test] @@ -520,7 +558,7 @@ public function does_not_validate_max_depth_when_collection_max_depth_is_null() ->assertOk(); } - private function seedUserAndCollection() + private function seedUserAndCollection(bool $enableRevisions = false) { $this->setTestRoles(['test' => [ 'access cp', @@ -529,7 +567,7 @@ private function seedUserAndCollection() 'access fr site', ]]); $user = tap(User::make()->assignRole('test'))->save(); - $collection = tap(Collection::make('test'))->save(); + $collection = tap(Collection::make('test')->revisionsEnabled($enableRevisions))->save(); return [$user, $collection]; } From 5e845ff2ca01ee8fd248c7f41d97d1fa0d89a524 Mon Sep 17 00:00:00 2001 From: edalzell Date: Fri, 3 Jan 2025 16:18:26 -0800 Subject: [PATCH 22/45] add test and pass --- .../CP/Collections/EntriesController.php | 18 ++++++++- tests/Feature/Entries/UpdateEntryTest.php | 39 +++++++++---------- 2 files changed, 35 insertions(+), 22 deletions(-) diff --git a/src/Http/Controllers/CP/Collections/EntriesController.php b/src/Http/Controllers/CP/Collections/EntriesController.php index 69138c8cc07..c38cf802be7 100644 --- a/src/Http/Controllers/CP/Collections/EntriesController.php +++ b/src/Http/Controllers/CP/Collections/EntriesController.php @@ -6,6 +6,7 @@ use Illuminate\Validation\ValidationException; use Statamic\Contracts\Entries\Entry as EntryContract; use Statamic\CP\Breadcrumbs; +use Statamic\Entries\Entry as EntriesEntry; use Statamic\Exceptions\BlueprintNotFoundException; use Statamic\Facades\Action; use Statamic\Facades\Asset; @@ -13,6 +14,7 @@ use Statamic\Facades\Site; use Statamic\Facades\Stache; use Statamic\Facades\User; +use Statamic\Fields\Field; use Statamic\Hooks\CP\EntriesIndexQuery; use Statamic\Http\Controllers\CP\CpController; use Statamic\Http\Requests\FilteredRequest; @@ -263,7 +265,7 @@ public function update(Request $request, $collection, $entry) // catch any changes through RevisionSaving event // have to save in case there are non-revisable fields - $entry = tap($entry->fromWorkingCopy())->save(); + $entry = $this->saveNonRevisableFields($entry); } else { if (! $entry->revisionsEnabled() && User::current()->can('publish', $entry)) { $entry->published($request->published); @@ -564,4 +566,18 @@ protected function ensureCollectionIsAvailableOnSite($collection, $site) return redirect()->back()->with('error', __('Collection is not available on site ":handle".', ['handle' => $site->handle])); } } + + private function saveNonRevisableFields(EntriesEntry $entry): EntriesEntry + { + /** @var EntriesEntry */ + $savedVersion = $entry->fresh(); + + $entry->blueprint()->fields()->all() + ->reject(fn (Field $field) => $field->isRevisable()) + ->each(fn ($ignore, string $fieldHandle) => $savedVersion->set($fieldHandle, $entry->{$fieldHandle})); + + $savedVersion->save(); + + return $savedVersion; + } } diff --git a/tests/Feature/Entries/UpdateEntryTest.php b/tests/Feature/Entries/UpdateEntryTest.php index f36833bfa80..5db854a3c2d 100644 --- a/tests/Feature/Entries/UpdateEntryTest.php +++ b/tests/Feature/Entries/UpdateEntryTest.php @@ -31,8 +31,9 @@ public function setUp(): void $this->dir = __DIR__.'/tmp'; config([ + 'statamic.editions.pro' => true, 'statamic.revisions.path' => $this->dir, - 'statamic.revisions.enable' => true, + 'statamic.revisions.enabled' => true, ]); } @@ -435,7 +436,7 @@ public function it_can_validate_against_published_value() #[Test] public function published_entry_gets_saved_to_working_copy() { - [$user, $collection] = $this->seedUserAndCollection(); + [$user, $collection] = $this->seedUserAndCollection(true); $this->seedBlueprintFields($collection, [ 'revisable' => ['type' => 'text'], @@ -445,9 +446,8 @@ public function published_entry_gets_saved_to_working_copy() $entry = EntryFactory::id('1') ->slug('test') ->collection('test') - ->data([ - 'title' => 'revisable test', - ])->create(); + ->data(['title' => 'Revisable Test', 'published' => true]) + ->create(); $this ->actingAs($user) @@ -457,23 +457,20 @@ public function published_entry_gets_saved_to_working_copy() ]) ->assertOk(); - // $entry = Entry::find($entry->id()); - // $this->assertEquals('test', $entry->slug()); - // $this->assertEquals([ - // 'blueprint' => 'test', - // 'title' => 'Original title', - // 'foo' => 'bar', - // 'updated_at' => $originalTimestamp, - // ], $entry->data()); - - // $workingCopy = $entry->fromWorkingCopy(); - // $this->assertEquals('updated-slug', $workingCopy->slug()); - // $this->assertEquals([ - // 'blueprint' => 'test', - // 'title' => 'Updated title', - // 'foo' => 'updated foo', - // ], $workingCopy->data()); + $entry = Entry::find($entry->id()); + $this->assertEquals('no revisions for you', $entry->non_revisable); + $this->assertEquals('Revisable Test', $entry->title); + $this->assertEquals('test', $entry->slug()); + $this->assertNull($entry->revisable); + $workingCopy = $entry->fromWorkingCopy(); + $this->assertEquals('updated-entry', $workingCopy->slug()); + $this->assertEquals([ + 'title' => 'Updated entry', + 'revisable' => 'revise me', + 'non_revisable' => 'no revisions for you', + 'published' => true + ], $workingCopy->data()->all()); } #[Test] From 5c149ad576c28daffc7b0afbd51c182c482d99ea Mon Sep 17 00:00:00 2001 From: edalzell Date: Fri, 3 Jan 2025 16:32:09 -0800 Subject: [PATCH 23/45] lint --- tests/Feature/Entries/UpdateEntryTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Feature/Entries/UpdateEntryTest.php b/tests/Feature/Entries/UpdateEntryTest.php index 5db854a3c2d..5962858368e 100644 --- a/tests/Feature/Entries/UpdateEntryTest.php +++ b/tests/Feature/Entries/UpdateEntryTest.php @@ -469,7 +469,7 @@ public function published_entry_gets_saved_to_working_copy() 'title' => 'Updated entry', 'revisable' => 'revise me', 'non_revisable' => 'no revisions for you', - 'published' => true + 'published' => true, ], $workingCopy->data()->all()); } From 2149c6406532d4f3b68cc641e9fb1c1248a3201f Mon Sep 17 00:00:00 2001 From: edalzell Date: Tue, 28 Jan 2025 13:07:33 -0800 Subject: [PATCH 24/45] whoops need to return all data to the front end --- .../Controllers/CP/Collections/EntriesController.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Http/Controllers/CP/Collections/EntriesController.php b/src/Http/Controllers/CP/Collections/EntriesController.php index c38cf802be7..47c628f8bf9 100644 --- a/src/Http/Controllers/CP/Collections/EntriesController.php +++ b/src/Http/Controllers/CP/Collections/EntriesController.php @@ -263,9 +263,11 @@ public function update(Request $request, $collection, $entry) ->user(User::current()) ->save(); - // catch any changes through RevisionSaving event // have to save in case there are non-revisable fields - $entry = $this->saveNonRevisableFields($entry); + $this->saveNonRevisableFields($entry); + + // catch any changes through RevisionSaving event + $entry = $entry->fromWorkingCopy(); } else { if (! $entry->revisionsEnabled() && User::current()->can('publish', $entry)) { $entry->published($request->published); @@ -567,17 +569,16 @@ protected function ensureCollectionIsAvailableOnSite($collection, $site) } } - private function saveNonRevisableFields(EntriesEntry $entry): EntriesEntry + private function saveNonRevisableFields(EntriesEntry $entry): void { /** @var EntriesEntry */ $savedVersion = $entry->fresh(); $entry->blueprint()->fields()->all() ->reject(fn (Field $field) => $field->isRevisable()) + ->each(fn ($ignore, string $fieldHandle) => ray($fieldHandle)) ->each(fn ($ignore, string $fieldHandle) => $savedVersion->set($fieldHandle, $entry->{$fieldHandle})); $savedVersion->save(); - - return $savedVersion; } } From 0e2ea62bb2cc1378050531b00fc7929f6f6fde1c Mon Sep 17 00:00:00 2001 From: edalzell Date: Tue, 28 Jan 2025 13:10:53 -0800 Subject: [PATCH 25/45] remove debugging --- src/Http/Controllers/CP/Collections/EntriesController.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Http/Controllers/CP/Collections/EntriesController.php b/src/Http/Controllers/CP/Collections/EntriesController.php index 47c628f8bf9..eff42358c26 100644 --- a/src/Http/Controllers/CP/Collections/EntriesController.php +++ b/src/Http/Controllers/CP/Collections/EntriesController.php @@ -576,7 +576,6 @@ private function saveNonRevisableFields(EntriesEntry $entry): void $entry->blueprint()->fields()->all() ->reject(fn (Field $field) => $field->isRevisable()) - ->each(fn ($ignore, string $fieldHandle) => ray($fieldHandle)) ->each(fn ($ignore, string $fieldHandle) => $savedVersion->set($fieldHandle, $entry->{$fieldHandle})); $savedVersion->save(); From 17067fa7c3895416a00b7becbe656831978d6da3 Mon Sep 17 00:00:00 2001 From: Erin Dalzell Date: Tue, 28 Jan 2025 13:12:58 -0800 Subject: [PATCH 26/45] Discard changes to src/Http/Controllers/CP/Taxonomies/TermsController.php --- src/Http/Controllers/CP/Taxonomies/TermsController.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Http/Controllers/CP/Taxonomies/TermsController.php b/src/Http/Controllers/CP/Taxonomies/TermsController.php index f0e7d2e09d4..d336504858c 100644 --- a/src/Http/Controllers/CP/Taxonomies/TermsController.php +++ b/src/Http/Controllers/CP/Taxonomies/TermsController.php @@ -200,9 +200,6 @@ public function update(Request $request, $taxonomy, $term, $site) ->makeWorkingCopy() ->user(User::current()) ->save(); - - // have to save in case there are non-revisable fields - $term = tap($term->fromWorkingCopy())->save(); } else { if (! $term->revisionsEnabled()) { $term->published($request->published); From 0473902462e14d838a45b5d86be6946b9669e32c Mon Sep 17 00:00:00 2001 From: Erin Dalzell Date: Tue, 28 Jan 2025 13:13:09 -0800 Subject: [PATCH 27/45] Discard changes to src/Taxonomies/LocalizedTerm.php --- src/Taxonomies/LocalizedTerm.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Taxonomies/LocalizedTerm.php b/src/Taxonomies/LocalizedTerm.php index 97613a52ea3..f40b394ae00 100644 --- a/src/Taxonomies/LocalizedTerm.php +++ b/src/Taxonomies/LocalizedTerm.php @@ -244,23 +244,23 @@ protected function revisionAttributes() 'id' => $this->id(), 'slug' => $this->slug(), 'published' => $this->published(), - 'data' => $this->data()->except(['updated_by', 'updated_at', ...$this->nonRevisableFields()])->all(), + 'data' => $this->data()->except(['updated_by', 'updated_at'])->all(), ]; } public function makeFromRevision($revision) { - $term = clone $this; + $entry = clone $this; if (! $revision) { - return $term; + return $entry; } $attrs = $revision->attributes(); - return $term + return $entry ->published($attrs['published']) - ->data($this->data()->merge($attrs['data'])) + ->data($attrs['data']) ->slug($attrs['slug']); } From 439235787a27eee480344a1cb1486747aceda410 Mon Sep 17 00:00:00 2001 From: edalzell Date: Fri, 14 Feb 2025 14:31:37 -0800 Subject: [PATCH 28/45] remove term stuff --- tests/Revisions/RevisableTest.php | 34 ------------------------------- 1 file changed, 34 deletions(-) diff --git a/tests/Revisions/RevisableTest.php b/tests/Revisions/RevisableTest.php index 2d422f0cdb9..3fe74c9f58d 100644 --- a/tests/Revisions/RevisableTest.php +++ b/tests/Revisions/RevisableTest.php @@ -7,10 +7,7 @@ use Statamic\Entries\Entry; use Statamic\Facades\Blueprint; use Statamic\Facades\Collection; -use Statamic\Facades\Taxonomy; use Statamic\Revisions\RevisionRepository; -use Statamic\Taxonomies\LocalizedTerm; -use Statamic\Taxonomies\Term; use Tests\PreventSavingStacheItemsToDisk; use Tests\TestCase; @@ -50,9 +47,7 @@ public function it_sets_proper_revision_attributes() 'non_revisable' => ['type' => 'text', 'revisable' => false], ]); BlueprintRepository::shouldReceive('in')->with('collections/blog')->andReturn(collect(['blog' => $blueprint])); - BlueprintRepository::shouldReceive('in')->with('taxonomies/tags')->andReturn(collect(['tags' => $blueprint])); Collection::make('blog')->save(); - Taxonomy::make('tags')->save(); $entry = (new Entry) ->collection('blog') @@ -60,11 +55,7 @@ public function it_sets_proper_revision_attributes() ->set('revisable', 'see me') ->set('non_revisable', "don't see me"); - $term = (new Term)->taxonomy('tags')->slug('foo')->data(['revisable' => 'see me', 'non_revisable' => "don't see me"]); - $localizedTerm = (new LocalizedTerm($term, 'en')); - $this->assertEquals(['revisable' => 'see me'], $entry->makeRevision()->attributes()['data']); - $this->assertEquals(['revisable' => 'see me'], $localizedTerm->makeRevision()->attributes()['data']); } #[Test] @@ -91,29 +82,4 @@ public function it_can_make_entry_from_revision() $entry->makeFromRevision($revision)->data() ); } - - #[Test] - public function it_can_make_term_from_revision() - { - $blueprint = Blueprint::makeFromFields([ - 'revisable' => ['type' => 'text'], - 'non_revisable' => ['type' => 'text', 'revisable' => false], - ]); - - BlueprintRepository::shouldReceive('in')->with('taxonomies/tags')->andReturn(collect(['tags' => $blueprint])); - Taxonomy::make('tags')->save(); - - $term = (new Term)->taxonomy('tags')->slug('foo')->data(['revisable' => 'override me', 'non_revisable' => "don't override me"]); - $localizedTerm = (new LocalizedTerm($term, 'en')); - - $revision = $this->repo->whereKey('123')->first(); - - $this->assertEquals( - collect([ - 'revisable' => 'overridden', - 'non_revisable' => "don't override me", - ]), - $localizedTerm->makeFromRevision($revision)->data() - ); - } } From 96b25400159f5d0efb04463bead0df246e9b93f7 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 1 Oct 2025 11:45:01 -0700 Subject: [PATCH 29/45] =?UTF-8?q?Computed=20fields=20are=20always=20revisa?= =?UTF-8?q?ble=20cuz=20they=20shouldn=E2=80=99t=20be=20saved?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/Fields/Field.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Fields/Field.php b/src/Fields/Field.php index 7f671b3a3a1..ef496f6463e 100644 --- a/src/Fields/Field.php +++ b/src/Fields/Field.php @@ -257,6 +257,10 @@ public function isFilterable() public function isRevisable() { + if ($this->visibility() === 'computed') { + return true; + } + return (bool) $this->get('revisable', true); } From 63f722c86e6506914b99615fa42a34caba8ba487 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 1 Oct 2025 11:48:14 -0700 Subject: [PATCH 30/45] Add test --- tests/Fields/FieldTest.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/Fields/FieldTest.php b/tests/Fields/FieldTest.php index 01e54145839..c735a2ede4b 100644 --- a/tests/Fields/FieldTest.php +++ b/tests/Fields/FieldTest.php @@ -294,6 +294,7 @@ public function converts_to_array_suitable_for_rendering_fields_in_publish_compo ->andReturn(new class extends Fieldtype { protected $component = 'example'; + protected $configFields = [ 'a_config_field_with_pre_processing' => ['type' => 'with_processing'], 'a_config_field_without_pre_processing' => ['type' => 'without_processing'], @@ -689,4 +690,12 @@ public function it_gets_revisable() $this->assertFalse($field->isRevisable()); } + + #[Test] + public function its_revisable_when_computed() + { + $field = new Field('test', ['type' => 'text', 'revisable' => false, 'visibility' => 'computed']); + + $this->assertTrue($field->isRevisable()); + } } From 1d55d8c8f2b9ca5555486e6329fbeeb31da8218f Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 25 Mar 2026 10:32:55 -0700 Subject: [PATCH 31/45] get from container now that these have query builders --- tests/Revisions/RevisableTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Revisions/RevisableTest.php b/tests/Revisions/RevisableTest.php index 3fe74c9f58d..13791d0601d 100644 --- a/tests/Revisions/RevisableTest.php +++ b/tests/Revisions/RevisableTest.php @@ -21,7 +21,7 @@ protected function setUp(): void { parent::setUp(); config(['statamic.revisions.path' => __DIR__.'/__fixtures__']); - $this->repo = (new RevisionRepository); + $this->repo = app(RevisionRepository::class); } #[Test] From a26fcd49eee442dde4cec57752e9086fbe98cb85 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 25 Mar 2026 11:14:14 -0700 Subject: [PATCH 32/45] fix tests --- tests/Feature/Entries/EntryRevisionsTest.php | 4 ++-- tests/Fields/BlueprintTest.php | 8 ++++---- tests/Fields/FieldTest.php | 2 +- tests/Fields/SectionTest.php | 4 ++-- tests/Fields/TabTest.php | 4 ++-- tests/Fieldtypes/NestedFieldsTest.php | 2 +- tests/Fieldtypes/SetsTest.php | 5 +++-- 7 files changed, 15 insertions(+), 14 deletions(-) diff --git a/tests/Feature/Entries/EntryRevisionsTest.php b/tests/Feature/Entries/EntryRevisionsTest.php index dd790492eed..93db6a17042 100644 --- a/tests/Feature/Entries/EntryRevisionsTest.php +++ b/tests/Feature/Entries/EntryRevisionsTest.php @@ -53,7 +53,7 @@ public function it_gets_revisions() ], ] ); - $this->setTestRoles(['test' => ['access cp', 'publish blog entries']]); + $this->setTestRoles(['test' => ['access cp', 'view blog entries']]); $user = User::make()->id('user-1')->assignRole('test')->save(); $entry = EntryFactory::id('1') @@ -87,7 +87,7 @@ public function it_gets_revisions() $this ->actingAs($user) - ->get($entry->revisionsUrl()) + ->get(ray()->pass($entry->revisionsUrl())) ->assertOk() ->assertJsonPath('0.revisions.0.action', 'revision') ->assertJsonPath('0.revisions.0.message', 'Revision one') diff --git a/tests/Fields/BlueprintTest.php b/tests/Fields/BlueprintTest.php index 46aa1fd97a1..31f4e672641 100644 --- a/tests/Fields/BlueprintTest.php +++ b/tests/Fields/BlueprintTest.php @@ -450,8 +450,8 @@ public function converts_to_array_suitable_for_rendering_fields_in_publish_compo 'sortable' => true, 'replicator_preview' => true, 'duplicate' => true, - 'revisable' => true, 'actions' => true, + 'revisable' => true, 'type' => 'text', 'input_type' => 'text', 'character_limit' => null, @@ -489,8 +489,8 @@ public function converts_to_array_suitable_for_rendering_fields_in_publish_compo 'sortable' => true, 'replicator_preview' => true, 'duplicate' => true, - 'revisable' => true, 'actions' => true, + 'revisable' => true, 'type' => 'textarea', 'placeholder' => null, 'character_limit' => null, @@ -592,8 +592,8 @@ public function converts_to_array_suitable_for_rendering_prefixed_conditional_fi 'sortable' => true, 'replicator_preview' => true, 'duplicate' => true, - 'revisable' => true, 'actions' => true, + 'revisable' => true, 'type' => 'text', 'input_type' => 'text', 'character_limit' => null, @@ -620,8 +620,8 @@ public function converts_to_array_suitable_for_rendering_prefixed_conditional_fi 'sortable' => true, 'replicator_preview' => true, 'duplicate' => true, - 'revisable' => true, 'actions' => true, + 'revisable' => true, 'type' => 'text', 'input_type' => 'text', 'character_limit' => null, diff --git a/tests/Fields/FieldTest.php b/tests/Fields/FieldTest.php index ab705d252bf..f78eb84232f 100644 --- a/tests/Fields/FieldTest.php +++ b/tests/Fields/FieldTest.php @@ -343,8 +343,8 @@ public function preProcess($data) 'sortable' => true, 'replicator_preview' => true, 'duplicate' => true, - 'revisable' => true, 'actions' => true, + 'revisable' => true, 'type' => 'example', 'foo' => 'bar', 'a_config_field_with_pre_processing' => 'foo preprocessed', diff --git a/tests/Fields/SectionTest.php b/tests/Fields/SectionTest.php index 11d14d54f5c..af2cc386a38 100644 --- a/tests/Fields/SectionTest.php +++ b/tests/Fields/SectionTest.php @@ -125,8 +125,8 @@ public function converts_to_array_suitable_for_rendering_fields_in_publish_compo 'sortable' => true, 'replicator_preview' => true, 'duplicate' => true, - 'revisable' => true, 'actions' => true, + 'revisable' => true, 'type' => 'text', 'input_type' => 'text', 'character_limit' => null, @@ -153,8 +153,8 @@ public function converts_to_array_suitable_for_rendering_fields_in_publish_compo 'sortable' => true, 'replicator_preview' => true, 'duplicate' => true, - 'revisable' => true, 'actions' => true, + 'revisable' => true, 'type' => 'textarea', 'placeholder' => null, 'character_limit' => null, diff --git a/tests/Fields/TabTest.php b/tests/Fields/TabTest.php index 9f7f815fd7e..58055d00185 100644 --- a/tests/Fields/TabTest.php +++ b/tests/Fields/TabTest.php @@ -150,8 +150,8 @@ public function converts_to_array_suitable_for_rendering_fields_in_publish_compo 'sortable' => true, 'replicator_preview' => true, 'duplicate' => true, - 'revisable' => true, 'actions' => true, + 'revisable' => true, 'type' => 'text', 'input_type' => 'text', 'character_limit' => null, @@ -178,8 +178,8 @@ public function converts_to_array_suitable_for_rendering_fields_in_publish_compo 'sortable' => true, 'replicator_preview' => true, 'duplicate' => true, - 'revisable' => true, 'actions' => true, + 'revisable' => true, 'type' => 'textarea', 'placeholder' => null, 'character_limit' => null, diff --git a/tests/Fieldtypes/NestedFieldsTest.php b/tests/Fieldtypes/NestedFieldsTest.php index 2ad91496202..0c01d5b6ca7 100644 --- a/tests/Fieldtypes/NestedFieldsTest.php +++ b/tests/Fieldtypes/NestedFieldsTest.php @@ -85,8 +85,8 @@ public function preProcess($data) 'sortable' => true, 'replicator_preview' => true, 'duplicate' => true, - 'revisable' => true, 'actions' => true, + 'revisable' => true, 'type' => 'assets', 'max_files' => 2, 'container' => 'main', diff --git a/tests/Fieldtypes/SetsTest.php b/tests/Fieldtypes/SetsTest.php index 08de7a1d702..0eff6f2c5a3 100644 --- a/tests/Fieldtypes/SetsTest.php +++ b/tests/Fieldtypes/SetsTest.php @@ -233,6 +233,7 @@ public function it_preprocesses_for_config_with_groups() 'replicator_preview' => true, 'duplicate' => true, 'actions' => true, + 'revisable' => true, 'type' => 'text', 'input_type' => 'text', 'character_limit' => null, @@ -271,8 +272,8 @@ public function it_preprocesses_for_config_with_groups() 'sortable' => true, 'replicator_preview' => true, 'duplicate' => true, - 'revisable' => true, 'actions' => true, + 'revisable' => true, 'type' => 'text', 'input_type' => 'text', 'character_limit' => null, @@ -334,8 +335,8 @@ public function it_preprocesses_for_config_without_groups() 'sortable' => true, 'replicator_preview' => true, 'duplicate' => true, - 'revisable' => true, 'actions' => true, + 'revisable' => true, 'type' => 'text', 'input_type' => 'text', 'character_limit' => null, From 6f9ee067e26bc32f7dc8e8196a1d661afd60c330 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 25 Mar 2026 11:18:50 -0700 Subject: [PATCH 33/45] remove debugging --- tests/Feature/Entries/EntryRevisionsTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Feature/Entries/EntryRevisionsTest.php b/tests/Feature/Entries/EntryRevisionsTest.php index 93db6a17042..15ebafaab67 100644 --- a/tests/Feature/Entries/EntryRevisionsTest.php +++ b/tests/Feature/Entries/EntryRevisionsTest.php @@ -22,6 +22,7 @@ class EntryRevisionsTest extends TestCase use PreventSavingStacheItemsToDisk; private $dir; + private $collection; public function setUp(): void @@ -87,7 +88,7 @@ public function it_gets_revisions() $this ->actingAs($user) - ->get(ray()->pass($entry->revisionsUrl())) + ->get($entry->revisionsUrl()) ->assertOk() ->assertJsonPath('0.revisions.0.action', 'revision') ->assertJsonPath('0.revisions.0.message', 'Revision one') From 2b77d7809b4d7ac1fed8a6208ec86f06d81cf76d Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 25 Mar 2026 11:27:13 -0700 Subject: [PATCH 34/45] fix test --- tests/Revisions/RevisableTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Revisions/RevisableTest.php b/tests/Revisions/RevisableTest.php index 13791d0601d..2b85b2bc1fd 100644 --- a/tests/Revisions/RevisableTest.php +++ b/tests/Revisions/RevisableTest.php @@ -76,7 +76,7 @@ public function it_can_make_entry_from_revision() $this->assertEquals( collect([ - 'revisable' => 'overridden', + 'revisable' => 'override me', 'non_revisable' => "don't override me", ]), $entry->makeFromRevision($revision)->data() From 24d08bd724ab14aa10de15c0ddbc31e3935dea14 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 2 Sep 2026 11:06:58 -0700 Subject: [PATCH 35/45] Fully replace data when making an entry from a revision, keeping only non-revisable fields --- src/Entries/Entry.php | 2 +- tests/Feature/Entries/EntryRevisionsTest.php | 42 +++++++++++++++++++ tests/Revisions/RevisableTest.php | 26 ++++++------ .../__fixtures__/123/1553546421.yaml | 8 +--- 4 files changed, 56 insertions(+), 22 deletions(-) diff --git a/src/Entries/Entry.php b/src/Entries/Entry.php index 61d228eaa7e..2ac70a369e5 100644 --- a/src/Entries/Entry.php +++ b/src/Entries/Entry.php @@ -778,7 +778,7 @@ public function makeFromRevision($revision) $entry ->published($attrs['published']) - ->data($this->data()->merge($attrs['data'])) + ->data(collect($attrs['data'])->merge($this->data()->only($this->nonRevisableFields()))) ->slug($attrs['slug']); if ($this->collection()->dated() && ($date = Arr::get($attrs, 'date'))) { diff --git a/tests/Feature/Entries/EntryRevisionsTest.php b/tests/Feature/Entries/EntryRevisionsTest.php index a752c510269..adcc058afe6 100644 --- a/tests/Feature/Entries/EntryRevisionsTest.php +++ b/tests/Feature/Entries/EntryRevisionsTest.php @@ -580,6 +580,48 @@ public function it_restores_an_unpublished_entrys_contents_to_another_revision() $this->assertCount(1, $entry->revisions()); } + #[Test] + public function it_keeps_non_revisable_fields_when_restoring_an_unpublished_entrys_contents() + { + $this->setTestBlueprint('test', [ + 'foo' => ['type' => 'text'], + 'bar' => ['type' => 'text', 'revisable' => false], + ]); + $this->setTestRoles(['test' => ['access cp', 'edit blog entries']]); + $user = User::make()->id('user-1')->assignRole('test')->save(); + + tap((new Revision) + ->key('collections/blog/en/123') + ->date(Carbon::createFromTimestamp('1553546421', config('app.timezone'))) + ->attributes([ + 'published' => true, + 'slug' => 'existing-slug', + 'data' => ['foo' => 'existing foo'], + ]))->save(); + + $entry = EntryFactory::id('123') + ->slug('test') + ->collection('blog') + ->published(false) + ->data([ + 'blueprint' => 'test', + 'foo' => 'bar', + 'bar' => 'not tracked by revisions', + 'stale' => 'not in the revision', + ])->create(); + + $this + ->actingAs($user) + ->restore($entry, ['revision' => '1553546421']) + ->assertOk() + ->assertSessionHas('success'); + + $entry = Entry::find($entry->id()); + $this->assertEquals('existing foo', $entry->get('foo')); + $this->assertEquals('not tracked by revisions', $entry->get('bar')); + $this->assertFalse($entry->has('stale')); + } + private function publish($entry, $payload) { return $this->post($entry->publishUrl(), $payload); diff --git a/tests/Revisions/RevisableTest.php b/tests/Revisions/RevisableTest.php index 2b85b2bc1fd..0a92ca65e3b 100644 --- a/tests/Revisions/RevisableTest.php +++ b/tests/Revisions/RevisableTest.php @@ -7,7 +7,7 @@ use Statamic\Entries\Entry; use Statamic\Facades\Blueprint; use Statamic\Facades\Collection; -use Statamic\Revisions\RevisionRepository; +use Statamic\Revisions\Revision; use Tests\PreventSavingStacheItemsToDisk; use Tests\TestCase; @@ -15,15 +15,6 @@ class RevisableTest extends TestCase { use PreventSavingStacheItemsToDisk; - private $repo; - - protected function setUp(): void - { - parent::setUp(); - config(['statamic.revisions.path' => __DIR__.'/__fixtures__']); - $this->repo = app(RevisionRepository::class); - } - #[Test] public function it_gets_non_revisable_fields() { @@ -70,13 +61,20 @@ public function it_can_make_entry_from_revision() Collection::make('blog')->save(); $entry = (new Entry)->collection('blog')->id('123'); - $entry->set('revisable', 'override me')->set('non_revisable', "don't override me"); - - $revision = $this->repo->whereKey('123')->first(); + $entry + ->set('revisable', 'override me') + ->set('non_revisable', "don't override me") + ->set('stale', 'not in the revision, so drop me'); + + $revision = (new Revision)->attributes([ + 'published' => true, + 'slug' => 'the-slug', + 'data' => ['revisable' => 'overridden'], + ]); $this->assertEquals( collect([ - 'revisable' => 'override me', + 'revisable' => 'overridden', 'non_revisable' => "don't override me", ]), $entry->makeFromRevision($revision)->data() diff --git a/tests/Revisions/__fixtures__/123/1553546421.yaml b/tests/Revisions/__fixtures__/123/1553546421.yaml index cb828ee7ebd..833a2599843 100644 --- a/tests/Revisions/__fixtures__/123/1553546421.yaml +++ b/tests/Revisions/__fixtures__/123/1553546421.yaml @@ -1,8 +1,2 @@ date: 1553546421 -attributes: - id: 123 - slug: the-slug - published: true - date: 1633590000 - data: - revisable: 'overridden' +attributes: {} From 02250653b28203755e6a1d8c1163f18091c7f619 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 2 Sep 2026 11:06:58 -0700 Subject: [PATCH 36/45] Save non-revisable fields from raw data and skip the save when there is nothing to write --- .../CP/Collections/EntriesController.php | 15 +-- tests/Feature/Entries/UpdateEntryTest.php | 111 ++++++++++++++++++ 2 files changed, 117 insertions(+), 9 deletions(-) diff --git a/src/Http/Controllers/CP/Collections/EntriesController.php b/src/Http/Controllers/CP/Collections/EntriesController.php index de63d4e31e4..f9226dc90cb 100644 --- a/src/Http/Controllers/CP/Collections/EntriesController.php +++ b/src/Http/Controllers/CP/Collections/EntriesController.php @@ -6,7 +6,6 @@ use Illuminate\Validation\ValidationException; use Inertia\Inertia; use Statamic\Contracts\Entries\Entry as EntryContract; -use Statamic\Entries\Entry as EntriesEntry; use Statamic\Exceptions\BlueprintNotFoundException; use Statamic\Facades\Action; use Statamic\Facades\Blink; @@ -14,7 +13,6 @@ use Statamic\Facades\Site; use Statamic\Facades\Stache; use Statamic\Facades\User; -use Statamic\Fields\Field; use Statamic\Hooks\CP\EntriesIndexQuery; use Statamic\Http\Controllers\CP\CpController; use Statamic\Http\Requests\FilteredRequest; @@ -534,15 +532,14 @@ protected function ensureCollectionIsAvailableOnSite($collection, $site) } } - private function saveNonRevisableFields(EntriesEntry $entry): void + private function saveNonRevisableFields(EntryContract $entry): void { - /** @var EntriesEntry */ - $savedVersion = $entry->fresh(); + $values = $entry->data()->only($entry->nonRevisableFields()); - $entry->blueprint()->fields()->all() - ->reject(fn (Field $field) => $field->isRevisable()) - ->each(fn ($ignore, string $fieldHandle) => $savedVersion->set($fieldHandle, $entry->{$fieldHandle})); + if ($values->isEmpty()) { + return; + } - $savedVersion->save(); + $entry->fresh()->merge($values)->save(); } } diff --git a/tests/Feature/Entries/UpdateEntryTest.php b/tests/Feature/Entries/UpdateEntryTest.php index c04c4aa6b6b..3b2bd272ba1 100644 --- a/tests/Feature/Entries/UpdateEntryTest.php +++ b/tests/Feature/Entries/UpdateEntryTest.php @@ -460,6 +460,117 @@ public function auto_title_only_gets_saved_on_localization_when_different_from_o $this->assertNull($localization->get('title')); } + #[Test] + public function non_revisable_fields_are_saved_with_raw_values() + { + [$user, $collection] = $this->seedUserAndCollection(true); + + $this->seedBlueprintFields($collection, [ + 'related' => ['type' => 'entries', 'revisable' => false], + 'when' => ['type' => 'date', 'format' => 'Y-m-d', 'revisable' => false], + ]); + + $entry = EntryFactory::id('1') + ->slug('test') + ->collection('test') + ->published(true) + ->data(['title' => 'Test']) + ->create(); + + $this + ->actingAs($user) + ->update($entry, [ + 'related' => ['2', '3'], + 'when' => '2024-03-04', + ]) + ->assertOk(); + + $entry = Entry::find($entry->id()); + $this->assertSame(['2', '3'], $entry->get('related')); + $this->assertSame('2024-03-04', $entry->get('when')); + } + + #[Test] + public function published_entry_without_non_revisable_fields_is_not_saved() + { + [$user, $collection] = $this->seedUserAndCollection(true); + + $this->seedBlueprintFields($collection, [ + 'foo' => ['type' => 'text'], + ]); + + $entry = EntryFactory::id('1') + ->slug('test') + ->collection('test') + ->published(true) + ->data(['title' => 'Test']) + ->create(); + + Event::fake([EntrySaving::class]); + + $this + ->actingAs($user) + ->update($entry, ['foo' => 'bar']) + ->assertOk(); + + Event::assertNotDispatched(EntrySaving::class); + $this->assertTrue($entry->fresh()->hasWorkingCopy()); + } + + #[Test] + public function synced_non_revisable_fields_are_not_saved_to_the_localization() + { + $this->setSites([ + 'en' => ['locale' => 'en', 'url' => '/'], + 'fr' => ['locale' => 'fr', 'url' => '/fr/'], + ]); + + [$user, $collection] = $this->seedUserAndCollection(true); + $collection->sites(['en', 'fr'])->save(); + + $this->seedBlueprintFields($collection, [ + 'foo' => ['type' => 'text', 'revisable' => false], + ]); + + $origin = EntryFactory::collection($collection) + ->locale('en') + ->slug('origin') + ->published(true) + ->data(['title' => 'Origin', 'foo' => 'bar']) + ->create(); + + $localization = EntryFactory::collection($collection) + ->locale('fr') + ->origin($origin) + ->slug('localization') + ->published(true) + ->create(); + + $this + ->actingAs($user) + ->update($localization, [ + 'foo' => 'bar', + '_localized' => [], + ]) + ->assertOk(); + + $localization = $localization->fresh(); + $this->assertFalse($localization->has('foo')); + $this->assertEquals('bar', $localization->foo); + + $this + ->actingAs($user) + ->update($localization, [ + 'foo' => 'le bar', + '_localized' => ['foo'], + ]) + ->assertOk(); + + $localization = $localization->fresh(); + $this->assertEquals('le bar', $localization->get('foo')); + $this->assertEquals('bar', $origin->fresh()->get('foo')); + } + #[Test] public function it_can_validate_against_published_value() { From 527fa91e2028c2c3442fe0d5cc669c49136dece2 Mon Sep 17 00:00:00 2001 From: edalzell Date: Wed, 2 Sep 2026 11:06:58 -0700 Subject: [PATCH 37/45] Add revisable instructions text and hide the toggle for computed fields --- lang/en/messages.php | 1 + src/Fields/Field.php | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/lang/en/messages.php b/lang/en/messages.php index 19d1cdad492..c983a116680 100644 --- a/lang/en/messages.php +++ b/lang/en/messages.php @@ -107,6 +107,7 @@ 'fields_instructions_position_instructions' => 'Show instructions above or below the field.', 'fields_listable_instructions' => 'Control the listing column visibility.', 'fields_replicator_preview_instructions' => 'Control preview visibility in Replicator/Bard sets.', + 'fields_revisable_instructions' => 'Include this field in revisions. When disabled, changes are saved straight to the published entry instead of a working copy. Only applies to collection entries.', 'fields_sortable_instructions' => 'Control if the field should be sortable in listing views.', 'fields_visibility_instructions' => 'Control field visibility on publish forms.', 'fieldset_import_fieldset_instructions' => 'The fieldset to be imported.', diff --git a/src/Fields/Field.php b/src/Fields/Field.php index 9216304ab83..210a7bbec13 100644 --- a/src/Fields/Field.php +++ b/src/Fields/Field.php @@ -639,6 +639,10 @@ public static function commonFieldOptions(): Fields 'type' => 'toggle', 'validate' => 'boolean', 'default' => true, + 'unless' => [ + 'visibility' => 'equals computed', + ], + 'width' => 50, ], ])->map(fn ($field, $handle) => compact('handle', 'field'))->values()->all(); From 1c4dde1d206cb183183646070b86c75b690b9959 Mon Sep 17 00:00:00 2001 From: edalzell Date: Mon, 14 Sep 2026 12:19:44 -0700 Subject: [PATCH 38/45] Remove non-revisable fields from a localization when they're unsynced --- .../CP/Collections/EntriesController.php | 23 +++++++-- tests/Feature/Entries/UpdateEntryTest.php | 48 ++++++++++++++++++- 2 files changed, 65 insertions(+), 6 deletions(-) diff --git a/src/Http/Controllers/CP/Collections/EntriesController.php b/src/Http/Controllers/CP/Collections/EntriesController.php index 65285ec28a5..59b4559d053 100644 --- a/src/Http/Controllers/CP/Collections/EntriesController.php +++ b/src/Http/Controllers/CP/Collections/EntriesController.php @@ -259,7 +259,7 @@ public function update(Request $request, $collection, $entry) ->save(); // have to save in case there are non-revisable fields - $this->saveNonRevisableFields($entry); + $this->saveNonRevisableFields($entry, $request->input('_localized')); // catch any changes through RevisionSaving event $entry = $entry->fromWorkingCopy(); @@ -535,14 +535,27 @@ protected function ensureCollectionIsAvailableOnSite($collection, $site) } } - private function saveNonRevisableFields(EntryContract $entry): void + private function saveNonRevisableFields(EntryContract $entry, ?array $localized = null): void { - $values = $entry->data()->only($entry->nonRevisableFields()); + $handles = collect($entry->nonRevisableFields()); - if ($values->isEmpty()) { + $values = $entry->data()->only($handles->all()); + + // On a localization, a non-revisable field that's no longer localized has been + // dropped from the data entirely, so remove it from the saved localization. + // Otherwise the stale value would keep shadowing the origin. + $removed = $entry->hasOrigin() + ? $handles->diff($localized ?? [])->filter(fn ($handle) => $entry->fresh()->has($handle)) + : collect(); + + if ($values->isEmpty() && $removed->isEmpty()) { return; } - $entry->fresh()->merge($values)->save(); + $fresh = $entry->fresh()->merge($values); + + $removed->each(fn ($handle) => $fresh->remove($handle)); + + $fresh->save(); } } diff --git a/tests/Feature/Entries/UpdateEntryTest.php b/tests/Feature/Entries/UpdateEntryTest.php index 15c40988e7c..0e7c075be77 100644 --- a/tests/Feature/Entries/UpdateEntryTest.php +++ b/tests/Feature/Entries/UpdateEntryTest.php @@ -585,6 +585,31 @@ public function non_revisable_fields_are_saved_with_raw_values() $this->assertSame('2024-03-04', $entry->get('when')); } + #[Test] + public function non_revisable_fields_are_not_removed_from_an_entry_without_an_origin() + { + [$user, $collection] = $this->seedUserAndCollection(true); + + $this->seedBlueprintFields($collection, [ + 'foo' => ['type' => 'text', 'revisable' => false], + ]); + + $entry = EntryFactory::id('1') + ->slug('test') + ->collection('test') + ->published(true) + ->data(['title' => 'Test', 'foo' => 'bar']) + ->create(); + + // A single site install doesn't send _localized at all. + $this + ->actingAs($user) + ->update($entry, ['foo' => 'bar']) + ->assertOk(); + + $this->assertEquals('bar', $entry->fresh()->get('foo')); + } + #[Test] public function published_entry_without_non_revisable_fields_is_not_saved() { @@ -613,7 +638,7 @@ public function published_entry_without_non_revisable_fields_is_not_saved() } #[Test] - public function synced_non_revisable_fields_are_not_saved_to_the_localization() + public function non_revisable_fields_are_only_saved_to_the_localization_while_localized() { $this->setSites([ 'en' => ['locale' => 'en', 'url' => '/'], @@ -664,8 +689,29 @@ public function synced_non_revisable_fields_are_not_saved_to_the_localization() $localization = $localization->fresh(); $this->assertEquals('le bar', $localization->get('foo')); $this->assertEquals('bar', $origin->fresh()->get('foo')); + + $this + ->actingAs($user) + ->update($localization, [ + 'foo' => 'bar', + '_localized' => [], + ]) + ->assertOk(); + + $localization = $localization->fresh(); + $this->assertFalse($localization->has('foo')); + $this->assertEquals('bar', $localization->foo); + + // Publishing merges non-revisable fields back from the live entry, which no + // longer has the handle, so the field stays synced with the origin. + $localization->publishWorkingCopy(['user' => $user]); + + $localization = $localization->fresh(); + $this->assertFalse($localization->has('foo')); + $this->assertEquals('bar', $localization->foo); } + #[Test] public function it_can_validate_against_published_value() { From a2fbefaa22748bbe7f473caa684934ba942659e6 Mon Sep 17 00:00:00 2001 From: edalzell Date: Mon, 14 Sep 2026 12:19:57 -0700 Subject: [PATCH 39/45] Skip the non-revisable write-through when the revision is cancelled --- .../CP/Collections/EntriesController.php | 4 ++- tests/Feature/Entries/UpdateEntryTest.php | 28 ++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/Http/Controllers/CP/Collections/EntriesController.php b/src/Http/Controllers/CP/Collections/EntriesController.php index 59b4559d053..158f6a9f973 100644 --- a/src/Http/Controllers/CP/Collections/EntriesController.php +++ b/src/Http/Controllers/CP/Collections/EntriesController.php @@ -259,7 +259,9 @@ public function update(Request $request, $collection, $entry) ->save(); // have to save in case there are non-revisable fields - $this->saveNonRevisableFields($entry, $request->input('_localized')); + if ($saved) { + $this->saveNonRevisableFields($entry, $request->input('_localized')); + } // catch any changes through RevisionSaving event $entry = $entry->fromWorkingCopy(); diff --git a/tests/Feature/Entries/UpdateEntryTest.php b/tests/Feature/Entries/UpdateEntryTest.php index 0e7c075be77..9af33c48a02 100644 --- a/tests/Feature/Entries/UpdateEntryTest.php +++ b/tests/Feature/Entries/UpdateEntryTest.php @@ -8,6 +8,7 @@ use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\Attributes\Test; use Statamic\Events\EntrySaving; +use Statamic\Events\RevisionSaving; use Statamic\Facades\Blueprint; use Statamic\Facades\Collection; use Statamic\Facades\Entry; @@ -585,6 +586,32 @@ public function non_revisable_fields_are_saved_with_raw_values() $this->assertSame('2024-03-04', $entry->get('when')); } + #[Test] + public function non_revisable_fields_are_not_saved_when_the_revision_is_cancelled() + { + [$user, $collection] = $this->seedUserAndCollection(true); + + $this->seedBlueprintFields($collection, [ + 'foo' => ['type' => 'text', 'revisable' => false], + ]); + + $entry = EntryFactory::id('1') + ->slug('test') + ->collection('test') + ->published(true) + ->data(['title' => 'Test', 'foo' => 'bar']) + ->create(); + + Event::listen(RevisionSaving::class, fn () => false); + + $this + ->actingAs($user) + ->update($entry, ['foo' => 'not bar']) + ->assertOk(); + + $this->assertEquals('bar', $entry->fresh()->get('foo')); + } + #[Test] public function non_revisable_fields_are_not_removed_from_an_entry_without_an_origin() { @@ -711,7 +738,6 @@ public function non_revisable_fields_are_only_saved_to_the_localization_while_lo $this->assertEquals('bar', $localization->foo); } - #[Test] public function it_can_validate_against_published_value() { From 5cdadd2d66f38401fc4a6ecbea3a916487aff3ca Mon Sep 17 00:00:00 2001 From: edalzell Date: Mon, 14 Sep 2026 12:20:08 -0700 Subject: [PATCH 40/45] Touch updated_at when writing non-revisable fields --- src/Http/Controllers/CP/Collections/EntriesController.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Http/Controllers/CP/Collections/EntriesController.php b/src/Http/Controllers/CP/Collections/EntriesController.php index 158f6a9f973..95ee972e953 100644 --- a/src/Http/Controllers/CP/Collections/EntriesController.php +++ b/src/Http/Controllers/CP/Collections/EntriesController.php @@ -558,6 +558,6 @@ private function saveNonRevisableFields(EntryContract $entry, ?array $localized $removed->each(fn ($handle) => $fresh->remove($handle)); - $fresh->save(); + $fresh->updateLastModified(User::current())->save(); } } From db0882aa12b5e66b76b272185e083caa396be7b1 Mon Sep 17 00:00:00 2001 From: edalzell Date: Mon, 14 Sep 2026 12:20:10 -0700 Subject: [PATCH 41/45] Default an explicit null revisable to true --- src/Fields/Field.php | 6 +++++- tests/Fields/FieldTest.php | 8 ++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/Fields/Field.php b/src/Fields/Field.php index 210a7bbec13..fb71163ba85 100644 --- a/src/Fields/Field.php +++ b/src/Fields/Field.php @@ -270,7 +270,11 @@ public function isRevisable() return true; } - return (bool) $this->get('revisable', true); + if (is_null($this->get('revisable'))) { + return true; + } + + return (bool) $this->get('revisable'); } public function shouldBeDuplicated() diff --git a/tests/Fields/FieldTest.php b/tests/Fields/FieldTest.php index 592d87fabde..1817a428f5b 100644 --- a/tests/Fields/FieldTest.php +++ b/tests/Fields/FieldTest.php @@ -732,6 +732,14 @@ public function it_defaults_to_revisable() $this->assertTrue($field->isRevisable()); } + #[Test] + public function it_defaults_to_revisable_when_null() + { + $field = new Field('test', ['type' => 'text', 'revisable' => null]); + + $this->assertTrue($field->isRevisable()); + } + #[Test] public function it_gets_revisable() { From 36a52e974de7caa09acf96cafebf43617ac490dc Mon Sep 17 00:00:00 2001 From: edalzell Date: Mon, 14 Sep 2026 12:20:10 -0700 Subject: [PATCH 42/45] Clarify that revisable only applies to top level fields --- lang/en/messages.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lang/en/messages.php b/lang/en/messages.php index c983a116680..5f5bb01e347 100644 --- a/lang/en/messages.php +++ b/lang/en/messages.php @@ -107,7 +107,7 @@ 'fields_instructions_position_instructions' => 'Show instructions above or below the field.', 'fields_listable_instructions' => 'Control the listing column visibility.', 'fields_replicator_preview_instructions' => 'Control preview visibility in Replicator/Bard sets.', - 'fields_revisable_instructions' => 'Include this field in revisions. When disabled, changes are saved straight to the published entry instead of a working copy. Only applies to collection entries.', + 'fields_revisable_instructions' => 'Include this field in revisions. When disabled, changes are saved straight to the published entry instead of a working copy. Only applies to top level fields in collection entries. The date, slug and published fields are unaffected.', 'fields_sortable_instructions' => 'Control if the field should be sortable in listing views.', 'fields_visibility_instructions' => 'Control field visibility on publish forms.', 'fieldset_import_fieldset_instructions' => 'The fieldset to be imported.', From aa9044538f51139bbd83e2fdceca92854560f352 Mon Sep 17 00:00:00 2001 From: edalzell Date: Mon, 26 Oct 2026 14:56:57 -0700 Subject: [PATCH 43/45] Let the publish array take revisable from the field config --- src/Fields/Field.php | 1 - tests/Fieldtypes/NestedFieldsTest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/Fields/Field.php b/src/Fields/Field.php index fb71163ba85..b021eb3072e 100644 --- a/src/Fields/Field.php +++ b/src/Fields/Field.php @@ -299,7 +299,6 @@ public function toPublishArray() 'read_only' => $this->visibility() === 'read_only', // Deprecated: Addon fieldtypes should now reference new `visibility` state. 'always_save' => $this->alwaysSave(), 'reserve_space_when_hidden' => $this->reserveSpaceWhenHidden(), - 'revisable' => $this->isRevisable(), ]); unset($array['validate']); diff --git a/tests/Fieldtypes/NestedFieldsTest.php b/tests/Fieldtypes/NestedFieldsTest.php index 3c8e2ea10c6..5a024dd4a10 100644 --- a/tests/Fieldtypes/NestedFieldsTest.php +++ b/tests/Fieldtypes/NestedFieldsTest.php @@ -23,7 +23,6 @@ public function it_preprocesses_each_value_when_used_for_config() ->andReturn(new class extends Fieldtype { protected $component = 'assets'; - protected $configFields = [ 'max_files' => ['type' => 'integer'], 'container' => ['type' => 'plain'], From f95e67fb2d6bd1d7801090f8b761aa82aee33615 Mon Sep 17 00:00:00 2001 From: edalzell Date: Mon, 26 Oct 2026 14:56:58 -0700 Subject: [PATCH 44/45] Derive un-localized non-revisable fields from the entry data --- .../CP/Collections/EntriesController.php | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/src/Http/Controllers/CP/Collections/EntriesController.php b/src/Http/Controllers/CP/Collections/EntriesController.php index 95ee972e953..aa14c715b7e 100644 --- a/src/Http/Controllers/CP/Collections/EntriesController.php +++ b/src/Http/Controllers/CP/Collections/EntriesController.php @@ -260,7 +260,7 @@ public function update(Request $request, $collection, $entry) // have to save in case there are non-revisable fields if ($saved) { - $this->saveNonRevisableFields($entry, $request->input('_localized')); + $this->saveNonRevisableFields($entry); } // catch any changes through RevisionSaving event @@ -537,27 +537,22 @@ protected function ensureCollectionIsAvailableOnSite($collection, $site) } } - private function saveNonRevisableFields(EntryContract $entry, ?array $localized = null): void + private function saveNonRevisableFields(EntryContract $entry): void { + $fresh = $entry->fresh(); $handles = collect($entry->nonRevisableFields()); - $values = $entry->data()->only($handles->all()); - // On a localization, a non-revisable field that's no longer localized has been - // dropped from the data entirely, so remove it from the saved localization. - // Otherwise the stale value would keep shadowing the origin. - $removed = $entry->hasOrigin() - ? $handles->diff($localized ?? [])->filter(fn ($handle) => $entry->fresh()->has($handle)) - : collect(); + // A localization's data only holds the localized handles, so a non-revisable + // field missing from it has been synced back to the origin and must be removed. + $removed = $handles->diff($values->keys())->filter(fn ($handle) => $fresh->has($handle)); if ($values->isEmpty() && $removed->isEmpty()) { return; } - $fresh = $entry->fresh()->merge($values); - $removed->each(fn ($handle) => $fresh->remove($handle)); - $fresh->updateLastModified(User::current())->save(); + $fresh->merge($values)->updateLastModified(User::current())->save(); } } From d171d5fde01d493947254e8e771aba08e8df7916 Mon Sep 17 00:00:00 2001 From: edalzell Date: Mon, 26 Oct 2026 14:57:05 -0700 Subject: [PATCH 45/45] Trim non-revisable coverage in the entry revisions tests --- tests/Feature/Entries/EntryRevisionsTest.php | 56 +++----------------- 1 file changed, 8 insertions(+), 48 deletions(-) diff --git a/tests/Feature/Entries/EntryRevisionsTest.php b/tests/Feature/Entries/EntryRevisionsTest.php index da1d373a7cb..ecc6ac0f68f 100644 --- a/tests/Feature/Entries/EntryRevisionsTest.php +++ b/tests/Feature/Entries/EntryRevisionsTest.php @@ -22,7 +22,6 @@ class EntryRevisionsTest extends TestCase use PreventSavingStacheItemsToDisk; private $dir; - private $collection; public function setUp(): void @@ -44,17 +43,8 @@ public function it_gets_revisions() { $now = Carbon::parse('2017-02-03'); Carbon::setTestNow($now); - $this->setTestBlueprint( - 'test', - [ - 'foo' => ['type' => 'text'], - 'bar' => [ - 'type' => 'text', - 'revisable' => false, - ], - ] - ); - $this->setTestRoles(['test' => ['access cp', 'view blog entries']]); + $this->setTestBlueprint('test', ['foo' => ['type' => 'text']]); + $this->setTestRoles(['test' => ['access cp', 'view blog entries', 'publish blog entries']]); $user = User::make()->id('user-1')->assignRole('test')->save(); $entry = EntryFactory::id('1') @@ -66,7 +56,6 @@ public function it_gets_revisions() 'blueprint' => 'test', 'title' => 'Original title', 'foo' => 'bar', - 'bar' => 'foo', ])->create(); tap($entry->makeRevision(), function ($copy) { @@ -94,7 +83,6 @@ public function it_gets_revisions() ->assertJsonPath('0.revisions.0.message', 'Revision one') ->assertJsonPath('0.revisions.0.attributes.data.title', 'Original title') ->assertJsonPath('0.revisions.0.attributes.item_url', 'http://localhost/cp/collections/blog/entries/1/revisions/'.Carbon::parse('2017-02-01')->timestamp) - ->assertJsonPath('0.revisions.0.attributes.data.bar', null) ->assertJsonPath('1.revisions.0.action', 'working') ->assertJsonPath('1.revisions.0.message', null) @@ -215,16 +203,10 @@ public function it_publishes_an_entry() { $now = Carbon::parse('2017-02-03'); Carbon::setTestNow($now); - $this->setTestBlueprint( - 'test', - [ - 'foo' => ['type' => 'text'], - 'bar' => [ - 'type' => 'text', - 'revisable' => false, - ], - ] - ); + $this->setTestBlueprint('test', [ + 'foo' => ['type' => 'text'], + 'bar' => ['type' => 'text', 'revisable' => false], + ]); $this->setTestRoles(['test' => ['access cp', 'publish blog entries']]); $user = User::make()->id('user-1')->assignRole('test')->save(); @@ -290,16 +272,7 @@ public function it_unpublishes_an_entry() { $now = Carbon::parse('2017-02-03'); Carbon::setTestNow($now); - $this->setTestBlueprint( - 'test', - [ - 'foo' => ['type' => 'text'], - 'bar' => [ - 'type' => 'text', - 'revisable' => false, - ], - ] - ); + $this->setTestBlueprint('test', ['foo' => ['type' => 'text']]); $this->setTestRoles(['test' => ['access cp', 'publish blog entries']]); $user = User::make()->id('user-1')->assignRole('test')->save(); @@ -312,7 +285,6 @@ public function it_unpublishes_an_entry() 'blueprint' => 'test', 'title' => 'Title', 'foo' => 'bar', - 'bar' => 'foo', ])->create(); $this->assertTrue($entry->published()); @@ -328,7 +300,6 @@ public function it_unpublishes_an_entry() 'blueprint' => 'test', 'title' => 'Title', 'foo' => 'bar', - 'bar' => 'foo', 'updated_at' => $now->timestamp, 'updated_by' => $user->id(), ], $entry->data()->all()); @@ -384,16 +355,7 @@ public function it_denies_creating_a_revision_without_permission_to_edit_entry() #[Test] public function it_creates_a_revision() { - $this->setTestBlueprint( - 'test', - [ - 'foo' => ['type' => 'text'], - 'bar' => [ - 'type' => 'text', - 'revisable' => false, - ], - ] - ); + $this->setTestBlueprint('test', ['foo' => ['type' => 'text']]); $this->setTestRoles(['test' => ['access cp', 'edit blog entries']]); $user = User::make()->id('user-1')->assignRole('test')->save(); @@ -406,7 +368,6 @@ public function it_creates_a_revision() 'blueprint' => 'test', 'title' => 'Title', 'foo' => 'bar', - 'bar' => 'foo', ])->create(); tap($entry->makeWorkingCopy(), function ($copy) { @@ -428,7 +389,6 @@ public function it_creates_a_revision() 'blueprint' => 'test', 'title' => 'Title', 'foo' => 'bar', - 'bar' => 'foo', ], $entry->data()->all()); $this->assertFalse($entry->published()); $this->assertCount(1, $entry->revisions());