Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
130 changes: 69 additions & 61 deletions src/Router/RewriteHandler.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ class RewriteHandler {
*/
private ?WP_Post $markdown_post = null;

/**
* Cached ContentRenderer instance.
*
* @var ContentRenderer|null
*/
private ?ContentRenderer $renderer = null;

/**
* Register all hooks for URL routing.
*
Expand Down Expand Up @@ -155,6 +162,57 @@ private function is_supported_post_type(string $post_type): bool {
return in_array($post_type, $this->get_supported_post_types(), true);
}

/**
* Get cached ContentRenderer instance.
*
* @return ContentRenderer The renderer instance.
*/
private function get_renderer(): ContentRenderer {
if ($this->renderer === null) {
$this->renderer = new ContentRenderer();
}
return $this->renderer;
}

/**
* Validate post for markdown output.
*
* @param WP_Post $post The post to validate.
* @return bool True if valid for markdown output, false otherwise.
*/
private function validate_post_for_markdown(WP_Post $post): bool {
if (!$this->is_supported_post_type($post->post_type)) {
return false;
}

if (get_post_status($post) !== 'publish') {
return false;
}

return true;
}

/**
* Serve markdown response for a post.
*
* @param WP_Post $post The post to serve.
* @return void
*/
private function serve_markdown_response(WP_Post $post): void {
if (post_password_required($post)) {
status_header(403);
header('Content-Type: text/plain; charset=UTF-8');
echo 'This content is password protected.';
exit;
}

$markdown = $this->get_renderer()->render($post);

$this->set_response_headers($post, $markdown);
echo $markdown;
exit;
}

/**
* Handle format query parameter fallback.
*
Expand All @@ -164,55 +222,29 @@ private function is_supported_post_type(string $post_type): bool {
* @return void
*/
public function handle_format_parameter(): void {
// Skip if already a markdown request (URL wins over query parameter)
if (get_query_var('markdown_request')) {
return;
}

// Check for format=markdown query parameter (case-sensitive, strict equality)
$format = get_query_var('format');
if ($format !== 'markdown') {
if (get_query_var('format') !== 'markdown') {
return;
}

// Only for singular content
if (!is_singular()) {
return;
}

// Get the queried object
$post = get_queried_object();

// Validate post exists and is a WP_Post
if (!$post instanceof WP_Post) {
return;
}

// Check post type - only serve supported post types
if (!$this->is_supported_post_type($post->post_type)) {
return;
}

// Check post status - only serve published posts
if (get_post_status($post) !== 'publish') {
if (!$this->validate_post_for_markdown($post)) {
return;
}

// Check password protection
if (post_password_required($post)) {
status_header(403);
header('Content-Type: text/plain; charset=UTF-8');
echo 'This content is password protected.';
exit;
}

// Render and serve the markdown content
$renderer = new ContentRenderer();
$markdown = $renderer->render($post);

$this->set_response_headers($post, $markdown);
echo $markdown;
exit;
$this->serve_markdown_response($post);
}

/**
Expand All @@ -223,59 +255,35 @@ public function handle_format_parameter(): void {
* @return void
*/
public function handle_markdown_request(): void {
// Check if this is a markdown request
if (!get_query_var('markdown_request')) {
return;
}

// Get the request URI
$request_uri = $_SERVER['REQUEST_URI'] ?? '';

// Enforce lowercase .md extension - let WP 404 if wrong case
if (!preg_match('/\.md$/', $request_uri) && preg_match('/\.md$/i', $request_uri)) {
if (
!preg_match('/\.md$/', $request_uri) &&
preg_match('/\.md$/i', $request_uri)
) {
return;
}

// Handle trailing slash redirect: /post-slug.md/ -> /post-slug.md
if (preg_match('/\.md\/$/', $request_uri)) {
$redirect_url = rtrim($request_uri, '/');
wp_redirect($redirect_url, 301);
if (str_ends_with($request_uri, '.md/')) {
wp_redirect(rtrim($request_uri, '/'), 301);
exit;
}

// Get post - use cached post from parse_markdown_url (Nginx) or queried object (Apache)
$post = $this->markdown_post ?? get_queried_object();

// Validate post exists and is a WP_Post
if (!$post instanceof WP_Post) {
return;
}

// Check post type - only serve supported post types
if (!$this->is_supported_post_type($post->post_type)) {
if (!$this->validate_post_for_markdown($post)) {
return;
}

// Check post status - only serve published posts
if (get_post_status($post) !== 'publish') {
return;
}

// Check password protection
if (post_password_required($post)) {
status_header(403);
header('Content-Type: text/plain; charset=UTF-8');
echo 'This content is password protected.';
exit;
}

// Render and serve the markdown content
$renderer = new ContentRenderer();
$markdown = $renderer->render($post);

$this->set_response_headers($post, $markdown);
echo $markdown;
exit;
$this->serve_markdown_response($post);
}

/**
Expand Down