diff --git a/kb-antora-importer.zip b/kb-antora-importer.zip new file mode 100644 index 0000000..f2473fa Binary files /dev/null and b/kb-antora-importer.zip differ diff --git a/kb-antora-importer/assets/css/frontend.css b/kb-antora-importer/assets/css/frontend.css new file mode 100644 index 0000000..0f8ee0e --- /dev/null +++ b/kb-antora-importer/assets/css/frontend.css @@ -0,0 +1,210 @@ +.kb-docs-wrap { + --kb-surface: var(--wp--preset--color--base, #ffffff); + --kb-surface-muted: color-mix(in srgb, var(--kb-surface) 88%, #eef3f8); + --kb-text: var(--wp--preset--color--contrast, #1f2933); + --kb-muted: #667085; + --kb-border: color-mix(in srgb, var(--kb-text) 14%, transparent); + --kb-accent: var(--wp--preset--color--accent-3, #2563eb); + --kb-accent-soft: color-mix(in srgb, var(--kb-accent) 10%, var(--kb-surface)); + color: var(--kb-text); +} + +.kb-docs-wrap { + max-width: 1320px; + margin: 0 auto; + padding: 24px 20px 48px; +} + +.kb-product-list { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: 16px; +} + +.kb-product-card, +.kb-search { + border: 1px solid var(--kb-border); + border-radius: 8px; + padding: 20px; + background: var(--kb-surface); + box-shadow: 0 1px 2px rgba(16, 24, 40, 0.04); +} + +.kb-product-card h2 { + margin-top: 0; + font-size: 20px; +} + +.kb-product-card ul, +.kb-version-list, +.kb-page-list { + margin-left: 0; + padding-left: 0; + list-style: none; +} + +.kb-product-card li, +.kb-version-list li, +.kb-page-list li { + margin: 7px 0; +} + +.kb-doc-layout { + display: grid; + grid-template-columns: 300px minmax(0, 1fr); + gap: 40px; + align-items: start; +} + +.kb-sidebar { + position: sticky; + top: 24px; + max-height: calc(100vh - 48px); + overflow: auto; + border-right: 1px solid var(--kb-border); + padding: 8px 22px 16px 0; +} + +.kb-sidebar select, +.kb-search input { + width: 100%; + min-height: 40px; + border: 1px solid var(--kb-border); + border-radius: 6px; + background: var(--kb-surface); + color: var(--kb-text); +} + +.kb-sidebar-nav ul { + list-style: none; + margin: 12px 0 0; + padding-left: 0; +} + +.kb-sidebar-nav ul ul { + padding-left: 16px; +} + +.kb-sidebar-nav a, +.kb-sidebar-nav span { + display: block; + padding: 6px 8px; + border-radius: 4px; + font-size: 14px; + line-height: 1.35; + text-decoration: none; + color: var(--kb-text); +} + +.kb-sidebar-nav a:hover { + background: var(--kb-accent-soft); + color: var(--kb-accent); +} + +.kb-breadcrumbs { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-bottom: 18px; + color: var(--kb-muted); + font-size: 14px; +} + +.kb-breadcrumbs a { + color: inherit; + text-decoration: none; +} + +.kb-breadcrumbs a:hover { + color: var(--kb-accent); +} + +.kb-rendered-content pre { + overflow: auto; + padding: 16px; + background: #1e1e1e; + color: #f8f8f2; + border-radius: 6px; +} + +.kb-doc-content { + max-width: 860px; + min-width: 0; +} + +.kb-doc-content > h1 { + margin-top: 0; + margin-bottom: 22px; + font-size: clamp(28px, 4vw, 42px); + line-height: 1.1; +} + +.kb-rendered-content h1 { + display: none; +} + +.kb-rendered-content h2, +.kb-rendered-content h3, +.kb-rendered-content h4 { + scroll-margin-top: 24px; + margin-top: 32px; +} + +.kb-rendered-content p, +.kb-rendered-content li { + line-height: 1.7; +} + +.kb-rendered-content a { + color: var(--kb-accent); + text-underline-offset: 3px; +} + +.kb-admonition { + border-left: 4px solid var(--kb-accent); + border-radius: 0 6px 6px 0; + padding: 14px 16px; + background: var(--kb-accent-soft); + margin: 18px 0; +} + +.kb-image img { + max-width: 100%; + height: auto; +} + +.kb-current-version { + margin-left: 8px; + font-size: 12px; + color: var(--kb-accent); +} + +.kb-search-form { + display: flex; + gap: 8px; +} + +.kb-search button { + min-height: 40px; + border: 0; + border-radius: 6px; + padding: 0 16px; + background: var(--kb-accent); + color: #fff; + cursor: pointer; +} + +@media (max-width: 780px) { + .kb-doc-layout { + grid-template-columns: 1fr; + } + + .kb-sidebar { + position: static; + max-height: none; + border-right: 0; + border-bottom: 1px solid var(--kb-border); + padding-right: 0; + padding-bottom: 18px; + } +} diff --git a/kb-antora-importer/assets/js/frontend.js b/kb-antora-importer/assets/js/frontend.js new file mode 100644 index 0000000..2b9efc5 --- /dev/null +++ b/kb-antora-importer/assets/js/frontend.js @@ -0,0 +1,13 @@ +(function () { + const switcher = document.getElementById('kb-version-switcher'); + if (!switcher) { + return; + } + + switcher.addEventListener('change', function () { + const selected = switcher.options[switcher.selectedIndex]; + if (selected && selected.dataset.url) { + window.location.href = selected.dataset.url; + } + }); +}()); diff --git a/kb-antora-importer/composer.json b/kb-antora-importer/composer.json new file mode 100644 index 0000000..53ef416 --- /dev/null +++ b/kb-antora-importer/composer.json @@ -0,0 +1,14 @@ +{ + "name": "kb-antora-importer/kb-antora-importer", + "description": "WordPress plugin for importing GitLab/Antora based AsciiDoc documentation.", + "type": "wordpress-plugin", + "license": "proprietary", + "require": { + "php": ">=8.1" + }, + "autoload": { + "psr-4": { + "KbAntoraImporter\\": "includes/" + } + } +} diff --git a/kb-antora-importer/includes/Access/AccessController.php b/kb-antora-importer/includes/Access/AccessController.php new file mode 100644 index 0000000..ac271d1 --- /dev/null +++ b/kb-antora-importer/includes/Access/AccessController.php @@ -0,0 +1,25 @@ +canView()) { + return; + } + + auth_redirect(); + } +} diff --git a/kb-antora-importer/includes/Admin/SettingsPage.php b/kb-antora-importer/includes/Admin/SettingsPage.php new file mode 100644 index 0000000..8ef35da --- /dev/null +++ b/kb-antora-importer/includes/Admin/SettingsPage.php @@ -0,0 +1,171 @@ + 'array', + 'sanitize_callback' => [self::class, 'sanitize'], + 'default' => Settings::defaults(), + ]); + } + + public static function sanitize(array $input): array + { + $old = Plugin::settings(); + $settings = Settings::defaults(); + + $settings['gitlab_base_url'] = esc_url_raw(GitLabClient::normalizeBaseUrl((string) ($input['gitlab_base_url'] ?? ''))); + $settings['gitlab_token'] = trim((string) ($input['gitlab_token'] ?? '')) ?: (string) $old['gitlab_token']; + $settings['gitlab_group'] = sanitize_text_field((string) ($input['gitlab_group'] ?? 'knowledgebase')); + $settings['branch_pattern'] = sanitize_text_field((string) ($input['branch_pattern'] ?? '^v.*')); + $settings['docs_base_slug'] = sanitize_title((string) ($input['docs_base_slug'] ?? 'docs')) ?: 'docs'; + $settings['renderer_mode'] = in_array(($input['renderer_mode'] ?? 'php'), ['php', 'asciidoctor'], true) ? (string) $input['renderer_mode'] : 'php'; + $settings['asciidoctor_path'] = sanitize_text_field((string) ($input['asciidoctor_path'] ?? 'asciidoctor')); + $settings['image_lightbox'] = ! empty($input['image_lightbox']) ? '1' : '0'; + $settings['public_docs'] = ! empty($input['public_docs']) ? '1' : '0'; + $settings['allow_svg'] = ! empty($input['allow_svg']) ? '1' : '0'; + $settings['cron_interval'] = in_array(($input['cron_interval'] ?? 'disabled'), ['disabled', 'hourly', 'daily', 'weekly'], true) ? (string) $input['cron_interval'] : 'disabled'; + + Plugin::syncCronSchedule($settings); + if (($old['docs_base_slug'] ?? 'docs') !== $settings['docs_base_slug']) { + flush_rewrite_rules(false); + } + + return $settings; + } + + public static function render(): void + { + if (! current_user_can('manage_kb_docs')) { + wp_die(esc_html__('Insufficient permissions.', 'kb-antora-importer')); + } + + if (isset($_POST['kb_antora_test_connection']) && check_admin_referer('kb_antora_test_connection')) { + self::handleConnectionTest(); + } + + $settings = Plugin::settings(); + ?> +
' . esc_html__('No logs yet.', 'kb-antora-importer') . '
'; + return; + } + + echo '| Time | Level | Message |
|---|---|---|
| %s | %s | %s |
get_error_message()); ?>
| Name | Path | Action |
|---|---|---|
| + | |
+ + + | +
' . esc_html__('Synchronization finished.', 'kb-antora-importer') . '
' . esc_html__('Dry run finished.', 'kb-antora-importer') . '
' . esc_html__('Project synchronization finished.', 'kb-antora-importer') . '
' . $this->transformer->transformInline($text, $context) . '
' . "\n"; + $paragraph = []; + }; + + $closeList = static function () use (&$html, &$listOpen): void { + if ($listOpen) { + $html .= "\n"; + $listOpen = false; + } + }; + + foreach ($lines as $line) { + $trimmed = trim($line); + + if ('----' === $trimmed) { + $flushParagraph(); + $closeList(); + if ($codeOpen) { + $html .= '' . esc_html(implode("\n", $code)) . '' . "\n";
+ $code = [];
+ $codeOpen = false;
+ } else {
+ $codeOpen = true;
+ }
+ continue;
+ }
+
+ if ($codeOpen) {
+ $code[] = $line;
+ continue;
+ }
+
+ if ('' === $trimmed) {
+ $flushParagraph();
+ $closeList();
+ continue;
+ }
+
+ if (preg_match('/^:[A-Za-z0-9_-]+:\s*/', $trimmed)) {
+ continue;
+ }
+
+ if (preg_match('/^(={1,6})\s+(.+)$/', $trimmed, $matches)) {
+ $flushParagraph();
+ $closeList();
+ $level = min(6, strlen($matches[1]));
+ $html .= sprintf('post_content), 24)); ?>
+