mirror of
https://github.com/pelican-dev/panel.git
synced 2025-11-08 10:39:27 +01:00
add uninstall & remove
This commit is contained in:
parent
bfae4e7f30
commit
8aa23ccc97
39
app/Console/Commands/Plugin/UninstallPluginCommand.php
Normal file
39
app/Console/Commands/Plugin/UninstallPluginCommand.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands\Plugin;
|
||||
|
||||
use App\Facades\Plugins;
|
||||
use App\Models\Plugin;
|
||||
use Illuminate\Console\Command;
|
||||
|
||||
class UninstallPluginCommand extends Command
|
||||
{
|
||||
protected $signature = 'p:plugin:uninstall {id?} {--delete : Delete the plugin files}';
|
||||
|
||||
protected $description = 'Uninstalls a plugin';
|
||||
|
||||
public function handle(): void
|
||||
{
|
||||
$id = $this->argument('id') ?? $this->choice('Plugin', Plugin::pluck('name', 'id')->toArray());
|
||||
|
||||
$plugin = Plugin::find($id);
|
||||
|
||||
if (!$plugin) {
|
||||
$this->error('Plugin does not exist!');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$plugin->isInstalled()) {
|
||||
$this->error('Plugin is not installed!');
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$deleteFiles = $this->option('delete') ?? $this->confirm('Do you also want to delete the plugin files?');
|
||||
|
||||
Plugins::uninstallPlugin($plugin, $deleteFiles);
|
||||
|
||||
$this->info('Plugin uninstalled' . ($deleteFiles ? ' and files deleted' : '') . '.');
|
||||
}
|
||||
}
|
||||
@ -12,8 +12,11 @@ use Illuminate\Support\Facades\Facade;
|
||||
* @method static void loadPlugins()
|
||||
* @method static void loadPanelPlugins(Panel $panel)
|
||||
* @method static void requireComposerPackages(Plugin $plugin)
|
||||
* @method static void removeComposerPackages(Plugin $plugin)
|
||||
* @method static void runPluginMigrations(Plugin $plugin)
|
||||
* @method static void rollbackPluginMigrations(Plugin $plugin)
|
||||
* @method static void installPlugin(Plugin $plugin, bool $enable = true)
|
||||
* @method static void uninstallPlugin(Plugin $plugin, bool $deleteFiles = false)
|
||||
* @method static void updatePlugin(Plugin $plugin)
|
||||
* @method static void downloadPluginFromFile(UploadedFile $file)
|
||||
* @method static void downloadPluginFromUrl(string $url)
|
||||
|
||||
@ -7,6 +7,7 @@ use App\Filament\Admin\Resources\Plugins\Pages\ListPlugins;
|
||||
use App\Models\Plugin;
|
||||
use Exception;
|
||||
use Filament\Actions\Action;
|
||||
use Filament\Actions\ActionGroup;
|
||||
use Filament\Forms\Components\FileUpload;
|
||||
use Filament\Forms\Components\TextInput;
|
||||
use Filament\Infolists\Components\TextEntry;
|
||||
@ -17,6 +18,7 @@ use Filament\Schemas\Components\Tabs\Tab;
|
||||
use Filament\Tables\Columns\TextColumn;
|
||||
use Filament\Tables\Table;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\File;
|
||||
|
||||
class PluginResource extends Resource
|
||||
{
|
||||
@ -109,73 +111,109 @@ class PluginResource extends Resource
|
||||
->schema(fn (Plugin $plugin) => $plugin->getSettingsForm())
|
||||
->action(fn (array $data, Plugin $plugin) => $plugin->saveSettings($data))
|
||||
->slideOver(),
|
||||
Action::make('install')
|
||||
->label(trans('admin/plugin.install'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('update', $plugin))
|
||||
->icon('tabler-terminal')
|
||||
->color('success')
|
||||
->hidden(fn (Plugin $plugin) => $plugin->isInstalled())
|
||||
->action(function (Plugin $plugin, $livewire) {
|
||||
Plugins::installPlugin($plugin, !$plugin->isTheme() || !Plugins::hasThemePluginEnabled());
|
||||
ActionGroup::make([
|
||||
Action::make('install')
|
||||
->label(trans('admin/plugin.install'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('update', $plugin))
|
||||
->icon('tabler-terminal')
|
||||
->color('success')
|
||||
->hidden(fn (Plugin $plugin) => $plugin->isInstalled())
|
||||
->action(function (Plugin $plugin, $livewire) {
|
||||
Plugins::installPlugin($plugin, !$plugin->isTheme() || !Plugins::hasThemePluginEnabled());
|
||||
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.installed'))
|
||||
->send();
|
||||
}),
|
||||
Action::make('update')
|
||||
->label(trans('admin/plugin.update'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('update', $plugin))
|
||||
->icon('tabler-download')
|
||||
->color('success')
|
||||
->visible(fn (Plugin $plugin) => $plugin->isUpdateAvailable())
|
||||
->action(function (Plugin $plugin, $livewire) {
|
||||
Plugins::updatePlugin($plugin);
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.installed'))
|
||||
->send();
|
||||
}),
|
||||
Action::make('update')
|
||||
->label(trans('admin/plugin.update'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('update', $plugin))
|
||||
->icon('tabler-download')
|
||||
->color('success')
|
||||
->visible(fn (Plugin $plugin) => $plugin->isInstalled() && $plugin->isUpdateAvailable())
|
||||
->action(function (Plugin $plugin, $livewire) {
|
||||
Plugins::updatePlugin($plugin);
|
||||
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.updated'))
|
||||
->send();
|
||||
}),
|
||||
Action::make('enable')
|
||||
->label(trans('admin/plugin.enable'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('update', $plugin))
|
||||
->icon('tabler-check')
|
||||
->color('success')
|
||||
->visible(fn (Plugin $plugin) => $plugin->canEnable())
|
||||
->requiresConfirmation(fn (Plugin $plugin) => $plugin->isTheme() && Plugins::hasThemePluginEnabled())
|
||||
->modalHeading(fn (Plugin $plugin) => $plugin->isTheme() && Plugins::hasThemePluginEnabled() ? trans('admin/plugin.enable_theme_modal.heading') : null)
|
||||
->modalDescription(fn (Plugin $plugin) => $plugin->isTheme() && Plugins::hasThemePluginEnabled() ? trans('admin/plugin.enable_theme_modal.description') : null)
|
||||
->action(function (Plugin $plugin, $livewire) {
|
||||
Plugins::enablePlugin($plugin);
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.updated'))
|
||||
->send();
|
||||
}),
|
||||
Action::make('enable')
|
||||
->label(trans('admin/plugin.enable'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('update', $plugin))
|
||||
->icon('tabler-check')
|
||||
->color('success')
|
||||
->visible(fn (Plugin $plugin) => $plugin->canEnable())
|
||||
->requiresConfirmation(fn (Plugin $plugin) => $plugin->isTheme() && Plugins::hasThemePluginEnabled())
|
||||
->modalHeading(fn (Plugin $plugin) => $plugin->isTheme() && Plugins::hasThemePluginEnabled() ? trans('admin/plugin.enable_theme_modal.heading') : null)
|
||||
->modalDescription(fn (Plugin $plugin) => $plugin->isTheme() && Plugins::hasThemePluginEnabled() ? trans('admin/plugin.enable_theme_modal.description') : null)
|
||||
->action(function (Plugin $plugin, $livewire) {
|
||||
Plugins::enablePlugin($plugin);
|
||||
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.updated'))
|
||||
->send();
|
||||
}),
|
||||
Action::make('disable')
|
||||
->label(trans('admin/plugin.disable'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('update', $plugin))
|
||||
->icon('tabler-x')
|
||||
->color('danger')
|
||||
->visible(fn (Plugin $plugin) => $plugin->canDisable())
|
||||
->action(function (Plugin $plugin, $livewire) {
|
||||
Plugins::disablePlugin($plugin);
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.updated'))
|
||||
->send();
|
||||
}),
|
||||
Action::make('disable')
|
||||
->label(trans('admin/plugin.disable'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('update', $plugin))
|
||||
->icon('tabler-x')
|
||||
->color('warning')
|
||||
->visible(fn (Plugin $plugin) => $plugin->canDisable())
|
||||
->action(function (Plugin $plugin, $livewire) {
|
||||
Plugins::disablePlugin($plugin);
|
||||
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.disabled'))
|
||||
->send();
|
||||
}),
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.disabled'))
|
||||
->send();
|
||||
}),
|
||||
Action::make('delete')
|
||||
->label(trans('filament-actions::delete.single.label'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('create', $plugin))
|
||||
->icon('tabler-trash')
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->hidden(fn (Plugin $plugin) => $plugin->isInstalled())
|
||||
->action(function (Plugin $plugin, $livewire) {
|
||||
File::deleteDirectory(plugin_path($plugin->id));
|
||||
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.deleted'))
|
||||
->send();
|
||||
}),
|
||||
Action::make('uninstall')
|
||||
->label(trans('admin/plugin.uninstall'))
|
||||
->authorize(fn (Plugin $plugin) => user()?->can('update', $plugin))
|
||||
->icon('tabler-terminal')
|
||||
->color('danger')
|
||||
->requiresConfirmation()
|
||||
->visible(fn (Plugin $plugin) => $plugin->isInstalled())
|
||||
->action(function (Plugin $plugin, $livewire) {
|
||||
Plugins::uninstallPlugin($plugin);
|
||||
|
||||
redirect(ListPlugins::getUrl(['tab' => $livewire->activeTab]));
|
||||
|
||||
Notification::make()
|
||||
->success()
|
||||
->title(trans('admin/plugin.notifications.uninstalled'))
|
||||
->send();
|
||||
}),
|
||||
]),
|
||||
])
|
||||
->headerActions([
|
||||
Action::make('import')
|
||||
|
||||
@ -166,6 +166,22 @@ class PluginService
|
||||
}
|
||||
}
|
||||
|
||||
public function removeComposerPackages(Plugin $plugin): void
|
||||
{
|
||||
if ($plugin->composer_packages) {
|
||||
$composerPackages = collect(json_decode($plugin->composer_packages, true, 512, JSON_THROW_ON_ERROR))
|
||||
->map(fn ($version, $package) => "$package:$version")
|
||||
->flatten()
|
||||
->unique()
|
||||
->toArray();
|
||||
|
||||
$result = Process::path(base_path())->timeout(600)->run(['composer', 'remove', ...$composerPackages]);
|
||||
if ($result->failed()) {
|
||||
throw new Exception('Could not remove composer packages: ' . $result->errorOutput());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function runPluginMigrations(Plugin $plugin): void
|
||||
{
|
||||
$migrations = plugin_path($plugin->id, 'database', 'migrations');
|
||||
@ -178,6 +194,18 @@ class PluginService
|
||||
}
|
||||
}
|
||||
|
||||
public function rollbackPluginMigrations(Plugin $plugin): void
|
||||
{
|
||||
$migrations = plugin_path($plugin->id, 'database', 'migrations');
|
||||
if (file_exists($migrations)) {
|
||||
$success = Artisan::call('migrate:rollback', ['--realpath' => true, '--path' => $migrations, '--force' => true]) === 0;
|
||||
|
||||
if (!$success) {
|
||||
throw new Exception("Could not rollback migrations for plugin '{$plugin->id}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function buildAssets(): bool
|
||||
{
|
||||
try {
|
||||
@ -237,6 +265,25 @@ class PluginService
|
||||
}
|
||||
}
|
||||
|
||||
public function uninstallPlugin(Plugin $plugin, bool $deleteFiles = false): void
|
||||
{
|
||||
try {
|
||||
$this->removeComposerPackages($plugin);
|
||||
|
||||
$this->rollbackPluginMigrations($plugin);
|
||||
|
||||
$this->buildAssets();
|
||||
|
||||
if ($deleteFiles) {
|
||||
File::deleteDirectory(plugin_path($plugin->id));
|
||||
} else {
|
||||
$this->setStatus($plugin, PluginStatus::NotInstalled);
|
||||
}
|
||||
} catch (Exception $exception) {
|
||||
$this->handlePluginException($plugin, $exception);
|
||||
}
|
||||
}
|
||||
|
||||
public function downloadPluginFromFile(UploadedFile $file, bool $cleanDownload = false): void
|
||||
{
|
||||
// Validate file size to prevent zip bombs
|
||||
|
||||
@ -14,6 +14,7 @@ return [
|
||||
'visit_website' => 'Visit Website',
|
||||
'settings' => 'Settings',
|
||||
'install' => 'Install',
|
||||
'uninstall' => 'Uninstall',
|
||||
'update' => 'Update',
|
||||
'enable' => 'Enable',
|
||||
'disable' => 'Disable',
|
||||
@ -46,6 +47,8 @@ return [
|
||||
|
||||
'notifications' => [
|
||||
'installed' => 'Plugin installed',
|
||||
'uninstalled' => 'Plugin uninstalled',
|
||||
'deleted' => 'Plugin deleted',
|
||||
'updated' => 'Plugin updated',
|
||||
'enabled' => 'Plugin enabled',
|
||||
'disabled' => 'Plugin disabled',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user