' . "\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;
}
}