2026-02-08 15:11:00 +01:00
|
|
|
<?php
|
|
|
|
|
/**
|
|
|
|
|
* Twig Template Context Builder.
|
|
|
|
|
*
|
|
|
|
|
* Gathers WordPress data into structured arrays for Twig templates.
|
|
|
|
|
*
|
|
|
|
|
* @package WPBootstrap
|
|
|
|
|
* @since 0.1.1
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
namespace WPBootstrap\Template;
|
|
|
|
|
|
|
|
|
|
class ContextBuilder
|
|
|
|
|
{
|
|
|
|
|
private NavWalker $navWalker;
|
|
|
|
|
|
|
|
|
|
public function __construct()
|
|
|
|
|
{
|
|
|
|
|
$this->navWalker = new NavWalker();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Build the complete context for the current request.
|
|
|
|
|
*/
|
|
|
|
|
public function build(): array
|
|
|
|
|
{
|
|
|
|
|
$context = [
|
2026-02-08 16:05:29 +01:00
|
|
|
'site' => $this->getSiteData(),
|
|
|
|
|
'menu' => $this->getMenuData('primary'),
|
|
|
|
|
'footer_menu' => $this->getMenuData('footer'),
|
|
|
|
|
'dark_mode' => true,
|
|
|
|
|
'layout' => 'default',
|
|
|
|
|
'header_variant' => $this->getHeaderVariant(),
|
|
|
|
|
'footer_variant' => $this->getFooterVariant(),
|
2026-02-08 15:11:00 +01:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
if (is_singular()) {
|
|
|
|
|
$context['post'] = $this->getPostData();
|
|
|
|
|
|
|
|
|
|
if (is_singular('post')) {
|
|
|
|
|
$context['comments'] = $this->getCommentsData();
|
|
|
|
|
$context['post_navigation'] = $this->getPostNavigation();
|
|
|
|
|
$context['more_posts'] = $this->getMorePosts();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_home() || is_archive() || is_search()) {
|
|
|
|
|
$context['posts'] = $this->getPostsLoop();
|
|
|
|
|
$context['pagination'] = $this->getPagination();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_archive()) {
|
|
|
|
|
$context['archive'] = $this->getArchiveData();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (is_search()) {
|
|
|
|
|
$context['search_query'] = get_search_query();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sidebar layout detection.
|
|
|
|
|
if (is_home()) {
|
|
|
|
|
$pageId = (int) get_option('page_for_posts');
|
|
|
|
|
if ($pageId) {
|
|
|
|
|
$templateSlug = get_page_template_slug($pageId);
|
|
|
|
|
if ($templateSlug === 'home-sidebar') {
|
|
|
|
|
$context['layout'] = 'sidebar';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
$context['sidebar'] = $this->getSidebarData();
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 21:44:45 +01:00
|
|
|
// Sidebar data for pages/posts using the "Page with Sidebar" template.
|
|
|
|
|
if (is_page() || is_singular('post')) {
|
|
|
|
|
$slug = get_page_template_slug();
|
|
|
|
|
if ($slug === 'page-sidebar') {
|
|
|
|
|
$context['sidebar'] = $this->getSidebarData();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-08 15:11:00 +01:00
|
|
|
return $context;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get global site information.
|
|
|
|
|
*/
|
|
|
|
|
private function getSiteData(): array
|
|
|
|
|
{
|
|
|
|
|
return [
|
|
|
|
|
'name' => get_bloginfo('name'),
|
|
|
|
|
'description' => get_bloginfo('description'),
|
|
|
|
|
'url' => home_url('/'),
|
|
|
|
|
'charset' => get_bloginfo('charset'),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get navigation menu items for a location.
|
|
|
|
|
*/
|
|
|
|
|
private function getMenuData(string $location): array
|
|
|
|
|
{
|
|
|
|
|
$locations = get_nav_menu_locations();
|
|
|
|
|
|
|
|
|
|
if (isset($locations[$location])) {
|
|
|
|
|
$menu = wp_get_nav_menu_object($locations[$location]);
|
|
|
|
|
if ($menu) {
|
|
|
|
|
$items = wp_get_nav_menu_items($menu->term_id);
|
|
|
|
|
if ($items) {
|
|
|
|
|
return $this->navWalker->buildTree($items);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fallback for primary: list top-level pages.
|
|
|
|
|
if ($location === 'primary') {
|
|
|
|
|
return $this->getPagesFallback();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Fallback menu from published pages.
|
|
|
|
|
*/
|
|
|
|
|
private function getPagesFallback(): array
|
|
|
|
|
{
|
|
|
|
|
$pages = get_pages([
|
|
|
|
|
'sort_column' => 'menu_order,post_title',
|
|
|
|
|
'parent' => 0,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$items = [];
|
|
|
|
|
foreach ($pages as $page) {
|
|
|
|
|
$items[] = [
|
|
|
|
|
'id' => $page->ID,
|
|
|
|
|
'title' => $page->post_title,
|
|
|
|
|
'url' => get_permalink($page->ID),
|
|
|
|
|
'target' => '',
|
|
|
|
|
'classes' => '',
|
|
|
|
|
'active' => is_page($page->ID),
|
|
|
|
|
'children' => [],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $items;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get single post/page data.
|
|
|
|
|
*/
|
|
|
|
|
private function getPostData(): array
|
|
|
|
|
{
|
|
|
|
|
global $post;
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'id' => $post->ID,
|
2026-02-25 20:20:19 +01:00
|
|
|
'title' => wp_specialchars_decode( get_the_title() ),
|
2026-02-08 15:11:00 +01:00
|
|
|
'url' => get_permalink(),
|
|
|
|
|
'content' => apply_filters('the_content', get_the_content()),
|
|
|
|
|
'excerpt' => get_the_excerpt(),
|
|
|
|
|
'date' => get_the_date(),
|
|
|
|
|
'date_iso' => get_the_date('c'),
|
|
|
|
|
'modified' => get_the_modified_date(),
|
|
|
|
|
'author' => [
|
|
|
|
|
'name' => get_the_author(),
|
|
|
|
|
'url' => get_author_posts_url(get_the_author_meta('ID')),
|
|
|
|
|
],
|
|
|
|
|
'thumbnail' => get_the_post_thumbnail_url($post->ID, 'large') ?: '',
|
|
|
|
|
'categories' => $this->getTermsList('category'),
|
|
|
|
|
'tags' => $this->getTermsList('post_tag'),
|
|
|
|
|
'type' => get_post_type(),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get posts for the main query loop.
|
|
|
|
|
*/
|
|
|
|
|
private function getPostsLoop(): array
|
|
|
|
|
{
|
|
|
|
|
global $wp_query;
|
|
|
|
|
$posts = [];
|
|
|
|
|
|
|
|
|
|
if ($wp_query->have_posts()) {
|
|
|
|
|
while ($wp_query->have_posts()) {
|
|
|
|
|
$wp_query->the_post();
|
|
|
|
|
$posts[] = [
|
|
|
|
|
'id' => get_the_ID(),
|
2026-02-25 20:20:19 +01:00
|
|
|
'title' => wp_specialchars_decode( get_the_title() ),
|
2026-02-08 15:11:00 +01:00
|
|
|
'url' => get_permalink(),
|
|
|
|
|
'excerpt' => get_the_excerpt(),
|
|
|
|
|
'date' => get_the_date(),
|
|
|
|
|
'date_iso' => get_the_date('c'),
|
|
|
|
|
'author' => [
|
|
|
|
|
'name' => get_the_author(),
|
|
|
|
|
'url' => get_author_posts_url(get_the_author_meta('ID')),
|
|
|
|
|
],
|
|
|
|
|
'thumbnail' => get_the_post_thumbnail_url(null, 'medium_large') ?: '',
|
|
|
|
|
'categories' => $this->getTermsList('category'),
|
|
|
|
|
'tags' => $this->getTermsList('post_tag'),
|
|
|
|
|
'read_more' => __('Read more', 'wp-bootstrap'),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
wp_reset_postdata();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $posts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Build pagination data.
|
|
|
|
|
*/
|
|
|
|
|
private function getPagination(): array
|
|
|
|
|
{
|
|
|
|
|
global $wp_query;
|
|
|
|
|
|
|
|
|
|
$totalPages = (int) $wp_query->max_num_pages;
|
|
|
|
|
$currentPage = max(1, get_query_var('paged'));
|
|
|
|
|
|
|
|
|
|
if ($totalPages <= 1) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$pages = [];
|
|
|
|
|
for ($i = 1; $i <= $totalPages; $i++) {
|
|
|
|
|
$pages[] = [
|
|
|
|
|
'number' => $i,
|
|
|
|
|
'url' => get_pagenum_link($i),
|
|
|
|
|
'is_current' => ($i === $currentPage),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'pages' => $pages,
|
|
|
|
|
'current' => $currentPage,
|
|
|
|
|
'total' => $totalPages,
|
|
|
|
|
'prev_url' => ($currentPage > 1) ? get_pagenum_link($currentPage - 1) : null,
|
|
|
|
|
'next_url' => ($currentPage < $totalPages) ? get_pagenum_link($currentPage + 1) : null,
|
|
|
|
|
'prev_text' => __('Previous', 'wp-bootstrap'),
|
|
|
|
|
'next_text' => __('Next', 'wp-bootstrap'),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get archive page data.
|
|
|
|
|
*/
|
|
|
|
|
private function getArchiveData(): array
|
|
|
|
|
{
|
|
|
|
|
return [
|
2026-02-19 13:23:33 +01:00
|
|
|
// wp_kses_post() allows safe HTML (headings, links, spans) while stripping
|
|
|
|
|
// script/event-handler attributes that could be injected via term descriptions.
|
|
|
|
|
'title' => wp_kses_post(get_the_archive_title()),
|
|
|
|
|
'description' => wp_kses_post(get_the_archive_description()),
|
2026-02-08 15:11:00 +01:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get comments for the current post.
|
|
|
|
|
*/
|
|
|
|
|
private function getCommentsData(): array
|
|
|
|
|
{
|
|
|
|
|
$postId = get_the_ID();
|
|
|
|
|
$count = (int) get_comments_number($postId);
|
|
|
|
|
|
|
|
|
|
$comments = get_comments([
|
|
|
|
|
'post_id' => $postId,
|
|
|
|
|
'status' => 'approve',
|
|
|
|
|
'orderby' => 'comment_date_gmt',
|
|
|
|
|
'order' => 'ASC',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
return [
|
|
|
|
|
'list' => $this->buildCommentTree($comments),
|
|
|
|
|
'count' => $count,
|
|
|
|
|
'title' => sprintf(
|
|
|
|
|
_n('%s Comment', '%s Comments', $count, 'wp-bootstrap'),
|
|
|
|
|
number_format_i18n($count)
|
|
|
|
|
),
|
|
|
|
|
'form' => $this->getCommentFormHtml(),
|
|
|
|
|
'is_open' => comments_open($postId),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Build a nested comment tree from flat comments.
|
|
|
|
|
*/
|
|
|
|
|
private function buildCommentTree(array $comments, int $parentId = 0): array
|
|
|
|
|
{
|
|
|
|
|
$tree = [];
|
|
|
|
|
|
|
|
|
|
foreach ($comments as $comment) {
|
|
|
|
|
if ((int) $comment->comment_parent !== $parentId) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$tree[] = [
|
|
|
|
|
'id' => (int) $comment->comment_ID,
|
2026-02-19 13:23:33 +01:00
|
|
|
// Escape at source — comment_author is user-supplied, store as safe text.
|
|
|
|
|
'author' => esc_html($comment->comment_author),
|
|
|
|
|
// esc_url() strips dangerous schemes (javascript:, data:) and encodes for HTML.
|
|
|
|
|
'author_url' => esc_url($comment->comment_author_url),
|
2026-02-08 15:11:00 +01:00
|
|
|
'avatar_url' => get_avatar_url($comment, ['size' => 40]),
|
|
|
|
|
'date' => get_comment_date('', $comment),
|
|
|
|
|
'date_iso' => get_comment_date('c', $comment),
|
|
|
|
|
'content' => apply_filters('comment_text', $comment->comment_content, $comment),
|
|
|
|
|
'edit_url' => current_user_can('edit_comment', $comment->comment_ID)
|
|
|
|
|
? get_edit_comment_link($comment)
|
|
|
|
|
: '',
|
|
|
|
|
'reply_url' => get_comment_reply_link([
|
|
|
|
|
'depth' => 1,
|
|
|
|
|
'max_depth' => get_option('thread_comments_depth', 5),
|
|
|
|
|
], $comment),
|
|
|
|
|
'children' => $this->buildCommentTree($comments, (int) $comment->comment_ID),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $tree;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Capture the WordPress comment form HTML.
|
|
|
|
|
*/
|
|
|
|
|
private function getCommentFormHtml(): string
|
|
|
|
|
{
|
|
|
|
|
if (! comments_open()) {
|
|
|
|
|
return '';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ob_start();
|
|
|
|
|
comment_form([
|
|
|
|
|
'title_reply' => __('Leave a Comment', 'wp-bootstrap'),
|
|
|
|
|
'title_reply_before' => '<h3 id="reply-title" class="comment-reply-title h5 mb-3">',
|
|
|
|
|
'title_reply_after' => '</h3>',
|
|
|
|
|
'class_form' => 'needs-validation',
|
|
|
|
|
'class_submit' => 'btn btn-primary',
|
|
|
|
|
'submit_button' => '<input name="%1$s" type="submit" id="%2$s" class="%3$s" value="%4$s" />',
|
|
|
|
|
'comment_field' => '<div class="mb-3"><label for="comment" class="form-label">' . __('Comment', 'wp-bootstrap') . '</label><textarea id="comment" name="comment" class="form-control" rows="5" required></textarea></div>',
|
|
|
|
|
]);
|
|
|
|
|
return ob_get_clean();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get previous/next post navigation.
|
|
|
|
|
*/
|
|
|
|
|
private function getPostNavigation(): array
|
|
|
|
|
{
|
|
|
|
|
$prev = get_previous_post();
|
|
|
|
|
$next = get_next_post();
|
|
|
|
|
|
|
|
|
|
$navigation = [];
|
|
|
|
|
|
|
|
|
|
if ($prev) {
|
|
|
|
|
$navigation['previous'] = [
|
2026-02-25 20:20:19 +01:00
|
|
|
'title' => wp_specialchars_decode( get_the_title($prev) ),
|
2026-02-08 15:11:00 +01:00
|
|
|
'url' => get_permalink($prev),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($next) {
|
|
|
|
|
$navigation['next'] = [
|
2026-02-25 20:20:19 +01:00
|
|
|
'title' => wp_specialchars_decode( get_the_title($next) ),
|
2026-02-08 15:11:00 +01:00
|
|
|
'url' => get_permalink($next),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $navigation;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get recent posts for the "More posts" section.
|
|
|
|
|
*/
|
|
|
|
|
private function getMorePosts(int $count = 3): array
|
|
|
|
|
{
|
|
|
|
|
$currentId = get_the_ID();
|
|
|
|
|
|
|
|
|
|
$query = new \WP_Query([
|
|
|
|
|
'posts_per_page' => $count,
|
|
|
|
|
'post__not_in' => [$currentId],
|
|
|
|
|
'orderby' => 'date',
|
|
|
|
|
'order' => 'DESC',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$posts = [];
|
|
|
|
|
if ($query->have_posts()) {
|
|
|
|
|
while ($query->have_posts()) {
|
|
|
|
|
$query->the_post();
|
|
|
|
|
$posts[] = [
|
|
|
|
|
'id' => get_the_ID(),
|
2026-02-25 20:20:19 +01:00
|
|
|
'title' => wp_specialchars_decode( get_the_title() ),
|
2026-02-08 15:11:00 +01:00
|
|
|
'url' => get_permalink(),
|
|
|
|
|
'date' => get_the_date(),
|
|
|
|
|
'date_iso' => get_the_date('c'),
|
|
|
|
|
'thumbnail' => get_the_post_thumbnail_url(null, 'medium_large') ?: '',
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
wp_reset_postdata();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $posts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get sidebar widget data.
|
2026-02-08 18:43:09 +01:00
|
|
|
*
|
|
|
|
|
* If the 'primary-sidebar' widget area has widgets assigned,
|
|
|
|
|
* their rendered HTML is returned. Otherwise, fallback data
|
|
|
|
|
* (recent posts, tags) is provided for the default Twig sidebar.
|
2026-02-08 15:11:00 +01:00
|
|
|
*/
|
|
|
|
|
private function getSidebarData(): array
|
|
|
|
|
{
|
2026-02-08 18:43:09 +01:00
|
|
|
$widgets_active = is_active_sidebar( 'primary-sidebar' );
|
|
|
|
|
$widgets_html = '';
|
|
|
|
|
if ( $widgets_active ) {
|
|
|
|
|
ob_start();
|
|
|
|
|
dynamic_sidebar( 'primary-sidebar' );
|
|
|
|
|
$widgets_html = ob_get_clean();
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-08 15:11:00 +01:00
|
|
|
return [
|
2026-02-08 18:43:09 +01:00
|
|
|
'widgets_active' => $widgets_active,
|
|
|
|
|
'widgets_html' => $widgets_html,
|
|
|
|
|
'recent_posts' => $this->getSidebarRecentPosts(),
|
|
|
|
|
'tags' => $this->getSidebarTags(),
|
2026-02-08 15:11:00 +01:00
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get recent posts for sidebar.
|
|
|
|
|
*/
|
|
|
|
|
private function getSidebarRecentPosts(int $count = 4): array
|
|
|
|
|
{
|
|
|
|
|
$query = new \WP_Query([
|
|
|
|
|
'posts_per_page' => $count,
|
|
|
|
|
'orderby' => 'date',
|
|
|
|
|
'order' => 'DESC',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
$posts = [];
|
|
|
|
|
if ($query->have_posts()) {
|
|
|
|
|
while ($query->have_posts()) {
|
|
|
|
|
$query->the_post();
|
|
|
|
|
$posts[] = [
|
2026-02-25 20:20:19 +01:00
|
|
|
'title' => wp_specialchars_decode( get_the_title() ),
|
2026-02-08 15:11:00 +01:00
|
|
|
'url' => get_permalink(),
|
|
|
|
|
'date' => get_the_date(),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
wp_reset_postdata();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $posts;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get tags for sidebar tag cloud.
|
|
|
|
|
*/
|
|
|
|
|
private function getSidebarTags(int $count = 15): array
|
|
|
|
|
{
|
|
|
|
|
$tags = get_tags([
|
|
|
|
|
'number' => $count,
|
|
|
|
|
'orderby' => 'count',
|
|
|
|
|
'order' => 'DESC',
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
if (! $tags || is_wp_error($tags)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$items = [];
|
|
|
|
|
foreach ($tags as $tag) {
|
|
|
|
|
$items[] = [
|
|
|
|
|
'name' => $tag->name,
|
|
|
|
|
'url' => get_tag_link($tag->term_id),
|
|
|
|
|
'count' => $tag->count,
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $items;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-08 16:05:29 +01:00
|
|
|
/**
|
|
|
|
|
* Get the active header variant.
|
|
|
|
|
*
|
|
|
|
|
* @since 0.2.0
|
|
|
|
|
*/
|
|
|
|
|
private function getHeaderVariant(): string
|
|
|
|
|
{
|
|
|
|
|
return get_theme_mod('wp_bootstrap_header_variant', 'default');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the active footer variant.
|
|
|
|
|
*
|
|
|
|
|
* @since 0.2.0
|
|
|
|
|
*/
|
|
|
|
|
private function getFooterVariant(): string
|
|
|
|
|
{
|
|
|
|
|
return get_theme_mod('wp_bootstrap_footer_variant', 'default');
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-08 15:11:00 +01:00
|
|
|
/**
|
|
|
|
|
* Get terms list for a taxonomy.
|
|
|
|
|
*/
|
|
|
|
|
private function getTermsList(string $taxonomy): array
|
|
|
|
|
{
|
|
|
|
|
$terms = get_the_terms(get_the_ID(), $taxonomy);
|
|
|
|
|
if (! $terms || is_wp_error($terms)) {
|
|
|
|
|
return [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$list = [];
|
|
|
|
|
foreach ($terms as $term) {
|
|
|
|
|
$list[] = [
|
|
|
|
|
'name' => $term->name,
|
|
|
|
|
'url' => get_term_link($term),
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $list;
|
|
|
|
|
}
|
|
|
|
|
}
|