Bladeren bron

:construction: extension command

jqh 4 jaren geleden
bovenliggende
commit
a8fd9a583f

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

@@ -29,7 +29,7 @@ class CreateAdminExtensionsTable extends Migration
             $table->string('name', 100);
             $table->tinyInteger('type')->default(1);
             $table->string('version', 20)->default(0);
-            $table->text('description')->nullable();
+            $table->text('detail')->nullable();
 
             $table->index('name');
             $table->timestamps();

+ 8 - 1
src/AdminServiceProvider.php

@@ -2,6 +2,7 @@
 
 namespace Dcat\Admin;
 
+use Dcat\Admin\Console\ExtensionInstallCommand;
 use Dcat\Admin\Extend\UpdateManager;
 use Dcat\Admin\Extend\VersionManager;
 use Dcat\Admin\Layout\Asset;
@@ -27,7 +28,6 @@ class AdminServiceProvider extends ServiceProvider
         Console\InstallCommand::class,
         Console\PublishCommand::class,
         Console\UninstallCommand::class,
-        Console\ImportCommand::class,
         Console\CreateUserCommand::class,
         Console\ResetPasswordCommand::class,
         Console\ExtendCommand::class,
@@ -38,6 +38,13 @@ class AdminServiceProvider extends ServiceProvider
         Console\MenuCacheCommand::class,
         Console\MinifyCommand::class,
         Console\AppCommand::class,
+        Console\ExtensionInstallCommand::class,
+        Console\ExtensionUninstallCommand::class,
+        Console\ExtensionRefreshCommand::class,
+        Console\ExtensionRollbackCommand::class,
+        Console\ExtensionEnableCommand::class,
+        Console\ExtensionDiableCommand::class,
+        Console\ExtensionUpdateCommand::class,
     ];
 
     /**

+ 17 - 0
src/Console/ExtensionDiableCommand.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace Dcat\Admin\Console;
+
+use Illuminate\Console\Command;
+
+class ExtensionDiableCommand extends Command
+{
+    protected $signature = 'admin:extension-disable {name : The name of the extension. Eg: author-name/extension-name} ';
+
+    protected $description = 'Disable an existing extension.';
+
+    public function handle()
+    {
+        $name = $this->argument('name');
+    }
+}

+ 21 - 0
src/Console/ExtensionEnableCommand.php

@@ -0,0 +1,21 @@
+<?php
+
+namespace Dcat\Admin\Console;
+
+use Illuminate\Console\Command;
+
+class ExtensionEnableCommand extends Command
+{
+    protected $signature = 'admin:extension-enable 
+    {name : The name of the extension. Eg: author-name/extension-name}';
+
+    protected $description = 'Enable an existing extension.';
+
+    public function handle()
+    {
+        $name = $this->argument('name');
+
+
+
+    }
+}

+ 51 - 0
src/Console/ExtensionInstallCommand.php

@@ -0,0 +1,51 @@
+<?php
+
+namespace Dcat\Admin\Console;
+
+use Dcat\Admin\Admin;
+use Illuminate\Console\Command;
+use Illuminate\Support\Arr;
+
+class ExtensionInstallCommand extends Command
+{
+    protected $signature = 'admin:extension-install 
+    {name : The name of the extension. Eg: author-name/extension-name} 
+    {--path= : The path of the extension.}';
+
+    protected $description = 'Install an extension.';
+
+    public function handle()
+    {
+        $name = $this->argument('name');
+        $path = $this->option('path');
+
+        $manager = Admin::extension()->setOutput($this->output);
+
+        if ($path) {
+            if (! is_file($path)) {
+                $path = rtrim($path, '/').sprintf('/%s.zip', str_replace('/', '.', $name));
+            }
+        } else {
+            $extensionDetails = $manager->requestDetails($name);
+
+            $path = $hash = Arr::get($extensionDetails, 'hash');
+
+            $this->output->writeln(sprintf('<info>Downloading extension: %s[%s]</info>', $name, $hash));
+
+            $manager->download($name, $hash, true);
+        }
+
+        $this->output->writeln(sprintf('<info>Unpacking extension: %s</info>', $name));
+
+        $manager->extract($path);
+
+        $this->output->writeln(sprintf('<info>Migrating extension...</info>', $name));
+
+        Admin::extension()->load();
+
+        $manager
+            ->updateManager()
+            ->setOutPut($this->output)
+            ->update($name);
+    }
+}

+ 20 - 0
src/Console/ExtensionRefreshCommand.php

@@ -0,0 +1,20 @@
+<?php
+
+namespace Dcat\Admin\Console;
+
+use Illuminate\Console\Command;
+
+class ExtensionRefreshCommand extends Command
+{
+    protected $signature = 'admin:extension-refresh 
+    {name : The name of the extension. Eg: author-name/extension-name} 
+    {--path= : The path of the extension.}';
+
+    protected $description = 'Removes and re-adds an existing extension.';
+
+    public function handle()
+    {
+        $name = $this->argument('name');
+        $path = $this->argument('path');
+    }
+}

+ 19 - 0
src/Console/ExtensionRollbackCommand.php

@@ -0,0 +1,19 @@
+<?php
+
+namespace Dcat\Admin\Console;
+
+use Illuminate\Console\Command;
+
+class ExtensionRollbackCommand extends Command
+{
+    protected $signature = 'admin:extension-rollback 
+     {name : The name of the extension. Eg: author-name/extension-name} 
+     {--ver= : If this parameter is specified, the process will stop on the specified version, if not, it will completely rollback the extension. Example: 1.3.9} 
+     {--force : Force rollback}';
+
+    protected $description = 'Rollback an existing extension.';
+
+    public function handle()
+    {
+    }
+}

+ 17 - 0
src/Console/ExtensionUninstallCommand.php

@@ -0,0 +1,17 @@
+<?php
+
+namespace Dcat\Admin\Console;
+
+use Illuminate\Console\Command;
+
+class ExtensionUninstallCommand extends Command
+{
+    protected $signature = 'admin:extension-uninstall 
+    {name : The name of the extension. Eg: author-name/extension-name}';
+
+    protected $description = 'Uninstall an existing extension.';
+
+    public function handle()
+    {
+    }
+}

+ 30 - 0
src/Console/ExtensionUpdateCommand.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace Dcat\Admin\Console;
+
+use Dcat\Admin\Admin;
+use Illuminate\Console\Command;
+
+class ExtensionUpdateCommand extends Command
+{
+    protected $signature = 'admin:extension-update 
+    {name : The name of the extension. Eg: author-name/extension-name}
+    {--ver= : If this parameter is specified, the process will stop on the specified version, if not, it will update to the latest version. Example: 1.3.9}';
+
+    protected $description = 'Update an existing extension.';
+
+    public function handle()
+    {
+        $name = $this->argument('name');
+        $version = $this->option('ver');
+
+        Admin::extension()->load();
+
+        Admin::extension()
+            ->updateManager()
+            ->setOutPut($this->output)
+            ->update($name, $version);
+    }
+
+
+}

+ 0 - 106
src/Console/ImportCommand.php

@@ -1,106 +0,0 @@
-<?php
-
-namespace Dcat\Admin\Console;
-
-use Dcat\Admin\Admin;
-use Dcat\Admin\Extension;
-use Dcat\Admin\Support\Helper;
-use Illuminate\Foundation\Console\VendorPublishCommand;
-use Illuminate\Support\Arr;
-
-class ImportCommand extends VendorPublishCommand
-{
-    /**
-     * The console command name.
-     *
-     * @var string
-     */
-    protected $signature = 'admin:import {extension?} {--force : Overwrite any existing files}';
-
-    /**
-     * The console command description.
-     *
-     * @var string
-     */
-    protected $description = 'Import a dcat-admin extension';
-
-    /**
-     * Execute the console command.
-     *
-     * @return void
-     */
-    public function handle()
-    {
-        $extension = $this->argument('extension');
-
-        $extensions = Admin::extension();
-
-        if (empty($extension) || ! Arr::has($extensions, $extension)) {
-            $extension = $this->choice('Please choose a extension to import', array_keys($extensions));
-        }
-
-        $className = Arr::get($extensions, $extension);
-
-        if (! class_exists($className) || ! is_subclass_of($className, Extension::class) || ! $className::make()->getName()) {
-            $this->error("Invalid Extension [$className]");
-
-            return;
-        }
-        /* @var Extension $extension */
-        $extension = $className::make();
-
-        $this->setServiceProvider($extension);
-        $this->publish($extension);
-
-        $extension->import($this);
-
-        $this->publishTag(null);
-        $this->call('view:clear');
-        $this->call('admin:ide-helper');
-
-        $this->updateExtensionConfig($className);
-
-        $this->info("Extension [$className] imported");
-    }
-
-    protected function publish(Extension $extension)
-    {
-        if (
-            ($assets = $extension->assets())
-            && file_exists($assets)
-        ) {
-            $this->publishItem(
-                $assets,
-                public_path(
-                    Admin::asset()->getRealPath('@extension').'/'.$extension::NAME
-                )
-            );
-        }
-    }
-
-    protected function setServiceProvider(Extension $extension)
-    {
-        $this->provider = $extension->serviceProvider();
-
-        $this->laravel->register($this->provider);
-    }
-
-    /**
-     * @param $class
-     *
-     * @return bool
-     */
-    protected function updateExtensionConfig($class)
-    {
-        $config = (array) config('admin-extensions');
-
-        $name = $class::NAME;
-
-        $config[$name] = (array) ($config[$name] ?? []);
-
-        $config[$name]['imported'] = true;
-        $config[$name]['imported_at'] = date('Y-m-d H:i:s');
-
-        return Helper::updateExtensionConfig($config);
-    }
-}

+ 58 - 3
src/Extend/Manager.php

@@ -3,9 +3,12 @@
 namespace Dcat\Admin\Extend;
 
 use Dcat\Admin\Admin;
+use Dcat\Admin\Exception\AdminException;
 use Dcat\Admin\Models\Extension as ExtensionModel;
 use Dcat\Admin\Support\Composer;
+use Dcat\Admin\Support\Zip;
 use Illuminate\Contracts\Container\Container;
+use Illuminate\Filesystem\Filesystem;
 use Illuminate\Support\Collection;
 use RecursiveDirectoryIterator;
 use RecursiveIteratorIterator;
@@ -34,11 +37,22 @@ class Manager
      */
     protected $settings;
 
+    /**
+     * @var string
+     */
+    protected $tempDirectory;
+
     public function __construct(Container $app)
     {
         $this->app = $app;
 
         $this->extensions = new Collection();
+
+        $this->tempDirectory = storage_path('extensions');
+
+        if (! is_dir($this->tempDirectory)) {
+            app('files')->makeDirectory($this->tempDirectory, 0777, true);
+        }
     }
 
     /**
@@ -82,7 +96,7 @@ class Manager
      *
      * @return void
      */
-    protected function load()
+    public function load()
     {
         foreach ($this->getExtensionDirectories() as $directory) {
             $this->loadExtension($directory);
@@ -121,7 +135,21 @@ class Manager
             return $name;
         }
 
-        return $this->extensions->get($name);
+        return $this->extensions->get($this->formatName($name));
+    }
+
+    /**
+     * @param string $name
+     *
+     * @return mixed
+     */
+    protected function formatName($name)
+    {
+        if (! is_string($name)) {
+            return $name;
+        }
+
+        return str_replace('/', '.', $name);
     }
 
     /**
@@ -250,7 +278,34 @@ class Manager
             return $extension->getName();
         }
 
-        return $extension;
+        return $this->formatName($extension);
+    }
+
+    /**
+     * 解压缩扩展包.
+     */
+    public function extract($filePath)
+    {
+        $filePath = is_file($filePath) ? $filePath : $this->getFilePath($filePath);
+
+        if (! Zip::extract($filePath, admin_extension_path())) {
+            throw new AdminException(sprintf('Unable to extract core file \'%s\'.', $filePath));
+        }
+
+        @unlink($filePath);
+    }
+
+    /**
+     * Calculates a file path for a file code
+     *
+     * @param string $fileCode A unique file code
+     * @return string           Full path on the disk
+     */
+    protected function getFilePath($fileCode)
+    {
+        $name = md5($fileCode).'.arc';
+
+        return $this->tempDirectory.'/'.$name;
     }
 
     /**

+ 7 - 0
src/Extend/Note.php

@@ -24,4 +24,11 @@ trait Note
             $this->notes[] = $message;
         }
     }
+
+    public function setOutPut($output)
+    {
+        $this->output = $output;
+
+        return $this;
+    }
 }

+ 2 - 0
src/Models/Extension.php

@@ -8,6 +8,8 @@ class Extension extends Model
 {
     protected $table = 'admin_extensions';
 
+    protected $fillable = ['name', 'is_enabled', 'version', 'options'];
+
     public function __construct(array $attributes = [])
     {
         $connection = config('admin.database.connection') ?: config('database.default');

+ 2 - 0
src/Models/ExtensionHistory.php

@@ -8,6 +8,8 @@ class ExtensionHistory extends Model
 {
     protected $table = 'admin_extension_histories';
 
+    protected $fillable = ['name', 'type', 'version', 'detail'];
+
     public function __construct(array $attributes = [])
     {
         $connection = config('admin.database.connection') ?: config('database.default');

+ 254 - 0
src/Support/Zip.php

@@ -0,0 +1,254 @@
+<?php
+
+namespace Dcat\Admin\Support;
+
+/**
+ * Zip helper
+ *
+ * @package october\filesystem
+ * @author Alexey Bobkov, Samuel Georges
+ *
+ * Usage:
+ *
+ *   Zip::make('file.zip', '/some/path/*.php');
+ *
+ *   Zip::make('file.zip', function($zip) {
+ *
+ *       // Add all PHP files and directories
+ *       $zip->add('/some/path/*.php');
+ *
+ *       // Do not include subdirectories, one level only
+ *       $zip->add('/non/recursive/*', ['recursive' => false]);
+ *
+ *       // Add multiple paths
+ *       $zip->add([
+ *           '/collection/of/paths/*',
+ *           '/a/single/file.php'
+ *       ]);
+ *
+ *       // Add all INI files to a zip folder "config"
+ *       $zip->folder('/config', '/path/to/config/*.ini');
+ *
+ *       // Add multiple paths to a zip folder "images"
+ *       $zip->folder('/images', function($zip) {
+ *           $zip->add('/my/gifs/*.gif', );
+ *           $zip->add('/photo/reel/*.{png,jpg}', );
+ *       });
+ *
+ *       // Remove these files/folders from the zip
+ *       $zip->remove([
+ *           '.htaccess',
+ *           'config.php',
+ *           'some/folder'
+ *       ]);
+ *
+ *   });
+ *
+ *   Zip::extract('file.zip', '/destination/path');
+ *
+ */
+
+use ZipArchive;
+
+class Zip extends ZipArchive
+{
+    /**
+     * @var string Folder prefix
+     */
+    protected $folderPrefix = '';
+
+    /**
+     * Extract an existing zip file.
+     * @param  string $source Path for the existing zip
+     * @param  string $destination Path to extract the zip files
+     * @param  array  $options
+     * @return bool
+     */
+    public static function extract($source, $destination, $options = [])
+    {
+        extract(array_merge([
+            'mask' => 0777
+        ], $options));
+
+        if (file_exists($destination) || mkdir($destination, $mask, true)) {
+            $zip = new ZipArchive;
+            if ($zip->open($source) === true) {
+                $zip->extractTo($destination);
+                $zip->close();
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * Creates a new empty zip file.
+     * @param  string $destination Path for the new zip
+     * @param  mixed  $source
+     * @param  array  $options
+     * @return self
+     */
+    public static function make($destination, $source, $options = [])
+    {
+        $zip = new self;
+        $zip->open($destination, ZIPARCHIVE::CREATE | ZipArchive::OVERWRITE);
+
+        if (is_string($source)) {
+            $zip->add($source, $options);
+        }
+        elseif (is_callable($source)) {
+            $source($zip);
+        }
+        elseif (is_array($source)) {
+            foreach ($source as $_source) {
+                $zip->add($_source, $options);
+            }
+        }
+
+        $zip->close();
+        return $zip;
+    }
+
+    /**
+     * Includes a source to the Zip
+     * @param mixed $source
+     * @param array $options
+     * @return self
+     */
+    public function add($source, $options = [])
+    {
+        /*
+         * A directory has been supplied, convert it to a useful glob
+         *
+         * The wildcard for including hidden files:
+         * - isn't hidden with an '.'
+         * - is hidden with a '.' but is followed by a non '.' character
+         * - starts with '..' but has at least one character after it
+         */
+        if (is_dir($source)) {
+            $includeHidden = isset($options['includeHidden']) && $options['includeHidden'];
+            $wildcard = $includeHidden ? '{*,.[!.]*,..?*}' : '*';
+            $source = implode('/', [dirname($source), basename($source), $wildcard]);
+        }
+
+        extract(array_merge([
+            'recursive' => true,
+            'includeHidden' => false,
+            'basedir' => dirname($source),
+            'baseglob' => basename($source)
+        ], $options));
+
+        if (is_file($source)) {
+            $files = [$source];
+            $recursive = false;
+        }
+        else {
+            $files = glob($source, GLOB_BRACE);
+            $folders = glob(dirname($source) . '/*', GLOB_ONLYDIR);
+        }
+
+        foreach ($files as $file) {
+            if (!is_file($file)) {
+                continue;
+            }
+
+            $localpath = $this->removePathPrefix($basedir.'/', dirname($file).'/');
+            $localfile = $this->folderPrefix . $localpath . basename($file);
+            $this->addFile($file, $localfile);
+        }
+
+        if (!$recursive) {
+            return $this;
+        }
+
+        foreach ($folders as $folder) {
+            if (!is_dir($folder)) {
+                continue;
+            }
+
+            $localpath = $this->folderPrefix . $this->removePathPrefix($basedir.'/', $folder.'/');
+            $this->addEmptyDir($localpath);
+            $this->add($folder.'/'.$baseglob, array_merge($options, ['basedir' => $basedir]));
+        }
+
+        return $this;
+    }
+
+    /**
+     * Creates a new folder inside the Zip and adds source files (optional)
+     * @param  string $name Folder name
+     * @param  mixed  $source
+     * @return self
+     */
+    public function folder($name, $source = null)
+    {
+        $prefix = $this->folderPrefix;
+        $this->addEmptyDir($prefix . $name);
+        if ($source === null) {
+            return $this;
+        }
+
+        $this->folderPrefix = $prefix . $name . '/';
+
+        if (is_string($source)) {
+            $this->add($source);
+        }
+        elseif (is_callable($source)) {
+            $source($this);
+        }
+        elseif (is_array($source)) {
+            foreach ($source as $_source) {
+                $this->add($_source);
+            }
+        }
+
+        $this->folderPrefix = $prefix;
+        return $this;
+    }
+
+    /**
+     * Removes a file or folder from the zip collection.
+     * Does not support wildcards.
+     * @param  string $source
+     * @return self
+     */
+    public function remove($source)
+    {
+        if (is_array($source)) {
+            foreach ($source as $_source) {
+                $this->remove($_source);
+            }
+        }
+
+        if (!is_string($source)) {
+            return $this;
+        }
+
+        if (substr($source, 0, 1) == '/') {
+            $source = substr($source, 1);
+        }
+
+        for ($i = 0; $i < $this->numFiles; $i++) {
+            $stats = $this->statIndex($i);
+            if (substr($stats['name'], 0, strlen($source)) == $source) {
+                $this->deleteIndex($i);
+            }
+        }
+
+        return $this;
+    }
+
+    /**
+     * Removes a prefix from a path.
+     * @param  string $prefix /var/sites/
+     * @param  string $path /var/sites/moo/cow/
+     * @return string moo/cow/
+     */
+    protected function removePathPrefix($prefix, $path)
+    {
+        return (strpos($path, $prefix) === 0)
+            ? substr($path, strlen($prefix))
+            : $path;
+    }
+}