From f1d9dcac1d7c93ed6da6eea52f5be52077010a23 Mon Sep 17 00:00:00 2001 From: Kath Young Date: Tue, 1 Oct 2024 12:51:26 +0930 Subject: [PATCH 1/8] Fixes for unique slugs --- src/Contracts/HasSlug.php | 40 +++++++++++++++++++++---- src/Filament/Actions/CloneAction.php | 24 --------------- testbench.yaml | 1 + tests/Feature/PageTest.php | 22 +++++++++++++- tests/TestCase.php | 4 +++ tests/Unit/AbstractContentModelTest.php | 29 ++++++++++++++++++ 6 files changed, 89 insertions(+), 31 deletions(-) diff --git a/src/Contracts/HasSlug.php b/src/Contracts/HasSlug.php index 81d08fe8..6eb4f22a 100644 --- a/src/Contracts/HasSlug.php +++ b/src/Contracts/HasSlug.php @@ -11,18 +11,46 @@ protected function slugifyField(): string return 'title'; } + protected function scopeSlugQuery($query, $slug) + { + $query = $query->where('slug', $slug); + if($this->id) { + $query = $query->where('id', '!=', $this->id); + } + return $query; + } + protected static function bootHasSlug() { static::creating(function ($model) { - if ($model->slug === null) { - $model->slug = Str::slug($model->title); - } + $model->slug = $model->getNewSlug(); }); static::updating(function ($model) { - if ($model->slug === null) { - $model->slug = Str::slug($model->title); - } + $model->slug = $model->getNewSlug(); }); } + + protected function getNewSlug() + { + $newSlug = $this->slug ?? Str::slug($this->{$this->slugifyField()}); + + // if there is a -clone already, then append 1 or increment + $result = $this->scopeSlugQuery(static::withoutGlobalScopes(), $newSlug)->first(); + + $count = 1; + while ($result != null) { + $incrementedSlug = $newSlug . '-' . $count; + + $result = $result = $this->scopeSlugQuery(static::withoutGlobalScopes(), $incrementedSlug)->first(); + $count++; + + if ($result == null) { + $newSlug = $incrementedSlug; + break; + } + } + + return $newSlug; + } } diff --git a/src/Filament/Actions/CloneAction.php b/src/Filament/Actions/CloneAction.php index 5893a332..7012210b 100644 --- a/src/Filament/Actions/CloneAction.php +++ b/src/Filament/Actions/CloneAction.php @@ -27,7 +27,6 @@ protected function setUp(): void $this->action(function (Model $record): void { $model = get_class($record); $data = $record->toArray(); - $data['slug'] = $this->getNewSlug($model, $data['slug']); $data['title'] = '[CLONE] ' . $data['title']; $data['is_draft'] = true; @@ -97,27 +96,4 @@ protected function pickData(Model $data, $fields) return $newData; } - - protected function getNewSlug($model, $slug) - { - $newSlug = $slug; - - // if there is a -clone already, then append 1 or increment - $result = $model::withoutGlobalScopes()->where('slug', $newSlug)->first(); - - $count = 1; - while ($result != null) { - $incrementedSlug = $newSlug . '-' . $count; - - $result = $model::withoutGlobalScopes()->where('slug', $incrementedSlug)->first(); - $count++; - - if ($result == null) { - $newSlug = $incrementedSlug; - break; - } - } - - return $newSlug; - } } diff --git a/testbench.yaml b/testbench.yaml index 12593dc9..eddfee88 100644 --- a/testbench.yaml +++ b/testbench.yaml @@ -21,6 +21,7 @@ providers: - Laravel\Scout\ScoutServiceProvider - Spatie\Health\HealthServiceProvider - Lab404\Impersonate\ImpersonateServiceProvider + - Schmeits\FilamentCharacterCounter\FilamentCharacterCounterServiceProvider migrations: - workbench/database/migrations diff --git a/tests/Feature/PageTest.php b/tests/Feature/PageTest.php index 834d42fe..d2cc2f78 100644 --- a/tests/Feature/PageTest.php +++ b/tests/Feature/PageTest.php @@ -6,9 +6,13 @@ use Illuminate\Foundation\Testing\WithFaker; use Illuminate\Support\Facades\Schema; use Illuminate\Support\Str; +use Livewire\Livewire; +use Portable\FilaCms\Filament\Actions\CloneAction; +use Portable\FilaCms\Filament\Resources\PageResource\Pages\ListPages; use Portable\FilaCms\Models\Author; use Portable\FilaCms\Models\Page; use Portable\FilaCms\Tests\TestCase; +use Spatie\Permission\Models\Role; class PageTest extends TestCase { @@ -26,8 +30,10 @@ protected function setUp(): void 'email' => 'jeremy.layson@portable.com.au', 'password' => 'password' ]); + $adminRole = Role::where('name', 'Admin')->first(); + $user->assignRole($adminRole); - $this->be($user); + $this->actingAs($user); Author::create([ 'first_name' => 'Portable', @@ -102,6 +108,20 @@ public function test_published_status(): void $this->assertEquals($page->status, 'Published'); } + public function test_clone(): void + { + $page = Page::factory()->create([ + 'is_draft' => 0, + 'publish_at' => $this->faker->dateTimeBetween('-1 week', '-1 day'), + 'expire_at' => $this->faker->dateTimeBetween('+1 day', '+1 week'), + ]); + + Livewire::test(ListPages::class)->callTableAction(CloneAction::class, $page->id); + + $this->assertDatabaseHas('pages', [ 'title' => '[CLONE] ' . $page->title ]); + $this->assertDatabaseHas('pages', [ 'slug' => $page->slug . '-1' ]); + } + public function test_expired_status(): void { $author = Author::first(); diff --git a/tests/TestCase.php b/tests/TestCase.php index 2c80846c..f6ed8ae0 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -9,6 +9,7 @@ use Illuminate\Support\Facades\Process; use Orchestra\Testbench\Attributes\WithMigration; use Orchestra\Testbench\Concerns\WithWorkbench; +use Portable\FilaCms\Database\Seeders\RoleAndPermissionSeeder; #[WithMigration] abstract class TestCase extends \Orchestra\Testbench\TestCase @@ -16,6 +17,9 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase use RefreshDatabase; use WithWorkbench; + protected $seeder = RoleAndPermissionSeeder::class; + protected $seed = true; + protected static $hasInstalled = false; protected function setUp(): void diff --git a/tests/Unit/AbstractContentModelTest.php b/tests/Unit/AbstractContentModelTest.php index 413edc64..7673f273 100644 --- a/tests/Unit/AbstractContentModelTest.php +++ b/tests/Unit/AbstractContentModelTest.php @@ -16,6 +16,35 @@ class AbstractContentModelTest extends TestCase protected $author = null; + public function test_duplicate_slugs_prevented() + { + $page = Page::factory()->create(); + $page2 = Page::factory()->create(['title' => $page->title, 'slug' => $page->slug]); + + $this->assertNotEquals($page->slug, $page2->slug); + } + + public function test_can_edit_slugs_dont_change() + { + $page = Page::factory()->create(); + $originalSlug = $page->slug; + $page->title = $this->faker->sentence; + $page->save(); + $page->refresh(); + + $this->assertEquals($originalSlug, $page->slug); + } + + public function test_can_supply_slug() + { + $page = Page::factory()->create(); + $page->slug = 'test-slug'; + $page->save(); + $page->refresh(); + + $this->assertEquals('test-slug', $page->slug); + } + public function test_with_pending() { $page = Page::factory()->create(); From bc3c505188d655c39d7df95487dea087509a7f57 Mon Sep 17 00:00:00 2001 From: Kath Young Date: Tue, 1 Oct 2024 13:01:38 +0930 Subject: [PATCH 2/8] Fix pipeline tests --- tests/TestCase.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/TestCase.php b/tests/TestCase.php index f6ed8ae0..3fa44eb7 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -17,9 +17,6 @@ abstract class TestCase extends \Orchestra\Testbench\TestCase use RefreshDatabase; use WithWorkbench; - protected $seeder = RoleAndPermissionSeeder::class; - protected $seed = true; - protected static $hasInstalled = false; protected function setUp(): void @@ -58,6 +55,8 @@ protected function setUp(): void File::copy(getcwd() . '/resources/css/filacms.css', resource_path('css/filacms.css')); File::copy(getcwd() . '/package.json', resource_path('../package.json')); Process::path(app_path())->run('npm run build'); + + $this->artisan('db:seed', ['--class' => RoleAndPermissionSeeder::class]); } protected function defineEnvironment($app) From d16ed91645cbd7b4e7aa6bd264d6e2314168ed27 Mon Sep 17 00:00:00 2001 From: Kath Young Date: Tue, 1 Oct 2024 13:28:43 +0930 Subject: [PATCH 3/8] Login before request --- tests/Feature/PageTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Feature/PageTest.php b/tests/Feature/PageTest.php index d2cc2f78..19dfaa44 100644 --- a/tests/Feature/PageTest.php +++ b/tests/Feature/PageTest.php @@ -110,12 +110,12 @@ public function test_published_status(): void public function test_clone(): void { + $this->actingAs($this->userModel); $page = Page::factory()->create([ 'is_draft' => 0, 'publish_at' => $this->faker->dateTimeBetween('-1 week', '-1 day'), 'expire_at' => $this->faker->dateTimeBetween('+1 day', '+1 week'), ]); - Livewire::test(ListPages::class)->callTableAction(CloneAction::class, $page->id); $this->assertDatabaseHas('pages', [ 'title' => '[CLONE] ' . $page->title ]); From a58a5eb5cf6e7b37bf435ddb0a11dcfc26c2c7b8 Mon Sep 17 00:00:00 2001 From: Kath Young Date: Tue, 1 Oct 2024 13:31:17 +0930 Subject: [PATCH 4/8] Login --- tests/Feature/PageTest.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Feature/PageTest.php b/tests/Feature/PageTest.php index 19dfaa44..eee3ff8f 100644 --- a/tests/Feature/PageTest.php +++ b/tests/Feature/PageTest.php @@ -110,7 +110,8 @@ public function test_published_status(): void public function test_clone(): void { - $this->actingAs($this->userModel); + $user = $this->userModel::first(); + $this->actingAs($user); $page = Page::factory()->create([ 'is_draft' => 0, 'publish_at' => $this->faker->dateTimeBetween('-1 week', '-1 day'), From fc961ce8fa20f3c1a51c7b9e7f8a15e9348240ac Mon Sep 17 00:00:00 2001 From: Kath Young Date: Tue, 1 Oct 2024 17:20:19 +0930 Subject: [PATCH 5/8] Test role --- tests/Feature/PageTest.php | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/Feature/PageTest.php b/tests/Feature/PageTest.php index eee3ff8f..1cd88e03 100644 --- a/tests/Feature/PageTest.php +++ b/tests/Feature/PageTest.php @@ -111,6 +111,7 @@ public function test_published_status(): void public function test_clone(): void { $user = $this->userModel::first(); + $user->assignRole('admin'); $this->actingAs($user); $page = Page::factory()->create([ 'is_draft' => 0, From e1fe4531c9a64519b577237abec5026716a67821 Mon Sep 17 00:00:00 2001 From: Kath Young Date: Tue, 1 Oct 2024 17:23:05 +0930 Subject: [PATCH 6/8] Test role --- tests/Feature/PageTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/Feature/PageTest.php b/tests/Feature/PageTest.php index 1cd88e03..4e1c8ac0 100644 --- a/tests/Feature/PageTest.php +++ b/tests/Feature/PageTest.php @@ -111,7 +111,7 @@ public function test_published_status(): void public function test_clone(): void { $user = $this->userModel::first(); - $user->assignRole('admin'); + $user->assignRole('Admin'); $this->actingAs($user); $page = Page::factory()->create([ 'is_draft' => 0, From d0b84d2c194cc3bedd72e64ad5a3d93f15db0ce8 Mon Sep 17 00:00:00 2001 From: Kath Young Date: Tue, 1 Oct 2024 17:24:40 +0930 Subject: [PATCH 7/8] Test role --- tests/Feature/PageTest.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/Feature/PageTest.php b/tests/Feature/PageTest.php index 4e1c8ac0..cba9fe85 100644 --- a/tests/Feature/PageTest.php +++ b/tests/Feature/PageTest.php @@ -111,7 +111,9 @@ public function test_published_status(): void public function test_clone(): void { $user = $this->userModel::first(); - $user->assignRole('Admin'); + $adminRole = Role::where('name', 'Admin')->first(); + $this->assertNotNull($adminRole); + $user->assignRole($adminRole); $this->actingAs($user); $page = Page::factory()->create([ 'is_draft' => 0, From 7b02bad00acfec5b6688dcb46d8f5b07f1afe434 Mon Sep 17 00:00:00 2001 From: Kath Young Date: Tue, 1 Oct 2024 17:28:24 +0930 Subject: [PATCH 8/8] Test role --- tests/Feature/PageTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/Feature/PageTest.php b/tests/Feature/PageTest.php index cba9fe85..2e5eece9 100644 --- a/tests/Feature/PageTest.php +++ b/tests/Feature/PageTest.php @@ -7,6 +7,7 @@ use Illuminate\Support\Facades\Schema; use Illuminate\Support\Str; use Livewire\Livewire; +use Portable\FilaCms\Database\Seeders\RoleAndPermissionSeeder; use Portable\FilaCms\Filament\Actions\CloneAction; use Portable\FilaCms\Filament\Resources\PageResource\Pages\ListPages; use Portable\FilaCms\Models\Author; @@ -24,6 +25,7 @@ class PageTest extends TestCase protected function setUp(): void { parent::setUp(); + $this->artisan('db:seed', ['--class' => RoleAndPermissionSeeder::class]); $this->userModel = config('auth.providers.users.model'); $user = $this->userModel::create([ 'name' => 'Jeremy Layson',