Skip to content

fix: preserve full permalink structure in markdown URLs (#7)#9

Open
jdevalk wants to merge 2 commits intomainfrom
issue/7-permalink-structure-support
Open

fix: preserve full permalink structure in markdown URLs (#7)#9
jdevalk wants to merge 2 commits intomainfrom
issue/7-permalink-structure-support

Conversation

@jdevalk
Copy link
Member

@jdevalk jdevalk commented Feb 15, 2026

Closes #7

Problem

The plugin was not correctly handling permalink structures with file extensions (e.g., .html, .htm). When a site uses a structure like /%category%/%year%/%monthnum%/%day%/%postname%-%post_id%.html, the plugin would:

  • Generate markdown URLs like /postname.html.md (incorrect) by blindly appending .md
  • Or fail to resolve the post correctly when stripping .md from incoming requests

The user reported that their URL:

  • HTML: https://www.grappling-italia.com/news/2026/02/14/ufc-e-ai-quando-i-dati-iniziano-a-raccontare-storie-79946.html
  • Expected .md: https://www.grappling-italia.com/news/2026/02/14/ufc-e-ai-quando-i-dati-iniziano-a-raccontare-storie-79946.md
  • Actual .md: https://www.grappling-italia.com/ufc-e-ai-quando-i-dati-iniziano-a-raccontare-storie.md

Solution

  • Added helper methods in both AlternateLinkHandler and RewriteHandler to intelligently handle URL conversion
  • URL Generation: Detect when permalinks end with file extensions (.html, .htm, .php, .aspx, .asp) and replace them with .md instead of appending
  • URL Parsing: When a .md request comes in, try multiple strategies to resolve the original post:
    1. Try replacing .md with common extensions (.html, .htm, .php, .aspx, .asp)
    2. Fall back to the original behavior (no extension)
  • This preserves the full permalink structure including categories, dates, and post IDs

Changes

  • src/Discovery/AlternateLinkHandler.php:

    • Added get_markdown_url() helper method with regex-based extension detection
    • Updated output_alternate_link() to use the new helper
  • src/Router/RewriteHandler.php:

    • Added permalink_to_markdown_url() helper method (same logic as AlternateLinkHandler)
    • Updated handle_accept_negotiation() to use the new helper
    • Updated parse_markdown_url() to try multiple extension replacements when resolving incoming .md URLs

Testing Verified

Permalink structures now supported:

  • /%postname%//postname.md
  • /%category%/%postname%//category/postname.md
  • /%year%/%monthnum%/%day%/%postname%//2026/02/14/postname.md
  • /%category%/%year%/%monthnum%/%day%/%postname%-%post_id%.html/category/2026/02/14/postname-12345.md ✓ (issue The parmalink structure is not the same #7)
  • /%postname%.htm/postname.md
  • /%postname%.php/postname.md

🤖 Generated with Claude Code

- Add helper methods to detect and replace file extensions (.html, .htm, etc.) with .md
- Update AlternateLinkHandler to use new URL conversion logic
- Update RewriteHandler to handle both URL generation and parsing with file extensions
- Parse incoming .md URLs by trying multiple extension replacements
- Fixes issue where complex permalink structures were not preserved

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request fixes an issue where the markdown alternate plugin was not correctly handling WordPress permalink structures that include file extensions (e.g., .html, .htm, .php). Previously, the plugin would blindly append .md to any permalink, resulting in incorrect URLs like /postname.html.md instead of /postname.md. The fix ensures that when a permalink ends with a common file extension, that extension is replaced with .md rather than appended.

Changes:

  • Added intelligent URL conversion logic that detects and replaces file extensions in permalinks with .md
  • Implemented fallback resolution that tries multiple extension patterns when parsing incoming .md requests
  • Ensured full permalink structure preservation including categories, dates, and post IDs

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/Router/RewriteHandler.php Added permalink_to_markdown_url() helper method for intelligent extension replacement; updated parse_markdown_url() to try multiple extensions when resolving incoming .md URLs; updated handle_accept_negotiation() to use the new helper
src/Discovery/AlternateLinkHandler.php Added get_markdown_url() helper method with identical logic for extension replacement; updated output_alternate_link() to use the new helper for generating alternate link tags

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 62 to 70
// Check if permalink ends with a file extension
// Common extensions: .html, .htm, .php, .aspx, .asp
if ( preg_match('/\.(html?|php|aspx?)$/i', $permalink) ) {
// Replace the extension with .md
return preg_replace('/\.(html?|php|aspx?)$/i', '.md', $permalink);
}

// No extension found - append .md (trim trailing slash first)
return rtrim( $permalink, '/' ) . '.md';
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method duplicates the exact same logic found in RewriteHandler::permalink_to_markdown_url() (lines 333-343). Consider extracting this shared functionality into a common utility class or trait to follow the DRY (Don't Repeat Yourself) principle. This would make maintenance easier and ensure both code paths remain consistent if the logic needs to be updated in the future.

Suggested change
// Check if permalink ends with a file extension
// Common extensions: .html, .htm, .php, .aspx, .asp
if ( preg_match('/\.(html?|php|aspx?)$/i', $permalink) ) {
// Replace the extension with .md
return preg_replace('/\.(html?|php|aspx?)$/i', '.md', $permalink);
}
// No extension found - append .md (trim trailing slash first)
return rtrim( $permalink, '/' ) . '.md';
// Delegate permalink-to-markdown conversion to the shared handler
return \MarkdownAlternate\Rewrite\RewriteHandler::permalink_to_markdown_url( $permalink );

Copilot uses AI. Check for mistakes.
Comment on lines 333 to 343
private function permalink_to_markdown_url(string $permalink): string {
// Check if permalink ends with a file extension
// Common extensions: .html, .htm, .php, .aspx, .asp
if (preg_match('/\.(html?|php|aspx?)$/i', $permalink)) {
// Replace the extension with .md
return preg_replace('/\.(html?|php|aspx?)$/i', '.md', $permalink);
}

// No extension found - append .md (trim trailing slash first)
return rtrim($permalink, '/') . '.md';
}
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method duplicates the exact same logic found in AlternateLinkHandler::get_markdown_url() (lines 59-71). Consider extracting this shared functionality into a common utility class or trait to follow the DRY (Don't Repeat Yourself) principle. This would make maintenance easier and ensure both code paths remain consistent if the logic needs to be updated in the future.

Copilot uses AI. Check for mistakes.
Comment on lines +102 to +109
foreach ($extensions_to_try as $ext) {
$clean_url = home_url('/' . $path_without_md . $ext);
$post_id = url_to_postid($clean_url);

if ($post_id) {
break; // Found a match
}
}
Copy link

Copilot AI Feb 15, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The variable $post_id is not initialized before the loop, which means it will be undefined if no extension matches. While PHP will treat this as falsy in the subsequent check (line 111), it's better practice to initialize the variable before the loop to avoid potential undefined variable warnings and make the code more explicit. Consider adding $post_id = 0; before the loop.

Copilot uses AI. Check for mistakes.
- Extract duplicate permalink-to-markdown conversion logic into shared static method
- Initialize $post_id variable before loop to prevent undefined variable warnings
- Both AlternateLinkHandler and RewriteHandler now use permalink_to_markdown_url_static()

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@jdevalk
Copy link
Member Author

jdevalk commented Feb 17, 2026

Consolidation note: recommended merge order is #8 then #9 (functional URL fixes first), then refactor PRs touching RewriteHandler.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The parmalink structure is not the same

1 participant