From a58adcd04c477c4db43a5a3df9b41d778d258eea Mon Sep 17 00:00:00 2001 From: Sam Poyigi <6567634+sampoyigi@users.noreply.github.com> Date: Sun, 13 Oct 2024 16:01:56 +0100 Subject: [PATCH] feat: add theme publishing functionality, UI button and a new command `igniter:theme-publish` Signed-off-by: Sam Poyigi <6567634+sampoyigi@users.noreply.github.com> --- phpstan-baseline.neon | 25 ------- resources/lang/en/system.php | 3 + resources/models/main/theme.php | 6 ++ src/Admin/Traits/ListExtendable.php | 12 ++++ src/Admin/Widgets/Form.php | 1 - src/Flame/Igniter.php | 25 ++++++- src/Main/Classes/Theme.php | 17 +++-- src/Main/Http/Controllers/Themes.php | 12 ++++ src/System/Console/Commands/ThemePublish.php | 72 +++++++++++++++++++ .../Providers/ConsoleServiceProvider.php | 1 + 10 files changed, 138 insertions(+), 36 deletions(-) create mode 100644 src/System/Console/Commands/ThemePublish.php diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon index 2ece289..a08ff24 100644 --- a/phpstan-baseline.neon +++ b/phpstan-baseline.neon @@ -410,11 +410,6 @@ parameters: count: 1 path: src/Admin/FormWidgets/RecordEditor.php - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:getQuery\\(\\)\\.$#" - count: 2 - path: src/Admin/FormWidgets/Relation.php - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:sorted\\(\\)\\.$#" count: 1 @@ -2960,11 +2955,6 @@ parameters: count: 1 path: src/Flame/Providers/EventServiceProvider.php - - - message: "#^Call to an undefined method Illuminate\\\\Database\\\\Query\\\\Builder\\:\\:getQuery\\(\\)\\.$#" - count: 1 - path: src/Flame/Providers/MacroServiceProvider.php - - message: "#^Method Igniter\\\\Flame\\\\Scaffold\\\\GeneratorCommand\\:\\:makeDirectory\\(\\) should return string but return statement is missing\\.$#" count: 1 @@ -3255,11 +3245,6 @@ parameters: count: 1 path: src/Flame/Support/Helpers/helpers.php - - - message: "#^Function traceLog invoked with 1 parameter, 0 required\\.$#" - count: 7 - path: src/Flame/Support/Helpers/helpers.php - - message: "#^Parameter \\#2 \\$time of function mdate expects string\\|null, \\(int\\|false\\) given\\.$#" count: 2 @@ -3430,11 +3415,6 @@ parameters: count: 1 path: src/Main/Classes/MainController.php - - - message: "#^Function traceLog invoked with 1 parameter, 0 required\\.$#" - count: 1 - path: src/Main/Classes/MainController.php - - message: "#^PHPDoc tag @param for parameter \\$name with type mixed is not subtype of native type string\\.$#" count: 1 @@ -4300,11 +4280,6 @@ parameters: count: 1 path: src/System/Classes/ExtensionManager.php - - - message: "#^Function traceLog invoked with 1 parameter, 0 required\\.$#" - count: 2 - path: src/System/Classes/ExtensionManager.php - - message: "#^Property Illuminate\\\\Foundation\\\\PackageManifest\\:\\:\\$manifest \\(array\\) does not accept null\\.$#" count: 1 diff --git a/resources/lang/en/system.php b/resources/lang/en/system.php index 4ccf4e7..50cffbd 100644 --- a/resources/lang/en/system.php +++ b/resources/lang/en/system.php @@ -535,6 +535,7 @@ 'button_source' => '  Edit template files', 'button_customize' => '  Customize', 'button_child' => '  Create child theme', + 'button_publish' => 'Publish theme files', 'button_choose' => 'Choose a component to attach', 'button_new_source' => 'New %s', 'button_rename_source' => 'Rename %s', @@ -559,6 +560,8 @@ 'alert_changes_confirm' => 'Conflicting versions, template file has changed. Reload the page to continue.', 'alert_customize_not_active' => 'You can only customize an active theme.', 'alert_component_partial_not_found' => 'The selected component partial does not exist in the component directory', + 'alert_no_publish_custom' => 'Publishing custom theme files required a child theme located within the themes directory', + 'alert_publish_confirm' => 'Are you sure you wish to publish the theme files? This will overwrite the existing theme files.', ], 'updates' => [ diff --git a/resources/models/main/theme.php b/resources/models/main/theme.php index 56d88bf..d0febef 100644 --- a/resources/models/main/theme.php +++ b/resources/models/main/theme.php @@ -13,6 +13,12 @@ 'class' => 'btn btn-success', 'href' => 'updates', ], + 'publish' => [ + 'label' => 'lang:igniter::system.themes.button_publish', + 'class' => 'btn btn-default pull-right', + 'data-request' => 'onPublish', + 'data-request-confirm' => 'lang:igniter::system.themes.alert_publish_confirm', + ], ], ]; diff --git a/src/Admin/Traits/ListExtendable.php b/src/Admin/Traits/ListExtendable.php index dbbb7a4..f179093 100644 --- a/src/Admin/Traits/ListExtendable.php +++ b/src/Admin/Traits/ListExtendable.php @@ -89,4 +89,16 @@ public static function extendListColumns(callable $callback) $callback($widget, $widget->model); }); } + + public static function extendListQuery(callable $callback) + { + $calledClass = self::getCalledExtensionClass(); + Event::listen('admin.list.extendQuery', function($widget, $query) use ($calledClass, $callback) { + if (!is_a($widget->getController(), $calledClass)) { + return; + } + + $callback($widget, $query); + }); + } } diff --git a/src/Admin/Widgets/Form.php b/src/Admin/Widgets/Form.php index f1f6b31..aecea56 100644 --- a/src/Admin/Widgets/Form.php +++ b/src/Admin/Widgets/Form.php @@ -531,7 +531,6 @@ public function getTabs(): array public function getTab($tab): ?FormTabs { return $this->allTabs[$tab] ?? null; - } /** diff --git a/src/Flame/Igniter.php b/src/Flame/Igniter.php index c9a0aef..62fad2f 100644 --- a/src/Flame/Igniter.php +++ b/src/Flame/Igniter.php @@ -58,6 +58,8 @@ class Igniter protected static bool $autoloadExtensions = true; + protected static array $publishesThemeFiles = []; + /** * Set the extensions path for the application. * @@ -169,7 +171,7 @@ public static function loadMigrationsFrom(string $path, string $namespace) static::$migrationPaths[$namespace] = $path; } - public function ignoreMigrations(string $namespace = '*') + public static function ignoreMigrations(string $namespace = '*') { static::$ignoreMigrations[] = $namespace; @@ -317,4 +319,25 @@ public static function autoloadExtensions(?bool $value = null) static::$autoloadExtensions = $value; } + + public static function publishesThemeFiles(string|array $paths): void + { + foreach ((array)$paths as $path => $publishTo) { + if (is_numeric($path)) { + $path = $publishTo; + $publishTo = null; + } + + if (is_null($publishTo)) { + $publishTo = $path; + } + + static::$publishesThemeFiles[$path] = $publishTo; + } + } + + public static function publishableThemeFiles(): array + { + return static::$publishesThemeFiles; + } } diff --git a/src/Main/Classes/Theme.php b/src/Main/Classes/Theme.php index 51f7ec7..9581dd8 100644 --- a/src/Main/Classes/Theme.php +++ b/src/Main/Classes/Theme.php @@ -16,6 +16,7 @@ use Igniter\Main\Template\Layout as LayoutTemplate; use Igniter\Main\Template\Page as PageTemplate; use Igniter\Main\Template\Partial as PartialTemplate; +use Illuminate\Support\Collection; class Theme { @@ -124,14 +125,14 @@ public function getAssetPath(): string public function getPathsToPublish(): array { - $publishPath = $this->config['publish-paths'] ?? null; + $publishPath = $this->config['publish-paths'] ?? []; if (!$publishPath && File::exists($this->getAssetPath())) { return [$this->getAssetPath() => public_path('vendor/'.$this->name)]; } $result = []; - foreach ($this->config['publish-paths'] ?? [] as $path) { + foreach ($publishPath as $path) { if (File::isDirectory($this->path.$path)) { $result[$this->path.$path] = public_path('vendor/'.$this->name); } @@ -414,24 +415,22 @@ protected function getFindInPaths(): array // // - public function listPages() + public function listPages(): Collection { return PageTemplate::listInTheme($this->getName()); } - public function listPartials() + public function listPartials(): Collection { return PartialTemplate::listInTheme($this->getName()); } - public function listLayouts() + public function listLayouts(): Collection { return LayoutTemplate::listInTheme($this->getName()); } - public function getPagesOptions() {} - - public function listRequires() + public function listRequires(): array { return array_merge($this->hasParent() ? $this->getParent()->listRequires() : [], $this->requires); } @@ -458,7 +457,7 @@ public function makeFileSource(): SourceInterface return $this->fileSource = $source; } - public function onTemplate($dirName): Model + public function onTemplate(string $dirName): Model { $modelClass = $this->getTemplateClass($dirName); diff --git a/src/Main/Http/Controllers/Themes.php b/src/Main/Http/Controllers/Themes.php index a043011..dea710f 100644 --- a/src/Main/Http/Controllers/Themes.php +++ b/src/Main/Http/Controllers/Themes.php @@ -11,6 +11,7 @@ use Igniter\System\Helpers\CacheHelper; use Igniter\System\Traits\ManagesUpdates; use Illuminate\Http\RedirectResponse; +use Illuminate\Support\Facades\Artisan; class Themes extends \Igniter\Admin\Classes\AdminController { @@ -158,6 +159,17 @@ public function index_onSetDefault(): RedirectResponse return $this->redirectBack(); } + public function index_onPublish(): RedirectResponse + { + Artisan::call('igniter:theme-publish', ['--force' => true]); + + logger()->info($output = Artisan::output()); + + flash()->success(nl2br($output)); + + return $this->redirectBack(); + } + public function edit_onReset(string $context, string $themeCode): ?RedirectResponse { $formController = $this->asExtension('FormController'); diff --git a/src/System/Console/Commands/ThemePublish.php b/src/System/Console/Commands/ThemePublish.php new file mode 100644 index 0000000..2c0c08d --- /dev/null +++ b/src/System/Console/Commands/ThemePublish.php @@ -0,0 +1,72 @@ +determineWhatShouldBePublished(); + + $published = false; + + $activeThemePath = $this->activeTheme->getPath(); + foreach (Igniter::publishableThemeFiles() as $path => $publishTo) { + $this->publishItem($path, $activeThemePath.'/'.$publishTo); + $published = true; + } + + if ($published === false) { + $this->comment('No publishable custom files for theme ['.$this->activeTheme->getName().'].'); + } + + $this->info('Publishing complete.'); + } + + protected function determineWhatShouldBePublished() + { + throw_unless( + $this->activeTheme = resolve(ThemeManager::class)->getActiveTheme(), + new SystemException(lang('igniter::admin.alert_error_nothing')) + ); + + throw_if( + $this->activeTheme->locked, + new SystemException(lang('igniter::system.themes.alert_theme_locked')) + ); + + throw_if( + !str_starts_with($this->activeTheme->getPath(), theme_path()), + new SystemException(lang('igniter::system.themes.alert_no_publish_custom')) + ); + } + + protected function getOptions() + { + return [ + ['existing', null, InputOption::VALUE_NONE, 'Publish and overwrite only the files that have already been published'], + ['force', null, InputOption::VALUE_NONE, 'Force publish.'], + ]; + } +} diff --git a/src/System/Providers/ConsoleServiceProvider.php b/src/System/Providers/ConsoleServiceProvider.php index 7b54500..64c74e9 100644 --- a/src/System/Providers/ConsoleServiceProvider.php +++ b/src/System/Providers/ConsoleServiceProvider.php @@ -25,6 +25,7 @@ class ConsoleServiceProvider extends BaseConsoleServiceProvider 'extension.remove' => Console\Commands\ExtensionRemove::class, 'theme.install' => Console\Commands\ThemeInstall::class, 'theme.remove' => Console\Commands\ThemeRemove::class, + 'theme.publish' => Console\Commands\ThemePublish::class, 'theme.vendor-publish' => Console\Commands\ThemeVendorPublish::class, 'language.install' => Console\Commands\LanguageInstall::class, ];