Browse Source

extension

jqh 4 years ago
parent
commit
ea4018ffad

+ 6 - 5
config/admin.php

@@ -309,11 +309,12 @@ return [
 
     /*
     |--------------------------------------------------------------------------
-    | Extension Directory
+    | Extension
     |--------------------------------------------------------------------------
-    |
-    | When you use command `php artisan admin:extend` to generate extensions,
-    | the extension files will be generated in this directory.
     */
-    'extension_dir' => app_path('Admin/Extensions'),
+    'extension' => [
+        // When you use command `php artisan admin:extend` to generate extensions,
+        // the extension files will be generated in this directory.
+        'dir' => base_path('dcat-admin-extensions'),
+    ],
 ];

+ 1 - 1
database/migrations/2020_09_07_090635_create_admin_settings_table.php

@@ -20,7 +20,7 @@ class CreateAdminSettingsTable extends Migration
     {
         Schema::create('admin_settings', function (Blueprint $table) {
             $table->string('slug', 100)->primary();
-            $table->text('value');
+            $table->longText('value');
             $table->timestamps();
         });
     }

+ 12 - 3
src/Admin.php

@@ -11,7 +11,6 @@ use Dcat\Admin\Layout\Navbar;
 use Dcat\Admin\Layout\SectionManager;
 use Dcat\Admin\Repositories\EloquentRepository;
 use Dcat\Admin\Support\Composer;
-use Dcat\Admin\Support\Helper;
 use Dcat\Admin\Traits\HasAssets;
 use Dcat\Admin\Traits\HasPermissions;
 use Illuminate\Auth\GuardHelpers;
@@ -40,12 +39,12 @@ class Admin
     /**
      * @var string
      */
-    protected static $metaTitle;
+    public static $metaTitle;
 
     /**
      * @var string
      */
-    protected static $favicon;
+    public static $favicon;
 
     /**
      * @var array
@@ -175,6 +174,16 @@ class Admin
         return $manager;
     }
 
+    /**
+     * 配置.
+     *
+     * @return \Dcat\Admin\Support\Setting
+     */
+    public static function setting()
+    {
+        return app('admin.setting');
+    }
+
     /**
      * 注册路由.
      *

+ 4 - 0
src/AdminServiceProvider.php

@@ -8,6 +8,7 @@ use Dcat\Admin\Layout\Menu;
 use Dcat\Admin\Layout\Navbar;
 use Dcat\Admin\Layout\SectionManager;
 use Dcat\Admin\Extend\Manager;
+use Dcat\Admin\Support\Setting;
 use Dcat\Admin\Support\WebUploader;
 use Illuminate\Support\Arr;
 use Illuminate\Support\Facades\Blade;
@@ -210,6 +211,9 @@ class AdminServiceProvider extends ServiceProvider
         $this->app->singleton('admin.navbar', Navbar::class);
         $this->app->singleton('admin.menu', Menu::class);
         $this->app->singleton('admin.context', Fluent::class);
+        $this->app->singleton('admin.setting', function () {
+            return Setting::from();
+        });
         $this->app->singleton('admin.web-uploader', WebUploader::class);
     }
 

+ 98 - 1
src/Extend/Manager.php

@@ -2,8 +2,29 @@
 
 namespace Dcat\Admin\Extend;
 
+use Dcat\Admin\Admin;
+use Dcat\Admin\Support\Composer;
+use Illuminate\Support\Collection;
+use RecursiveDirectoryIterator;
+use RecursiveIteratorIterator;
+
 class Manager
 {
+    /**
+     * @var ServiceProvider[]|Collection
+     */
+    protected $extensions;
+
+    /**
+     * @var array
+     */
+    protected $extensionPaths = [];
+
+    public function __construct()
+    {
+        $this->extensions = new Collection();
+    }
+
     /**
      * 加载扩展,注册自动加载规则.
      *
@@ -11,6 +32,49 @@ class Manager
      */
     public function load()
     {
+        foreach ($this->getExtensionDirectories() as $directory) {
+            $this->loadExtension($directory);
+        }
+    }
+
+    /**
+     * 加载扩展.
+     *
+     * @param string $directory
+     */
+    public function loadExtension(string $directory)
+    {
+        if (array_key_exists($directory, $this->extensionPaths)) {
+            return $this->extensionPaths[$directory];
+        }
+        $this->extensionPaths[$directory] = null;
+
+        $composerProperty = Composer::parse($directory.'/composer.json');
+
+        $serviceProvider = $composerProperty->get('dcat-admin.provider');
+        $psr4 = $composerProperty->get('autoload.psr-4');
+
+        if (! $serviceProvider || ! $psr4) {
+            return;
+        }
+
+        if (! class_exists($serviceProvider)) {
+            $classLoader = Admin::classLoader();
+
+            foreach ($psr4 as $namespace => $path) {
+                //dd($namespace, $directory.'/'.trim($path, '/').'/');
+                $classLoader->addPsr4($namespace, $directory.'/'.trim($path, '/').'/');
+            }
+        }
+
+        /* @var ServiceProvider $serviceProvider */
+        $serviceProvider = new $serviceProvider();
+
+        $this->extensions->put($serviceProvider->name(), $serviceProvider);
+
+        $this->extensionPaths[$directory] = $serviceProvider;
+
+        return $serviceProvider;
     }
 
     /**
@@ -20,12 +84,45 @@ class Manager
      */
     public function register()
     {
+        foreach ($this->extensions as $extension) {
+            $extension->register();
+        }
+    }
+
+    public function boot()
+    {
+        foreach ($this->extensions as $extension) {
+            $extension->boot();
+        }
     }
 
     /**
      * @return array
      */
-    public function getPluginNamespaces()
+    public function getExtensionDirectories()
     {
+        $extensions = [];
+
+        $dirPath = admin_extension_path();
+
+        if (! is_dir($dirPath)) {
+            return $extensions;
+        }
+
+        $it = new RecursiveIteratorIterator(
+            new RecursiveDirectoryIterator($dirPath, RecursiveDirectoryIterator::FOLLOW_SYMLINKS)
+        );
+        $it->setMaxDepth(2);
+        $it->rewind();
+
+        while ($it->valid()) {
+            if ($it->getDepth() > 1 && $it->isFile() && $it->getFilename() === 'composer.json') {
+                $extensions[] = dirname($it->getPathname());
+            }
+
+            $it->next();
+        }
+
+        return $extensions;
     }
 }

+ 191 - 134
src/Extend/ServiceProvider.php

@@ -1,12 +1,12 @@
 <?php
 
-namespace Dcat\Admin\Plugin;
+namespace Dcat\Admin\Extend;
 
 use Dcat\Admin\Admin;
-use Illuminate\Console\Command;
 use Illuminate\Support\Arr;
 use Illuminate\Support\Facades\Validator;
 use Illuminate\Support\ServiceProvider as LaravelServiceProvider;
+use Symfony\Component\Console\Output\NullOutput;
 
 abstract class ServiceProvider extends LaravelServiceProvider
 {
@@ -15,22 +15,7 @@ abstract class ServiceProvider extends LaravelServiceProvider
     /**
      * @var string
      */
-    protected $assets = '';
-
-    /**
-     * @var string
-     */
-    protected $views = '';
-
-    /**
-     * @var string
-     */
-    protected $lang = '';
-
-    /**
-     * @var string
-     */
-    protected $migrations = '';
+    protected $path;
 
     /**
      * @var array
@@ -43,8 +28,6 @@ abstract class ServiceProvider extends LaravelServiceProvider
     protected $permission = [];
 
     /**
-     * The menu validation rules.
-     *
      * @var array
      */
     protected $menuValidationRules = [
@@ -54,8 +37,6 @@ abstract class ServiceProvider extends LaravelServiceProvider
     ];
 
     /**
-     * The permission validation rules.
-     *
      * @var array
      */
     protected $permissionValidationRules = [
@@ -65,149 +46,259 @@ abstract class ServiceProvider extends LaravelServiceProvider
     ];
 
     /**
+     * @var \Symfony\Component\Console\Output\OutputInterface
+     */
+    public $output;
+
+    public function __construct($app)
+    {
+        parent::__construct($app);
+
+        $this->output = new NullOutput();
+    }
+
+    /**
+     * {@inheritdoc}
+     */
+    public function boot()
+    {
+        if ($views = $this->views()) {
+            $this->loadViewsFrom($views, static::NAME);
+        }
+
+        if ($lang = $this->lang()) {
+            $this->loadTranslationsFrom($lang, static::NAME);
+        }
+
+        if ($migrations = $this->migrations()) {
+            $this->loadMigrationsFrom($migrations);
+        }
+
+        if ($routes = $this->routes()) {
+            return $this->registerRoutes($routes);
+        }
+    }
+
+    /**
+     * 获取扩展包名称.
+     *
      * @return string
      */
-    final public function getName()
+    final public function name()
     {
         return static::NAME;
     }
 
     /**
-     * Get the path of assets files.
+     * 获取扩展包路径.
+     *
+     * @param string $path
      *
      * @return string
+     *
+     * @throws \ReflectionException
      */
-    public function assets()
+    public function path(?string $path = null)
+    {
+        if (! $this->path) {
+            $this->path = realpath(dirname((new \ReflectionClass(static::class))->getFileName()).'/..');
+
+            if (! is_dir($this->path)) {
+                throw new \Exception("The {$this->path} is not a directory.");
+            }
+        }
+
+        $path = ltrim($path, '/');
+
+        return $path ? $this->path.'/'.$path : $this->path;
+    }
+
+    /**
+     * 判断扩展是否启用.
+     *
+     * @return bool
+     */
+    final public function enabled()
     {
-        return $this->assets;
     }
 
     /**
-     * Get the path of view files.
+     * 判断扩展是否禁用.
      *
-     * @return string
+     * @return bool
      */
-    public function views()
+    final public function disable()
     {
-        return $this->views;
     }
 
     /**
-     * Get the path of migration files.
+     * 获取配置.
      *
-     * @return string
+     * @param string $key
+     * @param null   $default
+     *
+     * @return \Illuminate\Config\Repository|mixed
      */
-    public function migrations()
+    final public function config($key = null, $default = null)
     {
-        return $this->migrations;
     }
 
     /**
-     * @return array
+     * 导入扩展.
      */
-    public function menu()
+    public function import()
     {
-        return $this->menu;
+        $this->importMenus();
+        $this->importPermissions();
     }
 
     /**
-     * @return array
+     * 卸载扩展.
      */
-    public function permission()
+    public function uninstall()
     {
-        return $this->permission;
     }
 
     /**
+     * 注册路由.
+     *
+     * @param $callback
+     */
+    public function registerRoutes($callback)
+    {
+        Admin::app()->routes(function ($router) use ($callback) {
+            $attributes = array_merge(
+                [
+                    'prefix'     => config('admin.route.prefix'),
+                    'middleware' => config('admin.route.middleware'),
+                ]
+            );
+
+            $router->group($attributes, $callback);
+        });
+    }
+
+    /**
+     * 获取静态资源路径.
+     *
      * @return string
      */
-    public function lang()
+    public function assets()
     {
-        return $this->lang;
+        return $this->path('resources/assets');
     }
 
     /**
-     * Whether the extension is enabled.
+     * 获取视图路径.
      *
-     * @return bool
+     * @return string
      */
-    final public static function enabled()
+    public function views()
     {
-        return config('admin-extensions.'.static::NAME.'.enable') ? true : false;
+        return $this->path('resources/views');
     }
 
     /**
-     * Whether the extension is disabled.
+     * 获取数据迁移文件路径.
      *
-     * @return bool
+     * @return string
      */
-    final public static function disable()
+    public function migrations()
     {
-        return ! static::enabled();
+        return $this->path('database/migrations');
     }
 
     /**
-     * Get config set in config/admin.php.
+     * 获取语言包路径.
      *
-     * @param string $key
-     * @param null   $default
+     * @return string
+     */
+    public function lang()
+    {
+        return $this->path('resources/lang');
+    }
+
+    /**
+     * 获取路由地址.
      *
-     * @return \Illuminate\Config\Repository|mixed
+     * @return string
+     *
+     * @throws \ReflectionException
      */
-    final public function config($key = null, $default = null)
+    public function routes()
     {
-        if (is_null($key)) {
-            $key = sprintf('admin.extensions.%s', static::NAME);
-        } else {
-            $key = sprintf('admin.extensions.%s.%s', static::NAME, $key);
-        }
+        $path = $this->path('src/Http/routes.php');
 
-        return config($key, $default);
+        return is_file($path) ? $path : null;
     }
 
     /**
-     * Import menu item and permission to dcat-admin.
+     * 获取菜单.
+     *
+     * @return array
      */
-    public function import(Command $command)
+    public function menu()
     {
-        if ($menu = $this->menu()) {
-            if ($this->validateMenu($menu)) {
-                extract($menu);
-
-                if ($this->checkMenuExist($path)) {
-                    $command->warn("Menu [$path] already exists!");
-                } else {
-                    $this->createMenu($title, $path, $icon);
-                    $command->info('Import extension menu succeeded!');
-                }
-            }
+        return $this->menu;
+    }
+
+    /**
+     * @return array
+     */
+    public function permission()
+    {
+        return $this->permission;
+    }
+
+
+    /**
+     * 导入菜单.
+     *
+     * @throws \Exception
+     */
+    protected function importMenus()
+    {
+        if (! ($menu = $this->menu()) || ! $this->validateMenu($menu)) {
+            return;
         }
 
-        if ($permission = $this->permission()) {
-            if ($this->validatePermission($permission)) {
-                extract($permission);
+        extract($menu);
 
-                if ($this->checkPermissionExist($slug)) {
-                    $command->warn("Permission [$slug] already exists!");
-                } else {
-                    $this->createPermission($name, $slug, $path);
-                    $command->info('Import extension permission succeeded!');
-                }
-            }
+        if ($this->checkMenu($path)) {
+            $this->output->writeln("<warn>Menu [$path] already exists!</warn>");
+        } else {
+            $this->createMenu($title, $path, $icon);
+            $this->output->writeln('<info>Import extension menu succeeded!</info>');
         }
     }
 
     /**
-     * Uninstall the extension.
+     * 导入权限.
      *
-     * @param Command $command
+     * @throws \Exception
      */
-    public function uninstall(Command $command)
+    protected function importPermissions()
     {
+        if (! $this->config('admin.permission.enable')) {
+            return;
+        }
+
+        if (! ($permission = $this->permission()) || ! $this->validatePermission($permission)) {
+            return;
+        }
+
+        extract($permission);
+
+        if ($this->checkPermission($slug)) {
+            $this->output->writeln("<warn>Permission [$slug] already exists!</warn>");
+        } else {
+            $this->createPermission($name, $slug, $path);
+            $this->output->writeln('<info>Import extension permission succeeded!</info>');
+        }
     }
 
     /**
-     * Validate menu fields.
+     * 验证菜单.
      *
      * @param array $menu
      *
@@ -215,7 +306,7 @@ abstract class ServiceProvider extends LaravelServiceProvider
      *
      * @return bool
      */
-    public function validateMenu(array $menu)
+    protected function validateMenu(array $menu)
     {
         /** @var \Illuminate\Validation\Validator $validator */
         $validator = Validator::make($menu, $this->menuValidationRules);
@@ -226,7 +317,7 @@ abstract class ServiceProvider extends LaravelServiceProvider
 
         $message = "Invalid menu:\r\n".implode("\r\n", Arr::flatten($validator->errors()->messages()));
 
-        throw new \Exception($message);
+        $this->output->writeln("<error>{$message}</error>");
     }
 
     /**
@@ -234,22 +325,15 @@ abstract class ServiceProvider extends LaravelServiceProvider
      *
      * @return bool
      */
-    protected function checkMenuExist($path)
+    protected function checkMenu($path)
     {
         $menuModel = config('admin.database.menu_model');
 
-        /* @var \Illuminate\Database\Eloquent\Builder $query */
-        $query = $menuModel::query();
-
-        $result = $query->where('uri', $path)
-            ->get()
-            ->first();
-
-        return $result ? true : false;
+        return $result = $menuModel::where('uri', $path)->exists();
     }
 
     /**
-     * Validate permission fields.
+     * 验证权限.
      *
      * @param array $permission
      *
@@ -257,7 +341,7 @@ abstract class ServiceProvider extends LaravelServiceProvider
      *
      * @return bool
      */
-    public function validatePermission(array $permission)
+    protected function validatePermission(array $permission)
     {
         /** @var \Illuminate\Validation\Validator $validator */
         $validator = Validator::make($permission, $this->permissionValidationRules);
@@ -268,11 +352,11 @@ abstract class ServiceProvider extends LaravelServiceProvider
 
         $message = "Invalid permission:\r\n".implode("\r\n", Arr::flatten($validator->errors()->messages()));
 
-        throw new \Exception($message);
+        $this->output->writeln("<error>{$message}</error>");
     }
 
     /**
-     * Create a item in dcat-admin left side menu.
+     * 创建菜单.
      *
      * @param string $title
      * @param string $uri
@@ -299,22 +383,15 @@ abstract class ServiceProvider extends LaravelServiceProvider
      *
      * @return bool
      */
-    protected function checkPermissionExist($slug)
+    protected function checkPermission($slug)
     {
         $permissionModel = config('admin.database.permissions_model');
 
-        /* @var \Illuminate\Database\Eloquent\Builder $query */
-        $query = $permissionModel::query();
-
-        $result = $query->where('slug', $slug)
-            ->get()
-            ->first();
-
-        return $result ? true : false;
+        return $permissionModel::where('slug', $slug)->exists();
     }
 
     /**
-     * Create a permission for this extension.
+     * 创建权限.
      *
      * @param $name
      * @param $slug
@@ -327,27 +404,7 @@ abstract class ServiceProvider extends LaravelServiceProvider
         $permissionModel::create([
             'name'      => $name,
             'slug'      => $slug,
-            'http_path' => '/'.trim($path, '/'),
+            'http_path' => trim($path, '/'),
         ]);
     }
-
-    /**
-     * Set routes for this extension.
-     *
-     * @param $callback
-     */
-    public function routes($callback)
-    {
-        Admin::app()->routes(function ($router) use ($callback) {
-            $attributes = array_merge(
-                [
-                    'prefix'     => config('admin.route.prefix'),
-                    'middleware' => config('admin.route.middleware'),
-                ],
-                $this->config('route', [])
-            );
-
-            $router->group($attributes, $callback);
-        });
-    }
 }

+ 10 - 0
src/Models/Setting.php

@@ -0,0 +1,10 @@
+<?php
+
+namespace Dcat\Admin\Models;
+
+use Illuminate\Database\Eloquent\Model;
+
+class Setting extends Model
+{
+    protected $fillable = ['slug', 'value'];
+}

+ 3 - 3
src/Support/Composer.php

@@ -37,7 +37,7 @@ class Composer
      */
     public static function parse(?string $path)
     {
-        return new ComposerProperty(static::readJson($path));
+        return new ComposerProperty(static::fromJson($path));
     }
 
     /**
@@ -54,7 +54,7 @@ class Composer
 
         $lockFile = $lockFile ?: base_path('composer.lock');
 
-        $content = collect(static::readJson($lockFile)['packages'] ?? [])
+        $content = collect(static::fromJson($lockFile)['packages'] ?? [])
             ->filter(function ($value) use ($packageName) {
                 return $value['name'] == $packageName;
             })->first();
@@ -67,7 +67,7 @@ class Composer
      *
      * @return array
      */
-    public static function readJson(?string $path)
+    public static function fromJson(?string $path)
     {
         if (isset(static::$files[$path])) {
             return static::$files[$path];

+ 20 - 0
src/Support/Setting.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace Dcat\Admin\Support;
+
+use Dcat\Admin\Models\Setting as Model;
+use Illuminate\Support\Arr;
+use Illuminate\Support\Fluent;
+
+class Setting extends Fluent
+{
+    public static function from()
+    {
+        return new static(Model::pluck('value', 'slug')->toArray());
+    }
+
+    public function get($key, $default = null)
+    {
+        return Arr::get($this->attributes, $key, $default);
+    }
+}

+ 13 - 4
src/Support/helpers.php

@@ -390,15 +390,24 @@ if (! function_exists('admin_api_route')) {
      *
      * @return string
      */
-    function admin_api_route(string $path = '')
+    function admin_api_route(?string $path = '')
     {
         return Dcat\Admin\Admin::app()->getCurrentApiRoutePrefix().$path;
     }
 }
 
-if (! function_exists('admin_plugins_path')) {
-    function admin_plugins_path()
+if (! function_exists('admin_extension_path')) {
+    /**
+     * @param string|null $path
+     *
+     * @return string
+     */
+    function admin_extension_path(?string $path = null)
     {
-        return admin_path('/Plugins');
+        $dir = rtrim(config('admin.extension.dir'), '/') ?: base_path('dcat-admin-extensions');
+
+        $path = ltrim($path, '/');
+
+        return $path ? $dir.'/'.$path : $dir;
     }
 }