' . "\n", /* translators: %s: site name */ esc_attr(sprintf(__('%s Markdown Feed', 'aiutoma'), get_bloginfo('name'))), esc_url(get_feed_link('markdown')) ); if (!is_singular()) return; $post = get_queried_object(); if (!$post instanceof \WP_Post || ($post->post_status !== 'publish' && !($post->post_type === 'attachment' && $post->post_status === 'inherit')) || post_password_required($post)) return; $allowed_cpts = get_option('aiutoma_markdown_cpts', false); if ($allowed_cpts === false) { $allowed_cpts = array_values(array_diff(array_keys(get_post_types(['public' => true])), ['attachment'])); } if (!in_array($post->post_type, $allowed_cpts)) return; $canonical = get_permalink($post); if (!$canonical) return; $md_url = rtrim($canonical, '/') . '.md'; $query_url = add_query_arg('output_format', 'markdown', $canonical); printf('' . "\n", esc_attr__('Markdown version', 'aiutoma'), esc_url($md_url)); printf('' . "\n", esc_url($query_url)); } public function handle_accept_markdown_header() { if (get_query_var('aiutoma_md')) return; $accept = isset($_SERVER['HTTP_ACCEPT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_ACCEPT'])) : ''; if (strpos($accept, 'text/markdown') !== false) { if (is_singular()) { $post = get_queried_object(); if ($post instanceof \WP_Post && ($post->post_status === 'publish' || ($post->post_type === 'attachment' && $post->post_status === 'inherit'))) { $allowed_cpts = get_option('aiutoma_markdown_cpts', false); if ($allowed_cpts === false) { $allowed_cpts = array_values(array_diff(array_keys(get_post_types(['public' => true])), ['attachment'])); } if (!in_array($post->post_type, $allowed_cpts)) return; $canonical = get_permalink($post); if ($canonical) { $md_url = rtrim($canonical, '/') . '.md'; header('Vary: Accept'); wp_safe_redirect($md_url, 303); exit; } } } } } public function serve_markdown_content() { // phpcs:ignore WordPress.Security.NonceVerification.Recommended $is_query_format = isset($_GET['output_format']) && 'markdown' === sanitize_key(wp_unslash((string)$_GET['output_format'])); $is_md_var = (bool) get_query_var('aiutoma_md'); if (!$is_md_var && !$is_query_format) return; if ($is_query_format && !is_singular() && !is_front_page()) return; // Prevent loopback if (isset($_SERVER['HTTP_X_WAI_RENDER'])) { status_header(404); exit; } $post = get_queried_object(); if (!$post instanceof \WP_Post) { if (is_front_page()) { $page_on_front = get_option('page_on_front'); if ($page_on_front) { $post = get_post((int)$page_on_front); } } } if (!$post instanceof \WP_Post || ($post->post_status !== 'publish' && !($post->post_type === 'attachment' && $post->post_status === 'inherit'))) { status_header(404); exit; } $allowed_cpts = get_option('aiutoma_markdown_cpts', false); if ($allowed_cpts === false) { $allowed_cpts = array_values(array_diff(array_keys(get_post_types(['public' => true])), ['attachment'])); } if (!in_array($post->post_type, $allowed_cpts)) { status_header(404); exit; } if (post_password_required($post)) { status_header(403); header('Content-Type: text/plain; charset=UTF-8'); echo esc_html__('Content is password protected.', 'aiutoma'); exit; } // Detect AI Bots $this->detect_and_log_ai_bot($post); $canonical = get_permalink($post); $markdown = $this->generate_post_markdown($post, $canonical); status_header(200); header('Content-Type: text/markdown; charset=' . get_bloginfo('charset')); header('Vary: Accept'); header('X-Robots-Tag: noindex'); header('Link: <' . $canonical . '>; rel="canonical"'); header('X-Content-Type-Options: nosniff'); header('X-Markdown-Tokens: ' . (int)(strlen($markdown) / 4)); echo wp_kses_post($markdown); exit; } /** * Filters the content type reported for the markdown feed. */ public function filter_markdown_feed_content_type($content_type, $type) { if ('markdown' === $type) { return 'text/markdown'; } return $content_type; } /** * Renders the current feed query as a clean Markdown document. */ public function render_markdown_feed() { if (!headers_sent()) { status_header(200); header('Content-Type: text/markdown; charset=' . get_bloginfo('charset')); header('X-Robots-Tag: noindex'); } $use_excerpt = (bool) get_option('rss_use_excerpt'); $site_name = wp_specialchars_decode(get_bloginfo('name'), ENT_QUOTES); $site_desc = wp_specialchars_decode(get_bloginfo('description'), ENT_QUOTES); $blocks = [ '# ' . $site_name, ]; if (!empty($site_desc)) { $blocks[] = $site_desc; } /* translators: %s: site home url */ $blocks[] = '- ' . sprintf(__('Site: %s', 'aiutoma'), home_url('/')); while (have_posts()) { the_post(); $post = get_post(); if (!$post instanceof \WP_Post) { continue; } $permalink = (string) get_permalink($post); if ($use_excerpt) { $content = trim(wp_strip_all_tags((string) get_the_excerpt($post), true)); } else { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound $content_html = (string) apply_filters('the_content', get_the_content(null, false, $post)); $content = trim($this->extract_markdown_from_html($content_html)); } $meta_lines = [ /* translators: %s: post permalink URL */ '- ' . sprintf(__('Link: %s', 'aiutoma'), esc_url($permalink)), /* translators: %s: post publish date */ '- ' . sprintf(__('Published: %s', 'aiutoma'), (string) get_the_date('c', $post)), /* translators: %s: post author display name */ '- ' . sprintf(__('Author: %s', 'aiutoma'), (string) get_the_author_meta('display_name', (int) $post->post_author)), ]; $item_sections = [ 'title' => '## ' . wp_specialchars_decode((string) get_the_title($post), ENT_QUOTES), 'meta' => implode("\n", $meta_lines), 'content' => $content, ]; $item_sections = apply_filters('aiutoma_markdown_feed_item_sections', $item_sections, $post); $item_text = implode("\n\n", array_filter(array_map('trim', $item_sections))); if (!empty($item_text)) { $blocks[] = $item_text; } } wp_reset_postdata(); $output = implode("\n\n", array_filter($blocks)) . "\n"; echo wp_kses_post($output); exit; } }