Initial baseline: LaraBlog core with plugin commerce surface.

Captures the current working tree after theme slots, ArticleAccess, and the payment / paid-content plugins so subsequent work has a reviewable git history.
This commit is contained in:
ak
2026-08-12 01:15:38 +08:00
commit 263b98b218
337 changed files with 31393 additions and 0 deletions
+125
View File
@@ -0,0 +1,125 @@
<?php
declare(strict_types=1);
namespace App\Filament\Pages;
use App\Domain\Plugin\PluginManager;
use App\Models\Plugin;
use BackedEnum;
use Filament\Actions\Action;
use Filament\Notifications\Notification;
use Filament\Pages\Page;
use Filament\Support\Icons\Heroicon;
use UnitEnum;
class ManagePlugins extends Page
{
protected static string|BackedEnum|null $navigationIcon = Heroicon::OutlinedPuzzlePiece;
protected static ?int $navigationSort = 81;
protected string $view = 'filament.pages.manage-plugins';
/** @var array<int, array<string, mixed>> */
public array $plugins = [];
public static function getNavigationGroup(): ?string
{
return __('admin.groups.system');
}
public static function getNavigationLabel(): string
{
return __('admin.nav.plugins');
}
public function getTitle(): string
{
return __('admin.pages.plugins_title');
}
public function mount(PluginManager $manager): void
{
$manager->syncDiscoveredPlugins();
$this->reload($manager);
}
public function enable(string $name, PluginManager $manager): void
{
try {
$manager->enable($name);
$this->reload($manager);
Notification::make()->title(__('admin.pages.enable').''.$name)->success()->send();
} catch (\Throwable $e) {
Notification::make()->title($e->getMessage())->danger()->send();
}
}
public function disable(string $name, PluginManager $manager): void
{
try {
$manager->disable($name);
$this->reload($manager);
Notification::make()->title(__('admin.pages.disable').''.$name)->success()->send();
} catch (\Throwable $e) {
Notification::make()->title($e->getMessage())->danger()->send();
}
}
public function showDocs(string $name, PluginManager $manager): void
{
$docs = $manager->readDocs($name);
if ($docs === null || trim($docs) === '') {
Notification::make()->title(__('admin.messages.plugin_docs_missing'))->warning()->send();
return;
}
Notification::make()
->title(__('admin.pages.plugin_docs'))
->body(str($docs)->limit(1800)->toString())
->persistent()
->info()
->send();
}
protected function reload(PluginManager $manager): void
{
$discovered = $manager->discover();
$records = Plugin::query()->get()->keyBy('name');
$accents = ['#0f8a7a', '#c45c26', '#2563eb', '#7c3aed', '#db2777', '#0891b2'];
$this->plugins = $discovered->values()->map(function (array $manifest, int $index) use ($records, $accents, $manager) {
$name = (string) $manifest['name'];
$titleKey = 'admin.plugins.'.$name.'.title';
$descKey = 'admin.plugins.'.$name.'.description';
$record = $records->get($name);
$requires = (array) ($manifest['requires'] ?? []);
return [
'name' => $name,
'title' => __($titleKey) !== $titleKey ? __($titleKey) : ($manifest['title'] ?? $name),
'version' => $manifest['version'] ?? '1.0.0',
'description' => __($descKey) !== $descKey ? __($descKey) : ($manifest['description'] ?? ''),
'enabled' => (bool) ($record?->enabled),
'requires' => $requires,
'has_docs' => $manager->docsPath($name) !== null,
'accent' => $accents[$index % count($accents)],
];
})->all();
}
protected function getHeaderActions(): array
{
return [
Action::make('sync')
->label(__('admin.pages.plugins_sync'))
->action(function (PluginManager $manager): void {
$manager->syncDiscoveredPlugins();
$this->reload($manager);
Notification::make()->title(__('admin.pages.plugins_sync'))->success()->send();
}),
];
}
}