Customization
This guide explains how to customize InspireCMS with custom publishing states, Filament components, and model overrides.
Creating Custom Publish States
InspireCMS provides a flexible publishing system that you can extend with your own custom states for workflow management.
Implementation Steps
- Create a class implementing
PublishStateInterface - Register your new state in a service provider
- (Optional) Add custom styling and behavior
Example: Content Approval Workflow
Adding a Custom Content Status
use SolutionForest\InspireCms\Facades\ContentStatusManifest;
use SolutionForest\InspireCms\DataTypes\Manifest\ContentStatusOption;
use Filament\Actions\Action;
use SolutionForest\InspireCms\Models\Content;
use SolutionForest\InspireCms\Helpers\ContentHelper;
// Register "approved" state with value 5
ContentStatusManifest::replaceOption(
new ContentStatusOption(
value: 5,
name: 'approved',
formAction: fn () => Action::make('approved')
->authorize('approved')
->action(function (null | Content $record, Action $action, $livewire) {
if (is_null($record)) {
$action->cancel();
return;
}
$publishableState = 'approved';
if (! ContentHelper::handlePublishableRecord($record, $publishableState, $livewire, [])) {
return;
}
$action->success();
}),
)
);
Setting Up Permissions
After adding new Filament components:
php artisan inspirecms:repair-permissions
Creating Content Approval Policies
Option 1: Add a Policy Using Gate
// In your service provider
public function boot(): void
{
Gate::policy(Content::class, YourContentPolicy::class);
}
Option 2: Override Default Policy in Config
// config/inspirecms.php
return [
'models' => [
'policies' => [
'content' => \App\Policies\YourContentPolicy::class,
]
],
];
Policy Class Example
namespace App\Policies;
use App\Models\User;
use Illuminate\Contracts\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use SolutionForest\InspireCms\Models\Contracts\Content;
class YourContentPolicy
{
/**
* Determine if user can approve content
*/
public function approved($user, $content)
{
return $user->hasAnyRole(['editor', 'senior_editor']);
}
/**
* Determine if user can publish content
*/
public function publish($user, $content)
{
return $user->hasRole('senior_editor');
}
}
Overriding the Content Model
- Create Extended Model
namespace App\Models;
use SolutionForest\InspireCms\Models\Content as BaseModel;
use SolutionForest\InspireCms\Models\Contracts\Content as ContentContract;
class Content extends BaseModel implements ContentContract
{
public function isPublished(): bool
{
// Published if status is 1 (published) or 5 (approved)
return $this->status === 1 || $this->status === 5;
}
public function scopeWhereIsPublished($query, bool $condition = true)
{
if ($condition) {
return $query->where(function($query) {
$query->where('status', 1)->orWhere('status', 5);
});
}
return $query->where(function($query) {
$query->where('status', '!=', 1)->where('status', '!=', 5);
});
}
}
- Update Configuration
// config/inspirecms.php
return [
'models' => [
'content' => \App\Models\Content::class,
],
];
Complete Implementation Example
Service Provider Setup
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use SolutionForest\InspireCms\Facades\ContentStatusManifest;
use SolutionForest\InspireCms\DataTypes\Manifest\ContentStatusOption;
use Filament\Actions\Action;
use SolutionForest\InspireCms\Models\Content;
use Illuminate\Support\Facades\Gate;
class ContentPublishingServiceProvider extends ServiceProvider
{
public function boot(): void
{
// Register custom status
ContentStatusManifest::replaceOption(
new ContentStatusOption(
value: 5,
name: 'approved',
formAction: fn () => Action::make('approved')
->label('Approve')
->color('success')
->icon('heroicon-o-check-circle')
->authorize('approved')
->action(function (null | Content $record, Action $action, $livewire) {
if (is_null($record)) {
$action->cancel();
return;
}
$record->status = 5;
$record->save();
$action->success();
$livewire->notify('success', 'Content has been approved!');
}),
)
);
// Register policy
Gate::policy(Content::class, \App\Policies\ContentApprovalPolicy::class);
}
}
Workflow Configuration
This creates a workflow where:
- Authors create and submit content (draft)
- Editors review and approve content (approved)
- Senior editors make final publishing decisions (published)
Content is visible on the frontend if either approved or published.
Adding Filament Components
Adding Clusters
Option 1: Using Artisan
php artisan make:filament-cluster YourClusterName --panel=cms
Option 2: Manual Creation
Create your cluster and add it to filament.cluster in the config file.
Required Implementation:
use Filament\Clusters\Cluster;
use SolutionForest\InspireCms\Filament\Concerns\ClusterSectionTrait;
use SolutionForest\InspireCms\Filament\Contracts\ClusterSection;
class Test extends Cluster implements ClusterSection
{
use ClusterSectionTrait;
}
Adding Resources
Option 1: Using Artisan
php artisan make:filament-resource YourResourceName --panel=cms
Option 2: Manual Creation
Create your resource and add it to filament.resources in the config file.
Required Implementation:
use Filament\Resources\Resource;
use SolutionForest\InspireCms\Filament\Concerns\ClusterSectionResourceTrait;
use SolutionForest\InspireCms\Filament\Contracts\ClusterSectionResource;
class TestResource extends Resource implements ClusterSectionResource
{
use ClusterSectionResourceTrait;
protected static ?string $cluster = \App\Clusters\Test::class;
}
Adding Pages
Option 1: Using Artisan
php artisan make:filament-page YourPageName --panel=cms
Option 2: Manual Creation
Create your page and add it to filament.pages in the config file.
Required Implementation:
use Filament\Pages\Page;
use SolutionForest\InspireCms\Filament\Concerns\ClusterSectionPageTrait;
use SolutionForest\InspireCms\Filament\Contracts\ClusterSectionPage;
use SolutionForest\InspireCms\Filament\Contracts\GuardPage;
class Test extends Page implements ClusterSectionPage, GuardPage
{
use ClusterSectionPageTrait;
protected static ?string $cluster = \App\Clusters\Test::class;
public static function getPermissionName(): string
{
return 'view_test_page';
}
public static function getPermissionDisplayName(): string
{
return 'View test page';
}
}
Always ensure your Cluster implements the SolutionForest\InspireCms\Filament\Contracts\ClusterSection interface.
Customize Model
public function register(): void
{
\SolutionForest\InspireCms\Facades\ModelManifest::replace(
\SolutionForest\InspireCms\Models\Contracts\Content::class,
Your\Model\Class::class,
);
}