diff --git a/app/Http/Controllers/PluginDirectoryController.php b/app/Http/Controllers/PluginDirectoryController.php index 6e2496bb..6b71c4bb 100644 --- a/app/Http/Controllers/PluginDirectoryController.php +++ b/app/Http/Controllers/PluginDirectoryController.php @@ -85,19 +85,23 @@ public function license(string $vendor, string $package): View { $plugin = Plugin::findByVendorPackageOrFail($vendor, $package); - abort_unless($plugin->isApproved(), 404); + $user = Auth::user(); + + $isAdmin = $user?->isAdmin() ?? false; + $isOwner = $user && $plugin->user_id === $user->id; + abort_unless($plugin->isPaid(), 404); abort_unless($plugin->license_html, 404); + abort_unless(($plugin->isApproved() && $plugin->is_active) || $isAdmin || $isOwner, 404); - $user = Auth::user(); - - // For paid plugins, check if user has an accessible price - if (! $plugin->hasAccessiblePriceFor($user)) { + // For paid plugins, check if user has an accessible price (admins and owners bypass) + if (! $isAdmin && ! $isOwner && ! $plugin->hasAccessiblePriceFor($user)) { abort(404); } return view('plugin-license', [ 'plugin' => $plugin, + 'isAdminPreview' => (! $plugin->isApproved() || ! $plugin->is_active) && ($isAdmin || $isOwner), ]); } } diff --git a/resources/views/plugin-license.blade.php b/resources/views/plugin-license.blade.php index 91d9c589..d4e78b1a 100644 --- a/resources/views/plugin-license.blade.php +++ b/resources/views/plugin-license.blade.php @@ -3,6 +3,19 @@ class="mx-auto mt-10 w-full max-w-3xl px-5 md:mt-14" aria-labelledby="license-title" > + @if ($isAdminPreview ?? false) +
+ Preview — This plugin is not publicly visible. + @if ($plugin->isApproved() && ! $plugin->is_active) + It has been de-listed. + @else + Status: {{ $plugin->status->label() }} + @endif +
+License agreement content
', + ], $pluginAttributes)); + + PluginPrice::factory()->regular()->create([ + 'plugin_id' => $plugin->id, + 'amount' => 2999, + ]); + + return $plugin; + } + + public function test_guest_cannot_view_pending_paid_plugin_license(): void + { + $plugin = $this->createPaidPluginWithLicense(); + + $this->get(route('plugins.license', $plugin->routeParams())) + ->assertStatus(404); + } + + public function test_regular_user_cannot_view_pending_paid_plugin_license(): void + { + $user = User::factory()->create(); + $plugin = $this->createPaidPluginWithLicense(); + + $this->actingAs($user) + ->get(route('plugins.license', $plugin->routeParams())) + ->assertStatus(404); + } + + public function test_admin_can_view_pending_paid_plugin_license(): void + { + $admin = User::factory()->create(['email' => 'admin@test.com']); + config(['filament.users' => ['admin@test.com']]); + + $plugin = $this->createPaidPluginWithLicense(); + + $this->actingAs($admin) + ->get(route('plugins.license', $plugin->routeParams())) + ->assertStatus(200); + } + + public function test_owner_can_view_pending_paid_plugin_license(): void + { + $owner = User::factory()->create(); + $plugin = $this->createPaidPluginWithLicense(['user_id' => $owner->id]); + + $this->actingAs($owner) + ->get(route('plugins.license', $plugin->routeParams())) + ->assertStatus(200); + } + + public function test_admin_sees_preview_banner_on_pending_paid_plugin_license(): void + { + $admin = User::factory()->create(['email' => 'admin@test.com']); + config(['filament.users' => ['admin@test.com']]); + + $plugin = $this->createPaidPluginWithLicense(); + + $this->actingAs($admin) + ->get(route('plugins.license', $plugin->routeParams())) + ->assertSee('Preview') + ->assertSee('This plugin is not publicly visible') + ->assertSee('Pending Review'); + } + + public function test_owner_sees_preview_banner_on_pending_paid_plugin_license(): void + { + $owner = User::factory()->create(); + $plugin = $this->createPaidPluginWithLicense(['user_id' => $owner->id]); + + $this->actingAs($owner) + ->get(route('plugins.license', $plugin->routeParams())) + ->assertSee('Preview') + ->assertSee('This plugin is not publicly visible') + ->assertSee('Pending Review'); + } + + public function test_approved_paid_plugin_license_does_not_show_preview_banner(): void + { + $plugin = $this->createPaidPluginWithLicense(['status' => 'approved', 'approved_at' => now()]); + + $this->get(route('plugins.license', $plugin->routeParams())) + ->assertStatus(200) + ->assertDontSee('This plugin is not publicly visible'); + } + + public function test_admin_sees_preview_banner_on_delisted_paid_plugin_license(): void + { + $admin = User::factory()->create(['email' => 'admin@test.com']); + config(['filament.users' => ['admin@test.com']]); + + $plugin = $this->createPaidPluginWithLicense([ + 'status' => 'approved', + 'approved_at' => now(), + 'is_active' => false, + ]); + + $this->actingAs($admin) + ->get(route('plugins.license', $plugin->routeParams())) + ->assertSee('Preview') + ->assertSee('It has been de-listed'); + } + + public function test_guest_cannot_view_delisted_paid_plugin_license(): void + { + $plugin = $this->createPaidPluginWithLicense([ + 'status' => 'approved', + 'approved_at' => now(), + 'is_active' => false, + ]); + + $this->get(route('plugins.license', $plugin->routeParams())) + ->assertStatus(404); + } + + public function test_free_plugin_license_returns_404(): void + { + $plugin = Plugin::factory()->approved()->free()->create([ + 'license_html' => 'License content
', + ]); + + $this->get(route('plugins.license', $plugin->routeParams())) + ->assertStatus(404); + } + + public function test_paid_plugin_without_license_html_returns_404(): void + { + $admin = User::factory()->create(['email' => 'admin@test.com']); + config(['filament.users' => ['admin@test.com']]); + + $plugin = Plugin::factory()->pending()->paid()->create([ + 'license_html' => null, + ]); + + PluginPrice::factory()->regular()->create([ + 'plugin_id' => $plugin->id, + ]); + + $this->actingAs($admin) + ->get(route('plugins.license', $plugin->routeParams())) + ->assertStatus(404); + } +}