From 18555acacbf57dcf8df8814ea799ebb059e6191b Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Tue, 18 Feb 2020 14:56:07 -0500 Subject: [PATCH 001/137] Editing entries gets a collection url ... Basically reverts ae2022bcf477a3adb8c232e518b2e4147ad3591a --- routes/cp.php | 2 -- src/Entries/Entry.php | 4 +--- src/Http/Controllers/CP/Collections/EntriesController.php | 4 ---- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/routes/cp.php b/routes/cp.php index 2b616969d83..5bb2cb0f508 100644 --- a/routes/cp.php +++ b/routes/cp.php @@ -31,8 +31,6 @@ Route::resource('structures.pages', 'StructurePagesController', ['only' => ['index', 'store']]); }); - Route::get('structures/{collection}/entries/{entry}/{slug}', 'Collections\EntriesController@edit')->name('structures.entries.edit'); - Route::group(['namespace' => 'Collections'], function () { Route::resource('collections', 'CollectionsController'); Route::get('collections/{collection}/scaffold', 'ScaffoldCollectionController@index')->name('collections.scaffold'); diff --git a/src/Entries/Entry.php b/src/Entries/Entry.php index cc78a5d703e..ca08a793561 100644 --- a/src/Entries/Entry.php +++ b/src/Entries/Entry.php @@ -130,9 +130,7 @@ public function delete() public function editUrl() { - return $this->hasStructure() - ? $this->cpUrl('structures.entries.edit') - : $this->cpUrl('collections.entries.edit'); + return $this->cpUrl('collections.entries.edit'); } public function updateUrl() diff --git a/src/Http/Controllers/CP/Collections/EntriesController.php b/src/Http/Controllers/CP/Collections/EntriesController.php index c0b4495a7ca..e0c861c11ef 100644 --- a/src/Http/Controllers/CP/Collections/EntriesController.php +++ b/src/Http/Controllers/CP/Collections/EntriesController.php @@ -79,10 +79,6 @@ protected function indexQuery($collection) public function edit(Request $request, $collection, $entry) { - if ($collection->hasStructure() && $request->route()->getName() === 'statamic.cp.collections.entries.edit') { - return redirect()->to(cp_route('structures.entries.edit', [$collection->handle(), $entry->id(), $entry->slug()])); - } - $this->authorize('view', $entry); $entry = $entry->fromWorkingCopy(); From fe11a91702db21c351ec2f9cf25f109b12ce66b1 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Tue, 18 Feb 2020 15:01:32 -0500 Subject: [PATCH 002/137] Only list "nav" structures. 404 when accessing collection structures. --- .../CP/Structures/StructuresController.php | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Http/Controllers/CP/Structures/StructuresController.php b/src/Http/Controllers/CP/Structures/StructuresController.php index 470d69f9c92..54501ebd510 100644 --- a/src/Http/Controllers/CP/Structures/StructuresController.php +++ b/src/Http/Controllers/CP/Structures/StructuresController.php @@ -20,7 +20,7 @@ public function index() $this->authorize('index', StructureContract::class, 'You are not authorized to view any structures.'); $structures = Structure::all()->filter(function ($structure) { - return User::current()->can('view', $structure); + return !$structure->isCollectionBased() && User::current()->can('view', $structure); })->map(function ($structure) { $tree = $structure->in(Site::selected()->handle()); @@ -42,6 +42,8 @@ public function edit($structure) { $structure = Structure::find($structure); + abort_if($structure->isCollectionBased(), 404); + $this->authorize('edit', $structure, 'You are not authorized to edit this structure.'); $values = [ @@ -79,6 +81,9 @@ public function edit($structure) public function show(Request $request, $structure) { $structure = Structure::find($structure); + + abort_if($structure->isCollectionBased(), 404); + $site = $request->site ?? Site::selected()->handle(); if (! $structure || ! $tree = $structure->in($site)) { @@ -132,6 +137,8 @@ public function update(Request $request, $structure) $structure = Structure::find($structure); + abort_if($structure->isCollectionBased(), 404); + $this->authorize('update', $structure, 'You are not authorized to edit this structure.'); $expectedRoot = $structure->expectsRoot(); @@ -313,6 +320,8 @@ public function destroy($structure) { $structure = Structure::findByHandle($structure); + abort_if($structure->isCollectionBased(), 404); + $this->authorize('delete', $structure, 'You are not authorized to delete this structure.'); $structure->delete(); From 6d0c018b3cd6824e4bbdf7e3f14df0f1bf4bf2fb Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Tue, 18 Feb 2020 15:31:45 -0500 Subject: [PATCH 003/137] Tests for not being able to edit or see collection structures --- .../Feature/Structures/EditStructureTest.php | 35 ++++++-------- tests/Feature/Structures/MocksStructures.php | 47 +++++++++++++++++++ .../Structures/ViewStructureListingTest.php | 45 ++++++------------ 3 files changed, 76 insertions(+), 51 deletions(-) create mode 100644 tests/Feature/Structures/MocksStructures.php diff --git a/tests/Feature/Structures/EditStructureTest.php b/tests/Feature/Structures/EditStructureTest.php index 8848e62278e..bf6323c9493 100644 --- a/tests/Feature/Structures/EditStructureTest.php +++ b/tests/Feature/Structures/EditStructureTest.php @@ -14,11 +14,12 @@ class EditStructureTest extends TestCase { use FakesRoles; use PreventSavingStacheItemsToDisk; + use MocksStructures; /** @test */ function it_shows_the_edit_form_if_user_has_edit_permission() { - $structure = $this->createStructure('foo'); + $structure = $this->createNavStructure('foo'); Facades\Structure::shouldReceive('all')->andReturn(collect([$structure])); Facades\Structure::shouldReceive('find')->andReturn($structure); @@ -35,7 +36,7 @@ function it_shows_the_edit_form_if_user_has_edit_permission() /** @test */ function it_denies_access_if_user_doesnt_have_edit_permission() { - $structure = $this->createStructure('foo'); + $structure = $this->createNavStructure('foo'); Facades\Structure::shouldReceive('all')->andReturn(collect([$structure])); Facades\Structure::shouldReceive('find')->andReturn($structure); @@ -50,25 +51,19 @@ function it_denies_access_if_user_doesnt_have_edit_permission() ->assertSessionHas('error'); } - private function createStructure($handle) + /** @test */ + function attempting_to_edit_a_collection_based_structure_should_404() { - return tap(Mockery::mock(Structure::class), function ($s) use ($handle) { - $s->shouldReceive('in')->andReturn($this->createStructureTree($handle)); - $s->shouldReceive('title')->andReturn($handle); - $s->shouldReceive('handle')->andReturn($handle); - $s->shouldReceive('uris')->andReturn(collect()); - $s->shouldReceive('collection')->andReturnFalse(); - $s->shouldReceive('collections')->andReturn(collect()); - $s->shouldReceive('expectsRoot')->andReturnTrue(); - $s->shouldReceive('flattenedPages')->andReturn(collect()); - }); - } + $structure = $this->createCollectionStructure('foo'); + Facades\Structure::shouldReceive('all')->andReturn(collect([$structure])); + Facades\Structure::shouldReceive('find')->andReturn($structure); - private function createStructureTree($handle) - { - return tap(Mockery::mock(Tree::class), function ($s) use ($handle) { - $s->shouldReceive('editUrl')->andReturn('/tree-edit-url'); - $s->shouldReceive('route')->andReturn('/route'); - }); + $this->setTestRoles(['test' => ['access cp', 'edit foo structure']]); + $user = Facades\User::make()->assignRole('test')->save(); + + $response = $this + ->actingAs($user) + ->get(route('statamic.cp.structures.edit', $structure->handle())) + ->assertNotFound(); } } diff --git a/tests/Feature/Structures/MocksStructures.php b/tests/Feature/Structures/MocksStructures.php new file mode 100644 index 00000000000..28576598022 --- /dev/null +++ b/tests/Feature/Structures/MocksStructures.php @@ -0,0 +1,47 @@ +shouldReceive('in')->andReturn($this->createStructureTree($handle)); + $s->shouldReceive('title')->andReturn($handle); + $s->shouldReceive('handle')->andReturn($handle); + $s->shouldReceive('editUrl')->andReturn('/structure-edit-url'); + $s->shouldReceive('deleteUrl')->andReturn('/structure-delete-url'); + }); + } + + private function createNavStructure($structure) + { + return tap($this->createStructure($structure), function ($s) { + $s->shouldReceive('collection')->andReturnNull(); + $s->shouldReceive('isCollectionBased')->andReturnFalse(); + $s->shouldReceive('collections')->andReturn(collect()); + $s->shouldReceive('expectsRoot')->andReturnTrue(); + }); + } + + private function createCollectionStructure($structure) + { + return tap($this->createStructure($structure), function ($s) { + $s->shouldReceive('collection')->andReturn(true); // should return a collection instance but we're not using it in tests yet + $s->shouldReceive('isCollectionBased')->andReturnTrue(); + }); + } + + private function createStructureTree($handle) + { + return tap(Mockery::mock(Tree::class), function ($s) use ($handle) { + $s->shouldReceive('editUrl')->andReturn('/tree-edit-url'); + $s->shouldReceive('route')->andReturn('/route'); + }); + } +} diff --git a/tests/Feature/Structures/ViewStructureListingTest.php b/tests/Feature/Structures/ViewStructureListingTest.php index 00238144b1d..9056298c0cb 100644 --- a/tests/Feature/Structures/ViewStructureListingTest.php +++ b/tests/Feature/Structures/ViewStructureListingTest.php @@ -9,17 +9,19 @@ use Statamic\Structures\Tree; use Statamic\Structures\Structure; use Tests\PreventSavingStacheItemsToDisk; +use Tests\Feature\Structures\MocksStructures; class ViewStructureListingTest extends TestCase { - use PreventSavingStacheItemsToDisk; + use PreventSavingStacheItemsToDisk, MocksStructures; /** @test */ - function it_shows_a_list_of_structures() + function it_shows_a_list_of_nav_structures() { Facades\Structure::shouldReceive('all')->andReturn(collect([ - 'foo' => $structureA = $this->createStructure('foo'), - 'bar' => $structureB = $this->createStructure('bar') + 'foo' => $structureA = $this->createNavStructure('foo'), + 'bar' => $structureB = $this->createNavStructure('bar'), + 'baz' => $structureC = $this->createCollectionStructure('baz'), ])); $user = Facades\User::make()->makeSuper()->save(); @@ -52,8 +54,8 @@ function it_filters_out_structures_the_user_cannot_access() { $this->withoutExceptionHandling(); Facades\Structure::shouldReceive('all')->andReturn(collect([ - 'foo' => $structureA = $this->createStructure('foo'), - 'bar' => $structureB = $this->createStructure('bar') + 'foo' => $structureA = $this->createNavStructure('foo'), + 'bar' => $structureB = $this->createNavStructure('bar') ])); $this->setTestRoles(['test' => ['access cp', 'view bar structure']]); $user = Facades\User::make()->assignRole('test')->save(); @@ -72,8 +74,8 @@ function it_filters_out_structures_the_user_cannot_access() function it_doesnt_filter_out_structures_if_they_have_permission_to_configure() { Facades\Structure::shouldReceive('all')->andReturn(collect([ - 'foo' => $structureA = $this->createStructure('foo'), - 'bar' => $structureB = $this->createStructure('bar') + 'foo' => $structureA = $this->createNavStructure('foo'), + 'bar' => $structureB = $this->createNavStructure('bar') ])); $this->setTestRoles(['test' => ['access cp', 'configure structures', 'view bar structure']]); $user = Facades\User::make()->assignRole('test')->save(); @@ -92,8 +94,8 @@ function it_doesnt_filter_out_structures_if_they_have_permission_to_configure() function it_denies_access_when_there_are_no_permitted_structures() { Facades\Structure::shouldReceive('all')->andReturn(collect([ - 'foo' => $structureA = $this->createStructure('foo'), - 'bar' => $structureB = $this->createStructure('bar') + 'foo' => $structureA = $this->createNavStructure('foo'), + 'bar' => $structureB = $this->createNavStructure('bar') ])); $this->setTestRoles(['test' => ['access cp']]); @@ -134,7 +136,7 @@ function create_structure_button_is_not_visible_without_permission_to_configure( function delete_button_is_visible_with_permission_to_configure() { Facades\Structure::shouldReceive('all')->andReturn(collect([ - 'foo' => $this->createStructure('foo'), + 'foo' => $this->createNavStructure('foo'), ])); $this->setTestRoles(['test' => ['access cp', 'configure structures']]); @@ -152,7 +154,7 @@ function delete_button_is_not_visible_without_permission_to_configure() $this->markTestIncomplete(); Facades\Structure::shouldReceive('all')->andReturn(collect([ - 'foo' => $this->createStructure('foo'), + 'foo' => $this->createNavStructure('foo'), ])); $this->setTestRoles(['test' => ['access cp', 'view foo structure']]); @@ -164,25 +166,6 @@ function delete_button_is_not_visible_without_permission_to_configure() ->assertDontSee('Delete'); } - private function createStructure($handle) - { - return tap(Mockery::mock(Structure::class), function ($s) use ($handle) { - $s->shouldReceive('in')->andReturn($this->createStructureTree($handle)); - $s->shouldReceive('title')->andReturn($handle); - $s->shouldReceive('handle')->andReturn($handle); - $s->shouldReceive('collection')->andReturnFalse(); - $s->shouldReceive('editUrl')->andReturn('/structure-edit-url'); - $s->shouldReceive('deleteUrl')->andReturn('/structure-delete-url'); - }); - } - - private function createStructureTree($handle) - { - return tap(Mockery::mock(Tree::class), function ($s) use ($handle) { - $s->shouldReceive('editUrl')->andReturn('/tree-edit-url'); - }); - } - private function setTestRoles($roles) { $roles = collect($roles)->map(function ($permissions, $handle) { From e38f9079ef7d54dcadf32201d9e55e4bc6a80c4a Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Tue, 18 Feb 2020 16:24:42 -0500 Subject: [PATCH 004/137] Bring back and fix collection listing test --- .../Collections/ViewCollectionListingTest.php | 127 +++++++----------- 1 file changed, 51 insertions(+), 76 deletions(-) diff --git a/tests/Feature/Collections/ViewCollectionListingTest.php b/tests/Feature/Collections/ViewCollectionListingTest.php index 4a2ac270ce0..83280e0be38 100644 --- a/tests/Feature/Collections/ViewCollectionListingTest.php +++ b/tests/Feature/Collections/ViewCollectionListingTest.php @@ -2,40 +2,53 @@ namespace Tests\Feature\Collections; -use Mockery; +use Facades\Tests\Factories\EntryFactory; use Statamic\Facades; use Tests\TestCase; use Tests\FakesRoles; use Statamic\Auth\User; use Statamic\Entries\Collection; +use Tests\PreventSavingStacheItemsToDisk; class ViewCollectionListingTest extends TestCase { use FakesRoles; - - public function setUp(): void - { - parent::setUp(); - $this->markTestIncomplete(); // TODO. It's changed since we moved to a vue component. - } + use PreventSavingStacheItemsToDisk; /** @test */ function it_shows_a_list_of_collections() { - Facades\Collection::shouldReceive('all')->andReturn(collect([ - 'foo' => $collectionA = $this->createCollection('foo'), - 'bar' => $collectionB = $this->createCollection('bar') - ])); + $collectionA = $this->createCollection('foo'); + $collectionB = $this->createCollection('bar'); + EntryFactory::id('1')->collection($collectionB)->create(); - $user = User::make()->makeSuper(); + $user = tap(User::make()->makeSuper())->save(); $response = $this ->actingAs($user) ->get(cp_route('collections.index')) ->assertSuccessful() ->assertViewHas('collections', collect([ - 'foo' => $collectionA, - 'bar' => $collectionB + [ + 'id' => 'foo', + 'title' => 'Foo', + 'entries' => 0, + 'edit_url' => 'http://localhost/cp/collections/foo/edit', + 'delete_url' => 'http://localhost/cp/collections/foo', + 'entries_url' => 'http://localhost/cp/collections/foo', + 'scaffold_url' => 'http://localhost/cp/collections/foo/scaffold', + 'deleteable' => true, + ], + [ + 'id' => 'bar', + 'title' => 'Bar', + 'entries' => 1, + 'edit_url' => 'http://localhost/cp/collections/bar/edit', + 'delete_url' => 'http://localhost/cp/collections/bar', + 'entries_url' => 'http://localhost/cp/collections/bar', + 'scaffold_url' => 'http://localhost/cp/collections/bar/scaffold', + 'deleteable' => true, + ] ])) ->assertDontSee('no-results'); } @@ -43,7 +56,7 @@ function it_shows_a_list_of_collections() /** @test */ function it_shows_no_results_when_there_are_no_collections() { - $user = User::make()->makeSuper(); + $user = tap(User::make()->makeSuper())->save(); $response = $this ->actingAs($user) @@ -56,54 +69,46 @@ function it_shows_no_results_when_there_are_no_collections() /** @test */ function it_filters_out_collections_the_user_cannot_access() { - Facades\Collection::shouldReceive('all')->andReturn(collect([ - 'foo' => $collectionA = $this->createCollection('foo'), - 'bar' => $collectionB = $this->createCollection('bar') - ])); - $this->setTestRoles(['test' => ['access cp', 'view bar collection']]); - $user = Facades\User::make()->assignRole('test'); + $collectionA = $this->createCollection('foo'); + $collectionB = $this->createCollection('bar'); + $this->setTestRoles(['test' => ['access cp', 'view bar entries']]); + $user = tap(Facades\User::make()->assignRole('test'))->save(); $response = $this ->actingAs($user) ->get(cp_route('collections.index')) ->assertSuccessful() - ->assertViewHas('collections', collect([ - 'bar' => $collectionB - ])) + ->assertViewHas('collections', function ($collections) { + return count($collections) === 1 && $collections[0]['id'] === 'bar'; + }) ->assertDontSee('no-results'); } /** @test */ function it_doesnt_filter_out_collections_if_they_have_permission_to_configure() { - Facades\Collection::shouldReceive('all')->andReturn(collect([ - 'foo' => $collectionA = $this->createCollection('foo'), - 'bar' => $collectionB = $this->createCollection('bar') - ])); - $this->setTestRoles(['test' => ['access cp', 'configure collections', 'view bar collection']]); - $user = Facades\User::make()->assignRole('test'); + $collectionA = $this->createCollection('foo'); + $collectionB = $this->createCollection('bar'); + $this->setTestRoles(['test' => ['access cp', 'configure collections', 'view bar entries']]); + $user = tap(Facades\User::make()->assignRole('test'))->save(); $response = $this ->actingAs($user) ->get(cp_route('collections.index')) ->assertSuccessful() - ->assertViewHas('collections', collect([ - 'foo' => $collectionA, - 'bar' => $collectionB - ])) + ->assertViewHas('collections', function ($collections) { + return $collections->map->id->all() === ['foo', 'bar']; + }) ->assertDontSee('no-results'); } /** @test */ function it_denies_access_when_there_are_no_permitted_collections() { - Facades\Collection::shouldReceive('all')->andReturn(collect([ - 'foo' => $collectionA = $this->createCollection('foo'), - 'bar' => $collectionB = $this->createCollection('bar') - ])); - + $collectionA = $this->createCollection('foo'); + $collectionB = $this->createCollection('bar'); $this->setTestRoles(['test' => ['access cp']]); - $user = Facades\User::make()->assignRole('test'); + $user = tap(Facades\User::make()->assignRole('test'))->save(); $response = $this ->from('/cp/original') @@ -116,7 +121,7 @@ function it_denies_access_when_there_are_no_permitted_collections() function create_collection_button_is_visible_with_permission_to_configure() { $this->setTestRoles(['test' => ['access cp', 'configure collections']]); - $user = Facades\User::make()->assignRole('test'); + $user = tap(Facades\User::make()->assignRole('test'))->save(); $response = $this ->actingAs($user) @@ -127,49 +132,19 @@ function create_collection_button_is_visible_with_permission_to_configure() /** @test */ function create_collection_button_is_not_visible_without_permission_to_configure() { - $this->setTestRoles(['test' => ['access cp']]); - $user = Facades\User::make()->assignRole('test'); + $collectionA = $this->createCollection('foo'); + $this->setTestRoles(['test' => ['access cp', 'view foo entries']]); + $user = tap(Facades\User::make()->assignRole('test'))->save(); $response = $this ->actingAs($user) ->get(cp_route('collections.index')) + ->assertOk() ->assertDontSee('Create Collection'); } - /** @test */ - function delete_button_is_visible_with_permission_to_configure() - { - Facades\Collection::shouldReceive('all')->andReturn(collect([ - 'foo' => $this->createCollection('foo'), - ])); - - $this->setTestRoles(['test' => ['access cp', 'configure collections']]); - $user = Facades\User::make()->assignRole('test'); - - $response = $this - ->actingAs($user) - ->get(cp_route('collections.index')) - ->assertSee('Delete'); - } - - /** @test */ - function delete_button_is_not_visible_without_permission_to_configure() - { - Facades\Collection::shouldReceive('all')->andReturn(collect([ - 'foo' => $this->createCollection('foo'), - ])); - - $this->setTestRoles(['test' => ['access cp', 'view foo collection']]); - $user = Facades\User::make()->assignRole('test'); - - $response = $this - ->actingAs($user) - ->get(cp_route('collections.index')) - ->assertDontSee('Delete'); - } - private function createCollection($handle) { - return tap(new Collection)->path($handle); + return tap((new Collection)->handle($handle))->save(); } } From b68e3810911bd7096893025733065536eb2c573d Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Tue, 18 Feb 2020 16:28:38 -0500 Subject: [PATCH 005/137] Bring back and fix delete collection test --- tests/Feature/Collections/DeleteCollectionTest.php | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/tests/Feature/Collections/DeleteCollectionTest.php b/tests/Feature/Collections/DeleteCollectionTest.php index 49da03d75ea..72183a3bdd9 100644 --- a/tests/Feature/Collections/DeleteCollectionTest.php +++ b/tests/Feature/Collections/DeleteCollectionTest.php @@ -35,10 +35,8 @@ function it_denies_access_if_you_dont_have_permission() /** @test */ function it_deletes_the_collection() { - $this->markTestIncomplete(); // TODO: Skipped until ->delete() is reimplemented - $this->setTestRoles(['test' => ['access cp', 'configure collections']]); - $user = User::make()->assignRole('test'); + $user = tap(User::make()->assignRole('test'))->save(); $collection = Collection::make('test')->save(); $this->assertCount(1, Collection::all()); @@ -46,8 +44,7 @@ function it_deletes_the_collection() $this ->actingAs($user) ->delete(cp_route('collections.destroy', $collection->handle())) - ->assertRedirect(cp_route('collections.index')) - ->assertSessionHas('success'); + ->assertOk(); $this->assertCount(0, Collection::all()); } From c57679c62c540b55742d553d11f38a79385e1374 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Tue, 18 Feb 2020 16:38:43 -0500 Subject: [PATCH 006/137] Basic test for regular collection show page (entry listing) --- .../Collections/ShowRegularCollectionTest.php | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 tests/Feature/Collections/ShowRegularCollectionTest.php diff --git a/tests/Feature/Collections/ShowRegularCollectionTest.php b/tests/Feature/Collections/ShowRegularCollectionTest.php new file mode 100644 index 00000000000..6401b64c982 --- /dev/null +++ b/tests/Feature/Collections/ShowRegularCollectionTest.php @@ -0,0 +1,61 @@ +setTestRoles(['test' => ['access cp', 'view test entries']]); + $user = tap(User::make()->assignRole('test'))->save(); + $collection = tap(Collection::make('test'))->save(); + EntryFactory::id('1')->collection($collection)->create(); + + $this + ->actingAs($user) + ->get($collection->showUrl()) + ->assertOk() + ->assertViewIs('statamic::collections.show') + ->assertViewHas('collection', $collection); + } + + /** @test */ + function it_shows_the_empty_entry_listing_page_if_you_have_permission_and_there_are_no_entries() + { + $this->setTestRoles(['test' => ['access cp', 'view test entries']]); + $user = tap(User::make()->assignRole('test'))->save(); + $collection = tap(Collection::make('test'))->save(); + + $this + ->actingAs($user) + ->get($collection->showUrl()) + ->assertOk() + ->assertViewIs('statamic::collections.empty'); + } + + /** @test */ + function it_denies_access_if_you_dont_have_permission() + { + $this->setTestRoles(['test' => ['access cp']]); + $user = tap(User::make()->assignRole('test'))->save(); + $collection = tap(Collection::make('test'))->save(); + + $this + ->from('/original') + ->actingAs($user) + ->get($collection->showUrl()) + ->assertRedirect('/original') + ->assertSessionHas('error'); + } +} From 4f070640b8d702e4a980767f384fda87219ae891 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Wed, 19 Feb 2020 09:57:27 -0500 Subject: [PATCH 007/137] Remove empty state from view. It has a dedicated empty view. --- resources/views/collections/show.blade.php | 39 ++++++---------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/resources/views/collections/show.blade.php b/resources/views/collections/show.blade.php index 60b34b64308..304e79ff52b 100644 --- a/resources/views/collections/show.blade.php +++ b/resources/views/collections/show.blade.php @@ -38,34 +38,15 @@ - @if ($collection->queryEntries()->count()) - - - - @else - - @component('statamic::partials.create-first', [ - 'resource' => __("{$collection->title()} entry"), - 'svg' => 'empty/collection', // TODO: Do we want separate entry SVG? - 'can' => $user->can('create', ['Statamic\Contracts\Entries\Entry', $collection]) - ]) - @slot('button') - - - @endslot - @endcomponent - - @endif + @endsection From e5e6ecf190f528f11028c0422edcb5cb5ca2828e Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Thu, 20 Feb 2020 11:32:49 -0500 Subject: [PATCH 008/137] Wrapper class is now bound in vue so we can change it on the fly --- resources/js/app.js | 4 ++++ resources/views/layout.blade.php | 2 +- resources/views/partials/global-header.blade.php | 2 +- resources/views/partials/scripts.blade.php | 4 +++- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/resources/js/app.js b/resources/js/app.js index 0eef2d9d570..f9e1ba75c88 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -163,6 +163,10 @@ Statamic.app({ stackCount() { return this.$stacks.count(); + }, + + wrapperClass() { + return this.$config.get('wrapperClass', 'max-w-xl'); } }, diff --git a/resources/views/layout.blade.php b/resources/views/layout.blade.php index 404f5f1ee5e..3e964e8af7f 100644 --- a/resources/views/layout.blade.php +++ b/resources/views/layout.blade.php @@ -14,7 +14,7 @@ @include('statamic::partials.nav-main')
-
+
@yield('content')
diff --git a/resources/views/partials/global-header.blade.php b/resources/views/partials/global-header.blade.php index cee5c422587..62c3e93c1f1 100644 --- a/resources/views/partials/global-header.blade.php +++ b/resources/views/partials/global-header.blade.php @@ -8,7 +8,7 @@
-
+
diff --git a/resources/views/partials/scripts.blade.php b/resources/views/partials/scripts.blade.php index e8218d98778..3b697c2b3e2 100644 --- a/resources/views/partials/scripts.blade.php +++ b/resources/views/partials/scripts.blade.php @@ -11,7 +11,9 @@ @endforeach From 6ec1293effb4a35378984036e44d2d8cdaeb4cdf Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Thu, 20 Feb 2020 11:42:38 -0500 Subject: [PATCH 009/137] Bunch of collection/structure tweaks ... - Collection show view now shows a page tree if the collection has a structure. - You can switch between the tree and regular listing. It'll remember it in local storage. - Structure::make() accepts a handle. - $structure->handle() can be passed null and it'll still be chainable - The page tree component just does the page tree stuff, emits events, and just leaves the buttons etc to be handled in a parent component. - Page tree loads intial pages with ajax instead of passing the initial values. Unnecessary maintenance burden, not a huge deal. --- resources/js/app.js | 3 +- resources/js/components/collections/View.vue | 193 ++++++++++++++++++ .../js/components/structures/PageTree.vue | 126 +++--------- resources/js/components/structures/View.vue | 101 +++++++++ resources/views/collections/empty.blade.php | 24 +-- resources/views/collections/show.blade.php | 79 ++++--- resources/views/structures/show.blade.php | 26 +-- .../Structures/StructureRepository.php | 1 + .../CP/Collections/CollectionsController.php | 42 +++- .../CP/Structures/StructuresController.php | 7 - .../Repositories/StructureRepository.php | 4 +- src/Structures/Structure.php | 2 +- tests/FakesRoles.php | 2 +- .../Collections/ShowCollectionTest.php | 47 +++++ .../Collections/ShowRegularCollectionTest.php | 41 +--- .../ShowStructuredCollectionTest.php | 41 ++++ 16 files changed, 500 insertions(+), 239 deletions(-) create mode 100644 resources/js/components/collections/View.vue create mode 100644 resources/js/components/structures/View.vue create mode 100644 tests/Feature/Collections/ShowCollectionTest.php create mode 100644 tests/Feature/Collections/ShowStructuredCollectionTest.php diff --git a/resources/js/app.js b/resources/js/app.js index f9e1ba75c88..3ab09dbd974 100644 --- a/resources/js/app.js +++ b/resources/js/app.js @@ -103,7 +103,6 @@ Statamic.app({ components: { GlobalSearch: require('./components/GlobalSearch.vue').default, SiteSelector: require('./components/SiteSelector.vue').default, - PageTree: require('./components/structures/PageTree.vue').default, Login: require('./components/login/login'), LoginModal: require('./components/login/LoginModal.vue').default, BaseEntryCreateForm: require('./components/entries/BaseCreateForm.vue').default, @@ -128,10 +127,12 @@ Statamic.app({ CollectionCreateForm: require('./components/collections/CreateForm.vue').default, CollectionScaffolder: require('./components/collections/Scaffolder.vue').default, CollectionEditForm: require('./components/collections/EditForm.vue').default, + CollectionView: require('./components/collections/View.vue').default, SessionExpiry: require('./components/SessionExpiry.vue').default, StructureWizard: require('./components/structures/Wizard.vue').default, StructureListing: require('./components/structures/Listing.vue').default, StructureEditForm: require('./components/structures/EditForm.vue').default, + StructureView: require('./components/structures/View.vue').default, Stacks: require('./components/stacks/Stacks.vue').default, TaxonomyWizard: require('./components/taxonomies/Wizard.vue').default, TaxonomyEditForm: require('./components/taxonomies/EditForm.vue').default, diff --git a/resources/js/components/collections/View.vue b/resources/js/components/collections/View.vue new file mode 100644 index 00000000000..3e1167614ff --- /dev/null +++ b/resources/js/components/collections/View.vue @@ -0,0 +1,193 @@ + + + diff --git a/resources/js/components/structures/PageTree.vue b/resources/js/components/structures/PageTree.vue index 2c73e3783cd..9788f1f7d1d 100644 --- a/resources/js/components/structures/PageTree.vue +++ b/resources/js/components/structures/PageTree.vue @@ -1,65 +1,13 @@ diff --git a/resources/js/components/structures/Wizard.vue b/resources/js/components/structures/Wizard.vue deleted file mode 100644 index 9a2d9d3bffd..00000000000 --- a/resources/js/components/structures/Wizard.vue +++ /dev/null @@ -1,229 +0,0 @@ - - - diff --git a/resources/views/structures/create.blade.php b/resources/views/structures/create.blade.php index 9a7385df4b2..3c493553f91 100644 --- a/resources/views/structures/create.blade.php +++ b/resources/views/structures/create.blade.php @@ -1,8 +1,8 @@ @extends('statamic::layout') -@section('title', __('Create Structure')) +@section('title', __('Create Navigation')) @section('content') - - + @stop diff --git a/src/Http/Controllers/CP/Structures/StructuresController.php b/src/Http/Controllers/CP/Structures/StructuresController.php index 2148dd38a67..af984d2cfda 100644 --- a/src/Http/Controllers/CP/Structures/StructuresController.php +++ b/src/Http/Controllers/CP/Structures/StructuresController.php @@ -189,37 +189,16 @@ public function store(Request $request) $values = $request->validate([ 'title' => 'required', 'handle' => 'required|alpha_dash', - 'collections' => 'array', - 'collection' => 'nullable', - 'max_depth' => 'nullable|integer', - 'expects_root' => 'nullable', - 'route' => 'nullable', // todo: change to the structuresites fieldtype ]); $structure = Structure::make() ->title($values['title']) - ->handle($values['handle']) - ->collections($values['collections'] ?? []) - ->maxDepth($values['max_depth']) - ->expectsRoot($values['expects_root']); - - $sites = [ // todo: change to the structuresites fieldtype - ['handle' => Site::default()->handle(), 'route' => $values['route']], - ]; + ->handle($values['handle']); - foreach ($sites as $site) { - $tree = $structure->makeTree($site['handle']); - $tree->route($site['route']); - $structure->addTree($tree); - } + $structure->addTree($structure->makeTree(Site::default()->handle())); $structure->save(); - if ($values['collection']) { - Collection::findByHandle($values['collection'])->structure($structure->handle())->save(); - // todo: add all the collection's entries to the tree. - } - return ['redirect' => $structure->showUrl()]; } From f5d807df3f594094243ecd9ae29fc827f916d6b9 Mon Sep 17 00:00:00 2001 From: Jason Varga Date: Fri, 21 Feb 2020 15:23:56 -0500 Subject: [PATCH 016/137] Add link dropdown only appears if collections have been provided. Otherwise it just opens the link pane immediately. --- resources/js/components/structures/View.vue | 24 +++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/resources/js/components/structures/View.vue b/resources/js/components/structures/View.vue index c864fdcdf99..fd9693fe8d1 100644 --- a/resources/js/components/structures/View.vue +++ b/resources/js/components/structures/View.vue @@ -14,12 +14,12 @@ - + - - + +