Skip to content

Commit f9e9918

Browse files
committed
fix(patterns): resolve theme pattern directory from theme metadata
Theme Gutenberg patterns were never registered. PatternService built the pattern directory from WP_Theme::get_theme_root(), which returns WordPress' raw theme root and never passes through the `theme_root` filter Pollora installs. On the default skeleton layout it resolved to WP_CONTENT_DIR/themes instead of the configured theme.path, so the is_dir() guard returned early and no pattern file was ever read — silently, with no error or log entry. The directory is now derived from ThemeMetadata, the authoritative source for Pollora theme paths. Discovery also walks the full theme ancestry via ThemeService::getParentThemes() (ancestors first, so the active theme can override an inherited slug) instead of a single parent level, and the WP_Theme instance used for TextDomain translation is built with an explicit theme root. Also removes PatternRegistrar, an unreferenced duplicate of PatternService carrying the same bug via get_stylesheet_directory(). Backport of 01605ba onto the v13.4.x line. Closes #295 Claude-Session: https://claude.ai/code/session_01Lsb47rNQPxRzFPUxCAkwxU
1 parent f28d236 commit f9e9918

6 files changed

Lines changed: 258 additions & 179 deletions

File tree

CHANGELOG.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,26 @@ All notable changes to the Pollora framework will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8-
## [Unreleased](https://github.com/Pollora/framework/compare/v13.4.0...develop)
8+
## [Unreleased](https://github.com/Pollora/framework/compare/v13.4.3...develop)
9+
10+
## [v13.4.3](https://github.com/Pollora/framework/compare/v13.4.2...v13.4.3) - 2026-08-31
11+
12+
### Fixed
13+
- Theme Gutenberg patterns are registered again ([#295](https://github.com/Pollora/framework/issues/295)) — `PatternService` resolved the pattern directory from `WP_Theme::get_theme_root()`, which bypasses the `theme_root` filter and pointed at `WP_CONTENT_DIR/themes` instead of the configured `theme.path`; the directory is now derived from `ThemeMetadata`
14+
- Patterns are discovered across the full theme ancestry (ancestors first, so the active theme can override an inherited slug) instead of a single parent level
15+
16+
### Removed
17+
- `Pollora\BlockPattern\Infrastructure\Registrars\PatternRegistrar` — dead duplicate of `PatternService` carrying the same theme-root resolution bug
18+
19+
## [v13.4.2](https://github.com/Pollora/framework/compare/v13.4.1...v13.4.2) - 2026-06-29
20+
21+
### Fixed
22+
- Force `$_SERVER['HTTPS']` when `APP_URL` uses HTTPS scheme
23+
24+
## [v13.4.1](https://github.com/Pollora/framework/compare/v13.4.0...v13.4.1) - 2026-06-29
25+
26+
### Fixed
27+
- Use `config('app.url')` for `WP_HOME`/`WP_SITEURL` instead of `url()` helper
928

1029
## [v13.4.0](https://github.com/Pollora/framework/compare/v13.3.0...v13.4.0) - 2026-04-22
1130

src/BlockPattern/Application/Services/PatternService.php

Lines changed: 66 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Pollora\BlockPattern\Domain\Support\PatternConstants;
1313
use Pollora\Config\Domain\Contracts\ConfigRepositoryInterface;
1414
use Pollora\Theme\Domain\Contracts\ThemeService;
15+
use Pollora\Theme\Domain\Models\ThemeMetadata;
1516

1617
/**
1718
* Application service for pattern use cases.
@@ -84,24 +85,18 @@ private function registerCategories(): void
8485
*/
8586
private function registerPatterns(): void
8687
{
87-
if (! function_exists('wp_get_theme')) {
88-
return;
89-
}
90-
9188
$theme = $this->themeService->theme();
9289

93-
if (! function_exists('get_stylesheet_directory') || ! $theme) {
90+
if (! $theme instanceof ThemeMetadata) {
9491
return;
9592
}
9693

97-
$parentTheme = $theme->getParentTheme();
98-
99-
if ($parentTheme) {
100-
$this->registerPattern($parentTheme);
94+
// Ancestors first, so the active theme can override an inherited pattern slug.
95+
foreach (array_reverse($this->themeService->getParentThemes()) as $parentTheme) {
96+
$this->registerPatternsFromTheme($parentTheme);
10197
}
10298

103-
$this->registerPattern($theme->getName());
104-
99+
$this->registerPatternsFromTheme($theme);
105100
}
106101

107102
/**
@@ -111,26 +106,77 @@ private function registerPatterns(): void
111106
* directory, following the established directory structure.
112107
*
113108
* @param string $themeName Name of the theme to process
114-
*
115-
* @throws \RuntimeException If WordPress functions are not available
116109
*/
117110
public function registerPattern(string $themeName): void
118111
{
119-
if (! function_exists('wp_get_theme')) {
120-
return;
121-
}
112+
$theme = $this->resolveTheme($themeName);
122113

123-
$theme = wp_get_theme($themeName);
124-
$themeRoot = $theme->get_theme_root();
114+
if ($theme instanceof ThemeMetadata) {
115+
$this->registerPatternsFromTheme($theme);
116+
}
117+
}
125118

126-
$patternDir = $themeRoot.DIRECTORY_SEPARATOR.$themeName.PatternConstants::PATTERN_DIRECTORY;
119+
/**
120+
* Register every pattern shipped by the given theme.
121+
*
122+
* The pattern directory is resolved from the theme metadata rather than from
123+
* WordPress: `WP_Theme::get_theme_root()` returns the raw theme root and never
124+
* goes through the `theme_root` filter Pollora installs, so it points at
125+
* `WP_CONTENT_DIR/themes` instead of the configured `theme.path`.
126+
*/
127+
private function registerPatternsFromTheme(ThemeMetadata $theme): void
128+
{
129+
$patternDir = $theme->getBasePath().PatternConstants::PATTERN_DIRECTORY;
127130

128131
// Skip if directory doesn't exist
129132
if (! is_dir($patternDir)) {
130133
return;
131134
}
132135

133-
$this->registerPatternsFromDirectory($patternDir, $theme);
136+
$this->registerPatternsFromDirectory($patternDir, $this->wordPressTheme($theme));
137+
}
138+
139+
/**
140+
* Find the metadata of a theme by name within the active theme hierarchy.
141+
*
142+
* Falls back to a metadata instance built from the active theme's root for
143+
* themes that are not part of the current hierarchy.
144+
*/
145+
private function resolveTheme(string $themeName): ?ThemeMetadata
146+
{
147+
$activeTheme = $this->themeService->theme();
148+
149+
$hierarchy = $activeTheme instanceof ThemeMetadata
150+
? [$activeTheme, ...$this->themeService->getParentThemes()]
151+
: $this->themeService->getParentThemes();
152+
153+
foreach ($hierarchy as $theme) {
154+
if ($theme->getName() === $themeName) {
155+
return $theme;
156+
}
157+
}
158+
159+
if (! $activeTheme instanceof ThemeMetadata) {
160+
return null;
161+
}
162+
163+
return new ThemeMetadata($themeName, dirname($activeTheme->getBasePath()));
164+
}
165+
166+
/**
167+
* Get the WordPress theme instance backing a theme, for metadata translation.
168+
*
169+
* The theme root is passed explicitly so WordPress resolves the theme from
170+
* Pollora's theme path instead of its own default one. Returns the metadata
171+
* itself when WordPress is unavailable; the extractor then skips translation.
172+
*/
173+
private function wordPressTheme(ThemeMetadata $theme): object
174+
{
175+
if (! function_exists('wp_get_theme')) {
176+
return $theme;
177+
}
178+
179+
return \wp_get_theme($theme->getName(), dirname($theme->getBasePath()));
134180
}
135181

136182
/**

src/BlockPattern/Infrastructure/Registrars/PatternRegistrar.php

Lines changed: 0 additions & 157 deletions
This file was deleted.

src/Theme/Application/Services/ThemeManager.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,9 @@ public function theme(): ?ThemeMetadata
245245
return $this->theme;
246246
}
247247

248+
/**
249+
* @return array<int, ThemeMetadata>
250+
*/
248251
public function getParentThemes(): array
249252
{
250253
return $this->parentThemes;

src/Theme/Domain/Contracts/ThemeService.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ public function theme(): ?ThemeMetadata;
4343
public function parent(): ?string;
4444

4545
/**
46-
* Get all parent themes
46+
* Get all parent themes, closest ancestor first
47+
*
48+
* @return array<int, ThemeMetadata>
4749
*/
4850
public function getParentThemes(): array;
4951

0 commit comments

Comments
 (0)