Skip to content

Commit 60185af

Browse files
committed
Initial commit
0 parents  commit 60185af

File tree

7 files changed

+256
-0
lines changed

7 files changed

+256
-0
lines changed

.distignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/.git
2+
/.github
3+
/.gitignore
4+
/.distignore
5+
/composer.lock
6+
/composer.json
7+
/scoper.inc.php
8+
/vendor

.github/workflows/release.yml

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
name: Build Release
2+
3+
on:
4+
release:
5+
types: [created]
6+
7+
jobs:
8+
build:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- name: Checkout code
13+
uses: actions/checkout@v3
14+
15+
- name: Setup PHP
16+
uses: shivammathur/setup-php@v2
17+
with:
18+
php-version: '8.1'
19+
tools: composer
20+
21+
- name: Install dependencies (with dev for build tools)
22+
run: composer install --optimize-autoloader
23+
24+
- name: Build prefixed vendor
25+
run: |
26+
vendor/bin/php-scoper add-prefix --config=scoper.inc.php --force
27+
cd vendor_prefixed && composer dump-autoload
28+
29+
- name: Remove dev dependencies after build
30+
run: |
31+
rm -rf vendor
32+
composer install --no-dev --optimize-autoloader
33+
34+
- name: Create release artifact
35+
run: |
36+
mkdir -p /tmp/post-content-to-markdown
37+
rsync -av --exclude-from='.distignore' . /tmp/post-content-to-markdown/
38+
cd /tmp
39+
zip -r post-content-to-markdown.zip post-content-to-markdown/
40+
41+
- name: Upload release asset
42+
uses: actions/upload-release-asset@v1
43+
env:
44+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
45+
with:
46+
upload_url: ${{ github.event.release.upload_url }}
47+
asset_path: /tmp/post-content-to-markdown.zip
48+
asset_name: post-content-to-markdown.zip
49+
asset_content_type: application/zip

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
vendor
2+
vendor_prefixed
3+
composer.lock

README.md

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# Post Content to Markdown
2+
3+
A WordPress plugin that returns post content in Markdown format when requested with an `Accept` header set to `text/markdown`.
4+
5+
## Usage
6+
7+
Visit any single post on your site with the `Accept` header set to `text/markdown` to get the post content directly as Markdown.
8+
9+
**Example:**
10+
11+
```bash
12+
curl -H "Accept: text/markdown" https://example.com/my-awesome-post
13+
```
14+
15+
## Installation
16+
17+
### via Composer
18+
19+
```bash
20+
composer require roots/post-content-to-markdown
21+
```
22+
23+
### Manual
24+
25+
1. Download the [latest release](https://github.com/roots/post-content-to-markdown/releases)
26+
2. Place in `wp-content/plugins/post-content-to-markdown/`
27+
3. Activate via wp-admin or WP-CLI
28+
29+
## Filters
30+
31+
The plugin provides several filters for customization:
32+
33+
### `post_content_to_markdown/post_types`
34+
35+
Filter the post types that can be served as markdown.
36+
37+
```php
38+
add_filter('post_content_to_markdown/post_types', function ($post_types) {
39+
// Add support for pages and custom post types
40+
return ['post', 'page', 'product'];
41+
});
42+
```
43+
44+
**Default:** `['post']`
45+
46+
### `post_content_to_markdown/converter_options`
47+
48+
Filter the HTML to Markdown converter options.
49+
50+
```php
51+
add_filter('post_content_to_markdown/converter_options', function ($options) {
52+
return [
53+
'header_style' => 'setext', // Use underline style for H1/H2
54+
'strip_tags' => false, // Keep HTML tags without markdown equivalents
55+
'remove_nodes' => 'script style img', // Remove script, style, and img elements
56+
'hard_break' => false, // Convert <br> to two spaces + newline
57+
];
58+
});
59+
```
60+
61+
**Available options:**
62+
- `header_style`: `'atx'` (default) or `'setext'`
63+
- `strip_tags`: Remove HTML tags without markdown equivalents (default: `true`)
64+
- `remove_nodes`: Space-separated list of DOM nodes to remove (default: `'script style'`)
65+
- `hard_break`: Convert `<br>` to newlines (default: `true`)
66+
67+
### `post_content_to_markdown/markdown_output`
68+
69+
Filter the final markdown output after conversion.
70+
71+
```php
72+
add_filter('post_content_to_markdown/markdown_output', function ($markdown, $original_html) {
73+
// Add a footer to all markdown output
74+
return $markdown . "\n\n---\nConverted from HTML to Markdown";
75+
}, 10, 2);
76+
```
77+
78+
**Parameters:**
79+
- `$markdown`: The converted markdown text
80+
- `$original_html`: The original HTML content

composer.json

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
{
2+
"name": "roots/post-content-to-markdown",
3+
"type": "wordpress-plugin",
4+
"description": "A plugin that converts post content to Markdown when requested with an `text/markdown` Accept header.",
5+
"license": "MIT",
6+
"homepage": "https://roots.io/sage/",
7+
"authors": [
8+
{
9+
"name": "Ben Word",
10+
"email": "[email protected]",
11+
"homepage": "https://github.com/retlehs"
12+
}
13+
],
14+
"scripts": {
15+
"build": "php-scoper add-prefix --force"
16+
},
17+
"require": {
18+
"league/html-to-markdown": "^5.1"
19+
},
20+
"require-dev": {
21+
"humbug/php-scoper": "0.18.7"
22+
}
23+
}

post-content-to-markdown.php

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<?php
2+
3+
/**
4+
* Plugin Name: Post Content to Markdown
5+
* Description: Exposes post content as Markdown via HTTP `Accept` headers.
6+
* Version: 1.0.0
7+
* Author: roots.io
8+
*/
9+
10+
namespace PostContentToMarkdown;
11+
12+
if (! defined('ABSPATH')) {
13+
exit;
14+
}
15+
16+
// Load the appropriate autoloader
17+
if (file_exists(__DIR__.'/vendor/autoload.php')) {
18+
// Composer installation
19+
require_once __DIR__.'/vendor/autoload.php';
20+
} else {
21+
// Standalone plugin with prefixed dependencies
22+
require_once __DIR__.'/vendor_prefixed/vendor/autoload.php';
23+
}
24+
25+
add_action('template_redirect', function () {
26+
if (! isset($_SERVER['HTTP_ACCEPT']) || ! str_contains($_SERVER['HTTP_ACCEPT'], 'text/markdown')) {
27+
return;
28+
}
29+
30+
$post = get_queried_object();
31+
if (! $post || ! is_a($post, 'WP_Post')) {
32+
return;
33+
}
34+
35+
$allowed_post_types = apply_filters('post_content_to_markdown/post_types', ['post']);
36+
37+
if (is_single() && in_array($post->post_type, $allowed_post_types, true)) {
38+
header('Content-Type: text/markdown');
39+
echo '# '.strip_tags($post->post_title)."\n\n".contentToMarkdown($post->post_content);
40+
exit;
41+
}
42+
});
43+
44+
/**
45+
* Converts an HTML string into Markdown using the league/html-to-markdown library.
46+
*
47+
* @param string $content The HTML content to convert.
48+
* @return string The converted Markdown.
49+
*/
50+
function contentToMarkdown($content)
51+
{
52+
if (empty(trim($content))) {
53+
return '';
54+
}
55+
56+
$default_options = [
57+
'header_style' => 'atx',
58+
'strip_tags' => true, // Remove HTML tags without markdown equivalents
59+
'remove_nodes' => 'script style', // Remove script and style elements
60+
'hard_break' => true, // Convert <br> to newlines
61+
];
62+
63+
$options = apply_filters('post_content_to_markdown/converter_options', $default_options);
64+
65+
// Determine which HtmlConverter class to use based on what's available
66+
if (class_exists('\League\HTMLToMarkdown\HtmlConverter')) {
67+
$converter = new \League\HTMLToMarkdown\HtmlConverter($options);
68+
} else {
69+
$converter = new \PostContentToMarkdown\Vendor\League\HTMLToMarkdown\HtmlConverter($options);
70+
}
71+
$markdown = $converter->convert($content);
72+
73+
// Clean up excessive newlines (more than 2 consecutive)
74+
$markdown = preg_replace("/\n{3,}/", "\n\n", $markdown);
75+
76+
return apply_filters('post_content_to_markdown/markdown_output', $markdown, $content);
77+
}

scoper.inc.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
use Isolated\Symfony\Component\Finder\Finder;
6+
7+
return [
8+
'prefix' => 'PostContentToMarkdown\Vendor',
9+
'output-dir' => 'vendor_prefixed',
10+
'finders' => [
11+
Finder::create()
12+
->files()
13+
->name(['*.php', 'composer.json'])
14+
->in('vendor/league/html-to-markdown'),
15+
],
16+
];

0 commit comments

Comments
 (0)