navWalker = new NavWalker(); } /** * Build the complete context for the current request. */ public function build(): array { $context = [ '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(), ]; 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(); } 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, 'title' => get_the_title(), '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(), 'title' => get_the_title(), '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 [ 'title' => get_the_archive_title(), 'description' => get_the_archive_description(), ]; } /** * 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, 'author' => $comment->comment_author, 'author_url' => $comment->comment_author_url, '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' => '

', 'title_reply_after' => '

', 'class_form' => 'needs-validation', 'class_submit' => 'btn btn-primary', 'submit_button' => '', 'comment_field' => '
', ]); 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'] = [ 'title' => get_the_title($prev), 'url' => get_permalink($prev), ]; } if ($next) { $navigation['next'] = [ 'title' => get_the_title($next), '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(), 'title' => get_the_title(), '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. */ private function getSidebarData(): array { return [ 'recent_posts' => $this->getSidebarRecentPosts(), 'tags' => $this->getSidebarTags(), ]; } /** * 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[] = [ 'title' => get_the_title(), '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; } /** * 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'); } /** * 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; } }