laravelViewsPath = Config::get('view.paths'); $this->loadThemes(); } /** * Return list of all registered themes. * * @return array */ public function all() { return $this->themes; } /** * Return list of registered themes. * * @return array */ public function getChannelThemes() { $themes = config('themes.shop', []); $channelThemes = []; foreach ($themes as $code => $data) { $channelThemes[] = new Theme( $code, $data['name'] ?? '', $data['assets_path'] ?? '', $data['views_path'] ?? '', isset($data['vite']) ? $data['vite'] : [], ); if (! empty($data['parent'])) { $parentThemes[$code] = $data['parent']; } } return $channelThemes; } /** * Check if specified exists. * * @return bool */ public function exists(string $themeName) { foreach ($this->themes as $theme) { if ($theme->code == $themeName) { return true; } } return false; } /** * Prepare all themes. * * @return void */ public function loadThemes() { $parentThemes = []; /** * Octane safety: Handle request context more safely. */ $isAdmin = false; try { $currentRequest = request(); if ($currentRequest && $currentRequest->url()) { $isAdmin = Str::contains($currentRequest->url(), config('app.admin_url').'/'); } } catch (\Exception $e) { /** * Fallback if request context is not available. */ $isAdmin = false; } if ($isAdmin) { $themes = config('themes.admin', []); } else { $themes = config('themes.shop', []); } foreach ($themes as $code => $data) { $this->themes[] = new Theme( $code, $data['name'] ?? '', $data['assets_path'] ?? '', $data['views_path'] ?? '', $data['views_namespace'] ?? null, $data['vite'] ?? [], ); if (! empty($data['parent'])) { $parentThemes[$code] = $data['parent']; } } foreach ($parentThemes as $childCode => $parentCode) { $child = $this->find($childCode); if ($this->exists($parentCode)) { $parent = $this->find($parentCode); } else { $parent = new Theme($parentCode); } $child->setParent($parent); } } /** * Enable theme. * * @return \Webkul\Theme\Theme */ public function set(string $themeName) { if ($this->exists($themeName)) { $theme = $this->find($themeName); } else { $theme = new Theme($themeName); } $this->activeTheme = $theme; $paths = $theme->getViewPaths(); foreach ($this->laravelViewsPath as $path) { if (! in_array($path, $paths)) { $paths[] = $path; } } Config::set('view.paths', $paths); $themeViewFinder = app('view.finder'); $themeViewFinder->setPaths($paths); return $theme; } /** * Get current theme. * * @return \Webkul\Theme\Theme */ public function current() { return $this->activeTheme ?? null; } /** * Get current theme's name. * * @return string */ public function getName() { return $this->current()?->name ?? ''; } /** * Find a theme by it's name. * * @return \Webkul\Theme\Theme */ public function find(string $themeName) { foreach ($this->themes as $theme) { if ($theme->code == $themeName) { return $theme; } } throw new Exceptions\ThemeNotFound($themeName); } /** * Original view paths defined in `config.view.php`. * * @return array */ public function getLaravelViewPaths() { return $this->laravelViewsPath; } /** * Return the asset URL of the current theme if a theme is found; otherwise, check from the namespace. * * @return string */ public function url(string $filename, ?string $namespace = null) { $url = trim($filename, '/'); /** * If the namespace is null, it means the theming system is activated. We use the request URI to * detect the theme and provide Vite assets based on the current theme. */ if (empty($namespace)) { $currentTheme = $this->current(); /** * Octane safety: Initialize theme if not set. */ if (! $currentTheme) { $this->ensureThemeInitialized(); $currentTheme = $this->current(); } return $currentTheme->url($url); } /** * If a namespace is provided, it means the developer knows what they are doing and must create the * registry in the provided configuration. We will analyze based on that. */ $viters = config('bagisto-vite.viters'); if (empty($viters[$namespace])) { throw new ViterNotFound($namespace); } $viteUrl = trim($viters[$namespace]['package_assets_directory'], '/').'/'.$url; return Vite::useHotFile($viters[$namespace]['hot_file']) ->useBuildDirectory($viters[$namespace]['build_directory']) ->asset($viteUrl); } /** * Set bagisto vite in current theme. * * @param mixed $entryPoints * @return mixed */ public function setBagistoVite($entryPoints, ?string $namespace = null) { /** * If the namespace is null, it means the theming system is activated. We use the request URI to * detect the theme and provide Vite assets based on the current theme. */ if (empty($namespace)) { $currentTheme = $this->current(); /** * Octane safety: Initialize theme if not set. */ if (! $currentTheme) { $this->ensureThemeInitialized(); $currentTheme = $this->current(); } return $currentTheme->setBagistoVite($entryPoints); } /** * If a namespace is provided, it means the developer knows what they are doing and must create the * registry in the provided configuration. We will analyze based on that. */ $viters = config('bagisto-vite.viters'); if (empty($viters[$namespace])) { throw new ViterNotFound($namespace); } return Vite::useHotFile($viters[$namespace]['hot_file']) ->useBuildDirectory($viters[$namespace]['build_directory']) ->withEntryPoints($entryPoints); } /** * Ensure theme is properly initialized (Octane Safety). * * @return void */ protected function ensureThemeInitialized() { if (! $this->activeTheme) { /** * Reload themes based on current request context. */ $this->loadThemes(); /** * Set default theme if no theme is set. */ if (! $this->activeTheme && ! empty($this->themes)) { $defaultTheme = $this->themes[0]; $this->set($defaultTheme->code); } } } }