boot(); } public static function activate(): void { self::instance()->registerContentTypes(); (new Router())->addRewriteRules(); self::grantCapabilities(); self::ensureDefaultSettings(); flush_rewrite_rules(); } public static function deactivate(): void { wp_clear_scheduled_hook('kb_markdown_importer_cron_sync'); flush_rewrite_rules(); } public function registerContentTypes(): void { register_post_type('kb_doc_page', [ 'labels' => [ 'name' => __('Documentation Pages', 'kb-markdown-importer'), 'singular_name' => __('Documentation Page', 'kb-markdown-importer'), ], 'public' => false, 'show_ui' => true, 'show_in_menu' => 'kb-markdown-importer', 'show_in_rest' => true, 'supports' => ['title', 'editor', 'excerpt', 'custom-fields'], 'capability_type' => 'post', ]); register_taxonomy('kb_product', ['kb_doc_page'], [ 'labels' => [ 'name' => __('Products', 'kb-markdown-importer'), 'singular_name' => __('Product', 'kb-markdown-importer'), ], 'public' => false, 'show_ui' => true, 'show_in_rest' => true, 'hierarchical' => false, 'rewrite' => false, ]); register_taxonomy('kb_version', ['kb_doc_page'], [ 'labels' => [ 'name' => __('Versions', 'kb-markdown-importer'), 'singular_name' => __('Version', 'kb-markdown-importer'), ], 'public' => false, 'show_ui' => true, 'show_in_rest' => true, 'hierarchical' => false, 'rewrite' => false, ]); register_taxonomy('kb_component', ['kb_doc_page'], [ 'labels' => [ 'name' => __('Components', 'kb-markdown-importer'), 'singular_name' => __('Component', 'kb-markdown-importer'), ], 'public' => false, 'show_ui' => true, 'show_in_rest' => true, 'hierarchical' => false, 'rewrite' => false, ]); } public function registerAdminPages(): void { add_menu_page( __('Knowledgebase', 'kb-markdown-importer'), __('Knowledgebase', 'kb-markdown-importer'), 'manage_kb_docs', 'kb-markdown-importer', [StatusPage::class, 'render'], 'dashicons-welcome-learn-more', 58 ); add_submenu_page('kb-markdown-importer', __('Overview', 'kb-markdown-importer'), __('Overview', 'kb-markdown-importer'), 'manage_kb_docs', 'kb-markdown-importer', [StatusPage::class, 'render']); add_submenu_page('kb-markdown-importer', __('Products', 'kb-markdown-importer'), __('Products', 'kb-markdown-importer'), 'manage_kb_docs', 'kb-markdown-products', [ProductsPage::class, 'render']); add_submenu_page('kb-markdown-importer', __('Synchronization', 'kb-markdown-importer'), __('Synchronization', 'kb-markdown-importer'), 'sync_kb_docs', 'kb-markdown-sync', [SyncPage::class, 'render']); add_submenu_page('kb-markdown-importer', __('Settings', 'kb-markdown-importer'), __('Settings', 'kb-markdown-importer'), 'manage_kb_docs', 'kb-markdown-settings', [SettingsPage::class, 'render']); } public function registerRestRoutes(): void { register_rest_route('kb-markdown/v1', '/status', [ 'methods' => 'GET', 'callback' => [StatusPage::class, 'restStatus'], 'permission_callback' => static fn (): bool => current_user_can('manage_kb_docs'), ]); register_rest_route('kb-markdown/v1', '/sync', [ 'methods' => 'POST', 'callback' => static function (\WP_REST_Request $request): \WP_REST_Response { $response = (new ImportManager())->syncAll((bool) $request->get_param('dry_run')); if (! (bool) $request->get_param('dry_run')) { (new ChangelogSync())->sync(); } return $response; }, 'permission_callback' => static fn (): bool => current_user_can('sync_kb_docs'), ]); register_rest_route('kb-markdown/v1', '/sync/project', [ 'methods' => 'POST', 'callback' => static fn (\WP_REST_Request $request): \WP_REST_Response => (new ImportManager())->syncProject((string) $request->get_param('project_id'), (bool) $request->get_param('dry_run')), 'permission_callback' => static fn (): bool => current_user_can('sync_kb_docs'), ]); register_rest_route('kb-markdown/v1', '/sync/changelog', [ 'methods' => 'POST', 'callback' => static fn (): \WP_REST_Response => (new ChangelogSync())->sync(), 'permission_callback' => static fn (): bool => current_user_can('sync_kb_docs'), ]); register_rest_route('kb-markdown/v1', '/search', [ 'methods' => 'GET', 'callback' => [SearchController::class, 'restSearch'], 'permission_callback' => '__return_true', ]); register_rest_route('kb-markdown/v1', '/gitlab-webhook', [ 'methods' => 'POST', 'callback' => static fn (): \WP_REST_Response => new \WP_REST_Response(['queued' => false, 'message' => 'Webhook endpoint is reserved for a later event-driven sync implementation.']), 'permission_callback' => static fn (): bool => current_user_can('sync_kb_docs'), ]); } public function registerShortcodes(): void { add_shortcode('kb_docs_index', [Router::class, 'shortcodeDocsIndex']); add_shortcode('kb_docs', [Router::class, 'shortcodeDocsApp']); add_shortcode('kb_product_index', [Router::class, 'shortcodeProductIndex']); add_shortcode('kb_search', [SearchController::class, 'shortcodeSearch']); } public function addCronSchedules(array $schedules): array { $schedules['kb_markdown_weekly'] = [ 'interval' => WEEK_IN_SECONDS, 'display' => __('Weekly', 'kb-markdown-importer'), ]; return $schedules; } public function runCronSync(): void { (new ImportManager())->syncAll(false); (new ChangelogSync())->sync(); } public function enqueueFrontendAssets(): void { $settings = self::settings(); wp_enqueue_style('kb-markdown-frontend', KB_MARKDOWN_IMPORTER_URL . 'assets/css/frontend.css', [], KB_MARKDOWN_IMPORTER_VERSION); $designHandle = 'kb-markdown-frontend'; if ('obyte' === $settings['design_theme']) { wp_enqueue_style('kb-markdown-theme-obyte', KB_MARKDOWN_IMPORTER_URL . 'assets/css/themes/obyte.css', ['kb-markdown-frontend'], KB_MARKDOWN_IMPORTER_VERSION); $designHandle = 'kb-markdown-theme-obyte'; } if (! empty($settings['custom_theme_css_url'])) { wp_enqueue_style('kb-markdown-custom-theme', esc_url_raw((string) $settings['custom_theme_css_url']), [$designHandle], KB_MARKDOWN_IMPORTER_VERSION); $designHandle = 'kb-markdown-custom-theme'; } $inlineCss = sprintf( '.kb-docs-wrap{--kb-accent:%1$s;--kb-radius:%2$dpx;} .kb-docs-wrap{--kb-primary:%1$s;--kb-ob-accent:%3$s;}', esc_html((string) $settings['design_primary_color']), max(0, min(32, (int) $settings['design_radius'])), esc_html((string) $settings['design_accent_color']) ); wp_add_inline_style($designHandle, $inlineCss); wp_enqueue_script('kb-markdown-frontend', KB_MARKDOWN_IMPORTER_URL . 'assets/js/frontend.js', [], KB_MARKDOWN_IMPORTER_VERSION, true); } public function enqueueAdminAssets(string $hook): void { if (! str_contains($hook, 'kb-markdown-settings')) { return; } wp_enqueue_media(); wp_enqueue_script('kb-markdown-admin-settings', KB_MARKDOWN_IMPORTER_URL . 'assets/js/admin-settings.js', ['jquery'], KB_MARKDOWN_IMPORTER_VERSION, true); } public function allowCssUploads(array $mimes): array { if (current_user_can('manage_kb_docs')) { $mimes['css'] = 'text/css'; } return $mimes; } public static function settings(): array { return wp_parse_args((array) get_option('kb_markdown_importer_settings', []), Settings::defaults()); } public static function syncCronSchedule(?array $settings = null): void { $settings = $settings ?: self::settings(); wp_clear_scheduled_hook('kb_markdown_importer_cron_sync'); if ('disabled' === $settings['cron_interval']) { return; } $schedule = match ($settings['cron_interval']) { 'hourly' => 'hourly', 'daily' => 'daily', 'weekly' => 'kb_markdown_weekly', default => '', }; if ($schedule && ! wp_next_scheduled('kb_markdown_importer_cron_sync')) { wp_schedule_event(time() + HOUR_IN_SECONDS, $schedule, 'kb_markdown_importer_cron_sync'); } } private static function ensureDefaultSettings(): void { if (false === get_option('kb_markdown_importer_settings', false)) { add_option('kb_markdown_importer_settings', Settings::defaults(), '', false); } } private static function grantCapabilities(): void { $role = get_role('administrator'); if (! $role) { return; } foreach (['manage_kb_docs', 'view_kb_docs', 'sync_kb_docs'] as $capability) { $role->add_cap($capability); } } }