233 lines
6.8 KiB
PHP
233 lines
6.8 KiB
PHP
<?php
|
|
/**
|
|
* AKSRT Docs Presenter
|
|
*
|
|
* Builds document tree structures, breadcrumb chains,
|
|
* and chapter navigation for the docs mode.
|
|
*
|
|
* @package AKSRT
|
|
*/
|
|
|
|
defined('ABSPATH') || exit;
|
|
|
|
class AKSRT_Docs_Presenter {
|
|
|
|
private static $instance = null;
|
|
|
|
private function __construct() {}
|
|
|
|
public static function get_instance(): self {
|
|
if (null === self::$instance) {
|
|
self::$instance = new self();
|
|
}
|
|
return self::$instance;
|
|
}
|
|
|
|
/**
|
|
* Get the document root page ID for a given page.
|
|
*
|
|
* @param int $page_id Current page ID.
|
|
* @return int Root page ID (self if top-level, parent otherwise).
|
|
*/
|
|
public function get_root_id(int $page_id): int {
|
|
$parent_id = wp_get_post_parent_id($page_id);
|
|
return $parent_id ?: $page_id;
|
|
}
|
|
|
|
/**
|
|
* Build a flat ordered list of all pages under a document root.
|
|
*
|
|
* @param int $root_id Root page ID.
|
|
* @param int $max_depth Maximum depth (default 4).
|
|
* @return array Array of page objects with added '_depth' property.
|
|
*/
|
|
public function get_flat_tree(int $root_id, int $max_depth = 4): array {
|
|
$result = [];
|
|
$this->collect_children($root_id, $result, 0, $max_depth);
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Recursive helper to collect pages in order.
|
|
*/
|
|
private function collect_children(int $parent_id, array &$result, int $depth, int $max_depth): void {
|
|
if ($depth >= $max_depth) {
|
|
return;
|
|
}
|
|
|
|
$children = get_pages([
|
|
'parent' => $parent_id,
|
|
'sort_column' => 'menu_order, post_title',
|
|
'sort_order' => 'ASC',
|
|
'post_status' => 'publish',
|
|
]);
|
|
|
|
foreach ($children as $child) {
|
|
$child->_depth = $depth;
|
|
$result[] = $child;
|
|
$this->collect_children($child->ID, $result, $depth + 1, $max_depth);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get sibling pages for chapter navigation (prev/next).
|
|
*
|
|
* @param int $page_id Current page ID.
|
|
* @return array{prev: object|null, next: object|null}
|
|
*/
|
|
public function get_chapter_nav(int $page_id): array {
|
|
$parent_id = wp_get_post_parent_id($page_id);
|
|
|
|
$siblings = get_pages([
|
|
'parent' => $parent_id ?: 0,
|
|
'sort_column' => 'menu_order, post_title',
|
|
'sort_order' => 'ASC',
|
|
'post_status' => 'publish',
|
|
]);
|
|
|
|
$current_index = -1;
|
|
foreach ($siblings as $i => $sibling) {
|
|
if ((int) $sibling->ID === $page_id) {
|
|
$current_index = $i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
$prev = $current_index > 0 ? $siblings[$current_index - 1] : null;
|
|
$next = ($current_index >= 0 && $current_index < count($siblings) - 1)
|
|
? $siblings[$current_index + 1]
|
|
: null;
|
|
|
|
return ['prev' => $prev, 'next' => $next];
|
|
}
|
|
|
|
/**
|
|
* Get root-level document pages for the document navigation index.
|
|
*
|
|
* @param string $order Sort order: 'menu_order', 'date', 'title'.
|
|
* @return array
|
|
*/
|
|
public function get_root_pages(string $order = 'menu_order'): array {
|
|
$sort_column = 'menu_order, post_title';
|
|
if ($order === 'title') {
|
|
$sort_column = 'post_title';
|
|
} elseif ($order === 'date') {
|
|
$sort_column = 'post_date';
|
|
}
|
|
|
|
return get_pages([
|
|
'sort_column' => $sort_column,
|
|
'sort_order' => 'ASC',
|
|
'parent' => 0,
|
|
'hierarchical' => 0,
|
|
'post_status' => 'publish',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Get recently modified pages for the sidebar.
|
|
*
|
|
* @param int $count Number of pages to return.
|
|
* @return array
|
|
*/
|
|
public function get_recent_pages(int $count = 8): array {
|
|
return get_pages([
|
|
'sort_column' => 'post_modified',
|
|
'sort_order' => 'DESC',
|
|
'number' => $count,
|
|
'post_status' => 'publish',
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Build breadcrumb trail for a document page.
|
|
*
|
|
* @param int $page_id Current page ID.
|
|
* @return array Array of {id, title, url} objects.
|
|
*/
|
|
public function get_breadcrumbs(int $page_id): array {
|
|
$trail = [];
|
|
$current = get_post($page_id);
|
|
|
|
while ($current) {
|
|
array_unshift($trail, [
|
|
'id' => $current->ID,
|
|
'title' => $current->post_title,
|
|
'url' => get_permalink($current->ID),
|
|
]);
|
|
$parent_id = wp_get_post_parent_id($current->ID);
|
|
$current = $parent_id ? get_post($parent_id) : null;
|
|
}
|
|
|
|
return $trail;
|
|
}
|
|
|
|
/**
|
|
* Search document pages by keyword.
|
|
*
|
|
* @param string $keyword Search keyword.
|
|
* @param int $root_id Optional root page ID to scope search.
|
|
* @param int $limit Max results.
|
|
* @return array Array of page objects with 'url' and 'excerpt' added.
|
|
*/
|
|
public function search_pages(string $keyword, int $root_id = 0, int $limit = 20): array {
|
|
$args = [
|
|
'post_type' => 'page',
|
|
'post_status' => 'publish',
|
|
's' => $keyword,
|
|
'posts_per_page' => $limit,
|
|
'orderby' => 'relevance',
|
|
];
|
|
|
|
if ($root_id > 0) {
|
|
$tree_ids = [$root_id];
|
|
$this->collect_children($root_id, $tree_ids, 0, 4);
|
|
$ids = array_map(static function ($item) {
|
|
return is_object($item) ? $item->ID : (int) $item;
|
|
}, $tree_ids);
|
|
$ids[] = $root_id;
|
|
$args['post__in'] = array_unique($ids);
|
|
}
|
|
|
|
$query = new WP_Query($args);
|
|
$results = [];
|
|
|
|
foreach ($query->posts as $post) {
|
|
$results[] = [
|
|
'id' => $post->ID,
|
|
'title' => $post->post_title,
|
|
'url' => get_permalink($post->ID),
|
|
'excerpt' => wp_trim_words(wp_strip_all_tags($post->post_content), 30),
|
|
'modified' => $post->post_modified,
|
|
'parent' => (int) $post->post_parent,
|
|
];
|
|
}
|
|
|
|
wp_reset_postdata();
|
|
return $results;
|
|
}
|
|
|
|
/**
|
|
* Check whether a given page belongs to the docs mode.
|
|
*
|
|
* @param int $page_id Page ID.
|
|
* @return bool
|
|
*/
|
|
public function is_docs_page(int $page_id): bool {
|
|
if (!class_exists('AKSRT_Settings')) {
|
|
return false;
|
|
}
|
|
|
|
$settings = AKSRT_Settings::get_docs_mode_settings();
|
|
if (empty($settings['enabled'])) {
|
|
return false;
|
|
}
|
|
|
|
$template = get_page_template_slug($page_id);
|
|
return in_array($template, [
|
|
'templates/pages/page-documentnav.php',
|
|
'templates/pages/page-special.php',
|
|
], true);
|
|
}
|
|
}
|