Skip to content
Open
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
1 change: 1 addition & 0 deletions app/Filament/Actions/RefundPluginLicenseAction.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ protected function setUp(): void
parent::setUp();

$this
->authorize('refund')
->label('Refund')
->icon('heroicon-o-receipt-refund')
->color('danger')
Expand Down
3 changes: 2 additions & 1 deletion app/Filament/Pages/Companies.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use App\Filament\Resources\UserResource;
use App\Services\CompanyAggregator;
use BezhanSalleh\FilamentShield\Traits\HasPageShield;
use Filament\Actions\Action;
use Filament\Pages\Page;
use Filament\Schemas\Components\EmbeddedTable;
Expand All @@ -18,7 +19,7 @@

class Companies extends Page implements HasTable
{
use InteractsWithTable;
use HasPageShield, InteractsWithTable;

protected static \BackedEnum|string|null $navigationIcon = 'heroicon-o-building-office-2';

Expand Down
42 changes: 42 additions & 0 deletions app/Filament/Pages/Dashboard.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,33 @@
use App\Filament\Widgets\StatsOverview;
use App\Filament\Widgets\SubscriberIncomeChart;
use App\Filament\Widgets\UsersChart;
use Filament\Facades\Filament;
use Filament\Pages\Dashboard as BaseDashboard;

class Dashboard extends BaseDashboard
{
protected static \BackedEnum|string|null $navigationIcon = 'heroicon-o-home';

/**
* Staff whose role can't see any dashboard widget are sent straight to
* the first section they can open instead of an empty page.
*/
public function mount(): void
{
if (static::hasVisibleWidgets()) {
return;
}

if ($url = static::firstAccessibleNavigationUrl()) {
$this->redirect($url);
}
}

public static function shouldRegisterNavigation(): bool
{
return parent::shouldRegisterNavigation() && static::hasVisibleWidgets();
}

public function getHeaderWidgets(): array
{
return [
Expand All @@ -29,4 +50,25 @@ public function getWidgets(): array
PluginRevenueChart::class,
];
}

protected static function hasVisibleWidgets(): bool
{
$dashboard = new static;

return collect([...$dashboard->getHeaderWidgets(), ...$dashboard->getWidgets()])
->contains(fn (string $widget): bool => $widget::canView());
}

protected static function firstAccessibleNavigationUrl(): ?string
{
foreach (Filament::getNavigation() as $group) {
foreach ($group->getItems() as $item) {
if ($item->isVisible() && filled($item->getUrl())) {
return $item->getUrl();
}
}
}

return null;
}
}
41 changes: 41 additions & 0 deletions app/Filament/Resources/Concerns/AuthorizesThroughOwnerRecord.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace App\Filament\Resources\Concerns;

use Filament\Facades\Filament;
use Illuminate\Auth\Access\Response;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Gate;

/**
* For relation managers whose records have no policy of their own (prices,
* activity, replies...). Seeing them follows the owner's "view" permission
* and changing them follows the owner's "update" permission.
*/
trait AuthorizesThroughOwnerRecord
{
public static function canViewForRecord(Model $ownerRecord, string $pageClass): bool
{
return static::shouldSkipAuthorization()
|| Gate::forUser(Filament::auth()->user())->allows('view', $ownerRecord);
}

public function getAuthorizationResponse(string $action, ?Model $record = null): Response
{
if (static::shouldSkipAuthorization()) {
return Response::allow();
}

$ability = in_array($action, ['viewAny', 'view'], true) ? 'view' : static::ownerUpdateAbility();

return Gate::forUser(Filament::auth()->user())->inspect($ability, $this->getOwnerRecord());
}

/**
* The owner ability needed to change these records.
*/
protected static function ownerUpdateAbility(): string
{
return 'update';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace App\Filament\Resources\PluginBundleResource\RelationManagers;

use App\Filament\Resources\Concerns\AuthorizesThroughOwnerRecord;
use App\Models\Plugin;
use App\Models\PluginBundle;
use Filament\Actions;
Expand All @@ -13,6 +14,8 @@

class PluginsRelationManager extends RelationManager
{
use AuthorizesThroughOwnerRecord;

protected static string $relationship = 'plugins';

protected static ?string $recordTitleAttribute = 'name';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
namespace App\Filament\Resources\PluginBundleResource\RelationManagers;

use App\Enums\PriceTier;
use App\Filament\Resources\Concerns\AuthorizesThroughOwnerRecord;
use Filament\Actions;
use Filament\Forms;
use Filament\Resources\RelationManagers\RelationManager;
Expand All @@ -12,6 +13,8 @@

class PricesRelationManager extends RelationManager
{
use AuthorizesThroughOwnerRecord;

protected static string $relationship = 'prices';

protected static ?string $title = 'Pricing';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,16 @@
namespace App\Filament\Resources\PluginIdeaResource\RelationManagers;

use App\Enums\PluginType;
use App\Filament\Resources\Concerns\AuthorizesThroughOwnerRecord;
use App\Models\MissedPluginSearch;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;

final class MissedSearchesRelationManager extends RelationManager
{
use AuthorizesThroughOwnerRecord;

protected static string $relationship = 'missedSearches';

protected static ?string $title = 'Searches';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

namespace App\Filament\Resources\PluginPayoutResource\RelationManagers;

use App\Filament\Resources\Concerns\AuthorizesThroughOwnerRecord;
use App\Models\PluginPayoutAttempt;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;

class AttemptsRelationManager extends RelationManager
{
use AuthorizesThroughOwnerRecord;

protected static string $relationship = 'attempts';

protected static ?string $title = 'Attempt History';
Expand Down
2 changes: 2 additions & 0 deletions app/Filament/Resources/PluginReportResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ private static function resolutionActions(): array
{
return [
Actions\Action::make('resolve')
->authorize('update')
->label('Mark Resolved')
->icon('heroicon-o-check-circle')
->color('success')
Expand All @@ -83,6 +84,7 @@ private static function resolutionActions(): array
}),

Actions\Action::make('dismiss')
->authorize('update')
->label('Dismiss')
->icon('heroicon-o-x-circle')
->color('gray')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ protected function getHeaderActions(): array
{
return [
Actions\Action::make('resolve')
->authorize('update')
->label('Mark Resolved')
->icon('heroicon-o-check-circle')
->color('success')
Expand All @@ -44,6 +45,7 @@ protected function getHeaderActions(): array
}),

Actions\Action::make('dismiss')
->authorize('update')
->label('Dismiss')
->icon('heroicon-o-x-circle')
->color('gray')
Expand Down
20 changes: 18 additions & 2 deletions app/Filament/Resources/PluginResource.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
use Filament\Tables\Contracts\HasTable;
use Filament\Tables\Table;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\HtmlString;
use Throwable;

Expand Down Expand Up @@ -69,12 +70,14 @@ public static function form(Schema $schema): Schema
->label('Description'),

Forms\Components\Select::make('type')
->options(PluginType::class),
->options(PluginType::class)
->disabled(fn (?Plugin $record): bool => static::cannotChangePricing($record)),

Forms\Components\Select::make('tier')
->options(PluginTier::class)
->placeholder('No tier')
->helperText('Set pricing tier for paid plugins'),
->helperText('Set pricing tier for paid plugins')
->disabled(fn (?Plugin $record): bool => static::cannotChangePricing($record)),

Forms\Components\Placeholder::make('name')
->label('Composer Package Name')
Expand Down Expand Up @@ -262,6 +265,7 @@ public static function form(Schema $schema): Schema
])
->headerActions([
Action::make('emailReviewChecks')
->authorize('messageDeveloper')
->label('Email Developer')
->icon('heroicon-o-envelope')
->color('warning')
Expand Down Expand Up @@ -502,6 +506,7 @@ public static function table(Table $table): Table
->actions([
Actions\ActionGroup::make([
Action::make('resync')
->authorize('resync')
->label('Re-sync from GitHub')
->icon('heroicon-o-arrow-path')
->color('primary')
Expand All @@ -520,6 +525,7 @@ public static function table(Table $table): Table
}),

Action::make('grantToUser')
->authorize('grantToUser')
->label('Grant to User')
->icon('heroicon-o-gift')
->color('success')
Expand Down Expand Up @@ -578,6 +584,7 @@ public static function table(Table $table): Table
->modalSubmitActionLabel('Grant'),

Action::make('runReviewChecks')
->authorize('runReviewChecks')
->label('Run Review Checks')
->icon('heroicon-o-clipboard-document-check')
->color('primary')
Expand Down Expand Up @@ -652,6 +659,15 @@ public static function table(Table $table): Table
);
}

/**
* Type and tier decide what a plugin costs, so they follow the
* "Convert to paid" permission rather than plain update access.
*/
protected static function cannotChangePricing(?Plugin $record): bool
{
return $record !== null && Gate::denies('convertToPaid', $record);
}

public static function getRelations(): array
{
return [
Expand Down
8 changes: 8 additions & 0 deletions app/Filament/Resources/PluginResource/Pages/EditPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ protected function getHeaderActions(): array
{
return [
Actions\Action::make('approve')
->authorize('approve')
->icon('heroicon-o-check')
->color('success')
->visible(fn () => $this->record->isPending())
Expand All @@ -61,6 +62,7 @@ protected function getHeaderActions(): array
: "Are you sure you want to approve '{$this->record->name}'?"),

Actions\Action::make('reject')
->authorize('reject')
->icon('heroicon-o-x-mark')
->color('danger')
->visible(fn () => $this->record->isPending() || $this->record->isApproved())
Expand All @@ -76,6 +78,7 @@ protected function getHeaderActions(): array
->modalDescription(fn () => "Are you sure you want to reject '{$this->record->name}'?"),

Actions\Action::make('messageDeveloper')
->authorize('messageDeveloper')
->label('Message Developer')
->icon('heroicon-o-chat-bubble-left-right')
->color('info')
Expand Down Expand Up @@ -111,6 +114,7 @@ protected function getHeaderActions(): array

Actions\ActionGroup::make([
Actions\Action::make('convertToPaid')
->authorize('convertToPaid')
->label('Convert to Paid')
->icon('heroicon-o-currency-dollar')
->color('success')
Expand Down Expand Up @@ -139,6 +143,7 @@ protected function getHeaderActions(): array
->modalSubmitActionLabel('Convert & Ingest'),

Actions\Action::make('syncToSatis')
->authorize('syncToSatis')
->label(fn () => $this->record->isSatisSynced() ? 'Re-sync to Satis' : 'Sync to Satis')
->icon('heroicon-o-arrow-path')
->color('warning')
Expand All @@ -159,6 +164,7 @@ protected function getHeaderActions(): array
}),

Actions\Action::make('grantToUser')
->authorize('grantToUser')
->label('Grant to User')
->icon('heroicon-o-gift')
->color('success')
Expand Down Expand Up @@ -217,6 +223,7 @@ protected function getHeaderActions(): array
->modalSubmitActionLabel('Grant'),

Actions\Action::make('runReviewChecks')
->authorize('runReviewChecks')
->label('Run Review Checks')
->icon('heroicon-o-clipboard-document-check')
->color('primary')
Expand Down Expand Up @@ -277,6 +284,7 @@ protected function getHeaderActions(): array
}),

Actions\Action::make('resync')
->authorize('resync')
->label('Re-sync from GitHub')
->icon('heroicon-o-arrow-path')
->color('primary')
Expand Down
2 changes: 2 additions & 0 deletions app/Filament/Resources/PluginResource/Pages/ViewPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ protected function getHeaderActions(): array
->visible(fn () => $this->record->isApproved() || $this->record->isPending()),

Actions\Action::make('approve')
->authorize('approve')
->icon('heroicon-o-check')
->color('success')
->visible(fn () => $this->record->isPending())
Expand All @@ -32,6 +33,7 @@ protected function getHeaderActions(): array
->modalDescription(fn () => "Are you sure you want to approve '{$this->record->name}'?"),

Actions\Action::make('reject')
->authorize('reject')
->icon('heroicon-o-x-mark')
->color('danger')
->visible(fn () => $this->record->isPending() || $this->record->isApproved())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@
namespace App\Filament\Resources\PluginResource\RelationManagers;

use App\Enums\PluginActivityType;
use App\Filament\Resources\Concerns\AuthorizesThroughOwnerRecord;
use Filament\Resources\RelationManagers\RelationManager;
use Filament\Tables;
use Filament\Tables\Table;

class ActivitiesRelationManager extends RelationManager
{
use AuthorizesThroughOwnerRecord;

protected static string $relationship = 'activities';

protected static ?string $title = 'Activity History';
Expand Down
Loading