v0.1.1 - Bootstrap frontend rendering via Twig templates
All checks were successful
Create Release Package / PHP Lint (push) Successful in 49s
Create Release Package / Build Release (push) Successful in 1m18s

Replace FSE block markup on the frontend with proper Bootstrap 5 HTML
rendered through Twig templates. The Site Editor remains functional for
admin editing while the public site outputs Bootstrap navbar, cards,
pagination, grid layout, and responsive components.

New PHP classes: TemplateController, ContextBuilder, NavWalker
New Twig templates: 20 files (base, pages, partials, components)
Enhanced TwigService with WordPress functions and globals

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-02-08 15:11:00 +01:00
parent d069a203b4
commit cb288d6e74
32 changed files with 1439 additions and 29 deletions

View File

@@ -11,6 +11,7 @@ namespace WPBootstrap\Twig;
use Twig\Environment;
use Twig\Loader\FilesystemLoader;
use Twig\TwigFunction;
use Twig\TwigFilter;
class TwigService
{
@@ -30,6 +31,8 @@ class TwigService
]);
$this->registerWordPressFunctions();
$this->registerWordPressGlobals();
$this->registerFilters();
}
public static function getInstance(): self
@@ -57,6 +60,7 @@ class TwigService
private function registerWordPressFunctions(): void
{
// Translation functions.
$this->twig->addFunction(new TwigFunction('__', function (string $text, string $domain = 'wp-bootstrap'): string {
return __($text, $domain);
}));
@@ -65,8 +69,88 @@ class TwigService
_e($text, $domain);
}));
$this->twig->addFunction(new TwigFunction('_n', function (string $single, string $plural, int $number, string $domain = 'wp-bootstrap'): string {
return _n($single, $plural, $number, $domain);
}));
// Escaping functions.
$this->twig->addFunction(new TwigFunction('esc_html', 'esc_html'));
$this->twig->addFunction(new TwigFunction('esc_attr', 'esc_attr'));
$this->twig->addFunction(new TwigFunction('esc_url', 'esc_url'));
// WordPress head/footer output (captured via output buffering).
$this->twig->addFunction(new TwigFunction('wp_head', function (): string {
ob_start();
wp_head();
return ob_get_clean();
}, ['is_safe' => ['html']]));
$this->twig->addFunction(new TwigFunction('wp_footer', function (): string {
ob_start();
wp_footer();
return ob_get_clean();
}, ['is_safe' => ['html']]));
$this->twig->addFunction(new TwigFunction('wp_body_open', function (): string {
ob_start();
wp_body_open();
return ob_get_clean();
}, ['is_safe' => ['html']]));
// HTML attribute helpers.
$this->twig->addFunction(new TwigFunction('language_attributes', function (): string {
ob_start();
language_attributes();
return ob_get_clean();
}, ['is_safe' => ['html']]));
$this->twig->addFunction(new TwigFunction('body_class', function (string $extra = ''): string {
ob_start();
body_class($extra);
return ob_get_clean();
}, ['is_safe' => ['html']]));
// URL and info helpers.
$this->twig->addFunction(new TwigFunction('home_url', function (string $path = '/'): string {
return home_url($path);
}));
$this->twig->addFunction(new TwigFunction('get_template_directory_uri', function (): string {
return get_template_directory_uri();
}));
$this->twig->addFunction(new TwigFunction('get_bloginfo', function (string $show): string {
return get_bloginfo($show);
}));
$this->twig->addFunction(new TwigFunction('get_search_query', function (): string {
return get_search_query();
}));
// Content filtering.
$this->twig->addFunction(new TwigFunction('wp_kses_post', function (string $content): string {
return wp_kses_post($content);
}, ['is_safe' => ['html']]));
// Formatting.
$this->twig->addFunction(new TwigFunction('number_format_i18n', function (float $number, int $decimals = 0): string {
return number_format_i18n($number, $decimals);
}));
}
private function registerWordPressGlobals(): void
{
$this->twig->addGlobal('site_name', get_bloginfo('name'));
$this->twig->addGlobal('site_description', get_bloginfo('description'));
$this->twig->addGlobal('site_url', home_url('/'));
$this->twig->addGlobal('theme_uri', get_template_directory_uri());
$this->twig->addGlobal('charset', get_bloginfo('charset'));
$this->twig->addGlobal('current_year', date('Y'));
}
private function registerFilters(): void
{
$this->twig->addFilter(new TwigFilter('wpautop', 'wpautop', ['is_safe' => ['html']]));
$this->twig->addFilter(new TwigFilter('wp_kses_post', 'wp_kses_post', ['is_safe' => ['html']]));
}
}