From 3e6b3bfc7947a727dd339d415babc54d829a77ca Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Tue, 25 Aug 2026 14:05:49 +0100 Subject: [PATCH] updaetd deployer --- .../Providers/HelpdeskServiceProvider.php | 28 ++----- .../Providers/StylistServiceProvider.php | 82 ++++++++++++++++++- .../Tests/Feature/WelcomeThemeViewTest.php | 19 +++++ 3 files changed, 107 insertions(+), 22 deletions(-) diff --git a/Modules/Helpdesk/Providers/HelpdeskServiceProvider.php b/Modules/Helpdesk/Providers/HelpdeskServiceProvider.php index beafdb5..c186c70 100644 --- a/Modules/Helpdesk/Providers/HelpdeskServiceProvider.php +++ b/Modules/Helpdesk/Providers/HelpdeskServiceProvider.php @@ -5,7 +5,6 @@ declare(strict_types=1); namespace Modules\Helpdesk\Providers; use Illuminate\Support\Facades\Blade; -use Illuminate\Support\Facades\Config; use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Gate; use Illuminate\Support\ServiceProvider; @@ -136,14 +135,19 @@ final class HelpdeskServiceProvider extends ServiceProvider */ protected function registerViews(): void { - $viewPath = resource_path('views/modules/'.ModuleInfo::nameLower()); $sourcePath = module_path(ModuleInfo::name(), 'Resources/views'); + $themeRoot = rtrim((string) config('stylist.themes_path', resource_path('themes')), DIRECTORY_SEPARATOR); + $backendTheme = (string) config('stylist.default_backend_theme', 'backend'); + $viewPath = $themeRoot.DIRECTORY_SEPARATOR.$backendTheme + .DIRECTORY_SEPARATOR.'views'.DIRECTORY_SEPARATOR.'modules'.DIRECTORY_SEPARATOR.ModuleInfo::nameLower(); + // Module source views remain module-owned. Any application override is + // published into the selected backend theme, never resources/views. $this->publishes([ $sourcePath => $viewPath, ], ['views', ModuleInfo::nameLower().'-module-views']); - $this->loadViewsFrom(array_merge($this->getPublishableViewPaths(), [$sourcePath]), ModuleInfo::nameLower()); + $this->loadViewsFrom($sourcePath, ModuleInfo::nameLower()); Blade::componentNamespace('Modules\Helpdesk\View\Components', 'helpdesk'); Blade::anonymousComponentPath($sourcePath.'/components', 'helpdesk'); @@ -165,24 +169,6 @@ final class HelpdeskServiceProvider extends ServiceProvider $this->loadTranslationsFrom(module_path(ModuleInfo::name(), 'Resources/lang'), ModuleInfo::nameLower()); } - /** - * Get publishable view override paths. - * - * @return array - */ - private function getPublishableViewPaths(): array - { - $paths = []; - - foreach (Config::get('view.paths') as $path) { - if (is_dir($path.'/modules/'.ModuleInfo::nameLower())) { - $paths[] = $path.'/modules/'.ModuleInfo::nameLower(); - } - } - - return $paths; - } - /** * Get provided services. * diff --git a/Modules/Stylist/Providers/StylistServiceProvider.php b/Modules/Stylist/Providers/StylistServiceProvider.php index 84d2510..85a0d85 100644 --- a/Modules/Stylist/Providers/StylistServiceProvider.php +++ b/Modules/Stylist/Providers/StylistServiceProvider.php @@ -73,6 +73,7 @@ final class StylistServiceProvider extends ServiceProvider $this->app->booted(function (): void { $this->registerSelectedThemeOverrides(); + $this->registerSelectedThemeFluxOverrides(); }); } @@ -218,7 +219,14 @@ final class StylistServiceProvider extends ServiceProvider // boot order and incremental prependLocation() calls. Theme paths stay first; // Laravel's normal view locations remain as the final fallback. if (method_exists($finder, 'getPaths') && method_exists($finder, 'setPaths')) { - $existingPaths = $finder->getPaths(); + // The application intentionally has no resources/views tree. Laravel's + // default FileViewFinder still starts with that location, and view:cache + // asks Symfony Finder to scan every registered path. Remove missing legacy + // locations so theme-only applications can be optimized on a clean release. + $existingPaths = array_values(array_filter( + $finder->getPaths(), + static fn (string $path): bool => is_dir($path), + )); $paths = array_values(array_unique(array_merge($themePaths, $existingPaths))); $finder->setPaths($paths); @@ -236,6 +244,78 @@ final class StylistServiceProvider extends ServiceProvider } } + /** + * Make Flux application overrides theme-owned. + * + * Flux normally discovers custom components from resources/views/flux. This + * application deliberately has no resources/views tree, so expose the selected + * theme's views/flux directory through Flux's existing view namespace instead. + * Package-provided Flux views remain available as the final fallback. + */ + private function registerSelectedThemeFluxOverrides(): void + { + $stylist = $this->app->make(Stylist::class); + $orderedThemes = [$stylist->current(), $stylist->frontend(), $stylist->backend()]; + $keys = []; + + foreach ($orderedThemes as $theme) { + foreach ($this->themeChain($theme) as $key) { + if (! in_array($key, $keys, true)) { + $keys[] = $key; + } + } + } + + if (! in_array('common', $keys, true)) { + $keys[] = 'common'; + } + + $themeFluxPaths = []; + + foreach ($keys as $key) { + try { + $theme = $this->app->make(Loader::class)->find($key); + } catch (\Throwable) { + continue; + } + + $fluxPath = $theme->path().DIRECTORY_SEPARATOR.'views'.DIRECTORY_SEPARATOR.'flux'; + + if (is_dir($fluxPath)) { + $themeFluxPaths[] = $fluxPath; + } + } + + if ($themeFluxPaths === []) { + return; + } + + $finder = View::getFinder(); + + if (! method_exists($finder, 'getHints')) { + return; + } + + $hints = $finder->getHints(); + $existingFluxPaths = array_values(array_filter( + (array) ($hints['flux'] ?? []), + static fn (string $path): bool => is_dir($path), + )); + $paths = array_values(array_unique(array_merge($themeFluxPaths, $existingFluxPaths))); + + if (method_exists($finder, 'replaceNamespace')) { + $finder->replaceNamespace('flux', $paths); + } else { + foreach (array_reverse($themeFluxPaths) as $fluxPath) { + View::prependNamespace('flux', $fluxPath); + } + } + + if (method_exists($finder, 'flush')) { + $finder->flush(); + } + } + /** @return array */ private function themeChain(Theme $theme): array { diff --git a/Modules/Stylist/Tests/Feature/WelcomeThemeViewTest.php b/Modules/Stylist/Tests/Feature/WelcomeThemeViewTest.php index a83046a..9e6fe75 100644 --- a/Modules/Stylist/Tests/Feature/WelcomeThemeViewTest.php +++ b/Modules/Stylist/Tests/Feature/WelcomeThemeViewTest.php @@ -42,4 +42,23 @@ final class WelcomeThemeViewTest extends TestCase $this->assertFileExists(resource_path('themes/backend/assets/css/app.css')); $this->assertFileExists(resource_path('themes/backend/assets/js/passkeys.js')); } + + public function test_view_finder_contains_only_existing_application_view_locations(): void + { + foreach (view()->getFinder()->getPaths() as $path) { + $this->assertDirectoryExists($path); + } + } + + public function test_backend_flux_icon_overrides_are_owned_by_the_backend_theme(): void + { + $this->assertTrue(view()->exists('flux::icon.folder-git-2')); + $this->assertTrue(view()->exists('flux::icon.book-open-text')); + $this->assertTrue(view()->exists('flux::icon.chevrons-up-down')); + + $this->assertStringContainsString( + resource_path('themes/backend/views/flux/icon/folder-git-2.blade.php'), + view('flux::icon.folder-git-2')->getPath(), + ); + } }