mirror of
https://github.com/lkddi/nexusphp.git
synced 2026-04-24 03:57:22 +08:00
improve plugin store
This commit is contained in:
@@ -14,12 +14,10 @@ use Illuminate\Console\Command;
|
||||
use NexusPlugin\Menu\Filament\MenuItemResource\Pages\ManageMenuItems;
|
||||
use NexusPlugin\Menu\MenuRepository;
|
||||
use NexusPlugin\Menu\Models\MenuItem;
|
||||
use NexusPlugin\Permission\Models\Permission;
|
||||
use NexusPlugin\Permission\Models\Role;
|
||||
use NexusPlugin\PostLike\PostLikeRepository;
|
||||
use NexusPlugin\StickyPromotion\Models\StickyPromotion;
|
||||
use NexusPlugin\StickyPromotion\Models\StickyPromotionParticipator;
|
||||
use NexusPlugin\Tracker\TrackerRepository;
|
||||
use NexusPlugin\Work\Models\RoleWork;
|
||||
use NexusPlugin\Work\WorkRepository;
|
||||
use Stichoza\GoogleTranslate\GoogleTranslate;
|
||||
@@ -57,9 +55,9 @@ class Test extends Command
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
$a = [1,2,3];
|
||||
$b = array_slice($a, 0, 2);
|
||||
dd($a, $b);
|
||||
$rep = new MenuRepository();
|
||||
$result = \Nexus\Plugin\Plugin::listEnabled();
|
||||
dd($result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
trait ExecuteCommandTrait
|
||||
{
|
||||
protected function executeCommand($command)
|
||||
{
|
||||
$this->info("Running $command ...");
|
||||
$result = exec($command, $output, $result_code);
|
||||
do_log(sprintf('command: %s, result_code: %s, output: %s, result: %s', $command, $result_code, json_encode($output), $result));
|
||||
if ($result_code != 0) {
|
||||
throw new \RuntimeException(json_encode($output));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -35,7 +35,7 @@ class PluginResource extends Resource
|
||||
return self::getNavigationLabel();
|
||||
}
|
||||
|
||||
protected static bool $shouldRegisterNavigation = true;
|
||||
protected static bool $shouldRegisterNavigation = false;
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
|
||||
@@ -18,6 +18,7 @@ use Filament\Infolists\Components;
|
||||
use Filament\Infolists;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\SoftDeletingScope;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use Filament\Actions\Action;
|
||||
use Livewire\Livewire;
|
||||
@@ -29,6 +30,13 @@ class PluginStoreResource extends Resource
|
||||
|
||||
protected static ?string $navigationGroup = 'System';
|
||||
|
||||
protected static ?int $navigationSort = 99;
|
||||
|
||||
public static function getNavigationBadge(): ?string
|
||||
{
|
||||
return PluginStore::getHasNewVersionCount();
|
||||
}
|
||||
|
||||
public static function form(Form $form): Form
|
||||
{
|
||||
return $form
|
||||
@@ -43,15 +51,26 @@ class PluginStoreResource extends Resource
|
||||
->columns([
|
||||
Tables\Columns\Layout\Stack::make([
|
||||
Tables\Columns\Layout\Stack::make([
|
||||
Tables\Columns\TextColumn::make('title')
|
||||
Tables\Columns\TextColumn::make(self::getColumnLabelKey("title"))
|
||||
->weight(FontWeight::Bold)
|
||||
,
|
||||
Tables\Columns\TextColumn::make('description'),
|
||||
Tables\Columns\TextColumn::make(self::getColumnLabelKey("description")),
|
||||
]),
|
||||
Tables\Columns\Layout\Stack::make([
|
||||
Tables\Columns\TextColumn::make('version')
|
||||
->formatStateUsing(fn (PluginStore $record) => sprintf("版本: %s | 更新时间: %s", $record->version, $record->release_date))
|
||||
->color('gray')
|
||||
->formatStateUsing(function (PluginStore $record) {
|
||||
$installedVersion = $record->installed_version;
|
||||
$latestVersion = $record->version;
|
||||
if ($installedVersion) {
|
||||
return sprintf('%s: %s', nexus_trans("plugin.labels.installed_version"), $installedVersion);
|
||||
}
|
||||
return sprintf(
|
||||
'%s: %s | %s: %s',
|
||||
nexus_trans("plugin.labels.latest_version"), $latestVersion,
|
||||
nexus_trans("plugin.labels.release_date"), $record->release_date
|
||||
);
|
||||
})
|
||||
->color(fn ($record) => $record->installed_version ? 'success' : 'gray')
|
||||
,
|
||||
])
|
||||
])->space(3),
|
||||
@@ -65,24 +84,29 @@ class PluginStoreResource extends Resource
|
||||
])
|
||||
->actions([
|
||||
Tables\Actions\ViewAction::make()
|
||||
->modalHeading("详细介绍")
|
||||
->modalHeading(nexus_trans("plugin.labels.introduce"))
|
||||
->modalContent(fn (PluginStore $record) => $record->getFullDescription())
|
||||
->extraModalFooterActions([
|
||||
Action::make("viewOnBlog")
|
||||
Action::make(nexus_trans("plugin.labels.view_on_blog"))
|
||||
->url(fn (PluginStore $record) => $record->getBlogPostUrl())
|
||||
->extraAttributes(['target' => '_blank'])
|
||||
,
|
||||
])
|
||||
,
|
||||
Tables\Actions\Action::make("install")
|
||||
->label("安装")
|
||||
->modalHeading(fn (PluginStore $record) => sprintf("安装插件: %s", $record->title))
|
||||
->label(function(PluginStore $record) {
|
||||
if ($record->hasNewVersion()) {
|
||||
return sprintf('%s(new: %s)', nexus_trans("plugin.actions.update"), $record->version);
|
||||
}
|
||||
return nexus_trans("plugin.actions.install");
|
||||
})
|
||||
->modalHeading(fn (PluginStore $record) => sprintf("%s: %s", nexus_trans("plugin.actions.install_or_update") ,$record->title))
|
||||
->modalContent(function (PluginStore $record) {
|
||||
$infolist = new Infolist();
|
||||
$infolist->record = $record;
|
||||
$infolist->schema([
|
||||
Infolists\Components\TextEntry::make('plugin_id')
|
||||
->label(fn () => sprintf("进入目录: %s, 以 root 用户的身份依次执行以下命令进行安装: ", base_path()))
|
||||
->label(fn () => nexus_trans("plugin.labels.install_title", ['web_root' => base_path()]))
|
||||
->html(true)
|
||||
->formatStateUsing(function (PluginStore $record) {
|
||||
return self::getPluginInstruction($record);
|
||||
@@ -92,6 +116,7 @@ class PluginStoreResource extends Resource
|
||||
return $infolist;
|
||||
})
|
||||
->modalFooterActions(fn () => [])
|
||||
->color(fn (PluginStore $record) => $record->hasNewVersion() ? 'danger' : 'primary')
|
||||
,
|
||||
])
|
||||
->recordAction(null)
|
||||
@@ -99,14 +124,23 @@ class PluginStoreResource extends Resource
|
||||
;
|
||||
}
|
||||
|
||||
private static function getColumnLabelKey($column): string
|
||||
{
|
||||
$locale = App::getLocale();
|
||||
if (in_array($locale, ['zh_CN', 'zh_TW'])) {
|
||||
return "$column.zh_CN";
|
||||
}
|
||||
return "$column.en";
|
||||
}
|
||||
|
||||
private static function getPluginInstruction(PluginStore $record): string
|
||||
{
|
||||
$result = [];
|
||||
$result[] = "配置扩展地址";
|
||||
$result[] = nexus_trans("plugin.labels.config_plugin_address");
|
||||
$result[] = sprintf("<code>composer config repositories.%s git %s</code>", $record->plugin_id, $record->remote_url);
|
||||
$result[] = "<br/>下载扩展. 这里展示的最新版本号, 如果需要安装其他版本(可在查看页面底部获得)自行替换";
|
||||
$result[] = "<br/>" . nexus_trans("plugin.labels.download_specific_version");
|
||||
$result[] = sprintf("<code>composer require %s:%s</code>", $record->package_name, $record->version);
|
||||
$result[] = "<br/>执行安装";
|
||||
$result[] = "<br/>" . nexus_trans("plugin.labels.execute_install");
|
||||
$result[] = sprintf("<code>php artisan plugin install %s</code>", $record->package_name);
|
||||
return implode("<br/>", $result);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ class SeedBoxRecordResource extends Resource
|
||||
|
||||
protected static ?string $navigationGroup = 'System';
|
||||
|
||||
protected static ?int $navigationSort = 98;
|
||||
protected static ?int $navigationSort = 8;
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ class SettingResource extends Resource
|
||||
|
||||
protected static ?string $navigationGroup = 'System';
|
||||
|
||||
protected static ?int $navigationSort = 100;
|
||||
protected static ?int $navigationSort = 1000;
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
|
||||
@@ -24,7 +24,7 @@ class TorrentStateResource extends Resource
|
||||
|
||||
protected static ?string $navigationGroup = 'System';
|
||||
|
||||
protected static ?int $navigationSort = 99;
|
||||
protected static ?int $navigationSort = 9;
|
||||
|
||||
public static function getNavigationLabel(): string
|
||||
{
|
||||
|
||||
@@ -5,20 +5,35 @@ namespace App\Models;
|
||||
use Illuminate\Contracts\Support\Htmlable;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\HtmlString;
|
||||
use Nexus\Database\NexusDB;
|
||||
use Sushi\Sushi;
|
||||
use Nexus\Plugin\Plugin;
|
||||
|
||||
class PluginStore extends Model
|
||||
{
|
||||
use Sushi;
|
||||
|
||||
protected $casts = [
|
||||
'title' => 'array',
|
||||
'description' => 'array',
|
||||
];
|
||||
|
||||
const PLUGIN_LIST_API = "https://nppl.nexusphp.workers.dev";
|
||||
const BLOG_POST_INFO_API = "https://nexusphp.org/wp-json/wp/v2/posts/%d";
|
||||
const BLOG_POST_URL = "https://nexusphp.org/?p=%d";
|
||||
|
||||
private static array|null $rows =null;
|
||||
|
||||
public function getRows()
|
||||
{
|
||||
return Http::get(self::PLUGIN_LIST_API)->json();
|
||||
$list = self::listAll(true);
|
||||
$enabled = Plugin::listEnabled();
|
||||
foreach ($list as &$row) {
|
||||
$row['installed_version'] = $enabled[$row['plugin_id']] ?? '';
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
public function getBlogPostUrl(): string
|
||||
@@ -30,7 +45,7 @@ class PluginStore extends Model
|
||||
{
|
||||
$url = $this->getBlogPostInfoUrl($this->post_id);
|
||||
$logPrefix = sprintf("post_id: %s, url: %s", $this->post_id, $url);
|
||||
$defaultContent = "无法获取详细信息 ...";
|
||||
$defaultContent = "Fail to get content ...";
|
||||
try {
|
||||
$result = Http::get($url)->json();
|
||||
do_log("$logPrefix, result: " . json_encode($result));
|
||||
@@ -50,8 +65,69 @@ class PluginStore extends Model
|
||||
return sprintf(self::BLOG_POST_INFO_API, $postId);
|
||||
}
|
||||
|
||||
public function hasNewVersion(): bool
|
||||
{
|
||||
return $this->installed_version
|
||||
&& version_compare($this->version, $this->installed_version, '>');
|
||||
}
|
||||
|
||||
public static function getInfo(string $id)
|
||||
{
|
||||
return Http::get(self::PLUGIN_LIST_API . "/plugin/$id")->json();
|
||||
}
|
||||
|
||||
public static function listAll($withoutCache = false)
|
||||
{
|
||||
$log = "listAll, withoutCache: $withoutCache";
|
||||
$cacheKey = "nexus_plugin_store_all";
|
||||
$cacheTime = 86400*100;
|
||||
if (is_null(self::$rows)) {
|
||||
$log .= ", is_null";
|
||||
if ($withoutCache) {
|
||||
$log .= ", WITHOUT_CACHE";
|
||||
self::$rows = self::listAllFromRemote();
|
||||
NexusDB::cache_put($cacheKey, self::$rows, $cacheTime);
|
||||
} else {
|
||||
$log .= ", WITH_CACHE";
|
||||
self::$rows = NexusDB::remember($cacheKey, $cacheTime, function () {
|
||||
return self::listAllFromRemote();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
$log .= ", not_null";
|
||||
}
|
||||
do_log($log, 'debug');
|
||||
return self::$rows;
|
||||
}
|
||||
|
||||
private static function listAllFromRemote()
|
||||
{
|
||||
$list = Http::get(self::PLUGIN_LIST_API)->json();
|
||||
foreach ($list as &$row) {
|
||||
foreach ($row as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$row[$key] = json_encode($value);
|
||||
}
|
||||
}
|
||||
}
|
||||
return $list;
|
||||
}
|
||||
|
||||
public static function getHasNewVersionCount(): int
|
||||
{
|
||||
$currentRouteName = Route::currentRouteName();
|
||||
$withoutCacheRouteName = ['filament.admin.resources.system.plugin-stores.index', 'filament.admin.pages.dashboard'];
|
||||
$list = self::listAll(in_array($currentRouteName, $withoutCacheRouteName));
|
||||
$enabled = Plugin::listEnabled();
|
||||
$count = 0;
|
||||
foreach ($list as $row) {
|
||||
$installedVersion = $enabled[$row['plugin_id']] ?? '';
|
||||
if ($installedVersion && version_compare($installedVersion, $row['version'], '<=')) {
|
||||
$count++;
|
||||
}
|
||||
}
|
||||
return $count;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
|
||||
namespace App\Policies;
|
||||
|
||||
use App\Models\PluginStore;
|
||||
use App\Models\User;
|
||||
use Illuminate\Auth\Access\Response;
|
||||
|
||||
class PluginStorePolicy extends BasePolicy
|
||||
{
|
||||
/**
|
||||
* Determine whether the user can view any models.
|
||||
*/
|
||||
public function viewAny(User $user): bool
|
||||
{
|
||||
return $this->can($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can view the model.
|
||||
*/
|
||||
public function view(User $user, PluginStore $pluginStore): bool
|
||||
{
|
||||
return $this->can($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can create models.
|
||||
*/
|
||||
public function create(User $user): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can update the model.
|
||||
*/
|
||||
public function update(User $user, PluginStore $pluginStore): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can delete the model.
|
||||
*/
|
||||
public function delete(User $user, PluginStore $pluginStore): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can restore the model.
|
||||
*/
|
||||
public function restore(User $user, PluginStore $pluginStore): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the user can permanently delete the model.
|
||||
*/
|
||||
public function forceDelete(User $user, PluginStore $pluginStore): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
private function can(User $user)
|
||||
{
|
||||
if ($user->class >= User::CLASS_SYSOP) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
namespace App\Support;
|
||||
|
||||
/**
|
||||
* Trait StaticMake
|
||||
*/
|
||||
trait StaticMake
|
||||
{
|
||||
public static function make(): static
|
||||
{
|
||||
return app(static::class);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user