From 857d87ea75a3adf0c5802f1664ec826e31494778 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 28 Nov 2025 23:54:05 +0000 Subject: [PATCH 1/4] Initial plan From efcfbb592180e51f9633f1d44ac23fa6e160c129 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 00:11:22 +0000 Subject: [PATCH 2/4] Add markdown-export plugin for serving posts/pages as markdown Co-authored-by: adamziel <205419+adamziel@users.noreply.github.com> --- plugins/markdown-export/plugin.php | 126 +++++++++++++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 plugins/markdown-export/plugin.php diff --git a/plugins/markdown-export/plugin.php b/plugins/markdown-export/plugin.php new file mode 100644 index 00000000..f3d52490 --- /dev/null +++ b/plugins/markdown-export/plugin.php @@ -0,0 +1,126 @@ +post_name ) . '.md"' ); + + echo $markdown; + exit; + } +); + +/** + * Get a post by its path/slug. + * + * @param string $path The URL path to find the post for. + * @return WP_Post|null The post object or null if not found. + */ +function markdown_export_get_post_by_path( $path ) { + // Clean up the path + $path = trim( $path, '/' ); + + if ( empty( $path ) ) { + return null; + } + + // Try to get by path as a page (hierarchical) + $page = get_page_by_path( $path, OBJECT, array( 'page', 'post' ) ); + if ( $page ) { + return $page; + } + + // Try to find by slug for non-hierarchical post types + $posts = get_posts( + array( + 'name' => basename( $path ), + 'post_type' => array( 'post', 'page' ), + 'post_status' => 'publish', + 'posts_per_page' => 1, + ) + ); + + if ( ! empty( $posts ) ) { + return $posts[0]; + } + + return null; +} + +/** + * Convert a post to Markdown format using MarkdownProducer. + * + * @param WP_Post $post The post to convert. + * @return string The markdown representation of the post. + */ +function markdown_export_convert_post_to_markdown( $post ) { + // Prepare metadata for the markdown frontmatter + $metadata = array( + 'post_title' => array( $post->post_title ), + ); + + // Add optional metadata if available + if ( ! empty( $post->post_date ) ) { + $metadata['post_date'] = array( $post->post_date ); + } + + if ( ! empty( $post->post_excerpt ) ) { + $metadata['post_excerpt'] = array( $post->post_excerpt ); + } + + // Create BlocksWithMetadata and convert to markdown + $blocks_with_metadata = new BlocksWithMetadata( + $post->post_content, + $metadata + ); + + $producer = new MarkdownProducer( $blocks_with_metadata ); + + return $producer->produce(); +} From 7c8ed6730f51b575bccb39427c900b3fd9a65999 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 00:12:48 +0000 Subject: [PATCH 3/4] Address code review feedback for markdown-export plugin Co-authored-by: adamziel <205419+adamziel@users.noreply.github.com> --- plugins/markdown-export/plugin.php | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/plugins/markdown-export/plugin.php b/plugins/markdown-export/plugin.php index f3d52490..c395733e 100644 --- a/plugins/markdown-export/plugin.php +++ b/plugins/markdown-export/plugin.php @@ -24,7 +24,8 @@ add_action( 'template_redirect', function () { - $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ) : ''; + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- We need the raw URI path; further validation below. + $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : ''; // Check if the request ends with .md if ( ! preg_match( '/\.md$/i', $request_uri ) ) { @@ -47,7 +48,11 @@ function () { // Convert the post content to markdown $markdown = markdown_export_convert_post_to_markdown( $post ); - // Set appropriate headers + /* + * Use text/markdown MIME type as defined in RFC 7763. + * While not universally standardized, it's the most appropriate + * Content-Type for markdown files. + */ header( 'Content-Type: text/markdown; charset=utf-8' ); header( 'Content-Disposition: inline; filename="' . sanitize_file_name( $post->post_name ) . '.md"' ); @@ -103,14 +108,11 @@ function markdown_export_convert_post_to_markdown( $post ) { // Prepare metadata for the markdown frontmatter $metadata = array( 'post_title' => array( $post->post_title ), + 'post_date' => array( $post->post_date ), ); - // Add optional metadata if available - if ( ! empty( $post->post_date ) ) { - $metadata['post_date'] = array( $post->post_date ); - } - - if ( ! empty( $post->post_excerpt ) ) { + // Add excerpt if it has content + if ( '' !== $post->post_excerpt ) { $metadata['post_excerpt'] = array( $post->post_excerpt ); } From 17490722ce2e067b0ef46ec7a17b70d9f511a4cf Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 29 Nov 2025 00:14:01 +0000 Subject: [PATCH 4/4] Improve header handling and add phpcs comments Co-authored-by: adamziel <205419+adamziel@users.noreply.github.com> --- plugins/markdown-export/plugin.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/markdown-export/plugin.php b/plugins/markdown-export/plugin.php index c395733e..3b54a86b 100644 --- a/plugins/markdown-export/plugin.php +++ b/plugins/markdown-export/plugin.php @@ -24,7 +24,7 @@ add_action( 'template_redirect', function () { - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- We need the raw URI path; further validation below. + // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- We need the raw URI path; validation below. $request_uri = isset( $_SERVER['REQUEST_URI'] ) ? wp_unslash( $_SERVER['REQUEST_URI'] ) : ''; // Check if the request ends with .md @@ -41,6 +41,7 @@ function () { if ( ! $post ) { status_header( 404 ); + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Plain text error response. echo 'Post not found'; exit; } @@ -54,7 +55,10 @@ function () { * Content-Type for markdown files. */ header( 'Content-Type: text/markdown; charset=utf-8' ); - header( 'Content-Disposition: inline; filename="' . sanitize_file_name( $post->post_name ) . '.md"' ); + + // Use rawurlencode for RFC 5987 compliant filename in Content-Disposition header. + $filename = rawurlencode( sanitize_file_name( $post->post_name ) . '.md' ); + header( "Content-Disposition: inline; filename*=UTF-8''" . $filename ); echo $markdown; exit;