-
-
Notifications
You must be signed in to change notification settings - Fork 197
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
355 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,203 @@ | ||
<?php | ||
|
||
namespace System\Classes\Extensions; | ||
|
||
use Cms\Classes\ThemeManager; | ||
use Illuminate\Support\Facades\App; | ||
use Illuminate\Support\Facades\File; | ||
use System\Classes\Packager\Composer; | ||
use Winter\Packager\Exceptions\CommandException; | ||
use Winter\Storm\Exception\ApplicationException; | ||
use Winter\Storm\Support\Str; | ||
|
||
class ExtensionSource | ||
{ | ||
public const SOURCE_COMPOSER = 'composer'; | ||
public const SOURCE_MARKET = 'market'; | ||
public const SOURCE_LOCAL = 'local'; | ||
|
||
public const TYPE_PLUGIN = 'plugin'; | ||
public const TYPE_THEME = 'theme'; | ||
public const TYPE_MODULE = 'module'; | ||
|
||
public const STATUS_UNINSTALLED = 'uninstalled'; | ||
public const STATUS_INSTALLED = 'installed'; | ||
public const STATUS_UNPACKED = 'unpacked'; | ||
|
||
protected array $extensionManagerMapping = [ | ||
self::TYPE_PLUGIN => PluginManager::class, | ||
self::TYPE_THEME => ThemeManager::class, | ||
self::TYPE_MODULE => ModuleManager::class, | ||
]; | ||
|
||
protected string $status = 'uninstalled'; | ||
|
||
public function __construct( | ||
public string $source, | ||
public string $type, | ||
public ?string $code = null, | ||
public ?string $composerPackage = null, | ||
public ?string $path = null | ||
) { | ||
if (!in_array($this->source, [static::SOURCE_COMPOSER, static::SOURCE_MARKET, static::SOURCE_LOCAL])) { | ||
throw new \InvalidArgumentException("Invalid source '{$this->source}'"); | ||
} | ||
|
||
if (!in_array($this->type, [static::TYPE_PLUGIN, static::TYPE_THEME, static::TYPE_MODULE])) { | ||
throw new \InvalidArgumentException("Invalid type '{$this->type}'"); | ||
} | ||
|
||
if ($this->source === static::SOURCE_COMPOSER && !$this->composerPackage) { | ||
throw new ApplicationException('You must provide a composer package for a composer source.'); | ||
} | ||
|
||
if ($this->source !== static::SOURCE_COMPOSER && !$this->code) { | ||
if (!$this->path) { | ||
throw new ApplicationException('You must provide a code or path.'); | ||
} | ||
|
||
$this->code = $this->guessCodeFromPath($this->path); | ||
} | ||
|
||
$this->status = $this->checkStatus(); | ||
} | ||
|
||
public function getStatus(): string | ||
{ | ||
return $this->status; | ||
} | ||
|
||
public function getCode(): ?string | ||
{ | ||
if ($this->code) { | ||
return $this->code; | ||
} | ||
|
||
if (!$this->path) { | ||
return null; | ||
} | ||
|
||
return $this->code = $this->guessCodeFromPath($this->path); | ||
} | ||
|
||
/** | ||
* @throws ApplicationException | ||
*/ | ||
public function createFiles(): static | ||
{ | ||
switch ($this->source) { | ||
case static::SOURCE_COMPOSER: | ||
try { | ||
Composer::require($this->composerPackage); | ||
} catch (CommandException $e) { | ||
throw new ApplicationException('Unable to require composer package', previous: $e); | ||
} | ||
|
||
$info = Composer::show('installed', $this->composerPackage); | ||
$this->path = $this->relativePath($info['path']); | ||
$this->source = static::SOURCE_LOCAL; | ||
break; | ||
case static::SOURCE_MARKET: | ||
throw new ApplicationException('need to implement market support'); | ||
break; | ||
case static::SOURCE_LOCAL: | ||
break; | ||
} | ||
|
||
if ($this->status !== static::STATUS_INSTALLED) { | ||
$this->status = static::STATUS_UNPACKED; | ||
} | ||
|
||
return $this; | ||
} | ||
|
||
/** | ||
* @throws ApplicationException | ||
*/ | ||
public function install(): WinterExtension | ||
{ | ||
if ($this->status === static::STATUS_UNINSTALLED) { | ||
throw new ApplicationException('Extension source is not unpacked'); | ||
} | ||
|
||
if ($this->status === static::STATUS_INSTALLED) { | ||
return $this->getExtensionManager()->getExtension($this); | ||
} | ||
|
||
return $this->getExtensionManager()->install($this); | ||
} | ||
|
||
public function uninstall(): bool | ||
{ | ||
if ($this->status !== static::STATUS_INSTALLED) { | ||
throw new ApplicationException('Extension source is not installed'); | ||
} | ||
|
||
return $this->getExtensionManager()->uninstall($this); | ||
} | ||
|
||
protected function getExtensionManager(): ExtensionManager | ||
{ | ||
return App::make($this->extensionManagerMapping[$this->type]); | ||
} | ||
|
||
protected function checkStatus(): string | ||
{ | ||
switch ($this->source) { | ||
case static::SOURCE_COMPOSER: | ||
try { | ||
$info = Composer::show('installed', $this->composerPackage); | ||
} catch (CommandException $e) { | ||
return static::STATUS_UNINSTALLED; | ||
} | ||
|
||
$this->path = $this->relativePath($info['path']); | ||
|
||
if (!$this->getExtensionManager()->isInstalled($this)) { | ||
return static::STATUS_UNPACKED; | ||
} | ||
break; | ||
case static::SOURCE_MARKET: | ||
case static::SOURCE_LOCAL: | ||
$path = $this->path ?? $this->guessPackagePath($this->code); | ||
if (!File::exists($path)) { | ||
return static::STATUS_UNINSTALLED; | ||
} | ||
break; | ||
} | ||
|
||
if (!$this->getExtensionManager()->isInstalled($this)) { | ||
return static::STATUS_UNPACKED; | ||
} | ||
|
||
return static::STATUS_INSTALLED; | ||
} | ||
|
||
protected function guessPackagePath(string $code): ?string | ||
{ | ||
return match ($this->type) { | ||
static::TYPE_PLUGIN => plugins_path(str_replace('.', '/', $code)), | ||
static::TYPE_THEME => themes_path($code), | ||
static::TYPE_MODULE => base_path('modules/' . $code), | ||
default => null, | ||
}; | ||
} | ||
|
||
protected function guessCodeFromPath(string $path): ?string | ||
{ | ||
return match ($this->type) { | ||
static::TYPE_PLUGIN => str_replace('/', '.', ltrim(Str::after($path, basename(plugins_path())), '/')), | ||
static::TYPE_THEME => Str::after($path, themes_path()), | ||
static::TYPE_MODULE => Str::after($path, base_path('modules/')), | ||
default => null, | ||
}; | ||
} | ||
|
||
protected function relativePath(string $path): string | ||
{ | ||
return ltrim(Str::after($path, match ($this->type) { | ||
static::TYPE_PLUGIN, static::TYPE_THEME => base_path(), | ||
static::TYPE_MODULE => base_path('modules'), | ||
}), '/'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.