MD Umbau
This commit is contained in:
30
kb-markdown-importer/includes/Frontend/BreadcrumbBuilder.php
Normal file
30
kb-markdown-importer/includes/Frontend/BreadcrumbBuilder.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KbMarkdownImporter\Frontend;
|
||||
|
||||
use KbMarkdownImporter\Plugin;
|
||||
|
||||
final class BreadcrumbBuilder
|
||||
{
|
||||
public function build(array $parts): string
|
||||
{
|
||||
$base = trim((string) Plugin::settings()['docs_base_slug'], '/');
|
||||
$items = [
|
||||
sprintf('<a href="%s">%s</a>', esc_url(home_url('/' . $base . '/')), esc_html__('Docs', 'kb-markdown-importer')),
|
||||
];
|
||||
$path = $base;
|
||||
|
||||
foreach ($parts as $label => $slug) {
|
||||
if ('' === (string) $slug) {
|
||||
$items[] = esc_html((string) $label);
|
||||
continue;
|
||||
}
|
||||
|
||||
$path .= '/' . trim((string) $slug, '/');
|
||||
$items[] = sprintf('<a href="%s">%s</a>', esc_url(home_url('/' . $path . '/')), esc_html((string) $label));
|
||||
}
|
||||
|
||||
return '<nav class="kb-breadcrumbs" aria-label="Breadcrumb">' . implode('<span>/</span>', $items) . '</nav>';
|
||||
}
|
||||
}
|
||||
413
kb-markdown-importer/includes/Frontend/Router.php
Normal file
413
kb-markdown-importer/includes/Frontend/Router.php
Normal file
@@ -0,0 +1,413 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KbMarkdownImporter\Frontend;
|
||||
|
||||
use KbMarkdownImporter\Access\AccessController;
|
||||
use KbMarkdownImporter\Plugin;
|
||||
use KbMarkdownImporter\Repository\PageRepository;
|
||||
|
||||
final class Router
|
||||
{
|
||||
public function boot(): void
|
||||
{
|
||||
add_action('init', [$this, 'addRewriteRules']);
|
||||
add_filter('request', [$this, 'routeRequest']);
|
||||
add_filter('query_vars', [$this, 'addQueryVars']);
|
||||
add_action('template_redirect', [$this, 'dispatch']);
|
||||
}
|
||||
|
||||
public function addRewriteRules(): void
|
||||
{
|
||||
$base = trim((string) Plugin::settings()['docs_base_slug'], '/') ?: 'docs';
|
||||
add_rewrite_rule('^' . preg_quote($base, '#') . '/?$', 'index.php?kb_markdown_route=index', 'top');
|
||||
add_rewrite_rule('^' . preg_quote($base, '#') . '/([^/]+)/?$', 'index.php?kb_markdown_route=product&kb_product_slug=$matches[1]', 'top');
|
||||
add_rewrite_rule('^' . preg_quote($base, '#') . '/([^/]+)/([^/]+)/?$', 'index.php?kb_markdown_route=version&kb_product_slug=$matches[1]&kb_version_slug=$matches[2]', 'top');
|
||||
add_rewrite_rule('^' . preg_quote($base, '#') . '/([^/]+)/([^/]+)/(.+?)/?$', 'index.php?kb_markdown_route=page&kb_product_slug=$matches[1]&kb_version_slug=$matches[2]&kb_page_slug=$matches[3]', 'top');
|
||||
}
|
||||
|
||||
public function addQueryVars(array $vars): array
|
||||
{
|
||||
return array_merge($vars, ['kb_markdown_route', 'kb_product_slug', 'kb_version_slug', 'kb_page_slug']);
|
||||
}
|
||||
|
||||
public function routeRequest(array $queryVars): array
|
||||
{
|
||||
$route = $this->routeFromRequestUri();
|
||||
|
||||
if (! $route) {
|
||||
return $queryVars;
|
||||
}
|
||||
|
||||
$queryVars = [
|
||||
'kb_markdown_route' => $route['route'],
|
||||
];
|
||||
|
||||
if (! empty($route['product'])) {
|
||||
$queryVars['kb_product_slug'] = $route['product'];
|
||||
}
|
||||
|
||||
if (! empty($route['version'])) {
|
||||
$queryVars['kb_version_slug'] = $route['version'];
|
||||
}
|
||||
|
||||
if (! empty($route['page'])) {
|
||||
$queryVars['kb_page_slug'] = $route['page'];
|
||||
}
|
||||
|
||||
return $queryVars;
|
||||
}
|
||||
|
||||
public function dispatch(): void
|
||||
{
|
||||
$route = get_query_var('kb_markdown_route');
|
||||
$requestRoute = $this->routeFromRequestUri();
|
||||
|
||||
if (! $route && $requestRoute) {
|
||||
$route = $requestRoute['route'];
|
||||
}
|
||||
|
||||
if (! $route) {
|
||||
$queryRoute = $this->routeFromQuery();
|
||||
if (! $queryRoute) {
|
||||
return;
|
||||
}
|
||||
|
||||
$requestRoute = $queryRoute;
|
||||
$route = $queryRoute['route'];
|
||||
}
|
||||
|
||||
(new AccessController())->enforce();
|
||||
|
||||
get_header();
|
||||
|
||||
match ($route) {
|
||||
'index' => $this->renderIndex(),
|
||||
'product' => $this->renderProduct((string) ($requestRoute['product'] ?? get_query_var('kb_product_slug'))),
|
||||
'version' => $this->renderVersion((string) ($requestRoute['product'] ?? get_query_var('kb_product_slug')), (string) ($requestRoute['version'] ?? get_query_var('kb_version_slug'))),
|
||||
'page' => $this->renderPage((string) ($requestRoute['product'] ?? get_query_var('kb_product_slug')), (string) ($requestRoute['version'] ?? get_query_var('kb_version_slug')), trim((string) ($requestRoute['page'] ?? get_query_var('kb_page_slug')), '/')),
|
||||
default => $this->render404(),
|
||||
};
|
||||
|
||||
get_footer();
|
||||
exit;
|
||||
}
|
||||
|
||||
private function routeFromRequestUri(): array
|
||||
{
|
||||
$base = trim((string) Plugin::settings()['docs_base_slug'], '/') ?: 'docs';
|
||||
$path = (string) wp_parse_url((string) ($_SERVER['REQUEST_URI'] ?? ''), PHP_URL_PATH);
|
||||
$path = trim(rawurldecode($path), '/');
|
||||
|
||||
if ($path === $base) {
|
||||
return ['route' => 'index'];
|
||||
}
|
||||
|
||||
if (! str_starts_with($path . '/', $base . '/')) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$parts = array_values(array_filter(explode('/', substr($path, strlen($base))), static fn (string $part): bool => '' !== $part));
|
||||
|
||||
if (1 === count($parts)) {
|
||||
return ['route' => 'product', 'product' => sanitize_title($parts[0])];
|
||||
}
|
||||
|
||||
if (2 === count($parts)) {
|
||||
return ['route' => 'version', 'product' => sanitize_title($parts[0]), 'version' => sanitize_title($parts[1])];
|
||||
}
|
||||
|
||||
return [
|
||||
'route' => 'page',
|
||||
'product' => sanitize_title($parts[0] ?? ''),
|
||||
'version' => sanitize_title($parts[1] ?? ''),
|
||||
'page' => sanitize_title(implode('/', array_slice($parts, 2))),
|
||||
];
|
||||
}
|
||||
|
||||
private function routeFromQuery(): array
|
||||
{
|
||||
$route = sanitize_key(wp_unslash((string) ($_GET['kb_markdown_route'] ?? '')));
|
||||
|
||||
if (! $route) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
'route' => $route,
|
||||
'product' => sanitize_title(wp_unslash((string) ($_GET['kb_product_slug'] ?? ''))),
|
||||
'version' => sanitize_title(wp_unslash((string) ($_GET['kb_version_slug'] ?? ''))),
|
||||
'page' => sanitize_title(wp_unslash((string) ($_GET['kb_page_slug'] ?? ''))),
|
||||
];
|
||||
}
|
||||
|
||||
public static function shortcodeDocsIndex(): string
|
||||
{
|
||||
return (new TemplateLoader())->capture('documentation-index', [
|
||||
'products' => self::productsWithVersions(),
|
||||
'base_slug' => trim((string) Plugin::settings()['docs_base_slug'], '/'),
|
||||
'url_builder' => UrlBuilder::class,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function shortcodeDocsApp(array $atts = []): string
|
||||
{
|
||||
if (! (new AccessController())->canView()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$atts = shortcode_atts([
|
||||
'product' => '',
|
||||
'version' => '',
|
||||
'page' => '',
|
||||
], $atts, 'kb_docs');
|
||||
|
||||
$router = new self();
|
||||
$baseUrl = get_permalink() ?: home_url(add_query_arg([], (string) ($_SERVER['REQUEST_URI'] ?? '/')));
|
||||
|
||||
UrlBuilder::beginEmbed($baseUrl);
|
||||
|
||||
try {
|
||||
$route = sanitize_key(wp_unslash((string) ($_GET['kb_docs_route'] ?? '')));
|
||||
$product = sanitize_title(wp_unslash((string) ($_GET['kb_docs_product'] ?? $atts['product'])));
|
||||
$version = sanitize_title(wp_unslash((string) ($_GET['kb_docs_version'] ?? $atts['version'])));
|
||||
$page = sanitize_title(wp_unslash((string) ($_GET['kb_docs_page'] ?? $atts['page'])));
|
||||
|
||||
if (! $route) {
|
||||
$route = $product ? ($version ? ($page ? 'page' : 'version') : 'product') : 'index';
|
||||
}
|
||||
|
||||
return $router->captureRoute($route, $product, $version, $page);
|
||||
} finally {
|
||||
UrlBuilder::endEmbed();
|
||||
}
|
||||
}
|
||||
|
||||
public static function shortcodeProductIndex(array $atts): string
|
||||
{
|
||||
$atts = shortcode_atts(['product' => ''], $atts, 'kb_product_index');
|
||||
$router = new self();
|
||||
|
||||
return $router->captureProduct((string) $atts['product']);
|
||||
}
|
||||
|
||||
private function renderIndex(): void
|
||||
{
|
||||
echo $this->captureIndex();
|
||||
}
|
||||
|
||||
private function captureIndex(): string
|
||||
{
|
||||
return (new TemplateLoader())->capture('documentation-index', [
|
||||
'products' => self::productsWithVersions(),
|
||||
'base_slug' => trim((string) Plugin::settings()['docs_base_slug'], '/'),
|
||||
'url_builder' => UrlBuilder::class,
|
||||
]);
|
||||
}
|
||||
|
||||
private function renderProduct(string $productSlug): void
|
||||
{
|
||||
echo $this->captureProduct($productSlug);
|
||||
}
|
||||
|
||||
private function captureProduct(string $productSlug): string
|
||||
{
|
||||
$product = get_term_by('slug', $productSlug, 'kb_product');
|
||||
if (! $product) {
|
||||
return $this->capture404();
|
||||
}
|
||||
|
||||
$versions = $this->versionsForProduct($productSlug);
|
||||
|
||||
return (new TemplateLoader())->capture('product', [
|
||||
'product' => $product,
|
||||
'versions' => $versions,
|
||||
'base_slug' => trim((string) Plugin::settings()['docs_base_slug'], '/'),
|
||||
'url_builder' => UrlBuilder::class,
|
||||
]);
|
||||
}
|
||||
|
||||
private function renderVersion(string $productSlug, string $versionSlug): void
|
||||
{
|
||||
echo $this->captureVersion($productSlug, $versionSlug);
|
||||
}
|
||||
|
||||
private function captureVersion(string $productSlug, string $versionSlug): string
|
||||
{
|
||||
$product = get_term_by('slug', $productSlug, 'kb_product');
|
||||
$version = get_term_by('slug', $versionSlug, 'kb_version');
|
||||
|
||||
if (! $product || ! $version) {
|
||||
return $this->capture404();
|
||||
}
|
||||
|
||||
$landing = $this->landingPageForVersion($productSlug, $versionSlug);
|
||||
if ($landing) {
|
||||
return $this->captureDocPage($landing, $productSlug, $versionSlug);
|
||||
}
|
||||
|
||||
return (new TemplateLoader())->capture('version', [
|
||||
'product' => $product,
|
||||
'version' => $version,
|
||||
'pages' => $this->pagesForVersion($productSlug, $versionSlug),
|
||||
'base_slug' => trim((string) Plugin::settings()['docs_base_slug'], '/'),
|
||||
'url_builder' => UrlBuilder::class,
|
||||
]);
|
||||
}
|
||||
|
||||
private function renderPage(string $productSlug, string $versionSlug, string $pageSlug): void
|
||||
{
|
||||
echo $this->capturePage($productSlug, $versionSlug, $pageSlug);
|
||||
}
|
||||
|
||||
private function capturePage(string $productSlug, string $versionSlug, string $pageSlug): string
|
||||
{
|
||||
$post = (new PageRepository())->findFrontendPage($productSlug, $versionSlug, $pageSlug);
|
||||
|
||||
if (! $post && '' === $pageSlug) {
|
||||
$post = (new PageRepository())->findFrontendPage($productSlug, $versionSlug, 'index');
|
||||
}
|
||||
|
||||
if (! $post) {
|
||||
return $this->capture404();
|
||||
}
|
||||
|
||||
return $this->captureDocPage($post, $productSlug, $versionSlug);
|
||||
}
|
||||
|
||||
private function renderDocPage(\WP_Post $post, string $productSlug, string $versionSlug): void
|
||||
{
|
||||
echo $this->captureDocPage($post, $productSlug, $versionSlug);
|
||||
}
|
||||
|
||||
private function captureDocPage(\WP_Post $post, string $productSlug, string $versionSlug): string
|
||||
{
|
||||
$product = get_term_by('slug', $productSlug, 'kb_product');
|
||||
$version = get_term_by('slug', $versionSlug, 'kb_version');
|
||||
$navTree = json_decode((string) get_post_meta($post->ID, '_kb_nav_tree', true), true);
|
||||
|
||||
return (new TemplateLoader())->capture('page', [
|
||||
'post' => $post,
|
||||
'product' => $product,
|
||||
'version' => $version,
|
||||
'versions' => $this->versionsForProduct($productSlug),
|
||||
'nav_tree' => is_array($navTree) ? $navTree : [],
|
||||
'base_slug' => trim((string) Plugin::settings()['docs_base_slug'], '/'),
|
||||
'product_slug' => $productSlug,
|
||||
'version_slug' => $versionSlug,
|
||||
'url_builder' => UrlBuilder::class,
|
||||
]);
|
||||
}
|
||||
|
||||
private function captureRoute(string $route, string $productSlug = '', string $versionSlug = '', string $pageSlug = ''): string
|
||||
{
|
||||
return match ($route) {
|
||||
'index' => $this->captureIndex(),
|
||||
'product' => $productSlug ? $this->captureProduct($productSlug) : $this->captureIndex(),
|
||||
'version' => ($productSlug && $versionSlug) ? $this->captureVersion($productSlug, $versionSlug) : $this->captureIndex(),
|
||||
'page' => ($productSlug && $versionSlug) ? $this->capturePage($productSlug, $versionSlug, $pageSlug) : $this->captureIndex(),
|
||||
default => $this->captureIndex(),
|
||||
};
|
||||
}
|
||||
|
||||
private function render404(): void
|
||||
{
|
||||
status_header(404);
|
||||
(new TemplateLoader())->render('search', [
|
||||
'title' => __('Documentation page not found.', 'kb-markdown-importer'),
|
||||
'results' => [],
|
||||
'query' => '',
|
||||
]);
|
||||
}
|
||||
|
||||
private function capture404(): string
|
||||
{
|
||||
status_header(404);
|
||||
return (new TemplateLoader())->capture('search', [
|
||||
'title' => __('Documentation page not found.', 'kb-markdown-importer'),
|
||||
'results' => [],
|
||||
'query' => '',
|
||||
]);
|
||||
}
|
||||
|
||||
public static function productsWithVersions(): array
|
||||
{
|
||||
$products = get_terms(['taxonomy' => 'kb_product', 'hide_empty' => false]);
|
||||
$items = [];
|
||||
|
||||
if (is_wp_error($products)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
foreach ($products as $product) {
|
||||
$versions = (new self())->versionsForProduct($product->slug);
|
||||
|
||||
if (! $versions) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$items[] = [
|
||||
'term' => $product,
|
||||
'versions' => $versions,
|
||||
];
|
||||
}
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
private function versionsForProduct(string $productSlug): array
|
||||
{
|
||||
$query = new \WP_Query([
|
||||
'post_type' => 'kb_doc_page',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => -1,
|
||||
'fields' => 'ids',
|
||||
'tax_query' => [
|
||||
['taxonomy' => 'kb_product', 'field' => 'slug', 'terms' => $productSlug],
|
||||
],
|
||||
]);
|
||||
$versions = [];
|
||||
|
||||
foreach ($query->posts as $postId) {
|
||||
foreach (wp_get_object_terms((int) $postId, 'kb_version') as $term) {
|
||||
$versions[$term->slug] = $term;
|
||||
}
|
||||
}
|
||||
|
||||
uasort($versions, static fn ($a, $b): int => strnatcasecmp($b->name, $a->name));
|
||||
|
||||
return array_values($versions);
|
||||
}
|
||||
|
||||
private function pagesForVersion(string $productSlug, string $versionSlug): array
|
||||
{
|
||||
$query = new \WP_Query([
|
||||
'post_type' => 'kb_doc_page',
|
||||
'post_status' => 'publish',
|
||||
'posts_per_page' => -1,
|
||||
'meta_key' => '_kb_nav_order',
|
||||
'orderby' => ['meta_value_num' => 'ASC', 'title' => 'ASC'],
|
||||
'tax_query' => [
|
||||
'relation' => 'AND',
|
||||
['taxonomy' => 'kb_product', 'field' => 'slug', 'terms' => $productSlug],
|
||||
['taxonomy' => 'kb_version', 'field' => 'slug', 'terms' => $versionSlug],
|
||||
],
|
||||
]);
|
||||
|
||||
return $query->posts;
|
||||
}
|
||||
|
||||
private function landingPageForVersion(string $productSlug, string $versionSlug): ?\WP_Post
|
||||
{
|
||||
$repository = new PageRepository();
|
||||
$landing = $repository->findFrontendPage($productSlug, $versionSlug, '');
|
||||
|
||||
if ($landing) {
|
||||
return $landing;
|
||||
}
|
||||
|
||||
$pages = $this->pagesForVersion($productSlug, $versionSlug);
|
||||
|
||||
return $pages[0] ?? null;
|
||||
}
|
||||
}
|
||||
69
kb-markdown-importer/includes/Frontend/SearchController.php
Normal file
69
kb-markdown-importer/includes/Frontend/SearchController.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KbMarkdownImporter\Frontend;
|
||||
|
||||
use KbMarkdownImporter\Access\AccessController;
|
||||
|
||||
final class SearchController
|
||||
{
|
||||
public static function shortcodeSearch(array $atts = []): string
|
||||
{
|
||||
if (! (new AccessController())->canView()) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$query = sanitize_text_field(wp_unslash((string) ($_GET['kbq'] ?? '')));
|
||||
$results = $query ? self::search($query, sanitize_text_field(wp_unslash((string) ($_GET['product'] ?? ''))), sanitize_text_field(wp_unslash((string) ($_GET['version'] ?? '')))) : [];
|
||||
|
||||
return (new TemplateLoader())->capture('search', [
|
||||
'title' => __('Search Documentation', 'kb-markdown-importer'),
|
||||
'query' => $query,
|
||||
'results' => $results,
|
||||
]);
|
||||
}
|
||||
|
||||
public static function restSearch(\WP_REST_Request $request): \WP_REST_Response
|
||||
{
|
||||
if (! (new AccessController())->canView()) {
|
||||
return new \WP_REST_Response(['results' => []], 403);
|
||||
}
|
||||
|
||||
$query = sanitize_text_field((string) $request->get_param('q'));
|
||||
$product = sanitize_title((string) $request->get_param('product'));
|
||||
$version = sanitize_title((string) $request->get_param('version'));
|
||||
|
||||
return new \WP_REST_Response(['results' => self::search($query, $product, $version)]);
|
||||
}
|
||||
|
||||
private static function search(string $query, string $productSlug = '', string $versionSlug = ''): array
|
||||
{
|
||||
if ('' === $query) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$taxQuery = [];
|
||||
if ($productSlug) {
|
||||
$taxQuery[] = ['taxonomy' => 'kb_product', 'field' => 'slug', 'terms' => $productSlug];
|
||||
}
|
||||
if ($versionSlug) {
|
||||
$taxQuery[] = ['taxonomy' => 'kb_version', 'field' => 'slug', 'terms' => $versionSlug];
|
||||
}
|
||||
if (count($taxQuery) > 1) {
|
||||
$taxQuery['relation'] = 'AND';
|
||||
}
|
||||
|
||||
$args = [
|
||||
'post_type' => 'kb_doc_page',
|
||||
'post_status' => 'publish',
|
||||
's' => $query,
|
||||
'posts_per_page' => 20,
|
||||
];
|
||||
|
||||
if ($taxQuery) {
|
||||
$args['tax_query'] = $taxQuery;
|
||||
}
|
||||
|
||||
return (new \WP_Query($args))->posts;
|
||||
}
|
||||
}
|
||||
28
kb-markdown-importer/includes/Frontend/TemplateLoader.php
Normal file
28
kb-markdown-importer/includes/Frontend/TemplateLoader.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KbMarkdownImporter\Frontend;
|
||||
|
||||
final class TemplateLoader
|
||||
{
|
||||
public function render(string $template, array $vars = []): void
|
||||
{
|
||||
$path = KB_MARKDOWN_IMPORTER_DIR . 'templates/' . $template . '.php';
|
||||
|
||||
if (! is_readable($path)) {
|
||||
status_header(500);
|
||||
echo esc_html__('Knowledgebase template missing.', 'kb-markdown-importer');
|
||||
return;
|
||||
}
|
||||
|
||||
extract($vars, EXTR_SKIP);
|
||||
include $path;
|
||||
}
|
||||
|
||||
public function capture(string $template, array $vars = []): string
|
||||
{
|
||||
ob_start();
|
||||
$this->render($template, $vars);
|
||||
return (string) ob_get_clean();
|
||||
}
|
||||
}
|
||||
163
kb-markdown-importer/includes/Frontend/UrlBuilder.php
Normal file
163
kb-markdown-importer/includes/Frontend/UrlBuilder.php
Normal file
@@ -0,0 +1,163 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace KbMarkdownImporter\Frontend;
|
||||
|
||||
use KbMarkdownImporter\Plugin;
|
||||
|
||||
final class UrlBuilder
|
||||
{
|
||||
private static string $embedBaseUrl = '';
|
||||
|
||||
public static function beginEmbed(string $baseUrl): void
|
||||
{
|
||||
self::$embedBaseUrl = remove_query_arg(['kb_docs_route', 'kb_docs_product', 'kb_docs_version', 'kb_docs_page'], $baseUrl);
|
||||
}
|
||||
|
||||
public static function endEmbed(): void
|
||||
{
|
||||
self::$embedBaseUrl = '';
|
||||
}
|
||||
|
||||
public static function isEmbed(): bool
|
||||
{
|
||||
return '' !== self::$embedBaseUrl;
|
||||
}
|
||||
|
||||
public static function docsIndex(): string
|
||||
{
|
||||
return self::route('index');
|
||||
}
|
||||
|
||||
public static function product(string $productSlug): string
|
||||
{
|
||||
return self::route('product', $productSlug);
|
||||
}
|
||||
|
||||
public static function version(string $productSlug, string $versionSlug): string
|
||||
{
|
||||
return self::route('version', $productSlug, $versionSlug);
|
||||
}
|
||||
|
||||
public static function page(string $productSlug, string $versionSlug, string $pageSlug = ''): string
|
||||
{
|
||||
return self::route('page', $productSlug, $versionSlug, $pageSlug);
|
||||
}
|
||||
|
||||
private static function route(string $route, string $productSlug = '', string $versionSlug = '', string $pageSlug = ''): string
|
||||
{
|
||||
if (self::isEmbed()) {
|
||||
$args = ['kb_docs_route' => $route];
|
||||
|
||||
if ($productSlug) {
|
||||
$args['kb_docs_product'] = $productSlug;
|
||||
}
|
||||
|
||||
if ($versionSlug) {
|
||||
$args['kb_docs_version'] = $versionSlug;
|
||||
}
|
||||
|
||||
if ($pageSlug) {
|
||||
$args['kb_docs_page'] = $pageSlug;
|
||||
}
|
||||
|
||||
return add_query_arg($args, self::$embedBaseUrl);
|
||||
}
|
||||
|
||||
$base = trim((string) Plugin::settings()['docs_base_slug'], '/') ?: 'docs';
|
||||
|
||||
if (self::supportsPrettyPermalinks()) {
|
||||
$parts = array_filter([$base, $productSlug, $versionSlug, $pageSlug], static fn (string $part): bool => '' !== $part);
|
||||
return home_url('/' . implode('/', array_map('rawurlencode', $parts)) . '/');
|
||||
}
|
||||
|
||||
$args = ['kb_markdown_route' => $route];
|
||||
|
||||
if ($productSlug) {
|
||||
$args['kb_product_slug'] = $productSlug;
|
||||
}
|
||||
|
||||
if ($versionSlug) {
|
||||
$args['kb_version_slug'] = $versionSlug;
|
||||
}
|
||||
|
||||
if ($pageSlug) {
|
||||
$args['kb_page_slug'] = $pageSlug;
|
||||
}
|
||||
|
||||
return add_query_arg($args, home_url('/'));
|
||||
}
|
||||
|
||||
private static function supportsPrettyPermalinks(): bool
|
||||
{
|
||||
return '' !== (string) get_option('permalink_structure', '');
|
||||
}
|
||||
|
||||
public static function rewriteHtml(string $html): string
|
||||
{
|
||||
if (! self::isEmbed()) {
|
||||
return $html;
|
||||
}
|
||||
|
||||
return preg_replace_callback('/href=(["\'])([^"\']+)\1/i', static function (array $matches): string {
|
||||
$url = html_entity_decode((string) $matches[2], ENT_QUOTES);
|
||||
$replacement = self::rewriteUrl($url);
|
||||
|
||||
if (! $replacement) {
|
||||
return $matches[0];
|
||||
}
|
||||
|
||||
return 'href=' . $matches[1] . esc_url($replacement) . $matches[1];
|
||||
}, $html) ?? $html;
|
||||
}
|
||||
|
||||
private static function rewriteUrl(string $url): string
|
||||
{
|
||||
$parts = wp_parse_url($url);
|
||||
|
||||
if (! is_array($parts)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$query = [];
|
||||
if (! empty($parts['query'])) {
|
||||
wp_parse_str((string) $parts['query'], $query);
|
||||
}
|
||||
|
||||
if (! empty($query['kb_markdown_route'])) {
|
||||
return self::route(
|
||||
sanitize_key((string) $query['kb_markdown_route']),
|
||||
sanitize_title((string) ($query['kb_product_slug'] ?? '')),
|
||||
sanitize_title((string) ($query['kb_version_slug'] ?? '')),
|
||||
sanitize_title((string) ($query['kb_page_slug'] ?? ''))
|
||||
);
|
||||
}
|
||||
|
||||
$base = trim((string) Plugin::settings()['docs_base_slug'], '/') ?: 'docs';
|
||||
$path = trim((string) ($parts['path'] ?? ''), '/');
|
||||
|
||||
if ($path === $base) {
|
||||
return self::docsIndex();
|
||||
}
|
||||
|
||||
if (! str_starts_with($path . '/', $base . '/')) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$routeParts = array_values(array_filter(explode('/', substr($path, strlen($base))), static fn (string $part): bool => '' !== $part));
|
||||
|
||||
if (1 === count($routeParts)) {
|
||||
return self::product(sanitize_title($routeParts[0]));
|
||||
}
|
||||
|
||||
if (2 === count($routeParts)) {
|
||||
return self::version(sanitize_title($routeParts[0]), sanitize_title($routeParts[1]));
|
||||
}
|
||||
|
||||
return self::page(
|
||||
sanitize_title($routeParts[0] ?? ''),
|
||||
sanitize_title($routeParts[1] ?? ''),
|
||||
sanitize_title(implode('/', array_slice($routeParts, 2)))
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user