From 55414072e76d902b255723c6f7095deceeb270f0 Mon Sep 17 00:00:00 2001 From: printempw Date: Mon, 17 Oct 2016 12:20:55 +0800 Subject: [PATCH] working on framework of plugins --- app/Http/Controllers/AdminController.php | 42 ++++ app/Http/Routes/web.php | 1 + app/Providers/PluginServiceProvider.php | 37 ++++ app/Services/Plugin.php | 183 +++++++++++++++++ app/Services/PluginManager.php | 244 ++++++++++++++++++++++- config/app.php | 1 + config/menu.php | 17 +- config/options.php | 5 +- plugins/.gitignore | 2 + resources/lang/en/general.yml | 1 + resources/lang/zh-CN/general.yml | 1 + resources/views/admin/plugins.tpl | 125 ++++++++++++ 12 files changed, 647 insertions(+), 12 deletions(-) create mode 100644 app/Providers/PluginServiceProvider.php create mode 100644 app/Services/Plugin.php create mode 100644 plugins/.gitignore create mode 100644 resources/views/admin/plugins.tpl diff --git a/app/Http/Controllers/AdminController.php b/app/Http/Controllers/AdminController.php index 74a46d4d..d0ee6838 100644 --- a/app/Http/Controllers/AdminController.php +++ b/app/Http/Controllers/AdminController.php @@ -9,6 +9,7 @@ use App\Models\Player; use App\Models\Texture; use App\Models\UserModel; use Illuminate\Http\Request; +use App\Services\PluginManager; use App\Exceptions\PrettyPageException; class AdminController extends Controller @@ -63,6 +64,47 @@ class AdminController extends Controller } } + public function plugins(Request $request, PluginManager $plugins) + { + if ($request->has('action') && $request->has('id')) { + $id = $request->get('id'); + + if ($plugins->getPlugins()->has($id)) { + switch ($request->get('action')) { + case 'enable': + $plugins->enable($id); + break; + + case 'disable': + $plugins->disable($id); + break; + + case 'delete': + if ($request->isMethod('post')) { + $plugins->uninstall($id); + + return json('插件已被成功删除', 0); + } + break; + + default: + # code... + break; + } + } + + } + + $data = [ + 'installed' => $plugins->getPlugins(), + 'enabled' => $plugins->getEnabledPlugins() + ]; + + // dd($data); + + return view('admin.plugins', $data); + } + /** * Show Manage Page of Users. * diff --git a/app/Http/Routes/web.php b/app/Http/Routes/web.php index 06873002..c5f94659 100644 --- a/app/Http/Routes/web.php +++ b/app/Http/Routes/web.php @@ -99,6 +99,7 @@ Route::group(['middleware' => 'admin', 'prefix' => 'admin'], function() { Route::get('/', 'AdminController@index'); + Route::any('/plugins', 'AdminController@plugins'); Route::any('/customize', 'AdminController@customize'); Route::any('/score', 'AdminController@score'); Route::any('/options', 'AdminController@options'); diff --git a/app/Providers/PluginServiceProvider.php b/app/Providers/PluginServiceProvider.php new file mode 100644 index 00000000..3df1e26d --- /dev/null +++ b/app/Providers/PluginServiceProvider.php @@ -0,0 +1,37 @@ +app->bind('plugins', 'App\Services\PluginManager'); + + $bootstrappers = $this->app->make('plugins')->getEnabledBootstrappers(); + + foreach ($bootstrappers as $file) { + // bootstraper is a closure + $bootstrapper = require $file; + + $this->app->call($bootstrapper); + } + } +} diff --git a/app/Services/Plugin.php b/app/Services/Plugin.php new file mode 100644 index 00000000..5338da86 --- /dev/null +++ b/app/Services/Plugin.php @@ -0,0 +1,183 @@ +path = $path; + $this->packageInfo = $packageInfo; + } + + /** + * {@inheritdoc} + */ + public function __get($name) + { + return $this->packageInfoAttribute(Str::snake($name, '-')); + } + + /** + * {@inheritdoc} + */ + public function __isset($name) + { + return isset($this->{$name}) || $this->packageInfoAttribute(Str::snake($name, '-')); + } + + /** + * Dot notation getter for composer.json attributes. + * + * @see https://laravel.com/docs/5.1/helpers#arrays + * + * @param $name + * @return mixed + */ + public function packageInfoAttribute($name) + { + return Arr::get($this->packageInfo, $name); + } + + /** + * @param bool $installed + * @return Plugin + */ + public function setInstalled($installed) + { + $this->installed = $installed; + + return $this; + } + + /** + * @return bool + */ + public function isInstalled() + { + return $this->installed; + } + + /** + * @param string $version + * @return Plugin + */ + public function setVersion($version) + { + $this->version = $version; + + return $this; + } + + /** + * @return string + */ + public function getVersion() + { + return $this->version; + } + + /** + * @param bool $enabled + * @return Plugin + */ + public function setEnabled($enabled) + { + $this->enabled = $enabled; + + return $this; + } + + /** + * @return bool + */ + public function isEnabled() + { + return $this->enabled; + } + + /** + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Generates an array result for the object. + * + * @return array + */ + public function toArray() + { + return (array) array_merge([ + 'name' => $this->name, + 'version' => $this->getVersion(), + 'path' => $this->path + ], $this->packageInfo); + } +} diff --git a/app/Services/PluginManager.php b/app/Services/PluginManager.php index 78ab5a56..83e3aa12 100644 --- a/app/Services/PluginManager.php +++ b/app/Services/PluginManager.php @@ -2,10 +2,250 @@ namespace App\Services; +use Illuminate\Contracts\Foundation\Application; +use Illuminate\Contracts\Events\Dispatcher; +use Illuminate\Filesystem\Filesystem; +use Illuminate\Support\Arr; +use Illuminate\Support\Collection; +use App\Events\PluginWasDisabled; +use App\Events\PluginWasEnabled; +use App\Events\PluginWasUninstalled; + class PluginManager { - public function __construct() - { + protected $option; + protected $app; + + /** + * @var Dispatcher + */ + protected $dispatcher; + + /** + * @var Filesystem + */ + protected $filesystem; + + /** + * @var Collection|null + */ + protected $plugins; + + public function __construct( + OptionRepository $option, + Application $app, + Dispatcher $dispatcher, + Filesystem $filesystem + ) { + $this->option = $option; + $this->app = $app; + $this->dispatcher = $dispatcher; + $this->filesystem = $filesystem; } + + /** + * @return Collection + */ + public function getPlugins() + { + if (is_null($this->plugins)) { + $plugins = new Collection(); + + $installed = []; + + $resource = opendir(base_path('plugins')); + // traverse plugins dir + while($filename = @readdir($resource)) { + if ($filename == "." || $filename == "..") + continue; + + $path = base_path('plugins')."/".$filename; + + if (is_dir($path)) { + if (file_exists($path."/package.json")) { + // load packages installed + $installed[$filename] = json_decode($this->filesystem->get($path."/package.json"), true); + } + } + + } + closedir($resource); + + foreach ($installed as $path => $package) { + + // Instantiates an Plugin object using the package path and package.json file. + $plugin = new Plugin($this->getPluginsDir().'/'.$path, $package); + + // Per default all plugins are installed if they are registered in composer. + $plugin->setInstalled(true); + $plugin->setVersion(Arr::get($package, 'version')); + $plugin->setEnabled($this->isEnabled($plugin->name)); + + $plugins->put($plugin->name, $plugin); + } + + $this->plugins = $plugins->sortBy(function ($plugin, $name) { + return $plugin->name; + }); + } + + return $this->plugins; + } + + /** + * Loads an Plugin with all information. + * + * @param string $name + * @return Plugin|null + */ + public function getPlugin($name) + { + return $this->getPlugins()->get($name); + } + + /** + * Enables the plugin. + * + * @param string $name + */ + public function enable($name) + { + if (! $this->isEnabled($name)) { + $plugin = $this->getPlugin($name); + + $enabled = $this->getEnabled(); + + $enabled[] = $name; + + // $this->migrate($plugin); + + // $this->publishAssets($plugin); + + $this->setEnabled($enabled); + + $plugin->setEnabled(true); + + // $this->dispatcher->fire(new PluginWasEnabled($plugin)); + } + } + + /** + * Disables an plugin. + * + * @param string $name + */ + public function disable($name) + { + $enabled = $this->getEnabled(); + + if (($k = array_search($name, $enabled)) !== false) { + unset($enabled[$k]); + + $plugin = $this->getPlugin($name); + + $this->setEnabled($enabled); + + $plugin->setEnabled(false); + + // $this->dispatcher->fire(new PluginWasDisabled($plugin)); + } + } + + /** + * Uninstalls an plugin. + * + * @param string $name + */ + public function uninstall($name) + { + $plugin = $this->getPlugin($name); + + $this->disable($name); + + // $this->migrateDown($plugin); + + // $this->unpublishAssets($plugin); + + // $plugin->setInstalled(false); + + $this->filesystem->deleteDirectory($plugin->getPath()); + + // refresh plugin list + $this->plugins = null; + + // $this->dispatcher->fire(new PluginWasUninstalled($plugin)); + } + + /** + * Get only enabled plugins. + * + * @return Collection + */ + public function getEnabledPlugins() + { + return $this->getPlugins()->only($this->getEnabled()); + } + + /** + * Loads all bootstrap.php files of the enabled plugins. + * + * @return Collection + */ + public function getEnabledBootstrappers() + { + $bootstrappers = new Collection; + + foreach ($this->getEnabledPlugins() as $plugin) { + if ($this->filesystem->exists($file = $plugin->getPath().'/bootstrap.php')) { + $bootstrappers->push($file); + } + } + + return $bootstrappers; + } + + /** + * The id's of the enabled plugins. + * + * @return array + */ + public function getEnabled() + { + return json_decode($this->option->get('plugins_enabled'), true); + } + + /** + * Persist the currently enabled plugins. + * + * @param array $enabled + */ + protected function setEnabled(array $enabled) + { + $enabled = array_values(array_unique($enabled)); + + $this->option->set('plugins_enabled', json_encode($enabled)); + } + + /** + * Whether the plugin is enabled. + * + * @param $plugin + * @return bool + */ + public function isEnabled($plugin) + { + return in_array($plugin, $this->getEnabled()); + } + + /** + * The plugins path. + * + * @return string + */ + protected function getPluginsDir() + { + return $this->app->basePath().'/plugins'; + } + } diff --git a/config/app.php b/config/app.php index bb182a3e..7349cc5c 100644 --- a/config/app.php +++ b/config/app.php @@ -162,6 +162,7 @@ return [ */ App\Providers\BootServiceProvider::class, App\Providers\AppServiceProvider::class, + App\Providers\PluginServiceProvider::class, App\Providers\EventServiceProvider::class, App\Providers\RouteServiceProvider::class, App\Providers\ResponseMacroServiceProvider::class, diff --git a/config/menu.php b/config/menu.php index 31596a9f..80bc7d07 100644 --- a/config/menu.php +++ b/config/menu.php @@ -17,14 +17,15 @@ $menu['user'] = array( ); $menu['admin'] = array( - ['title' => 'general.dashboard', 'link' => 'admin', 'icon' => 'fa-dashboard'], - ['title' => 'general.user-manage', 'link' => 'admin/users', 'icon' => 'fa-users'], - ['title' => 'general.player-manage', 'link' => 'admin/players', 'icon' => 'fa-gamepad'], - ['title' => 'general.customize', 'link' => 'admin/customize', 'icon' => 'fa-paint-brush'], - ['title' => 'general.score-options', 'link' => 'admin/score', 'icon' => 'fa-credit-card'], - ['title' => 'general.options', 'link' => 'admin/options', 'icon' => 'fa-cog'], - ['title' => 'general.import-v2', 'link' => 'setup/migrations', 'icon' => 'fa-undo'], - ['title' => 'general.check-update', 'link' => 'admin/update', 'icon' => 'fa-arrow-up'] + ['title' => 'general.dashboard', 'link' => 'admin', 'icon' => 'fa-dashboard'], + ['title' => 'general.user-manage', 'link' => 'admin/users', 'icon' => 'fa-users'], + ['title' => 'general.player-manage', 'link' => 'admin/players', 'icon' => 'fa-gamepad'], + ['title' => 'general.plugin-manage', 'link' => 'admin/plugins', 'icon' => 'fa-plug'], + ['title' => 'general.customize', 'link' => 'admin/customize', 'icon' => 'fa-paint-brush'], + ['title' => 'general.score-options', 'link' => 'admin/score', 'icon' => 'fa-credit-card'], + ['title' => 'general.options', 'link' => 'admin/options', 'icon' => 'fa-cog'], + ['title' => 'general.import-v2', 'link' => 'setup/migrations', 'icon' => 'fa-undo'], + ['title' => 'general.check-update', 'link' => 'admin/update', 'icon' => 'fa-arrow-up'] ); return $menu; diff --git a/config/options.php b/config/options.php index ae887d79..b76aac39 100644 --- a/config/options.php +++ b/config/options.php @@ -3,7 +3,7 @@ * @Author: printempw * @Date: 2016-07-29 11:53:11 * @Last Modified by: printempw - * @Last Modified time: 2016-10-17 09:40:16 + * @Last Modified time: 2016-10-17 11:11:52 */ return [ @@ -37,5 +37,6 @@ return [ 'return_200_when_notfound' => '0', 'cache_expire_time' => '31536000', 'max_upload_file_size' => '1024', - 'auto_detect_asset_url' => '1' + 'auto_detect_asset_url' => '1', + 'plugins_enabled' => '' ]; diff --git a/plugins/.gitignore b/plugins/.gitignore new file mode 100644 index 00000000..d6b7ef32 --- /dev/null +++ b/plugins/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/resources/lang/en/general.yml b/resources/lang/en/general.yml index ba38fb75..57fb2676 100644 --- a/resources/lang/en/general.yml +++ b/resources/lang/en/general.yml @@ -16,6 +16,7 @@ my-closet: Closet player-manage: Player Manage user-manage: User Manage generate-config: Generate Config +plugin-manage: Plugin Manage customize: Customize options: Options import-v2: Import Data diff --git a/resources/lang/zh-CN/general.yml b/resources/lang/zh-CN/general.yml index 2d6267fd..01172745 100644 --- a/resources/lang/zh-CN/general.yml +++ b/resources/lang/zh-CN/general.yml @@ -16,6 +16,7 @@ my-closet: 我的衣柜 player-manage: 角色管理 user-manage: 用户管理 generate-config: 配置生成 +plugin-manage: 插件管理 customize: 个性化 options: 站点配置 import-v2: 导入数据 diff --git a/resources/views/admin/plugins.tpl b/resources/views/admin/plugins.tpl new file mode 100644 index 00000000..0302f9c0 --- /dev/null +++ b/resources/views/admin/plugins.tpl @@ -0,0 +1,125 @@ +@extends('admin.master') + +@section('title', trans('general.plugin-manage')) + +@section('style') + +@endsection + +@section('content') + + +
+ +
+

+ {{ trans('general.plugin-manage') }} + Plugin Manage +

+
+ + +
+
+
+ + + + + + + + + + + + + + @forelse($installed as $plugin) + + + + + + + + + + @empty + + + + + + @endforelse + +
名称描述作者版本状态操作
{{ $plugin->title }}{{ $plugin->description }}{{ $plugin->author }}{{ $plugin->version }} + @if ($plugin->isEnabled()) + 已启用 + @else + 已禁用 + @endif + + @if ($plugin->isEnabled()) + 禁用插件 + @else + 启用插件 + @endif + + 插件配置 + 删除插件 + +
0无结果(´・ω・`)
+
+
+ +
+
+ +@endsection + +@section('style') + +@endsection + +@section('script') + +@endsection