=== QH Markdown Exporter ===
Contributors:      quyettran298
Tags:              export, markdown, obsidian, backup, posts
Requires at least: 5.8
Tested up to:      6.9
Requires PHP:      7.4
Stable tag:        2.2.5
License:           GPLv2 or later
License URI:       https://www.gnu.org/licenses/gpl-2.0.html

Export WordPress posts to Obsidian-compatible Markdown files (.md) with YAML frontmatter, plus an optional sitemap.json of your full site structure. Bulk export as ZIP archive.

== Description ==

QH Markdown Exporter lets you export any combination of post types and statuses as Markdown files — perfectly formatted for [Obsidian](https://obsidian.md/) and other Markdown-based note-taking apps.

**Key features:**

* **YAML frontmatter** — title, source URL, author, published date, description, tags, and full hierarchical categories
* **Extended metadata** (optional, on by default) — adds `word_count`, `outline` (ordered H2–H4 heading list), and `internal_links` (structured list of links to other exported posts) to each file's frontmatter — ideal for AI/SEO tooling
* **Clean content** — strips sidebars, widgets, navigation, related-posts blocks, social-share plugins, and other injected noise
* **GFM tables** — converts HTML tables to GitHub-Flavored Markdown pipe tables
* **Bulk export** — export your entire site at once or filter by post type, status, and date range
* **Large-site support** — chunked AJAX export with live progress bar handles sites with thousands of posts
* **Sitemap export** — optional `sitemap.json` bundled in the ZIP: hierarchical pages tree, posts grouped by category, and custom post types — ready for AI/LLM ingestion
* **No dependencies** — pure PHP with no external libraries required

**Output format:**

Each post becomes a `.md` file named `YYYY-MM-DD_Post Title.md` containing:

```
---
title: "Post Title"
source: "https://yoursite.com/post-slug/"
author:
  - "[[Author Name]]"
published: 2024-01-15
created: 2024-06-01
description: "Post excerpt or auto-generated summary..."
tags:
  - tag-name
categories:
  - "Parent > Child"
word_count: 1250
outline:
  - "## What This Post Covers"
  - "### Step One"
  - "## Conclusion"
internal_links:
  - slug: "related-post-slug"
    anchor: "related post title"
    url: "https://yoursite.com/related-post-slug/"
---

# Post Title

Post body in Markdown...
```

The `word_count`, `outline`, and `internal_links` fields are added when **Include extended metadata** is ticked (default: on). `internal_links` only lists links to other posts included in the current export — external links and links to non-exported content are excluded.

All files are bundled into a single ZIP archive named `{sitename}_md_export_YYYY-MM-DD.zip`.

== Installation ==

1. Upload the `qh-markdown-exporter` folder to the `/wp-content/plugins/` directory, or install directly through the WordPress plugin screen.
2. Activate the plugin through the **Plugins** screen in WordPress.
3. Go to **Tools → MD Exporter**.
4. Select the post types and statuses you want to export, optionally set a date range, and click **Export & Download ZIP**.

== Frequently Asked Questions ==

= Which post types are supported? =

All public post types registered on your site are available. The `attachment` post type is excluded by default.

= How does it handle large sites? =

For exports above 100 posts the plugin automatically switches to a chunked AJAX export. You will see a live progress bar. Each batch of 100 posts is processed separately to avoid server timeouts.

= Does it export images? =

Image tags are converted to Markdown `![alt](src)` format pointing to the original URLs on your site. The image files themselves are not downloaded or included in the ZIP.

= What capability is required to use the exporter? =

Users must have the `export` capability (Editor role and above by default).

= Will it conflict with page-builder plugins? =

The exporter reads raw `post_content` and processes it through `do_blocks()` (Gutenberg) then converts HTML to Markdown. It intentionally bypasses `the_content` filters to avoid injected content from page builders and other plugins.

== Screenshots ==

1. The export page showing post type and status filters, date range, and export buttons.
2. Progress bar during a large chunked export.
3. Example `.md` file opened in Obsidian showing YAML frontmatter and converted content.

== Changelog ==

= 2.2.4 =
* Prevented plugin activation fatal errors when an incomplete release package is missing `includes/v3-handlers.php`.
* Entitron V3 export now checks both V3 release files before enabling the V3 export button and reports the exact missing files in wp-admin.
* Kept the split V3 handler file while loading it only when it is present.

= 2.2.3 =
* Removed legacy Entitron V2 builder code that is no longer used by the V3 export flow.
* Removed stale V2-only comments and unused link-count helper code.
* Replaced inline progress visibility styles with markup/CSS state and kept the single export button behavior.

= 2.2.2 =
* Fixed the release package for Entitron V3 export so `includes/class-export-v3-builder.php` is included with the plugin.
* Added an install integrity check for the Entitron V3 builder file. If a server has an incomplete plugin upload, the export page now shows a clear admin error instead of a PHP fatal error.

= 2.1.0 =
* **Chunked V3 export** — Entitron V3 export now uses a three-phase AJAX flow (Prepare → Chunk → Finalize) instead of a single synchronous POST. Eliminates HTTP 503 (Varnish timeout) and PHP fatal errors (memory exhaustion) on large sites.
* **Single DOM per post** — V3 builder now uses one DOMDocument per post instead of three, reducing peak memory by ~60%.
* **File-based JSONL append** — articles.jsonl is built incrementally on disk using `fopen('ab')` + `flock(LOCK_EX)` instead of in-memory accumulation. Handles 10,000+ posts without OOM.
* **Zero-copy ZIP** — `ZipArchive::addFile()` for large articles.jsonl files (no memory buffer).
* **Error handling** — `register_shutdown_function` + `try/catch` + `wp_send_json_error` on every V3 AJAX handler. Legacy sync handler also wrapped.
* **PHP 7.4 compatibility** — no `match`, `readonly`, or `enum` syntax. Uses `array()` where appropriate.
* **Output schema unchanged** — manifest.json + articles.jsonl + taxonomy.json, same format as v2.0.0.

= 2.0.0 =
* **Entitron export** — new "Export for Entitron" button generates a JSONL-based ZIP for the Entitron SEO Analyzer (V2 format).
* **V2 format files**: `entitron_manifest.json` (detection sentinel + package metadata), `articles.jsonl` (one record per post with full content and metadata), `links.jsonl` (raw internal link edges with before/after text context), `taxonomy.json` (full categories + tags hierarchy), `site_inventory.json` (site-wide totals, date range, and languages).
* **Link context extraction** — each internal link edge captures 60-character before/after snippet and anchor text, enabling downstream tools to understand link intent.
* **Link count aggregation** — `articles.jsonl` includes `internal_links_in`, `internal_links_out`, and `external_links_out` counts per post.
* **Shared traits** — introduced reusable link context and link count helpers for Entitron exports.

= 1.6.0 =
* Added **Include extended metadata** option (enabled by default) — appends three new fields to each file's YAML frontmatter:
  * `word_count` — integer word count of the post body (plain text, no HTML).
  * `outline` — ordered list of H2, H3, and H4 headings with Markdown prefix (`##`, `###`, `####`).
  * `internal_links` — structured list of links that point to other posts included in the same export, each with `slug`, `anchor` text, and canonical `url`. Links to pages, categories, or posts outside the export set are excluded, preventing false "broken link" reports in downstream tools.
* Extended metadata can be disabled by unchecking the option — the export then produces the same output as previous versions.

= 1.5.1 =
* Refactored sitemap.json output for cleaner AI/LLM consumption.
* Replaced `posts_by_category` object (dynamic keys) with a `categories` array — each category has `id` (slug), `name`, and `posts`.
* Deduplicated posts: each post now appears only once, under its primary category.
* Decoded HTML entities in all titles (`&amp;` → `&`, etc.).
* Filtered out internal builder CPTs (Elementor library, Gutenberg reusable blocks, etc.) using the `show_in_nav_menus` flag.
* Converted `custom_post_types` from a slug-keyed object to a typed array `{ type, label, posts }`.
* Added `metadata` block: site URL, generation date, `total_pages`, `total_categories`, `total_posts`.

= 1.5.0 =
* Added optional sitemap.json export — tick "Include sitemap.json" in the export form to bundle a site structure file into the ZIP.
* Sitemap includes: hierarchical pages tree (parent → child), posts grouped by category, and public custom post types.
* Sitemap reflects the live site and intentionally ignores any date-range filters set on the export form.

= 1.4.0 =
* Renamed plugin to "QH Markdown Exporter" with slug `qh-markdown-exporter` for WordPress.org compliance.
* Renamed all internal prefixes from `pme_`/`PME_` (3 chars) to `qhmaex_`/`QHMAEX_` (6 chars) to meet the 4+ character prefix requirement.
* Updated text domain from `post-markdown-exporter` to `qh-markdown-exporter` across all files.
* Fixed upload directory path to use `wp_upload_dir()` instead of hardcoded `WP_CONTENT_DIR . '/uploads/'`.

= 1.3.0 =
* Renamed plugin to "Post Markdown Exporter" for WordPress.org compliance.
* Fixed output escaping to meet WordPress coding standards.
* Replaced unlink() with wp_delete_file() and file_put_contents() with WP_Filesystem.
* Added wp_unslash() to all sanitized input handling.
* Fixed i18n placeholder ordering in translatable strings.
* Updated "Tested up to" to WordPress 6.9.

= 1.2.0 =
* Added chunked AJAX export with live progress bar for large sites.
* Added date range filter.
* Improved filename sanitization with Unicode support.
* Added hierarchical category paths (Parent > Child).

= 1.1.0 =
* Added GFM pipe-table conversion.
* Added junk-node removal (navigation, sidebars, social share widgets).
* Added YAML frontmatter with author, tags, and categories.

= 1.0.0 =
* Initial release. Basic HTML-to-Markdown export with ZIP download.

== Upgrade Notice ==

= 2.2.4 =
Fixes activation failure caused by incomplete 2.2.3 packages missing the new V3 handlers file. No database changes.

= 2.2.3 =
Removes unused V2 export code and cleans the admin export UI markup. No database changes.

= 2.2.2 =
Fixes incomplete installs that could make Entitron V3 export fail with a missing `class-export-v3-builder.php` PHP fatal error. Reinstall/update the plugin so all files are present.

= 2.1.0 =
Rewrites the Entitron export engine to a three-phase chunked AJAX flow, eliminating HTTP 503 and memory exhaustion on large sites. The output format (manifest.json + articles.jsonl + taxonomy.json) is identical to v2.0.0. No database changes; safe to upgrade.

= 2.0.0 =
Introduces a new "Export for Entitron" button with a separate V2 JSONL-based export format. Your existing Markdown export is completely unchanged. No database changes; safe to upgrade.

= 1.6.0 =
New optional "Include extended metadata" feature adds word_count, outline, and internal_links to YAML frontmatter. Enabled by default; uncheck to keep previous output format. No database changes; safe to upgrade.

= 1.5.1 =
Improves sitemap.json structure and data quality. No database changes; safe to upgrade.

= 1.5.0 =
New optional feature: tick "Include sitemap.json" on the export form to get a full site structure JSON file in your ZIP. No database changes; safe to upgrade.

= 1.4.0 =
Plugin renamed to "QH Markdown Exporter". Temp files now stored in uploads/qh-markdown-exporter/. No database changes; safe to upgrade.

= 1.3.0 =
Plugin renamed from "WP MD Exporter" to "Post Markdown Exporter" for WordPress.org compliance. No database changes; safe to upgrade.
