diff --git a/.github/changelog/integrations b/.github/changelog/integrations new file mode 100644 index 0000000..22169e6 --- /dev/null +++ b/.github/changelog/integrations @@ -0,0 +1,4 @@ +Significance: minor +Type: added + +Add integrations framework for third-party plugin support. diff --git a/composer.json b/composer.json index 887285b..d0940c5 100644 --- a/composer.json +++ b/composer.json @@ -27,7 +27,8 @@ }, "autoload": { "classmap": [ - "includes/" + "includes/", + "integrations/" ] }, "scripts": { diff --git a/includes/class-atmosphere.php b/includes/class-atmosphere.php index b0099ed..bef91c2 100644 --- a/includes/class-atmosphere.php +++ b/includes/class-atmosphere.php @@ -13,6 +13,7 @@ use Atmosphere\Transformer\Document; use Atmosphere\Transformer\Publication; use Atmosphere\Transformer\TID; +use Atmosphere\Integrations\Load; use Atmosphere\WP_Admin\Admin; /** @@ -43,6 +44,9 @@ public function init(): void { // JSON preview for AT Protocol records. \add_action( 'template_redirect', array( $this, 'preview' ) ); + // Plugin integrations. + Load::init(); + // Post lifecycle hooks. \add_action( 'transition_post_status', array( $this, 'on_status_change' ), 10, 3 ); diff --git a/includes/content-parser/class-markpub.php b/includes/content-parser/class-markpub.php index e7d416e..e3d1165 100644 --- a/includes/content-parser/class-markpub.php +++ b/includes/content-parser/class-markpub.php @@ -82,6 +82,23 @@ private static function transform_block( array $block ): ?string { return self::inline_html_to_markdown( $html ); } + /** + * Filters the markdown output for a single block. + * + * Integrations can hook this to handle custom block types + * (e.g. jetpack/slideshow, woocommerce/product). Return a + * string to use as the block's markdown, or null to let + * the default handler process it. + * + * @param string|null $markdown Markdown output. Default null. + * @param array $block Parsed WordPress block. + */ + $markdown = \apply_filters( 'atmosphere_markpub_block', null, $block ); + + if ( null !== $markdown ) { + return $markdown; + } + return match ( $block['blockName'] ) { 'core/paragraph' => self::paragraph( $block ), 'core/heading' => self::heading( $block ), diff --git a/integrations/README.md b/integrations/README.md new file mode 100644 index 0000000..6b618d2 --- /dev/null +++ b/integrations/README.md @@ -0,0 +1,74 @@ +# Integrations + +Plugin-specific integrations that teach ATmosphere how to handle custom block types and content from third-party plugins. + +## How it works + +When ATmosphere converts a WordPress post to an AT Protocol record, the Markpub content parser walks through each Gutenberg block and converts it to markdown. Core blocks (paragraph, heading, image, list, etc.) are handled by default. For custom blocks from other plugins, integrations hook into the `atmosphere_markpub_block` filter to provide their own conversion. + +## Adding an integration + +1. Create `class-{plugin-name}.php` in this directory. +2. Register the integration in `class-load.php` with a check for the target plugin. +3. Hook into `atmosphere_markpub_block` to handle the plugin's custom blocks. + +### Example: Jetpack + +**`class-jetpack.php`** + +```php + self::slideshow( $block ), + 'jetpack/tiled-gallery' => self::gallery( $block ), + default => $markdown, + }; + } + + private static function slideshow( array $block ): ?string { + // Convert slideshow block to markdown image list. + } + + private static function gallery( array $block ): ?string { + // Convert gallery block to markdown image list. + } +} +``` + +**In `class-load.php`** + +```php +public static function register(): void { + if ( \defined( 'JETPACK__VERSION' ) ) { + Jetpack::init(); + } +} +``` + +## Available filters + +| Filter | Arguments | Description | +|---|---|---| +| `atmosphere_markpub_block` | `?string $markdown`, `array $block` | Handle a custom block type. Return markdown string or `null` to pass through to default handling. | +| `atmosphere_content_parser` | `Content_Parser\|null $parser`, `WP_Post $post` | Replace the entire content parser or return `null` to disable. | +| `atmosphere_document_content` | `array $content`, `WP_Post $post`, `Content_Parser $parser` | Modify the parsed content object before it is added to the document record. | +| `atmosphere_html_to_markdown` | `string $markdown`, `string $content` | Override the final markdown output from the Markpub parser. | + +## Conventions + +- One class per plugin, all methods static. +- File naming: `class-{plugin-name}.php`. +- Namespace: `Atmosphere\Integrations`. +- Always guard with a plugin check (`\defined()`, `\class_exists()`, etc.) in `class-load.php`. +- Return `$markdown` (the first argument) unchanged from the filter when the block is not yours. diff --git a/integrations/class-load.php b/integrations/class-load.php new file mode 100644 index 0000000..1abd8a5 --- /dev/null +++ b/integrations/class-load.php @@ -0,0 +1,37 @@ +