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,
];