app->register(RouteServiceProvider::class); $this->mergeConfigFrom(module_path(ModuleInfo::name(), 'Config/config.php'), ModuleInfo::nameLower()); $this->app->singleton('stylist', fn ($app) => $app->make(Stylist::class)); $this->app->singleton('stylist.html', fn ($app) => $app->make(ThemeHtmlBuilder::class)); $this->app->singleton('stylist.seo', fn ($app) => $app->make(ThemeSeoBuilder::class)); } public function boot(): void { \Illuminate\Support\Facades\View::addNamespace( 'sonardesk-base', resource_path('views') ); $this->registerViews(); $this->registerThemeViews(); $this->registerTranslations(); $this->registerPublishing(); $this->registerHelpers(); $this->registerBlade(); if ($this->app->runningInConsole()) { $this->commands([ PublishAssetsCommand::class, ValidateThemeCommand::class, InspectThemesCommand::class, FindDuplicateViewsCommand::class, ThemeDoctorCommand::class, CreateThemeCommand::class, ]); } $this->loadMigrationsFrom(module_path(ModuleInfo::name(), 'Database/Migrations')); View::composer('*', function ($view): void { if (! $this->app->bound(Stylist::class)) { return; } $view->with('activeTheme', $this->app->make(Stylist::class)->current()); }); View::composer('stylist-theme-saasland::*', function ($view): void { $theme = $this->app->make(Loader::class)->find('saasland'); $settings = $this->app->make(ThemeSettings::class)->get($theme); $siteSettings = $this->app->make(SiteSettingsRepository::class)->site(); $navigation = $this->app->make(FrontendNavigation::class); $homepage = $this->app->make(FrontendContent::class)->homepage($settings); $view->with([ 'siteSettings' => $siteSettings, 'siteTitle' => $siteSettings['site_name'], 'siteDescription' => $siteSettings['site_description'], 'logoUrl' => $siteSettings['logo_url'] ?: null, 'facebookUrl' => $siteSettings['facebook_url'] ?: null, 'xUrl' => $siteSettings['x_url'] ?: null, 'linkedinUrl' => $siteSettings['linkedin_url'] ?: null, 'primaryNavigation' => $navigation->primary(), 'footerNavigation' => $navigation->footer(), 'themeSettings' => $settings, ...$homepage, ]); }); } private function registerViews(): void { $sourcePath = module_path(ModuleInfo::name(), 'Resources/views'); $viewPath = resource_path('views/modules/'.ModuleInfo::nameLower()); $this->publishes([$sourcePath => $viewPath], ['views', 'stylist-module-views']); $this->loadViewsFrom([$viewPath, $sourcePath], ModuleInfo::nameLower()); } private function registerThemeViews(): void { foreach ($this->themeRoots() as $root) { if (! is_dir($root)) { logger()->warning('Stylist theme root does not exist', [ 'root' => $root, ]); continue; } $themeDirectories = glob($root.'/*', GLOB_ONLYDIR) ?: []; foreach ($themeDirectories as $themeDirectory) { $themeViewsPath = $themeDirectory.'/views'; if (! is_dir($themeViewsPath)) { continue; } $themeKey = strtolower(basename($themeDirectory)); // Themes always live outside the module, normally in resources/themes. // Register the canonical namespace and the legacy namespace as an alias // to the same application-owned directory. The alias keeps existing // routes and Blade templates working without reintroducing module themes. $namespaces = [ "stylist-theme-{$themeKey}", "stylist-module-theme-{$themeKey}", ]; $componentsPath = $themeViewsPath.'/components'; // The common theme is the application-wide owner of layouts, // authentication, settings and shared components. Livewire 4 // resolves its default full-page layout through layouts::app, // so point that compatibility namespace at the common theme. if ($themeKey === 'common') { $layoutsPath = $componentsPath.'/layouts'; if (is_dir($layoutsPath)) { View::addNamespace('layouts', $layoutsPath); Blade::anonymousComponentPath($layoutsPath, 'layouts'); } View::addNamespace('common', $themeViewsPath); if (is_dir($componentsPath)) { Blade::anonymousComponentPath($componentsPath, 'common'); } } foreach ($namespaces as $namespace) { View::addNamespace($namespace, $themeViewsPath); if (is_dir($componentsPath)) { Blade::anonymousComponentPath($componentsPath, $namespace); } } } } } private function normalisePath(string $path): string { return rtrim( str_replace('\\', '/', realpath($path) ?: $path), '/' ); } /** @return array */ private function themeRoots(): array { return array_values(array_unique(array_filter([ (string) config('stylist.themes_path'), ]))); } private function registerTranslations(): void { $langPath = resource_path('lang/modules/'.ModuleInfo::nameLower()); $sourcePath = module_path(ModuleInfo::name(), 'Resources/lang'); $this->loadTranslationsFrom(is_dir($langPath) ? $langPath : $sourcePath, ModuleInfo::nameLower()); } private function registerPublishing(): void { $this->publishes([ module_path(ModuleInfo::name(), 'Config/config.php') => config_path('stylist.php'), ], ['config', 'stylist-config']); } private function registerHelpers(): void { foreach (glob(module_path(ModuleInfo::name(), 'Helpers/*.php')) ?: [] as $filename) { require_once $filename; } } private function registerBlade(): void { Blade::directive('themeVariables', fn () => 'variables(); ?>'); Blade::directive('themeStylesheet', fn ($expression) => 'stylesheet('.($expression ?: 'null').'); ?>'); Blade::directive('themeScript', fn ($expression) => 'script('.($expression ?: 'null').'); ?>'); Blade::directive('themeSwitcher', fn () => 'switcher(); ?>'); Blade::directive('themeSeo', fn ($expression) => 'render('.($expression ?: '[]').'); ?>'); } }