Refactor plugin system (part 9)
This commit is contained in:
parent
3594b7abf8
commit
f182f799f6
|
|
@ -7,12 +7,6 @@ namespace App\Services;
|
|||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @property string $name
|
||||
* @property string $description
|
||||
* @property string $title
|
||||
* @property array $author
|
||||
*/
|
||||
class Plugin
|
||||
{
|
||||
/**
|
||||
|
|
@ -22,40 +16,12 @@ class Plugin
|
|||
*/
|
||||
protected $path;
|
||||
|
||||
/**
|
||||
* The directory name where the plugin installed.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $dirname;
|
||||
|
||||
/**
|
||||
* package.json of the package.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $packageInfo;
|
||||
|
||||
/**
|
||||
* Whether the plugin is installed.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $installed = true;
|
||||
|
||||
/**
|
||||
* The installed version of the plugin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $version;
|
||||
|
||||
/**
|
||||
* The namespace used by the plugin.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $namespace;
|
||||
protected $manifest;
|
||||
|
||||
/**
|
||||
* Whether the plugin is enabled.
|
||||
|
|
@ -64,71 +30,40 @@ class Plugin
|
|||
*/
|
||||
protected $enabled = false;
|
||||
|
||||
public function __construct(string $path, array $packageInfo)
|
||||
public function __construct(string $path, array $manifest)
|
||||
{
|
||||
$this->path = $path;
|
||||
$this->packageInfo = $packageInfo;
|
||||
$this->manifest = $manifest;
|
||||
}
|
||||
|
||||
public function __get(string $name)
|
||||
{
|
||||
return $this->packageInfoAttribute(Str::snake($name, '-'));
|
||||
return $this->getManifestAttr(Str::snake($name, '-'));
|
||||
}
|
||||
|
||||
public function __isset(string $name)
|
||||
{
|
||||
return isset($this->{$name}) || $this->packageInfoAttribute(snake_case($name, '-'));
|
||||
}
|
||||
|
||||
public function packageInfoAttribute(string $name)
|
||||
{
|
||||
return Arr::get($this->packageInfo, $name);
|
||||
return isset($this->{$name}) || $this->getManifestAttr(Str::snake($name, '-'));
|
||||
}
|
||||
|
||||
public function getManifest()
|
||||
{
|
||||
return $this->packageInfo;
|
||||
return $this->manifest;
|
||||
}
|
||||
|
||||
public function getManifestAttr(string $name, $default = null)
|
||||
{
|
||||
return Arr::get($this->manifest, $name, $default);
|
||||
}
|
||||
|
||||
public function assets(string $relativeUri): string
|
||||
{
|
||||
$baseUrl = config('plugins.url') ?: url('plugins');
|
||||
|
||||
return "$baseUrl/{$this->getDirname()}/assets/$relativeUri?v=".$this->version;
|
||||
return "$baseUrl/{$this->name}/assets/$relativeUri?v=".$this->version;
|
||||
}
|
||||
|
||||
public function setInstalled(bool $installed): self
|
||||
{
|
||||
$this->installed = $installed;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDirname(): string
|
||||
{
|
||||
return $this->dirname;
|
||||
}
|
||||
|
||||
public function setDirname(string $dirname): self
|
||||
{
|
||||
$this->dirname = $dirname;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getNamespace(): string
|
||||
{
|
||||
return $this->namespace;
|
||||
}
|
||||
|
||||
public function setNameSpace(string $namespace): self
|
||||
{
|
||||
$this->namespace = $namespace;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getViewPathByFileName(string $filename): string
|
||||
public function getViewPath(string $filename): string
|
||||
{
|
||||
return $this->path."/views/$filename";
|
||||
}
|
||||
|
|
@ -136,39 +71,15 @@ class Plugin
|
|||
public function getConfigView()
|
||||
{
|
||||
return $this->hasConfigView()
|
||||
? view()->file($this->getViewPathByFileName(Arr::get($this->packageInfo, 'config', 'config.blade.php')))
|
||||
? view()->file($this->getViewPath(Arr::get($this->manifest, 'config', 'config.blade.php')))
|
||||
: null;
|
||||
}
|
||||
|
||||
public function hasConfigView(): bool
|
||||
{
|
||||
$filename = Arr::get($this->packageInfo, 'config', 'config.blade.php');
|
||||
$filename = Arr::get($this->manifest, 'config', 'config.blade.php');
|
||||
|
||||
return $filename && file_exists($this->getViewPathByFileName($filename));
|
||||
}
|
||||
|
||||
public function setVersion(string $version): self
|
||||
{
|
||||
$this->version = $version;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getVersion(): string
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
public function setRequirements(array $require): self
|
||||
{
|
||||
$this->require = $require;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getRequirements(): array
|
||||
{
|
||||
return (array) $this->require;
|
||||
return $filename && file_exists($this->getViewPath($filename));
|
||||
}
|
||||
|
||||
public function setEnabled(bool $enabled): self
|
||||
|
|
|
|||
|
|
@ -244,97 +244,6 @@ class PluginManager
|
|||
}
|
||||
|
||||
/**
|
||||
* @return Collection
|
||||
*/
|
||||
public function getPlugins()
|
||||
{
|
||||
if (is_null($this->plugins)) {
|
||||
$plugins = new Collection();
|
||||
$enabled = $this->getFullEnabled();
|
||||
|
||||
$installed = [];
|
||||
|
||||
$cwd = getcwd();
|
||||
chdir(base_path());
|
||||
|
||||
try {
|
||||
$resource = opendir($this->getPluginsDir());
|
||||
} catch (\Exception $e) {
|
||||
throw new PrettyPageException(trans('errors.plugins.directory', ['msg' => $e->getMessage()]), 500);
|
||||
}
|
||||
|
||||
// traverse plugins dir
|
||||
while ($filename = @readdir($resource)) {
|
||||
if ($filename == '.' || $filename == '..') {
|
||||
continue;
|
||||
}
|
||||
|
||||
$path = $this->getPluginsDir().DIRECTORY_SEPARATOR.$filename;
|
||||
|
||||
if (is_dir($path)) {
|
||||
$packageJsonPath = $path.DIRECTORY_SEPARATOR.'package.json';
|
||||
|
||||
if (file_exists($packageJsonPath)) {
|
||||
// load packages installed
|
||||
$installed[$filename] = json_decode($this->filesystem->get($packageJsonPath), true);
|
||||
}
|
||||
}
|
||||
}
|
||||
closedir($resource);
|
||||
|
||||
foreach ($installed as $dirname => $package) {
|
||||
|
||||
// Instantiates an Plugin object using the package path and package.json file.
|
||||
$plugin = new Plugin($this->getPluginsDir().DIRECTORY_SEPARATOR.$dirname, $package);
|
||||
|
||||
// Each default all plugins are installed if they are registered in composer.
|
||||
$plugin->setDirname($dirname);
|
||||
$plugin->setInstalled(true);
|
||||
$plugin->setNameSpace(Arr::get($package, 'namespace'));
|
||||
$plugin->setVersion(Arr::get($package, 'version'));
|
||||
$plugin->setEnabled($this->isEnabled($plugin->name));
|
||||
|
||||
if ($plugins->has($plugin->name)) {
|
||||
throw new PrettyPageException(trans('errors.plugins.duplicate', [
|
||||
'dir1' => $plugin->getDirname(),
|
||||
'dir2' => $plugins->get($plugin->name)->getDirname(),
|
||||
]), 5);
|
||||
}
|
||||
|
||||
$plugins->put($plugin->name, $plugin);
|
||||
|
||||
if (
|
||||
$enabled->has($plugin->name) &&
|
||||
Comparator::notEqualTo($plugin->getVersion(), $enabled->get($plugin->name))
|
||||
) {
|
||||
$this->copyPluginAssets($plugin);
|
||||
}
|
||||
}
|
||||
|
||||
$this->plugins = $plugins->sortBy(function ($plugin, $name) {
|
||||
return $plugin->name;
|
||||
});
|
||||
|
||||
chdir($cwd);
|
||||
}
|
||||
|
||||
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)
|
||||
|
|
@ -351,8 +260,6 @@ class PluginManager
|
|||
}
|
||||
|
||||
/**
|
||||
* Disables an plugin.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function disable($name)
|
||||
|
|
@ -377,8 +284,6 @@ class PluginManager
|
|||
}
|
||||
|
||||
/**
|
||||
* Uninstalls an plugin.
|
||||
*
|
||||
* @param string $name
|
||||
*/
|
||||
public function delete($name)
|
||||
|
|
@ -397,86 +302,13 @@ class PluginManager
|
|||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads composer autoloader for the enabled plugins if exists.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getEnabledComposerAutoloaders()
|
||||
{
|
||||
$autoloaders = new Collection;
|
||||
|
||||
foreach ($this->getEnabledPlugins() as $plugin) {
|
||||
if ($this->filesystem->exists($file = $plugin->getPath().'/vendor/autoload.php')) {
|
||||
$autoloaders->push($file);
|
||||
}
|
||||
}
|
||||
|
||||
return $autoloaders;
|
||||
}
|
||||
|
||||
/**
|
||||
* The id's of the enabled plugins.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function getEnabled()
|
||||
{
|
||||
$enabled = collect(json_decode($this->option->get('plugins_enabled'), true));
|
||||
|
||||
return $enabled->map(function ($item) {
|
||||
if (is_string($item)) {
|
||||
return $item;
|
||||
} else {
|
||||
return $item['name'];
|
||||
}
|
||||
})->values()->toArray();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return enabled plugins with version information.
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function getFullEnabled()
|
||||
{
|
||||
$enabled = collect(json_decode($this->option->get('plugins_enabled'), true));
|
||||
$ret = collect();
|
||||
|
||||
$enabled->each(function ($item) use ($ret) {
|
||||
if (is_array($item)) {
|
||||
$ret->put($item['name'], $item['version']);
|
||||
}
|
||||
return $this->all()->filter(function ($plugin) {
|
||||
return $plugin->isEnabled();
|
||||
});
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -485,19 +317,8 @@ class PluginManager
|
|||
protected function saveEnabled()
|
||||
{
|
||||
$this->option->set('plugins_enabled', $this->enabled->map(function ($info, $name) {
|
||||
//
|
||||
})->toJson());
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the plugin is enabled.
|
||||
*
|
||||
* @param string $pluginName
|
||||
* @return bool
|
||||
*/
|
||||
public function isEnabled($pluginName)
|
||||
{
|
||||
return in_array($pluginName, $this->getEnabled());
|
||||
return array_merge(compact('name'), $info);
|
||||
})->values()->toJson());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -529,78 +350,6 @@ class PluginManager
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the unsatisfied requirements of plugin.
|
||||
*
|
||||
* @param string|Plugin|array $plugin
|
||||
* @return array
|
||||
*/
|
||||
public function getUnsatisfiedRequirements($plugin)
|
||||
{
|
||||
if (is_array($plugin)) {
|
||||
$requirements = $plugin;
|
||||
} else {
|
||||
if (! $plugin instanceof Plugin) {
|
||||
$plugin = $this->getPlugin($plugin);
|
||||
}
|
||||
|
||||
if (! $plugin) {
|
||||
throw new \InvalidArgumentException('Plugin with given name does not exist.');
|
||||
}
|
||||
|
||||
$requirements = $plugin->getRequirements();
|
||||
}
|
||||
|
||||
$unsatisfied = [];
|
||||
|
||||
foreach ($requirements as $name => $versionConstraint) {
|
||||
// Version requirement for the main application
|
||||
if ($name == 'blessing-skin-server') {
|
||||
if (! Semver::satisfies(config('app.version'), $versionConstraint)) {
|
||||
$unsatisfied['blessing-skin-server'] = [
|
||||
'version' => config('app.version'),
|
||||
'constraint' => $versionConstraint,
|
||||
];
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
$requiredPlugin = $this->getPlugin($name);
|
||||
|
||||
if (! $requiredPlugin || ! $requiredPlugin->isEnabled()) {
|
||||
$unsatisfied[$name] = [
|
||||
'version' => null,
|
||||
'constraint' => $versionConstraint,
|
||||
];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (! Semver::satisfies($requiredPlugin->getVersion(), $versionConstraint)) {
|
||||
$unsatisfied[$name] = [
|
||||
'version' => $requiredPlugin->getVersion(),
|
||||
'constraint' => $versionConstraint,
|
||||
];
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $unsatisfied;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the plugin's requirements are satisfied.
|
||||
*
|
||||
* @param string|Plugin|array $plugin
|
||||
* @return bool
|
||||
*/
|
||||
public function isRequirementsSatisfied($plugin)
|
||||
{
|
||||
return empty($this->getUnsatisfiedRequirements($plugin));
|
||||
}
|
||||
|
||||
/**
|
||||
* The plugins path.
|
||||
*
|
||||
|
|
@ -610,26 +359,4 @@ class PluginManager
|
|||
{
|
||||
return config('plugins.directory') ? realpath(config('plugins.directory')) : base_path('plugins');
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy plugin assets.
|
||||
*
|
||||
* @param Plugin $plugin
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function copyPluginAssets($plugin)
|
||||
{
|
||||
$dir = public_path('plugins/'.$plugin->name);
|
||||
Storage::deleteDirectory($dir);
|
||||
|
||||
$this->filesystem->copyDirectory(
|
||||
$this->getPluginsDir().DIRECTORY_SEPARATOR.$plugin->name.DIRECTORY_SEPARATOR.'assets',
|
||||
$dir.'/assets'
|
||||
);
|
||||
$this->filesystem->copyDirectory(
|
||||
$this->getPluginsDir().DIRECTORY_SEPARATOR.$plugin->name.DIRECTORY_SEPARATOR.'lang',
|
||||
$dir.'/lang'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -124,21 +124,21 @@ if (! function_exists('bs_menu')) {
|
|||
|
||||
$menu[$type] = array_map(function ($item) {
|
||||
if (Arr::get($item, 'id') === 'plugin-configs') {
|
||||
$availablePluginConfigs = [];
|
||||
|
||||
foreach (app('plugins')->getEnabledPlugins() as $plugin) {
|
||||
if ($plugin->hasConfigView()) {
|
||||
$availablePluginConfigs[] = [
|
||||
$pluginConfigs = app('plugins')->getEnabledPlugins()
|
||||
->filter(function ($plugin) {
|
||||
return $plugin->hasConfigView();
|
||||
})
|
||||
->map(function ($plugin) {
|
||||
return [
|
||||
'title' => trans($plugin->title),
|
||||
'link' => 'admin/plugins/config/'.$plugin->name,
|
||||
'icon' => 'fa-circle',
|
||||
];
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Don't display this menu item when no plugin config is available
|
||||
if (count($availablePluginConfigs) > 0) {
|
||||
$item['children'] = array_merge($item['children'], $availablePluginConfigs);
|
||||
if ($pluginConfigs->isNotEmpty()) {
|
||||
$item['children'] = array_merge($item['children'], $pluginConfigs->values()->all());
|
||||
|
||||
return $item;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user