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