Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions app/Http/Controllers/PluginDirectoryController.php
Original file line number Diff line number Diff line change
Expand Up @@ -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),
]);
}
}
13 changes: 13 additions & 0 deletions resources/views/plugin-license.blade.php
Original file line number Diff line number Diff line change
Expand Up @@ -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)
<div class="mb-6 rounded-xl border border-amber-300 bg-amber-50 p-4 text-center dark:border-amber-600 dark:bg-amber-950/50">
<p class="text-sm font-medium text-amber-800 dark:text-amber-200">
Preview &mdash; This plugin is not publicly visible.
@if ($plugin->isApproved() && ! $plugin->is_active)
It has been de-listed.
@else
Status: {{ $plugin->status->label() }}
@endif
</p>
</div>
@endif

<header class="relative">
{{-- Back button --}}
<div
Expand Down
171 changes: 171 additions & 0 deletions tests/Feature/PluginLicensePreviewTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
<?php

namespace Tests\Feature;

use App\Features\ShowPlugins;
use App\Models\Plugin;
use App\Models\PluginPrice;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Pennant\Feature;
use Tests\TestCase;

class PluginLicensePreviewTest extends TestCase
{
use RefreshDatabase;

protected function setUp(): void
{
parent::setUp();

Feature::define(ShowPlugins::class, true);
}

private function createPaidPluginWithLicense(array $pluginAttributes = []): Plugin
{
$plugin = Plugin::factory()
->paid()
->create(array_merge([
'license_html' => '<p>License agreement content</p>',
], $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' => '<p>License content</p>',
]);

$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);
}
}
Loading