Changeset 3492765
- Timestamp:
- 03/27/2026 03:33:48 PM (5 days ago)
- Location:
- headless
- Files:
-
- 89 added
- 59 edited
-
tags/3.0.1 (added)
-
tags/3.0.1/LICENSE (added)
-
tags/3.0.1/README.txt (added)
-
tags/3.0.1/classes (added)
-
tags/3.0.1/classes/Ajax.php (added)
-
tags/3.0.1/classes/BlockPreparations (added)
-
tags/3.0.1/classes/BlockPreparations/CoreEmbedBlockPreparation.php (added)
-
tags/3.0.1/classes/BlockPreparations/EmbedBlockPreparation.php (added)
-
tags/3.0.1/classes/BlockPreparations/FreeFormBlockPreparation.php (added)
-
tags/3.0.1/classes/BlockPreparations/GalleryBlockPreparation.php (added)
-
tags/3.0.1/classes/BlockPreparations/ImageBlockPreparation.php (added)
-
tags/3.0.1/classes/BlockPreparations/MoreBlockPreparation.php (added)
-
tags/3.0.1/classes/BlockPreparations/ParagraphBlockPreparation.php (added)
-
tags/3.0.1/classes/BlockPreparations/ReferenceBlockPreparation.php (added)
-
tags/3.0.1/classes/BlockPreparations/TagCloudPreparation.php (added)
-
tags/3.0.1/classes/Components (added)
-
tags/3.0.1/classes/Components/Assets.php (added)
-
tags/3.0.1/classes/Components/Component.php (added)
-
tags/3.0.1/classes/Components/Database.php (added)
-
tags/3.0.1/classes/Components/Plugin.php (added)
-
tags/3.0.1/classes/Dashboard.php (added)
-
tags/3.0.1/classes/Extensions (added)
-
tags/3.0.1/classes/Extensions.php (added)
-
tags/3.0.1/classes/Extensions/AbsPostExtensionPost.php (added)
-
tags/3.0.1/classes/Extensions/CommentAuthorUser.php (added)
-
tags/3.0.1/classes/Extensions/ContentAttachments.php (added)
-
tags/3.0.1/classes/Extensions/ContentBlocks.php (added)
-
tags/3.0.1/classes/Extensions/FeaturedMedia.php (added)
-
tags/3.0.1/classes/Extensions/Taxonomies.php (added)
-
tags/3.0.1/classes/Extensions/Title.php (added)
-
tags/3.0.1/classes/Headers.php (added)
-
tags/3.0.1/classes/Headquarter.php (added)
-
tags/3.0.1/classes/Interfaces (added)
-
tags/3.0.1/classes/Interfaces/IBlockPreparation.php (added)
-
tags/3.0.1/classes/Interfaces/ICommentRouteExtension.php (added)
-
tags/3.0.1/classes/Interfaces/IPostRouteExtension.php (added)
-
tags/3.0.1/classes/Interfaces/ITermRouteExtension.php (added)
-
tags/3.0.1/classes/Interfaces/IUserRouteExtension.php (added)
-
tags/3.0.1/classes/Log.php (added)
-
tags/3.0.1/classes/Migration.php (added)
-
tags/3.0.1/classes/Model (added)
-
tags/3.0.1/classes/Model/BlockName.php (added)
-
tags/3.0.1/classes/Model/BlockPreparations.php (added)
-
tags/3.0.1/classes/Model/CommentRouteExtensions.php (added)
-
tags/3.0.1/classes/Model/Frontend.php (added)
-
tags/3.0.1/classes/Model/PostContentAttachmentCollector.php (added)
-
tags/3.0.1/classes/Model/PostRouteExtensions.php (added)
-
tags/3.0.1/classes/Model/TermRouteExtensions.php (added)
-
tags/3.0.1/classes/Model/UserRouteExtensions.php (added)
-
tags/3.0.1/classes/PluginAssets.php (added)
-
tags/3.0.1/classes/Post.php (added)
-
tags/3.0.1/classes/Preview.php (added)
-
tags/3.0.1/classes/Query.php (added)
-
tags/3.0.1/classes/Revalidate.php (added)
-
tags/3.0.1/classes/Routes (added)
-
tags/3.0.1/classes/Routes.php (added)
-
tags/3.0.1/classes/Routes/Menus.php (added)
-
tags/3.0.1/classes/Routes/Settings.php (added)
-
tags/3.0.1/classes/Schedule.php (added)
-
tags/3.0.1/classes/Security.php (added)
-
tags/3.0.1/classes/Store (added)
-
tags/3.0.1/classes/Store/RevalidationDatabase.php (added)
-
tags/3.0.1/classes/Utils.php (added)
-
tags/3.0.1/composer.json (added)
-
tags/3.0.1/composer.lock (added)
-
tags/3.0.1/dist (added)
-
tags/3.0.1/dist/admin.asset.php (added)
-
tags/3.0.1/dist/admin.js (added)
-
tags/3.0.1/dist/gutenberg-rtl.css (added)
-
tags/3.0.1/dist/gutenberg.asset.php (added)
-
tags/3.0.1/dist/gutenberg.css (added)
-
tags/3.0.1/dist/gutenberg.js (added)
-
tags/3.0.1/headless.php (added)
-
tags/3.0.1/public-functions.php (added)
-
tags/3.0.1/vendor (added)
-
tags/3.0.1/vendor/autoload.php (added)
-
tags/3.0.1/vendor/composer (added)
-
tags/3.0.1/vendor/composer/ClassLoader.php (added)
-
tags/3.0.1/vendor/composer/InstalledVersions.php (added)
-
tags/3.0.1/vendor/composer/LICENSE (added)
-
tags/3.0.1/vendor/composer/autoload_classmap.php (added)
-
tags/3.0.1/vendor/composer/autoload_namespaces.php (added)
-
tags/3.0.1/vendor/composer/autoload_psr4.php (added)
-
tags/3.0.1/vendor/composer/autoload_real.php (added)
-
tags/3.0.1/vendor/composer/autoload_static.php (added)
-
tags/3.0.1/vendor/composer/installed.json (added)
-
tags/3.0.1/vendor/composer/installed.php (added)
-
trunk/README.txt (modified) (2 diffs)
-
trunk/classes/Ajax.php (modified) (3 diffs)
-
trunk/classes/BlockPreparations/CoreEmbedBlockPreparation.php (modified) (1 diff)
-
trunk/classes/BlockPreparations/EmbedBlockPreparation.php (modified) (1 diff)
-
trunk/classes/BlockPreparations/FreeFormBlockPreparation.php (modified) (1 diff)
-
trunk/classes/BlockPreparations/GalleryBlockPreparation.php (modified) (1 diff)
-
trunk/classes/BlockPreparations/ImageBlockPreparation.php (modified) (2 diffs)
-
trunk/classes/BlockPreparations/MoreBlockPreparation.php (modified) (1 diff)
-
trunk/classes/BlockPreparations/ParagraphBlockPreparation.php (modified) (1 diff)
-
trunk/classes/BlockPreparations/ReferenceBlockPreparation.php (modified) (1 diff)
-
trunk/classes/BlockPreparations/TagCloudPreparation.php (modified) (2 diffs)
-
trunk/classes/Components/Assets.php (modified) (3 diffs)
-
trunk/classes/Components/Component.php (modified) (2 diffs)
-
trunk/classes/Components/Database.php (modified) (2 diffs)
-
trunk/classes/Components/Plugin.php (modified) (7 diffs)
-
trunk/classes/Dashboard.php (modified) (3 diffs)
-
trunk/classes/Extensions.php (modified) (5 diffs)
-
trunk/classes/Extensions/AbsPostExtensionPost.php (modified) (2 diffs)
-
trunk/classes/Extensions/CommentAuthorUser.php (modified) (1 diff)
-
trunk/classes/Extensions/ContentAttachments.php (modified) (1 diff)
-
trunk/classes/Extensions/ContentBlocks.php (modified) (5 diffs)
-
trunk/classes/Extensions/FeaturedMedia.php (modified) (2 diffs)
-
trunk/classes/Extensions/Taxonomies.php (modified) (1 diff)
-
trunk/classes/Extensions/Title.php (modified) (1 diff)
-
trunk/classes/Headers.php (modified) (2 diffs)
-
trunk/classes/Headquarter.php (modified) (1 diff)
-
trunk/classes/Interfaces/IBlockPreparation.php (modified) (1 diff)
-
trunk/classes/Interfaces/ICommentRouteExtension.php (modified) (1 diff)
-
trunk/classes/Interfaces/IPostRouteExtension.php (modified) (1 diff)
-
trunk/classes/Interfaces/ITermRouteExtension.php (modified) (2 diffs)
-
trunk/classes/Interfaces/IUserRouteExtension.php (modified) (1 diff)
-
trunk/classes/Log.php (modified) (3 diffs)
-
trunk/classes/Migration.php (modified) (1 diff)
-
trunk/classes/Model/BlockName.php (modified) (2 diffs)
-
trunk/classes/Model/BlockPreparations.php (modified) (2 diffs)
-
trunk/classes/Model/CommentRouteExtensions.php (modified) (2 diffs)
-
trunk/classes/Model/Frontend.php (modified) (1 diff)
-
trunk/classes/Model/PostContentAttachmentCollector.php (modified) (1 diff)
-
trunk/classes/Model/PostRouteExtensions.php (modified) (2 diffs)
-
trunk/classes/Model/TermRouteExtensions.php (modified) (2 diffs)
-
trunk/classes/Model/UserRouteExtensions.php (modified) (2 diffs)
-
trunk/classes/PluginAssets.php (modified) (4 diffs)
-
trunk/classes/Post.php (modified) (2 diffs)
-
trunk/classes/Preview.php (modified) (8 diffs)
-
trunk/classes/Query.php (modified) (3 diffs)
-
trunk/classes/Revalidate.php (modified) (9 diffs)
-
trunk/classes/Routes.php (modified) (2 diffs)
-
trunk/classes/Routes/Menus.php (modified) (3 diffs)
-
trunk/classes/Routes/Settings.php (modified) (2 diffs)
-
trunk/classes/Schedule.php (modified) (3 diffs)
-
trunk/classes/Security.php (modified) (2 diffs)
-
trunk/classes/Store/RevalidationDatabase.php (modified) (9 diffs)
-
trunk/classes/Utils.php (modified) (1 diff)
-
trunk/composer.json (added)
-
trunk/composer.lock (added)
-
trunk/dist/admin.asset.php (modified) (1 diff)
-
trunk/dist/admin.js (modified) (1 diff)
-
trunk/dist/gutenberg.asset.php (modified) (1 diff)
-
trunk/dist/gutenberg.js (modified) (1 diff)
-
trunk/headless.php (modified) (4 diffs)
-
trunk/vendor/composer/installed.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
headless/trunk/README.txt
r3254930 r3492765 6 6 Tested up to: 6.7.2 7 7 Requires PHP: 8.0 8 Stable tag: 2.3.18 Stable tag: 3.0.1 9 9 License: GPLv3 10 10 License URI: http://www.gnu.org/licenses/gpl … … 27 27 28 28 == Changelog == 29 30 = 3.0.1 = 31 * **core-image:** use alt text from block if set (b799413) 32 * **core-image:** use block alt if set (e2d0e6c) 33 * **core-image:** use image alt text from block props if set in block (0ed3974) 34 29 35 30 36 = 2.3.1 = -
headless/trunk/classes/Ajax.php
r3196946 r3492765 5 5 use Palasthotel\WordPress\Headless\Components\Component; 6 6 7 /** 8 * Handles AJAX endpoints for triggering cache revalidation from the WordPress admin. 9 * 10 * Registers two wp_ajax actions: one to revalidate a specific path or post, 11 * and one to process all pending revalidations from the queue. 12 */ 7 13 class Ajax extends Component { 8 14 … … 19 25 } 20 26 27 /** 28 * Handles the AJAX revalidate action. 29 * 30 * Accepts either a path or a post ID via GET parameters and triggers 31 * revalidation on the specified frontend. Requires edit_posts capability. 32 * 33 * @return void 34 */ 21 35 public function revalidate(){ 22 36 … … 73 87 74 88 89 /** 90 * Handles the AJAX revalidate_pending action. 91 * 92 * Triggers the scheduled revalidation run to process all pending items. 93 * Requires edit_posts capability. 94 * 95 * @return void 96 */ 75 97 function revalidate_pending() { 76 98 if(!current_user_can('edit_posts')){ -
headless/trunk/classes/BlockPreparations/CoreEmbedBlockPreparation.php
r2766747 r3492765 6 6 use Palasthotel\WordPress\Headless\Model\BlockName; 7 7 8 class CoreEmbedBlockPreparation implements IBlockPreparation { 9 10 function blockName(): ?BlockName { 8 /** 9 * Normalizes the legacy "core-embed/wordpress" block name to "core/embed" 10 * 11 * Handles blocks saved with the old "core-embed" namespace so they are 12 * treated as standard embed blocks by subsequent preparations 13 */ 14 class CoreEmbedBlockPreparation implements IBlockPreparation 15 { 16 /** 17 * Returns the legacy block name this preparation targets. 18 * 19 * @return BlockName The "core-embed/wordpress" block name. 20 */ 21 function blockName(): ?BlockName 22 { 11 23 return new BlockName("core-embed", "wordpress"); 12 24 } 13 25 14 function prepare( array $block ): array { 15 26 /** 27 * Rewrites the block name from "core-embed/wordpress" to "core/embed". 28 * 29 * @param array $block The parsed block data. 30 * @return array The block with the corrected name. 31 */ 32 function prepare(array $block): array 33 { 16 34 $block["blockName"] = "core/embed"; 17 35 -
headless/trunk/classes/BlockPreparations/EmbedBlockPreparation.php
r2766747 r3492765 6 6 use Palasthotel\WordPress\Headless\Model\BlockName; 7 7 8 /** 9 * Resolves oEmbed data for core/embed blocks. 10 * 11 * Fetches the oEmbed HTML for the block's URL and adds "isResolved" and 12 * "resolvedHTML" attributes. Removes innerContent from the block data. 13 */ 8 14 class EmbedBlockPreparation implements IBlockPreparation { 9 15 16 /** 17 * Returns the block name this preparation targets. 18 * 19 * @return BlockName The "core/embed" block name. 20 */ 10 21 function blockName(): ?BlockName { 11 22 return new BlockName("core", "embed"); 12 23 } 13 24 25 /** 26 * Resolves the oEmbed HTML for the block URL and strips innerContent. 27 * 28 * @param array $block The parsed block data. 29 * @return array The block with "isResolved" and "resolvedHTML" attrs added. 30 */ 14 31 function prepare( array $block ): array { 15 32 -
headless/trunk/classes/BlockPreparations/FreeFormBlockPreparation.php
r2752593 r3492765 6 6 use Palasthotel\WordPress\Headless\Model\BlockName; 7 7 8 /** 9 * Processes free-form (classic) blocks by running shortcodes and stripping innerContent. 10 * 11 * Returns null from blockName() to apply to all blocks without a specific name. 12 */ 8 13 class FreeFormBlockPreparation implements IBlockPreparation { 9 14 15 /** 16 * Returns null to indicate this preparation applies to all blocks. 17 * 18 * @return null 19 */ 10 20 function blockName(): ?BlockName { 11 21 return null; 12 22 } 13 23 24 /** 25 * Runs shortcodes on innerHTML and removes innerContent from the block. 26 * 27 * @param array $block The parsed block data. 28 * @return array The block with shortcodes processed and innerContent removed. 29 */ 14 30 function prepare( array $block ): array { 15 31 -
headless/trunk/classes/BlockPreparations/GalleryBlockPreparation.php
r2752593 r3492765 7 7 use Palasthotel\WordPress\Headless\Model\PostContentAttachmentCollector; 8 8 9 /** 10 * Prepares core/gallery blocks by extracting image IDs and attachment attributes. 11 * 12 * Handles both modern gallery blocks (innerBlocks) and legacy gallery markup (innerHTML). 13 * Registers attachment IDs with the PostContentAttachmentCollector and removes 14 * raw HTML from the block data. 15 */ 9 16 class GalleryBlockPreparation implements IBlockPreparation { 10 17 18 /** 19 * Returns the block name this preparation targets. 20 * 21 * @return BlockName The "core/gallery" block name. 22 */ 11 23 function blockName(): BlockName { 12 24 return new BlockName("core", "gallery"); 13 25 } 14 26 27 /** 28 * Extracts image IDs, builds inner image blocks for legacy markup, and cleans up raw HTML. 29 * 30 * @param array $block The parsed block data. 31 * @return array The prepared block with ids attribute and innerBlocks populated. 32 */ 15 33 function prepare( array $block ): array { 16 34 -
headless/trunk/classes/BlockPreparations/ImageBlockPreparation.php
r2901812 r3492765 7 7 use Palasthotel\WordPress\Headless\Model\BlockName; 8 8 use Palasthotel\WordPress\Headless\Model\PostContentAttachmentCollector; 9 use WP_HTML_Tag_Processor; 9 10 11 /** 12 * Prepares core/image blocks with attachment data including src, sizes, alt, and caption. 13 * 14 * Registers the attachment with the PostContentAttachmentCollector and strips 15 * innerHTML and innerContent from the block. 16 */ 10 17 class ImageBlockPreparation implements IBlockPreparation { 11 18 19 /** 20 * Returns the block name this preparation targets. 21 * 22 * @return BlockName The "core/image" block name. 23 */ 12 24 function blockName(): BlockName { 13 25 return new BlockName("core", "image"); 14 26 } 15 27 28 /** 29 * Enriches the image block with attachment metadata and removes raw HTML fields. 30 * 31 * @param array $block The parsed block data. 32 * @return array The block with src, sizes, alt, and caption attrs added. 33 */ 16 34 function prepare( array $block ): array { 17 35 … … 29 47 } 30 48 49 /** 50 * Adds attachment-related attributes (src, sizes, alt, caption) to a block attrs array. 51 * 52 * @param int $id The attachment ID. 53 * @param array $attrs The existing block attributes. 54 * @param string $innerHTML The block innerHTML, used to extract alt and caption. 55 * @return array The enriched attributes array. 56 */ 31 57 public static function addAttachmentAttributes($id, $attrs, $innerHTML){ 58 59 $tags = new WP_HTML_Tag_Processor( $innerHTML ); 60 $tags->next_tag(['tag_name' => 'img']); 61 // alt set in the core/image block settings 62 $block_alt = $tags->get_attribute('alt'); 63 $alt = empty($block_alt) ? get_post_meta($id, '_wp_attachment_image_alt', true) : $block_alt; 64 32 65 $attrs["src"] = wp_get_attachment_image_src($id, 'full'); 33 66 34 67 $attrs["sizes"] = FeaturedMedia::imageSizes($id); 35 68 36 $attrs["alt"] = get_post_meta($id, '_wp_attachment_image_alt', true);69 $attrs["alt"] = $alt; 37 70 $attrs["caption"] = str_replace( 38 71 ["\n","\r"], -
headless/trunk/classes/BlockPreparations/MoreBlockPreparation.php
r2730402 r3492765 6 6 use Palasthotel\WordPress\Headless\Model\BlockName; 7 7 8 /** 9 * Strips all data from core/more blocks, keeping only the block name. 10 * 11 * The "read more" block has no meaningful content for the headless frontend, 12 * so its presence alone is surfaced as a marker. 13 */ 8 14 class MoreBlockPreparation implements IBlockPreparation { 9 15 16 /** 17 * Returns the block name this preparation targets. 18 * 19 * @return BlockName The "core/more" block name. 20 */ 10 21 function blockName(): BlockName { 11 22 return new BlockName("core", "more"); 12 23 } 13 24 25 /** 26 * Returns a minimal block array containing only the block name. 27 * 28 * @param array $block The parsed block data. 29 * @return array A block array with only the "blockName" key. 30 */ 14 31 function prepare( array $block ): array { 15 32 return [ -
headless/trunk/classes/BlockPreparations/ParagraphBlockPreparation.php
r2752593 r3492765 6 6 use Palasthotel\WordPress\Headless\Model\BlockName; 7 7 8 /** 9 * Prepares core/paragraph blocks by removing the redundant innerContent field. 10 */ 8 11 class ParagraphBlockPreparation implements IBlockPreparation { 9 12 13 /** 14 * Returns the block name this preparation targets. 15 * 16 * @return BlockName The "core/paragraph" block name. 17 */ 10 18 function blockName(): ?BlockName { 11 19 return new BlockName("core", "paragraph"); 12 20 } 13 21 22 /** 23 * Removes innerContent from the paragraph block. 24 * 25 * @param array $block The parsed block data. 26 * @return array The block with innerContent removed. 27 */ 14 28 function prepare( array $block ): array { 15 29 -
headless/trunk/classes/BlockPreparations/ReferenceBlockPreparation.php
r2802385 r3492765 6 6 use Palasthotel\WordPress\Headless\Model\BlockName; 7 7 8 /** 9 * Resolves core/block (reusable block) references by inlining the referenced block content. 10 * 11 * When a block has a "ref" attribute pointing to a wp_block post, this preparation 12 * fetches and parses its content and replaces the reference block's innerBlocks. 13 * Must run before all other preparations so the inlined blocks can be further processed. 14 */ 8 15 class ReferenceBlockPreparation implements IBlockPreparation { 9 16 17 /** 18 * Returns the block name this preparation targets. 19 * 20 * @return BlockName The "core/block" block name. 21 */ 10 22 function blockName(): ?BlockName { 11 23 return new BlockName("core", "block"); 12 24 } 13 25 26 /** 27 * Inlines the referenced reusable block content into innerBlocks. 28 * 29 * @param array $block The parsed block data. 30 * @return array The block with innerBlocks replaced by the referenced block's content. 31 */ 14 32 function prepare( array $block ): array { 15 33 -
headless/trunk/classes/BlockPreparations/TagCloudPreparation.php
r2791162 r3492765 8 8 use Palasthotel\WordPress\Headless\Model\PostContentAttachmentCollector; 9 9 10 /** 11 * Prepares core/tag-cloud blocks by resolving and embedding term data. 12 * 13 * Fetches terms using block attributes (taxonomy, numberOfTags) and adds 14 * them as a "tags" array within the block's attributes. Removes innerBlocks, 15 * innerHTML, and innerContent from the output. 16 */ 10 17 class TagCloudPreparation implements IBlockPreparation { 11 18 19 /** 20 * Returns the block name this preparation targets. 21 * 22 * @return BlockName The "core/tag-cloud" block name. 23 */ 12 24 function blockName(): BlockName { 13 25 return new BlockName("core", "tag-cloud"); 14 26 } 15 27 28 /** 29 * Resolves taxonomy terms and embeds them in the block attributes. 30 * 31 * @param array $block The parsed block data. 32 * @return array The block with a "tags" attribute and raw HTML fields removed. 33 */ 16 34 function prepare( array $block ): array { 17 35 … … 49 67 } 50 68 69 /** 70 * Adds attachment-related attributes (src, sizes, alt, caption) to a block attrs array. 71 * 72 * @param int $id The attachment ID. 73 * @param array $attrs The existing block attributes. 74 * @param string $innerHTML The block innerHTML, used for caption extraction. 75 * @return array The enriched attributes array. 76 */ 51 77 public static function addAttachmentAttributes($id, $attrs, $innerHTML){ 52 78 $attrs["src"] = wp_get_attachment_image_src($id, 'full'); -
headless/trunk/classes/Components/Assets.php
r3196946 r3492765 4 4 namespace Palasthotel\WordPress\Headless\Components; 5 5 6 /** 7 * Helper for registering plugin scripts and styles with automatic versioning. 8 * 9 * Resolves file paths relative to the plugin root, uses file modification time 10 * for cache busting, and reads webpack-generated asset manifests for scripts. 11 */ 6 12 class Assets { 7 13 14 /** 15 * @var Plugin The plugin instance used to resolve paths and URLs. 16 */ 8 17 private Plugin $plugin; 9 18 19 /** 20 * @param Plugin $plugin The plugin instance. 21 */ 10 22 public function __construct(Plugin $plugin) { 11 23 $this->plugin = $plugin; 12 24 } 13 25 26 /** 27 * Registers a stylesheet with WordPress using a plugin-relative path. 28 * 29 * Logs an error and returns false if the file does not exist. 30 * 31 * @param string $handle The stylesheet handle. 32 * @param string $pluginPathToFile Path to the file relative to the plugin root. 33 * @param string[] $dependencies Array of registered stylesheet handles this depends on. 34 * @param string $media The media type for the stylesheet. 35 * @return bool True if the stylesheet was registered successfully, false otherwise. 36 */ 14 37 public function registerStyle(string $handle, string $pluginPathToFile, array $dependencies = [], string $media = 'all'): bool { 15 38 $filePath = $this->plugin->path . $pluginPathToFile; … … 25 48 } 26 49 50 /** 51 * Registers a script with WordPress using a plugin-relative path. 52 * 53 * If a corresponding .asset.php manifest exists, its dependencies and version 54 * are used; otherwise filemtime is used as the version. Logs an error and 55 * returns false if the file does not exist. 56 * 57 * @param string $handle The script handle. 58 * @param string $pluginPathToFile Path to the file relative to the plugin root. 59 * @param string[] $dependencies Additional script dependencies to merge with the manifest. 60 * @param bool $footer Whether to enqueue the script in the footer. 61 * @return bool True if the script was registered successfully, false otherwise. 62 */ 27 63 public function registerScript(string $handle, string $pluginPathToFile, array $dependencies = [], bool $footer = true): bool { 28 64 $filePath = $this->plugin->path . $pluginPathToFile; … … 53 89 } 54 90 91 /** 92 * Checks whether the given string ends with ".js". 93 * 94 * @param string $haystack The string to check. 95 * @return bool True if the string ends with ".js". 96 */ 55 97 private function endsWithJS($haystack): bool { 56 98 $length = strlen(".js"); -
headless/trunk/classes/Components/Component.php
r3196946 r3492765 4 4 namespace Palasthotel\WordPress\Headless\Components; 5 5 6 /** 7 * Base class for all plugin components. 8 * 9 * Receives the Plugin instance via constructor injection and calls onCreate() 10 * to allow subclasses to register hooks and initialize state. 11 */ 6 12 abstract class Component { 7 13 … … 13 19 14 20 /** 15 * overwrite this method in component implementations 21 * Called after construction. Override in subclasses to register hooks and initialize state. 22 * 23 * @return void 16 24 */ 17 25 public function onCreate(): void { -
headless/trunk/classes/Components/Database.php
r3196946 r3492765 6 6 use wpdb; 7 7 8 /** 9 * Base class for plugin database handlers. 10 * 11 * Injects the global wpdb instance and calls init() on construction to allow 12 * subclasses to set up table names and other properties. 13 */ 8 14 abstract class Database { 9 15 16 /** 17 * @var wpdb The WordPress database instance. 18 */ 10 19 protected wpdb $wpdb; 11 20 21 /** 22 * Initializes the database handler by injecting wpdb and calling init(). 23 */ 12 24 public function __construct() { 13 25 global $wpdb; … … 17 29 18 30 /** 19 * initialize table names and other properties 31 * Initializes table names and other properties. Must be implemented by subclasses. 32 * 33 * @return void 20 34 */ 21 35 abstract function init(): void; 22 36 37 /** 38 * Loads the WordPress database upgrade functions required for dbDelta(). 39 * 40 * Subclasses should call parent::createTables() before running dbDelta(). 41 * 42 * @return void 43 */ 23 44 public function createTables(): void { 24 45 require_once( ABSPATH . 'wp-admin/includes/upgrade.php' ); -
headless/trunk/classes/Components/Plugin.php
r3196946 r3492765 6 6 use ReflectionException; 7 7 8 /** 9 * Abstract base class for WordPress plugins using a singleton pattern. 10 * 11 * Resolves the plugin's file path, URL, and basename via reflection, registers 12 * activation and deactivation hooks, and provides multisite support. 13 * Subclasses must implement onCreate() for component initialization. 14 */ 8 15 abstract class Plugin { 9 16 10 17 /** 11 * @var ReflectionClass 18 * @var ReflectionClass Reflection of the concrete plugin class, used to resolve paths. 12 19 */ 13 20 private $ref; 21 22 /** 23 * @var bool Whether the textdomain registration window has passed. 24 */ 14 25 private $tooLateForTextdomain; 26 27 /** 28 * @var string Absolute path to the plugin directory, with trailing slash. 29 */ 15 30 public $path; 31 32 /** 33 * @var string URL to the plugin directory, with trailing slash. 34 */ 16 35 public $url; 36 37 /** 38 * @var string Plugin basename (e.g. plugin-folder/plugin-file.php). 39 */ 17 40 public $basename; 18 41 … … 38 61 // lifecycle methods 39 62 // ----------------------------------------------------------------------------- 63 /** 64 * Called during construction. Implement to initialize plugin components and hooks. 65 * 66 * @return void 67 */ 40 68 abstract function onCreate(); 41 69 70 /** 71 * Handles plugin activation, dispatching to each site on multisite networks. 72 * 73 * @param bool $networkWide Whether the plugin is being activated network-wide. 74 * @return void 75 */ 42 76 public function onActivation( $networkWide ) { 43 77 if ( $networkWide ) { … … 48 82 } 49 83 84 /** 85 * Called during activation for the current site. Override to run site-specific setup. 86 * 87 * @return void 88 */ 50 89 public function onSiteActivation() { 51 90 52 91 } 53 92 93 /** 94 * Handles plugin deactivation, dispatching to each site on multisite networks. 95 * 96 * @param bool $networkWide Whether the plugin is being deactivated network-wide. 97 * @return void 98 */ 54 99 public function onDeactivation( $networkWide ) { 55 100 if ( $networkWide ) { … … 60 105 } 61 106 107 /** 108 * Called during deactivation for the current site. Override to run site-specific teardown. 109 * 110 * @return void 111 */ 62 112 public function onSiteDeactivation() { 63 113 … … 67 117 // utility methods 68 118 // ----------------------------------------------------------------------------- 119 120 /** 121 * Registers the plugin textdomain for translations. 122 * 123 * Must be called within onCreate(). Calling it after construction will log an error. 124 * 125 * @param string $domain The textdomain identifier. 126 * @param string $relativeLanguagesPath Path to the languages directory relative to the plugin file. 127 * @return void 128 */ 69 129 public function loadTextdomain( string $domain, string $relativeLanguagesPath ) { 70 130 if ( $this->tooLateForTextdomain ) { … … 81 141 } 82 142 143 /** 144 * Iterates over all sites in a multisite network and calls the given callback for each. 145 * 146 * Does nothing if not running on a multisite installation. 147 * 148 * @param callable $onSite Callback to invoke for each site (called after switch_to_blog). 149 * @return void 150 */ 83 151 public function foreachMultisite(callable $onSite){ 84 152 if ( function_exists( 'is_multisite' ) && is_multisite() ) { … … 101 169 // singleton pattern 102 170 // ----------------------------------------------------------------------------- 171 /** 172 * @var static[] Map of class name to singleton instance. 173 */ 103 174 private static $instances = []; 104 175 176 /** 177 * Returns the singleton instance for the called class. 178 * 179 * @return static The plugin instance. 180 */ 105 181 public static function instance() { 106 182 $class = get_called_class(); -
headless/trunk/classes/Dashboard.php
r3196946 r3492765 3 3 namespace Palasthotel\WordPress\Headless; 4 4 5 /** 6 * Registers and renders the Headless dashboard widget in the WordPress admin. 7 * 8 * Displays revalidation schedule status, pending item counts, and provides 9 * buttons for manual and automatic cache revalidation. 10 */ 5 11 class Dashboard extends Components\Component { 6 12 public function onCreate(): void { … … 9 15 } 10 16 17 /** 18 * Registers the dashboard widget if revalidation is active and the user can edit posts. 19 * 20 * @return void 21 */ 11 22 public function setup(){ 12 23 if(!current_user_can('edit_posts')) return; … … 21 32 } 22 33 34 /** 35 * Renders the dashboard widget HTML including revalidation status and controls. 36 * 37 * @return void 38 */ 23 39 public function render(){ 24 40 $timeFormat = get_option('time_format'); -
headless/trunk/classes/Extensions.php
r3196946 r3492765 25 25 use Palasthotel\WordPress\Headless\Model\UserRouteExtensions; 26 26 27 /** 28 * Registers all built-in block preparations and REST API route extensions. 29 * 30 * Hooks into the plugin's registration actions during REST API init to add 31 * default extensions for posts, comments, blocks, users, and terms. 32 * Only active on headless requests with valid API key access. 33 */ 27 34 class Extensions extends Component { 28 35 36 /** 37 * @var BlockPreparations 38 */ 29 39 private BlockPreparations $blockPreparations; 40 41 /** 42 * @var PostRouteExtensions 43 */ 30 44 private PostRouteExtensions $postRouteExtensions; 45 46 /** 47 * @var CommentRouteExtensions 48 */ 31 49 private CommentRouteExtensions $commentRouteExtensions; 50 51 /** 52 * @var UserRouteExtensions 53 */ 32 54 private UserRouteExtensions $userRouteExtensions; 55 56 /** 57 * @var TermRouteExtensions 58 */ 33 59 private TermRouteExtensions $termRouteExtensions; 34 60 … … 59 85 } 60 86 87 /** 88 * Registers block preparations that must run before all others. 89 * 90 * @param BlockPreparations $extensions The block preparations collection. 91 * @return void 92 */ 61 93 public function block_preparation_extensions_with_prio( BlockPreparations $extensions ) { 62 94 // needs to be the very first preparation step so others can apply to the result … … 64 96 } 65 97 98 /** 99 * Registers the default set of block preparations. 100 * 101 * @param BlockPreparations $extensions The block preparations collection. 102 * @return void 103 */ 66 104 public function block_preparation_extensions( BlockPreparations $extensions ) { 67 105 $extensions->add( new ParagraphBlockPreparation() ); … … 77 115 } 78 116 117 /** 118 * Registers the default post route extensions. 119 * 120 * @param PostRouteExtensions $extensions The post route extensions collection. 121 * @return void 122 */ 79 123 public function post_route_extensions( PostRouteExtensions $extensions ) { 80 124 $extensions->add( new Title() ); … … 85 129 } 86 130 131 /** 132 * Registers the default comment route extensions. 133 * 134 * @param CommentRouteExtensions $extensions The comment route extensions collection. 135 * @return void 136 */ 87 137 public function comment_route_extensions( CommentRouteExtensions $extensions ) { 88 138 $extensions->add( new CommentAuthorUser() ); 89 139 } 90 140 141 /** 142 * Fires all extension registration actions and attaches REST response filters. 143 * 144 * Called on rest_api_init. Only proceeds if the request has valid API key access. 145 * 146 * @return void 147 */ 91 148 public function rest_api_init() { 92 149 -
headless/trunk/classes/Extensions/AbsPostExtensionPost.php
r2730402 r3492765 6 6 use Palasthotel\WordPress\Headless\Plugin; 7 7 8 /** 9 * Abstract base class for post route extensions that self-register via a filter. 10 * 11 * Subclasses automatically hook into Plugin::ACTION_REGISTER_POST_ROUTE_EXTENSIONS 12 * on construction and add themselves to the extension array. 13 */ 8 14 abstract class AbsPostExtensionPost implements IPostRouteExtension { 9 15 … … 17 23 * @return IPostRouteExtension[] 18 24 */ 25 /** 26 * Appends this extension to the provided extensions array. 27 * 28 * @param IPostRouteExtension[] $extensions The existing list of post route extensions. 29 * @return IPostRouteExtension[] The extensions array with this instance appended. 30 */ 19 31 public function register(array $extensions){ 20 32 $extensions[] = $this; -
headless/trunk/classes/Extensions/CommentAuthorUser.php
r2766747 r3492765 7 7 use WP_REST_Response; 8 8 9 /** 10 * Extends the comment REST response with the author's user data. 11 * 12 * Adds an "author_user" field containing display_name and nickname 13 * if the comment has an associated WordPress user account. 14 */ 9 15 class CommentAuthorUser implements ICommentRouteExtension { 10 16 17 /** 18 * Appends author user data to the comment REST response. 19 * 20 * @param WP_REST_Response $response The current REST response. 21 * @param \WP_Comment $comment The comment object. 22 * @param WP_REST_Request $request The current REST request. 23 * @return WP_REST_Response The modified response with "author_user" field added. 24 */ 11 25 function response( WP_REST_Response $response, \WP_Comment $comment, WP_REST_Request $request ): WP_REST_Response { 12 26 $data = $response->get_data(); -
headless/trunk/classes/Extensions/ContentAttachments.php
r2730402 r3492765 8 8 use WP_REST_Response; 9 9 10 /** 11 * Extends the post REST response with attachment IDs collected from post content. 12 * 13 * Adds a "headless_attachment_ids" field to the content object containing 14 * all attachment IDs found in the post's blocks during preparation. 15 */ 10 16 class ContentAttachments extends AbsPostExtensionPost { 11 17 18 /** 19 * Adds collected attachment IDs to the content field of the post REST response. 20 * 21 * @param WP_REST_Response $response The current REST response. 22 * @param WP_Post $post The post object. 23 * @param WP_REST_Request $request The current REST request. 24 * @return WP_REST_Response The modified response with "headless_attachment_ids" added. 25 */ 12 26 function response( WP_REST_Response $response, WP_Post $post, WP_REST_Request $request ): WP_REST_Response { 13 27 $data = $response->get_data(); -
headless/trunk/classes/Extensions/ContentBlocks.php
r3072776 r3492765 9 9 use WP_REST_Response; 10 10 11 /** 12 * Extends the post REST response with parsed and prepared Gutenberg block data. 13 * 14 * Adds a "headless_blocks" field to the content object when the post has blocks. 15 * Skips blocks and other heavy fields when the request uses the teasers variant. 16 * Applies all registered block preparations during parsing. 17 */ 11 18 class ContentBlocks extends AbsPostExtensionPost { 12 19 20 /** 21 * @var BlockPreparations The registered block preparation handlers. 22 */ 13 23 private BlockPreparations $preparations; 14 24 25 /** 26 * @param BlockPreparations $preparations The block preparations collection to use. 27 */ 15 28 public function __construct(BlockPreparations $preparations) { 16 29 parent::__construct(); … … 21 34 } 22 35 36 /** 37 * Adds parsed block data to the post REST response. 38 * 39 * @param WP_REST_Response $response The current REST response. 40 * @param WP_Post $post The post object. 41 * @param WP_REST_Request $request The current REST request. 42 * @return WP_REST_Response The modified response with "headless_blocks" added to content. 43 */ 23 44 function response( WP_REST_Response $response, WP_Post $post, WP_REST_Request $request ): WP_REST_Response { 24 45 $data = $response->get_data(); … … 41 62 } 42 63 64 /** 65 * Filters out blocks that should not be included in the response. 66 * 67 * Uses the Plugin::FILTER_BLOCKS_PREPARE_FILTER filter to determine inclusion. 68 * 69 * @param array $blocks The raw array of parsed blocks. 70 * @return array The filtered blocks with sequential keys. 71 */ 43 72 private function filterBlocks( $blocks ) { 44 73 return array_values( array_filter( $blocks, function ( $block ) { … … 47 76 } 48 77 78 /** 79 * Parses raw post content into a prepared array of blocks. 80 * 81 * @param string $post_content The raw block content string. 82 * @param int $level The current nesting depth (1 for top-level). 83 * @return array The prepared block array. 84 */ 49 85 private function parse($post_content, $level = 1): array { 50 86 $blocks = parse_blocks($post_content); … … 52 88 } 53 89 90 /** 91 * Filters and prepares a set of blocks by applying registered preparations recursively. 92 * 93 * @param array $blocks The blocks to prepare. 94 * @param int $level The current nesting depth. 95 * @return array The prepared blocks. 96 */ 54 97 private function prepare( $blocks, $level ) { 55 98 $blocks = $this->filterBlocks( $blocks ); -
headless/trunk/classes/Extensions/FeaturedMedia.php
r2766747 r3492765 8 8 use WP_REST_Response; 9 9 10 /** 11 * Extends the post REST response with detailed featured media information. 12 * 13 * Adds featured_media_url, featured_media_src, featured_media_sizes, 14 * featured_media_caption, featured_media_description, and featured_media_alt fields. 15 */ 10 16 class FeaturedMedia extends AbsPostExtensionPost { 11 17 18 /** 19 * Appends featured media data to the post REST response. 20 * 21 * @param WP_REST_Response $response The current REST response. 22 * @param WP_Post $post The post object. 23 * @param WP_REST_Request $request The current REST request. 24 * @return WP_REST_Response The modified response with featured media fields added. 25 */ 12 26 function response( WP_REST_Response $response, WP_Post $post, WP_REST_Request $request ): WP_REST_Response { 13 27 $data = $response->get_data(); … … 36 50 } 37 51 52 /** 53 * Returns all available intermediate image size sources for the given attachment. 54 * 55 * @param int $imageId The attachment ID. 56 * @return array[] Array of wp_get_attachment_image_src result arrays for each size. 57 */ 38 58 static function imageSizes( $imageId ) { 39 59 return array_values( -
headless/trunk/classes/Extensions/Taxonomies.php
r2748985 r3492765 7 7 use WP_REST_Response; 8 8 9 /** 10 * Extends the post REST response with taxonomy term IDs for all REST-visible taxonomies. 11 * 12 * Adds any missing taxonomy fields that may not appear by default, which is 13 * particularly useful for custom post types requested via hl_post_type. 14 */ 9 15 class Taxonomies extends AbsPostExtensionPost { 10 16 -
headless/trunk/classes/Extensions/Title.php
r2730402 r3492765 7 7 use WP_REST_Response; 8 8 9 /** 10 * Extends the post REST response by HTML-decoding the rendered title. 11 * 12 * Converts HTML entities in the title's "rendered" field to their plain text equivalents. 13 */ 9 14 class Title extends AbsPostExtensionPost { 10 15 16 /** 17 * Decodes HTML entities in the post title before returning the response. 18 * 19 * @param WP_REST_Response $response The current REST response. 20 * @param WP_Post $post The post object. 21 * @param WP_REST_Request $request The current REST request. 22 * @return WP_REST_Response The modified response with decoded title. 23 */ 11 24 function response( WP_REST_Response $response, WP_Post $post, WP_REST_Request $request ): WP_REST_Response { 12 25 $data = $response->get_data(); -
headless/trunk/classes/Headers.php
r3196946 r3492765 3 3 namespace Palasthotel\WordPress\Headless; 4 4 5 /** 6 * Sets cache-control headers on REST API responses for headless requests. 7 * 8 * Applies a stale-while-revalidate caching strategy to REST responses 9 * when the request is identified as a headless request. 10 */ 5 11 class Headers extends Components\Component { 6 12 public function onCreate(): void { … … 9 15 } 10 16 17 /** 18 * Adds Cache-Control headers to the REST response for headless requests. 19 * 20 * @param \WP_REST_Response $response The REST response object. 21 * @return \WP_REST_Response The modified response with cache headers applied. 22 */ 11 23 public function rest_post_dispatch(\WP_REST_Response $response){ 12 24 if($this->plugin->security->isHeadlessRequest()){ -
headless/trunk/classes/Headquarter.php
r2868064 r3492765 6 6 use Palasthotel\WordPress\Headless\Model\Frontend; 7 7 8 /** 9 * Provides the list of configured headless frontend instances. 10 * 11 * Returns Frontend objects based on the HEADLESS_HEAD_BASE_URL constant, 12 * with support for filtering via the Plugin::FILTER_FRONTENDS hook. 13 */ 8 14 class Headquarter extends Component { 9 15 -
headless/trunk/classes/Interfaces/IBlockPreparation.php
r2748985 r3492765 5 5 use Palasthotel\WordPress\Headless\Model\BlockName; 6 6 7 interface IBlockPreparation { 8 7 /** 8 * Contract for block preparation handlers. 9 * 10 * Implementations transform a parsed Gutenberg block array before it’s 11 * included in the headless REST API response. 12 */ 13 interface IBlockPreparation 14 { 15 /** 16 * Returns the block name this preparation applies to, or null to apply to all blocks. 17 * 18 * @return BlockName|null The target block name, or null for a catch-all preparation. 19 */ 9 20 function blockName(): ?BlockName; 10 21 11 function prepare( array $block ): array; 22 /** 23 * Transforms the block data array. 24 * 25 * @param array $block The parsed block data. 26 * @return array The modified block data. 27 */ 28 function prepare(array $block): array; 12 29 } -
headless/trunk/classes/Interfaces/ICommentRouteExtension.php
r2766747 r3492765 6 6 use WP_REST_Response; 7 7 8 /** 9 * Contract for extensions that modify the REST API response for comments. 10 */ 8 11 interface ICommentRouteExtension { 12 13 /** 14 * Modifies the REST response for a comment. 15 * 16 * @param WP_REST_Response $response The current REST response. 17 * @param \WP_Comment $comment The comment object. 18 * @param WP_REST_Request $request The current REST request. 19 * @return WP_REST_Response The modified response. 20 */ 9 21 function response( WP_REST_Response $response, \WP_Comment $comment, WP_REST_Request $request): WP_REST_Response; 10 22 } -
headless/trunk/classes/Interfaces/IPostRouteExtension.php
r2730402 r3492765 7 7 use WP_REST_Response; 8 8 9 /** 10 * Contract for extensions that modify the REST API response for posts. 11 */ 9 12 interface IPostRouteExtension { 13 14 /** 15 * Modifies the REST response for a post. 16 * 17 * @param WP_REST_Response $response The current REST response. 18 * @param WP_Post $post The post object. 19 * @param WP_REST_Request $request The current REST request. 20 * @return WP_REST_Response The modified response. 21 */ 10 22 function response( WP_REST_Response $response, WP_Post $post, WP_REST_Request $request): WP_REST_Response; 11 23 } -
headless/trunk/classes/Interfaces/ITermRouteExtension.php
r2791162 r3492765 6 6 use WP_REST_Response; 7 7 8 interface ITermRouteExtension { 9 8 /** 9 * Contract for extensions that modify the REST API response for taxonomy terms 10 */ 11 interface ITermRouteExtension 12 { 10 13 /** 11 14 * @return string[] … … 13 16 function taxonomies(): array; 14 17 15 function response( WP_REST_Response $response, \WP_Term $comment, WP_REST_Request $request): WP_REST_Response; 18 /** 19 * Modifies the REST response for a term. 20 * 21 * @param WP_REST_Response $response The current REST response. 22 * @param \WP_Term $comment The term object. 23 * @param WP_REST_Request $request The current REST request. 24 * @return WP_REST_Response The modified response. 25 */ 26 function response( 27 WP_REST_Response $response, 28 \WP_Term $comment, 29 WP_REST_Request $request, 30 ): WP_REST_Response; 16 31 } -
headless/trunk/classes/Interfaces/IUserRouteExtension.php
r2791162 r3492765 6 6 use WP_REST_Response; 7 7 8 /** 9 * Contract for extensions that modify the REST API response for users. 10 */ 8 11 interface IUserRouteExtension { 12 13 /** 14 * Modifies the REST response for a user. 15 * 16 * @param WP_REST_Response $response The current REST response. 17 * @param \WP_User $comment The user object. 18 * @param WP_REST_Request $request The current REST request. 19 * @return WP_REST_Response The modified response. 20 */ 9 21 function response( WP_REST_Response $response, \WP_User $comment, WP_REST_Request $request): WP_REST_Response; 10 22 } -
headless/trunk/classes/Log.php
r3196946 r3492765 3 3 namespace Palasthotel\WordPress\Headless; 4 4 5 /** 6 * Provides logging for the headless plugin. 7 * 8 * Writes info and warning messages to CronLogger if available, 9 * falling back to error_log. Also forwards messages to WP-CLI when running in CLI context. 10 */ 5 11 class Log extends Components\Component { 6 12 /** … … 16 22 } 17 23 24 /** 25 * Logs an info-level message. 26 * 27 * @param string $message The message to log. 28 * @return void 29 */ 18 30 public function add($message){ 19 31 if(class_exists('\CronLogger\Log') && $this->log instanceof \CronLogger\Log){ … … 27 39 } 28 40 41 /** 42 * Logs a warning-level message, prefixed with "WARNING:". 43 * 44 * @param string $message The warning message to log. 45 * @return void 46 */ 29 47 public function warning($message){ 30 48 if(class_exists('\CronLogger\Log') && $this->log instanceof \CronLogger\Log){ -
headless/trunk/classes/Migration.php
r3196946 r3492765 3 3 namespace Palasthotel\WordPress\Headless; 4 4 5 /** 6 * Handles database schema migrations for the headless plugin. 7 * 8 * Checks the current schema version on component creation and runs 9 * any necessary upgrade steps, including dropping and recreating tables. 10 */ 5 11 class Migration extends Components\Component { 6 12 7 13 const LATEST_VERSION = 3; 8 14 15 /** 16 * Persists the current schema version to the database. 17 * 18 * @param int $version The schema version number to store. 19 * @return bool True if the option was updated, false otherwise. 20 */ 9 21 private function setSchemaVersion(int $version): bool { 10 22 return update_option(Plugin::OPTION_SCHEMA_VERSION, $version); 11 23 } 24 /** 25 * Retrieves the current schema version from the database. 26 * 27 * @return int The stored schema version, or 0 if not set. 28 */ 12 29 private function getSchemaVersion(): int { 13 30 return intval(get_option(Plugin::OPTION_SCHEMA_VERSION, 0)); -
headless/trunk/classes/Model/BlockName.php
r2730402 r3492765 3 3 namespace Palasthotel\WordPress\Headless\Model; 4 4 5 /** 6 * Represents a Gutenberg block name composed of a namespace and a block ID. 7 * 8 * Stringifies to the standard "namespace/id" format used by WordPress block names. 9 */ 5 10 class BlockName { 6 11 12 /** 13 * @var string The block namespace (e.g. "core"). 14 */ 7 15 private string $namespace; 16 17 /** 18 * @var string The block identifier within the namespace (e.g. "paragraph"). 19 */ 8 20 private string $id; 9 21 22 /** 23 * @param string $namespace The block namespace. 24 * @param string $id The block identifier. 25 */ 10 26 public function __construct(string $namespace, string $id) { 11 27 $this->namespace = $namespace; … … 13 29 } 14 30 31 /** 32 * Named constructor for creating a BlockName instance. 33 * 34 * @param string $namespace The block namespace. 35 * @param string $id The block identifier. 36 * @return static A new BlockName instance. 37 */ 15 38 public static function build(string $namespace, string $id){ 16 39 return new static($namespace, $id); 17 40 } 18 41 42 /** 43 * Returns the full block name in "namespace/id" format. 44 * 45 * @return string The block name string. 46 */ 19 47 public function __toString(): string { 20 48 return $this->namespace."/".$this->id; -
headless/trunk/classes/Model/BlockPreparations.php
r2730402 r3492765 5 5 use Palasthotel\WordPress\Headless\Interfaces\IBlockPreparation; 6 6 7 /** 8 * Collection of IBlockPreparation instances. 9 * 10 * Used to register and retrieve all active block preparation handlers. 11 */ 7 12 class BlockPreparations { 8 13 … … 12 17 private array $items = []; 13 18 19 /** 20 * Adds a block preparation handler to the collection. 21 * 22 * @param IBlockPreparation $extension The block preparation to add. 23 * @return void 24 */ 14 25 public function add( IBlockPreparation $extension ) { 15 26 $this->items[] = $extension; 16 27 } 17 28 29 /** 30 * Returns all registered block preparation handlers. 31 * 32 * @return IBlockPreparation[] The registered handlers. 33 */ 18 34 public function get(){ 19 35 return $this->items; -
headless/trunk/classes/Model/CommentRouteExtensions.php
r2791162 r3492765 5 5 use Palasthotel\WordPress\Headless\Interfaces\ICommentRouteExtension; 6 6 7 /** 8 * Collection of ICommentRouteExtension instances. 9 * 10 * Used to register and retrieve all active comment route extension handlers. 11 */ 7 12 class CommentRouteExtensions { 8 13 … … 12 17 private array $items = []; 13 18 19 /** 20 * Adds a comment route extension to the collection. 21 * 22 * @param ICommentRouteExtension $extension The extension to add. 23 * @return void 24 */ 14 25 public function add( ICommentRouteExtension $extension ) { 15 26 $this->items[] = $extension; 16 27 } 17 28 29 /** 30 * Returns all registered comment route extensions. 31 * 32 * @return ICommentRouteExtension[] The registered extensions. 33 */ 18 34 public function get(){ 19 35 return $this->items; -
headless/trunk/classes/Model/Frontend.php
r2868064 r3492765 3 3 namespace Palasthotel\WordPress\Headless\Model; 4 4 5 /** 6 * Represents a headless frontend instance identified by its base URL. 7 */ 5 8 class Frontend { 9 10 /** 11 * @var string The base URL of the headless frontend, with trailing slash. 12 */ 6 13 private string $baseUrl; 14 15 /** 16 * @param string $baseUrl The base URL of the frontend. 17 */ 7 18 public function __construct( string $baseUrl ) { 8 19 $this->baseUrl = $baseUrl; 9 20 } 10 21 22 /** 23 * Returns the base URL of the frontend. 24 * 25 * @return string The base URL. 26 */ 11 27 public function getBaseUrl(): string { 12 28 return $this->baseUrl; -
headless/trunk/classes/Model/PostContentAttachmentCollector.php
r2730402 r3492765 3 3 namespace Palasthotel\WordPress\Headless\Model; 4 4 5 /** 6 * Tracks attachment IDs referenced within post content during block preparation. 7 * 8 * Acts as a static registry mapping post IDs to arrays of attachment IDs 9 * found in the post's content blocks. 10 */ 5 11 class PostContentAttachmentCollector { 6 12 13 /** 14 * @var array<int, int[]> Map of post ID to array of attachment IDs. 15 */ 7 16 public static array $map = []; 8 17 18 /** 19 * Returns all attachment IDs collected for the given post. 20 * 21 * @param int $postId The post ID. 22 * @return int[] Array of attachment IDs, or empty array if none collected. 23 */ 9 24 public static function get($postId){ 10 25 return static::$map[ $postId ] ?? []; 11 26 } 12 27 28 /** 29 * Registers an attachment ID for the given post, avoiding duplicates. 30 * 31 * @param int $postId The post ID to associate the attachment with. 32 * @param int $attachmentId The attachment ID to register. 33 * @return void 34 */ 13 35 public static function add($postId, $attachmentId){ 14 36 $attachmentId = intval($attachmentId); -
headless/trunk/classes/Model/PostRouteExtensions.php
r2730402 r3492765 5 5 use Palasthotel\WordPress\Headless\Interfaces\IPostRouteExtension; 6 6 7 /** 8 * Collection of IPostRouteExtension instances. 9 * 10 * Used to register and retrieve all active post route extension handlers. 11 */ 7 12 class PostRouteExtensions { 8 13 … … 12 17 private array $items = []; 13 18 19 /** 20 * Adds a post route extension to the collection. 21 * 22 * @param IPostRouteExtension $extension The extension to add. 23 * @return void 24 */ 14 25 public function add( IPostRouteExtension $extension ) { 15 26 $this->items[] = $extension; 16 27 } 17 28 29 /** 30 * Returns all registered post route extensions. 31 * 32 * @return IPostRouteExtension[] The registered extensions. 33 */ 18 34 public function get(){ 19 35 return $this->items; -
headless/trunk/classes/Model/TermRouteExtensions.php
r2791162 r3492765 5 5 use Palasthotel\WordPress\Headless\Interfaces\ITermRouteExtension; 6 6 7 /** 8 * Collection of ITermRouteExtension instances. 9 * 10 * Used to register and retrieve all active term route extension handlers. 11 */ 7 12 class TermRouteExtensions { 8 13 … … 12 17 private array $items = []; 13 18 19 /** 20 * Adds a term route extension to the collection. 21 * 22 * @param ITermRouteExtension $extension The extension to add. 23 * @return void 24 */ 14 25 public function add( ITermRouteExtension $extension ) { 15 26 $this->items[] = $extension; 16 27 } 17 28 29 /** 30 * Returns all registered term route extensions. 31 * 32 * @return ITermRouteExtension[] The registered extensions. 33 */ 18 34 public function get(){ 19 35 return $this->items; -
headless/trunk/classes/Model/UserRouteExtensions.php
r2791162 r3492765 5 5 use Palasthotel\WordPress\Headless\Interfaces\IUserRouteExtension; 6 6 7 /** 8 * Collection of IUserRouteExtension instances. 9 * 10 * Used to register and retrieve all active user route extension handlers. 11 */ 7 12 class UserRouteExtensions { 8 13 … … 12 17 private array $items = []; 13 18 19 /** 20 * Adds a user route extension to the collection. 21 * 22 * @param IUserRouteExtension $extension The extension to add. 23 * @return void 24 */ 14 25 public function add( IUserRouteExtension $extension ) { 15 26 $this->items[] = $extension; 16 27 } 17 28 29 /** 30 * Returns all registered user route extensions. 31 * 32 * @return IUserRouteExtension[] The registered extensions. 33 */ 18 34 public function get(){ 19 35 return $this->items; -
headless/trunk/classes/PluginAssets.php
r3196946 r3492765 6 6 use Palasthotel\WordPress\Headless\Components\Component; 7 7 8 /** 9 * Registers and enqueues plugin scripts and styles for the admin and block editor. 10 * 11 * Handles the Gutenberg editor script/style and the general admin script, 12 * including localized data for frontend URLs, AJAX actions, and preview settings. 13 */ 8 14 class PluginAssets extends Component { 9 15 … … 12 18 const HANDLE_GUTENBERG_STYLE = "headless_gutenberg_styles"; 13 19 20 /** 21 * @var Assets Asset registration helper. 22 */ 14 23 public Assets $assets; 15 24 … … 23 32 } 24 33 34 /** 35 * Registers all plugin scripts and styles and enqueues the admin script for editors. 36 * 37 * @return void 38 */ 25 39 public function admin_init() { 26 40 $this->assets->registerScript( … … 71 85 } 72 86 87 /** 88 * Enqueues the Gutenberg script and style for headless post types in the block editor. 89 * 90 * @return void 91 */ 73 92 public function enqueue() { 74 93 if ( ! $this->plugin->post->isHeadlessPostType( get_post_type() ) ) { -
headless/trunk/classes/Post.php
r3196946 r3492765 7 7 use WP_Term; 8 8 9 /** 10 * Handles post data preparation for the headless REST API response. 11 * 12 * Extends the default post response with featured media details, taxonomy terms, 13 * and other post fields needed by the headless frontend. 14 */ 9 15 class Post extends Component { 10 16 public function onCreate(): void { … … 13 19 } 14 20 21 /** 22 * Determines whether the given post type is a headless post type. 23 * 24 * @param string $postType The post type slug to check. 25 * @return bool True if the post type is considered headless, filterable via Plugin::FILTER_IS_HEADLESS_POST_TYPE. 26 */ 15 27 public function isHeadlessPostType(string $postType){ 16 28 return apply_filters(Plugin::FILTER_IS_HEADLESS_POST_TYPE, true, $postType); 17 29 } 18 30 31 /** 32 * Filters the post response array to include headless-specific fields. 33 * 34 * Adds id, type, title, slug, featured media data, excerpt, and REST-visible taxonomy terms. 35 * 36 * @param array $response The existing response data array. 37 * @param int|\WP_Post $id_or_post A post ID or WP_Post object. 38 * @return array The merged response array with headless fields appended. 39 */ 19 40 public function prepare_post( array $response, $id_or_post ): array { 20 41 $post = get_post( $id_or_post ); -
headless/trunk/classes/Preview.php
r3254930 r3492765 6 6 use WP_Post; 7 7 8 /** 9 * Manages the headless preview functionality for WordPress posts. 10 * 11 * Overrides the standard WordPress preview link to redirect editors to the 12 * headless frontend preview URL via an AJAX action. Supports enabling and 13 * disabling preview functionality through a filter. 14 */ 8 15 class Preview extends Component { 9 16 … … 19 26 } 20 27 28 /** 29 * Builds the WordPress admin-ajax.php redirect URL for headless preview. 30 * 31 * @param int|null $id The post ID, or null to use the placeholder string. 32 * @return string The admin AJAX URL for the headless preview action. 33 */ 21 34 public function getRedirectLink( $id ) { 22 35 if($id == null){ … … 27 40 } 28 41 42 /** 43 * Returns the path segment for the headless frontend preview endpoint. 44 * 45 * @return string The preview path including the secret token query parameter. 46 */ 29 47 public function getHeadlessPreviewPath(){ 30 48 return "/api/preview?secret_token=".HEADLESS_SECRET_TOKEN; … … 48 66 } 49 67 68 /** 69 * Filters the preview post link to redirect to the headless frontend preview. 70 * 71 * Only applies to headless post types; returns the original link for others. 72 * 73 * @param string $link The default WordPress preview link. 74 * @param WP_Post $post The post being previewed. 75 * @return string The headless redirect URL or the original link. 76 */ 50 77 public function preview_post_link( string $link, WP_Post $post ) { 51 78 … … 60 87 } 61 88 89 /** 90 * Handles the headless_preview AJAX action for logged-in users. 91 * 92 * Validates the post exists and the user has edit permission, then 93 * redirects to the headless frontend preview URL. 94 * 95 * @return void 96 */ 62 97 public function admin_preview() { 63 98 $postId = intval( $_GET["post"] ); … … 77 112 } 78 113 114 /** 115 * Handles the headless_preview AJAX action for unauthenticated users. 116 * 117 * Returns a 403 Forbidden response. 118 * 119 * @return void 120 */ 79 121 public function no_permission() { 80 122 header('HTTP/1.0 403 Forbidden'); … … 82 124 } 83 125 126 /** 127 * Removes preview hooks when preview functionality is inactive. 128 * 129 * @return void 130 */ 84 131 public function plugins_loaded() { 85 132 if($this->isPreviewInactive()){ … … 90 137 } 91 138 139 /** 140 * Checks whether preview functionality is inactive. 141 * 142 * @return bool True if preview is inactive. 143 */ 92 144 function isPreviewInactive() { 93 145 return !$this->isPreviewActive(); 94 146 } 147 /** 148 * Checks whether preview functionality is active. 149 * 150 * @return bool True if preview is active, filterable via Plugin::FILTER_PREVIEW_IS_ACTIVE. 151 */ 95 152 function isPreviewActive() { 96 153 return apply_filters(Plugin::FILTER_PREVIEW_IS_ACTIVE, true); -
headless/trunk/classes/Query.php
r3196946 r3492765 5 5 use Palasthotel\WordPress\Headless\Components\Component; 6 6 7 /** 8 * Extends REST API queries with custom meta and post type filtering parameters. 9 * 10 * Adds support for filtering REST posts queries by meta key/value pairs, 11 * meta existence checks, and multiple post types via custom request parameters. 12 * Only active on requests with valid API key access. 13 */ 7 14 class Query extends Component { 8 15 … … 27 34 } 28 35 36 /** 37 * Extracts and validates the post types requested via the hl_post_type parameter. 38 * 39 * @param \WP_REST_Request $request The current REST request. 40 * @return string[] An array of validated post type slugs, or ["any"] if requested. 41 */ 29 42 public static function getRequestPostTypes( \WP_REST_Request $request ) { 30 43 $post_types = $request->get_param( static::POST_TYPE ); … … 44 57 } 45 58 59 /** 60 * Filters the REST query arguments to apply meta and post type parameters. 61 * 62 * Processes hl_meta_keys, hl_meta_values, hl_meta_compares, hl_meta_exists, 63 * hl_meta_not_exists, hl_meta_relation, and hl_post_type request parameters. 64 * 65 * @param array $args The current WP_Query arguments. 66 * @param \WP_REST_Request $request The current REST request. 67 * @return array The modified query arguments. 68 */ 46 69 public function rest_query( array $args, \WP_REST_Request $request ) { 47 70 -
headless/trunk/classes/Revalidate.php
r3196946 r3492765 6 6 use Palasthotel\WordPress\Headless\Model\Frontend; 7 7 8 /** 9 * Triggers cache revalidation on headless frontends for posts and comments. 10 * 11 * Listens for post saves and comment changes to queue revalidation requests. 12 * Supports revalidation by path, tag, or post ID across all configured frontends. 13 * Revalidation can be toggled via the Plugin::FILTER_REVALIDATE_IS_ACTIVE filter. 14 */ 8 15 class Revalidate extends Component { 9 16 … … 15 22 } 16 23 24 /** 25 * Queues a post for revalidation when it is saved. 26 * 27 * @param int $post_id The ID of the saved post. 28 * @return void 29 */ 17 30 public function on_post_change($post_id){ 18 31 if($this->isRevalidationInactive()) return; … … 21 34 } 22 35 36 /** 37 * Queues the associated post and comment for revalidation when a comment changes. 38 * 39 * @param int $comment_id The ID of the inserted or edited comment. 40 * @return void 41 */ 23 42 public function on_comment_change($comment_id){ 24 43 if($this->isRevalidationInactive()) return; … … 28 47 } 29 48 49 /** 50 * Revalidates comments for the given post across all frontends by tag. 51 * 52 * @param int $post_id The post ID whose comment cache should be revalidated. 53 * @return (\WP_Error|true)[] Results from each frontend revalidation attempt. 54 */ 30 55 function revalidateComments($post_id){ 31 56 … … 61 86 } 62 87 88 /** 89 * Revalidates the permalink path of a post on the given frontend. 90 * 91 * @param Frontend $frontend The frontend to revalidate on. 92 * @param int|string $post_id The post ID to look up and revalidate. 93 * @return \WP_Error|true|array The revalidation result, or empty array if inactive. 94 */ 63 95 function revalidateByPathByPostId(Frontend $frontend, $post_id) { 64 96 … … 73 105 } 74 106 107 /** 108 * Revalidates a specific path on the given frontend. 109 * 110 * @param Frontend $frontend The frontend to revalidate on. 111 * @param string $path The URL path to revalidate. 112 * @return \WP_Error|true|array The revalidation result, or empty array if inactive. 113 */ 75 114 function revalidateByPath(Frontend $frontend, $path){ 76 115 … … 84 123 } 85 124 125 /** 126 * Revalidates all pages tagged with the given cache tag on the frontend. 127 * 128 * @param Frontend $frontend The frontend to revalidate on. 129 * @param string $tag The cache tag to revalidate. 130 * @return \WP_Error|true|array The revalidation result, or empty array if inactive. 131 */ 86 132 function revalidateByTag(Frontend $frontend, string $tag) { 87 133 … … 95 141 } 96 142 143 /** 144 * Executes a revalidation HTTP request to the given URL. 145 * 146 * Appends a cache-busting query parameter and issues a GET request. 147 * 148 * @param string $finalUrl The fully constructed revalidation URL. 149 * @return \WP_Error|true WP_Error on HTTP error or non-200 response, true on success. 150 */ 97 151 private function executeRavalidation($finalUrl){ 98 152 … … 112 166 } 113 167 168 /** 169 * Checks whether revalidation is currently inactive. 170 * 171 * @return bool True if revalidation is inactive. 172 */ 114 173 function isRevalidationInactive() { 115 174 return !$this->isRevalidationActive(); 116 175 } 176 /** 177 * Checks whether revalidation is currently active. 178 * 179 * @return bool True if revalidation is active, filterable via Plugin::FILTER_REVALIDATE_IS_ACTIVE. 180 */ 117 181 function isRevalidationActive() { 118 182 return apply_filters(Plugin::FILTER_REVALIDATE_IS_ACTIVE, true); -
headless/trunk/classes/Routes.php
r3196946 r3492765 7 7 use Palasthotel\WordPress\Headless\Routes\Settings; 8 8 9 /** 10 * Initializes and exposes the plugin's custom REST API routes. 11 * 12 * Registers the Menus and Settings REST route handlers on rest_api_init. 13 */ 9 14 class Routes extends Components\Component { 10 15 16 /** 17 * @var Menus REST route handler for navigation menus. 18 */ 11 19 public Menus $menus; 20 21 /** 22 * @var Settings REST route handler for site settings. 23 */ 12 24 public Settings $settings; 13 25 … … 17 29 } 18 30 31 /** 32 * Instantiates and initializes the Menus and Settings route handlers. 33 * 34 * @return void 35 */ 19 36 public function rest_api_init() { 20 37 -
headless/trunk/classes/Routes/Menus.php
r3254930 r3492765 6 6 use Palasthotel\WordPress\Headless\Plugin; 7 7 8 /** 9 * Registers REST API endpoints for retrieving WordPress navigation menus. 10 * 11 * Exposes GET /headless/v1/menus for all menus and 12 * GET /headless/v1/menus/{slug} for a specific menu. 13 * Requires both a headless request and valid API key access. 14 */ 8 15 class Menus extends Component { 9 16 17 /** 18 * @var array Cached menu items for the current request. 19 */ 10 20 private array $menu; 11 21 22 /** 23 * Registers the menus REST routes. 24 * 25 * @return void 26 */ 12 27 public function init(){ 13 28 register_rest_route( Plugin::REST_NAMESPACE, '/menus', array( … … 29 44 } 30 45 46 /** 47 * Returns all registered navigation menus keyed by slug. 48 * 49 * @return array<string, array> Map of menu slug to menu items array. 50 */ 31 51 public function get_all_menus() { 32 52 $menus = wp_get_nav_menus(); … … 38 58 } 39 59 60 /** 61 * Returns the cached menu items for the current menu request. 62 * 63 * @return array The menu items array. 64 */ 40 65 public function get_menu() { 41 66 return $this->menu; 42 67 } 43 68 69 /** 70 * Retrieves and prepares menu items for a given menu, decoding HTML entities in titles. 71 * 72 * @param \WP_Term|object $menu The menu object or term to fetch items for. 73 * @return array The array of prepared menu item objects, or empty array if not found. 74 */ 44 75 private function getMenuResponse($menu) { 45 76 $menu = wp_get_nav_menu_items( $menu ); -
headless/trunk/classes/Routes/Settings.php
r3196946 r3492765 6 6 use Palasthotel\WordPress\Headless\Plugin; 7 7 8 /** 9 * Registers the REST API endpoint for retrieving site settings. 10 * 11 * Exposes GET /headless/v1/settings with front page configuration and home URL. 12 * Requires both a headless request and valid API key access. 13 */ 8 14 class Settings extends Component { 9 15 16 /** 17 * Registers the settings REST route. 18 * 19 * @return void 20 */ 10 21 public function init(): void { 11 22 register_rest_route( Plugin::REST_NAMESPACE, '/settings', array( … … 19 30 } 20 31 32 /** 33 * Returns key site settings for the headless frontend. 34 * 35 * @return array{front_page: string, page_on_front: int, home_url: string} 36 */ 21 37 public function get_settings(): array { 22 38 return [ -
headless/trunk/classes/Schedule.php
r3196946 r3492765 5 5 use Palasthotel\WordPress\Headless\Components\Component; 6 6 7 /** 8 * Manages the WordPress cron schedule for periodic cache revalidation. 9 * 10 * Registers an hourly cron event that processes pending post and comment 11 * revalidations from the database queue. Unschedules the event when 12 * revalidation is inactive. 13 */ 7 14 class Schedule extends Component { 8 15 … … 14 21 } 15 22 23 /** 24 * Sets up or removes the revalidation cron schedule based on active state. 25 * 26 * Schedules an hourly event if revalidation is active and not yet scheduled. 27 * Removes the event if revalidation is inactive. 28 * 29 * @return void 30 */ 16 31 public function init(){ 17 32 if($this->plugin->revalidate->isRevalidationInactive()){ … … 27 42 } 28 43 44 /** 45 * Returns the timestamp for the next scheduled revalidation run. 46 * 47 * @return int|false The Unix timestamp, or false if not scheduled. 48 */ 29 49 public function getNextSchedule(){ 30 50 return wp_next_scheduled(Plugin::SCHEDULE_REVALIDATE); 31 51 } 32 52 53 /** 54 * Returns the Unix timestamp of the last completed revalidation run. 55 * 56 * @return int The timestamp, or 0 if it has never run. 57 */ 33 58 public function getLastRevalidationRun(): int{ 34 59 return intval(get_option(Plugin::OPTION_LAST_REVALIDATION_RUN, 0)); 35 60 } 36 61 62 /** 63 * Persists the timestamp of the last revalidation run. 64 * 65 * @param int $time Unix timestamp of the run. 66 * @return void 67 */ 37 68 public function setLastRevalidationRun(int $time) { 38 69 update_option(Plugin::OPTION_LAST_REVALIDATION_RUN, $time); 39 70 } 40 71 72 /** 73 * Processes all pending post and comment revalidations from the database queue. 74 * 75 * Called by the cron event. Iterates pending items, triggers revalidation, 76 * updates each item's state to "revalidated" or "error", then fires a side-effect action. 77 * 78 * @return void 79 */ 41 80 public function revalidate(){ 42 81 -
headless/trunk/classes/Security.php
r3196946 r3492765 5 5 use Palasthotel\WordPress\Headless\Components\Component; 6 6 7 /** 8 * Handles security checks for headless REST API access. 9 * 10 * Detects headless requests via a dedicated query parameter, validates 11 * request variants, and enforces optional API key header authentication. 12 * Also enables WordPress application passwords globally. 13 */ 7 14 class Security extends Component { 8 15 … … 12 19 } 13 20 21 /** 22 * Checks whether the current request is a headless REST request. 23 * 24 * @return bool True if the HEADLESS_REST_PARAM query parameter matches HEADLESS_REST_VALUE. 25 */ 14 26 public function isHeadlessRequest(): bool { 15 27 return isset( $_GET[ HEADLESS_REST_PARAM ] ) && HEADLESS_REST_VALUE == $_GET[ HEADLESS_REST_PARAM ]; 16 28 } 17 29 30 /** 31 * Checks whether the current headless request matches a specific variant. 32 * 33 * @param string $variant The variant value to check against HEADLESS_REST_VARIANT_PARAM. 34 * @return bool True if the variant parameter matches the given value. 35 */ 18 36 public function isHeadlessRequestVariant(string $variant): bool { 19 37 return isset( $_GET[ HEADLESS_REST_VARIANT_PARAM ] ) && $variant == $_GET[ HEADLESS_REST_VARIANT_PARAM ]; 20 38 } 21 39 40 /** 41 * Checks whether the current request has valid API key access. 42 * 43 * If no API key constants are configured, all requests are granted access. 44 * Otherwise, the request must include the correct API key header value. 45 * 46 * @return bool True if access is granted. 47 */ 22 48 public function hasApiKeyAccess(): bool { 23 49 -
headless/trunk/classes/Store/RevalidationDatabase.php
r3196946 r3492765 7 7 use Palasthotel\WordPress\Headless\Components\Database; 8 8 9 /** 10 * Manages the revalidation queue database table. 11 * 12 * Stores posts and comments pending cache revalidation, tracks their state 13 * (pending, revalidated, error), and provides methods to add, query, and update entries. 14 */ 9 15 class RevalidationDatabase extends Database { 10 16 … … 12 18 const TYPE_COMMENT = "comment"; 13 19 20 /** 21 * @var string The full database table name for revalidation queue entries. 22 */ 14 23 public string $table; 15 24 … … 18 27 } 19 28 29 /** 30 * Inserts or replaces a content entry in the revalidation queue with state "pending". 31 * 32 * @param string $id The content ID. 33 * @param string $type The content type (post or comment). 34 * @return int|false The number of rows affected, or false on error. 35 */ 20 36 private function addContent(string $id, string $type) { 21 37 return $this->wpdb->replace( … … 30 46 } 31 47 48 /** 49 * Adds a post to the revalidation queue. 50 * 51 * @param int $post_id The post ID to queue. 52 * @return int|false The number of rows affected, or false on error. 53 */ 32 54 public function addPost(int $post_id) { 33 55 return $this->addContent($post_id, self::TYPE_POST); 34 56 } 35 57 58 /** 59 * Adds a comment to the revalidation queue. 60 * 61 * @param string $comment_id The comment ID to queue. 62 * @return int|false The number of rows affected, or false on error. 63 */ 36 64 public function addComment(string $comment_id) { 37 65 return $this->addContent($comment_id, self::TYPE_COMMENT); … … 41 69 /** 42 70 * @return Int[] 71 */ 72 /** 73 * Returns all content IDs with state "pending" for the given content type. 74 * 75 * @param string $type The content type (post or comment). 76 * @return int[] Array of content IDs. 43 77 */ 44 78 private function getPendingContents(string $type): array { … … 58 92 } 59 93 94 /** 95 * Returns all comment IDs currently pending revalidation. 96 * 97 * @return int[] Array of comment IDs. 98 */ 60 99 public function getPendingComments(): array { 61 100 return $this->getPendingContents(self::TYPE_COMMENT); … … 63 102 64 103 104 /** 105 * Counts content entries with state "pending" for the given content type. 106 * 107 * @param string $type The content type (post or comment). 108 * @return int The count of pending entries. 109 */ 65 110 private function countPendingContents(string $type): int { 66 111 $sql = $this->wpdb->prepare( … … 71 116 } 72 117 118 /** 119 * Returns the count of posts currently pending revalidation. 120 * 121 * @return int The number of pending posts. 122 */ 73 123 public function countPendingPosts(): int { 74 124 return $this->countPendingContents(self::TYPE_POST); 75 125 } 76 126 127 /** 128 * Returns the count of comments currently pending revalidation. 129 * 130 * @return int The number of pending comments. 131 */ 77 132 public function countPendingComments(): int { 78 133 return $this->countPendingContents(self::TYPE_COMMENT); 79 134 } 80 135 136 /** 137 * Updates the revalidation state and timestamp for a content entry. 138 * 139 * @param int $id The content ID. 140 * @param string $type The content type (post or comment). 141 * @param string $state The new state (e.g. "revalidated" or "error"). 142 * @return int|false The number of rows updated, or false on error. 143 */ 81 144 public function setContentState(int $id, string $type, $state = "revalidated") { 82 145 return $this->wpdb->update( … … 95 158 } 96 159 160 /** 161 * Updates the revalidation state for a post entry. 162 * 163 * @param int $post_id The post ID. 164 * @param string $state The new state (default "revalidated"). 165 * @return int|false The number of rows updated, or false on error. 166 */ 97 167 public function setPostState(int $post_id, $state = "revalidated") { 98 168 return $this->setContentState($post_id, self::TYPE_POST, $state); 99 169 } 100 170 171 /** 172 * Updates the revalidation state for a comment entry. 173 * 174 * @param int $comment_id The comment ID. 175 * @param string $state The new state (default "revalidated"). 176 * @return int|false The number of rows updated, or false on error. 177 */ 101 178 public function setCommentState(int $comment_id, $state = "revalidated") { 102 179 return $this->setContentState($comment_id, self::TYPE_COMMENT, $state); 103 180 } 104 181 182 /** 183 * Creates the revalidation queue table if it does not already exist. 184 * 185 * @return void 186 */ 105 187 public function createTables(): void { 106 188 parent::createTables(); -
headless/trunk/classes/Utils.php
r2730402 r3492765 3 3 namespace Palasthotel\WordPress\Headless; 4 4 5 /** 6 * Utility helpers for the headless plugin. 7 */ 5 8 class Utils { 9 10 /** 11 * Sanitizes HTML content, allowing only a safe subset of tags, and strips newlines. 12 * 13 * Permitted tags: a (href, target, rel), b, i, strong. 14 * 15 * @param string $html The raw HTML string to sanitize. 16 * @return string The sanitized HTML with newlines removed. 17 */ 6 18 public static function prepareHTML($html){ 7 19 return str_replace( "\n", "", wp_kses( -
headless/trunk/dist/admin.asset.php
r3196946 r3492765 1 <?php return array('dependencies' => array(), 'version' => '8 241f02c6df14b01c628');1 <?php return array('dependencies' => array(), 'version' => '81924321d7c2c4b3be9a'); -
headless/trunk/dist/admin.js
r3196946 r3492765 1 (()=>{"use strict";window.addEventListener("DOMContentLoaded", (async()=>{window.HeadlessAdmin.preview_is_active&&await Promise.all(window.HeadlessAdmin.frontends.map((e=>e+window.HeadlessAdmin.preview_path)).map((e=>fetch(e,{credentials:"include"}).then((e=>e.json())))))}))})();1 (()=>{"use strict";window.addEventListener("DOMContentLoaded",async()=>{window.HeadlessAdmin.preview_is_active&&await Promise.all(window.HeadlessAdmin.frontends.map(e=>e+window.HeadlessAdmin.preview_path).map(e=>fetch(e,{credentials:"include"}).then(e=>e.json())))})})(); -
headless/trunk/dist/gutenberg.asset.php
r3196946 r3492765 1 <?php return array('dependencies' => array('react-jsx-runtime', 'wp-components', 'wp-data', 'wp-edit -post', 'wp-element', 'wp-plugins'), 'version' => 'f64387f5a68638e1237e');1 <?php return array('dependencies' => array('react-jsx-runtime', 'wp-components', 'wp-data', 'wp-editor', 'wp-element', 'wp-plugins'), 'version' => '05ef464d3548625e183a'); -
headless/trunk/dist/gutenberg.js
r3196946 r3492765 1 (()=>{"use strict";const t=window.wp.plugins,e=window.wp.edit Post,n=window.wp.components,s=window.wp.data,i=window.wp.element,a=()=>(0,s.useSelect)((t=>t("core/editor").getCurrentPost()),[]),r=window.ReactJSXRuntime,o=({index:t,baseUrl:e,controller:n,onStateChanged:s})=>{const{state:o,reload:l}=(t=>{const[e,n]=(0,i.useState)("idle"),s=a(),r=(0,i.useMemo)((()=>s?.link?new URL(s?.link).pathname:""),[s.link]);return{state:e,reload:()=>{n("loading"),(async()=>{try{const e=await fetch(((t,e)=>window.Headless.ajax+`?action=${window.Headless.actions.revalidate}&frontend=${t}&path=${e}`)(t,r)),s=await e.json();s.success?n("success"):(console.error(s),n("error"))}catch(t){n("error")}})()}}})(t),c=a(),d=(0,i.useMemo)((()=>{const t=c.link,n=new URL(t);return e.replace(/^\/|\/$/g,"")+n.pathname}),[c.link]);return(0,i.useEffect)((()=>{n.add(t,l)}),[t]),(0,i.useEffect)((()=>{s(t,o)}),[o]),(0,r.jsxs)("div",{title:d,children:[(0,r.jsxs)("a",{href:d,target:"_blank",children:["Frontend ",t]}),"loading"==o&&(0,r.jsx)(r.Fragment,{children:" 🧹"}),"success"==o&&(0,r.jsx)(r.Fragment,{children:" ✅"}),"error"==o&&(0,r.jsx)(r.Fragment,{children:" 🚨"})]})};window.HeadlessAdmin.revalidate_is_active&&(0,t.registerPlugin)("headless-plugin",{icon:()=>null,render:function(){const t=window.Headless.frontends,s=(0,i.useMemo)((()=>(()=>{const t=new Map;return{add:(e,n)=>{t.set(e,n)},run:()=>{t.forEach((t=>{t()}))}}})()),[]),[l,c]=(0,i.useState)({}),d="publish"==a().status,p=Object.values(l).find((t=>1==t));return(0,r.jsx)(e.PluginDocumentSettingPanel,{title:"Headless",children:d?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("ol",{children:t.map(((t,e)=>(0,r.jsx)("li",{children:(0,r.jsx)(o,{baseUrl:t,index:e,controller:s,onStateChanged:(t,e)=>{c((n=>{const s={...n};return s[t]="loading"==e,s}))}})},e)))}),(0,r.jsx)(n.Button,{variant:"secondary",disabled:p||!d,onClick:()=>{s.run()},children:"Revalidate cache"})]}):(0,r.jsx)("p",{className:"description",children:"Only published contents can be revalidated."})})}}),document.addEventListener("DOMContentLoaded",(function(){if(!window.HeadlessAdmin.preview_is_active)return;const t=(0,s.select)("core/editor"),e=t.getCurrentPostId,n=t.isSavingPost,i=(0,s.dispatch)("core/editor"),a=i.autosave,r=i.savePost,o=document.createElement("a");o.className="components-button",o.addEventListener("click",(e=>{if(e.preventDefault(),n())return;const s=window.open("about:blank",o.target);!function(t){let e="";e+='\n\t\t<style>\n\t\t\tbody {\n\t\t\t\tmargin: 0;\n\t\t\t}\n\t\t\t.editor-post-preview-button__interstitial-message {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-direction: column;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\theight: 100vh;\n\t\t\t\twidth: 100vw;\n\t\t\t}\n\t\t\t@-webkit-keyframes paint {\n\t\t\t\t0% {\n\t\t\t\t\tstroke-dashoffset: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t@-moz-keyframes paint {\n\t\t\t\t0% {\n\t\t\t\t\tstroke-dashoffset: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t@-o-keyframes paint {\n\t\t\t\t0% {\n\t\t\t\t\tstroke-dashoffset: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t@keyframes paint {\n\t\t\t\t0% {\n\t\t\t\t\tstroke-dashoffset: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t.editor-post-preview-button__interstitial-message svg {\n\t\t\t\twidth: 192px;\n\t\t\t\theight: 192px;\n\t\t\t\tstroke: #555d66;\n\t\t\t\tstroke-width: 0.75;\n\t\t\t}\n\t\t\t.editor-post-preview-button__interstitial-message svg .outer,\n\t\t\t.editor-post-preview-button__interstitial-message svg .inner {\n\t\t\t\tstroke-dasharray: 280;\n\t\t\t\tstroke-dashoffset: 280;\n\t\t\t\t-webkit-animation: paint 1.5s ease infinite alternate;\n\t\t\t\t-moz-animation: paint 1.5s ease infinite alternate;\n\t\t\t\t-o-animation: paint 1.5s ease infinite alternate;\n\t\t\t\tanimation: paint 1.5s ease infinite alternate;\n\t\t\t}\n\t\t\tp {\n\t\t\t\ttext-align: center;\n\t\t\t\tfont-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;\n\t\t\t}\n\t\t</style>\n\t',e+='\n <div class="editor-post-preview-button__interstitial-message">\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96">\n <path class="outer" d="M48 12c19.9 0 36 16.1 36 36S67.9 84 48 84 12 67.9 12 48s16.1-36 36-36" fill="none" />\n <path class="inner" d="M69.5 46.4c0-3.9-1.4-6.7-2.6-8.8-1.6-2.6-3.1-4.9-3.1-7.5 0-2.9 2.2-5.7 5.4-5.7h.4C63.9 19.2 56.4 16 48 16c-11.2 0-21 5.7-26.7 14.4h2.1c3.3 0 8.5-.4 8.5-.4 1.7-.1 1.9 2.4.2 2.6 0 0-1.7.2-3.7.3L40 67.5l7-20.9L42 33c-1.7-.1-3.3-.3-3.3-.3-1.7-.1-1.5-2.7.2-2.6 0 0 5.3.4 8.4.4 3.3 0 8.5-.4 8.5-.4 1.7-.1 1.9 2.4.2 2.6 0 0-1.7.2-3.7.3l11.5 34.3 3.3-10.4c1.6-4.5 2.4-7.8 2.4-10.5zM16.1 48c0 12.6 7.3 23.5 18 28.7L18.8 35c-1.7 4-2.7 8.4-2.7 13zm32.5 2.8L39 78.6c2.9.8 5.9 1.3 9 1.3 3.7 0 7.3-.6 10.6-1.8-.1-.1-.2-.3-.2-.4l-9.8-26.9zM76.2 36c0 3.2-.6 6.9-2.4 11.4L64 75.6c9.5-5.5 15.9-15.8 15.9-27.6 0-5.5-1.4-10.8-3.9-15.3.1 1 .2 2.1.2 3.3z" fill="none" />\n </svg>\n <p>Generating preview…</p>\n </div>\n ',t.write('\n\t\t<style>\n\t\t\tbody {\n\t\t\t\tmargin: 0;\n\t\t\t}\n\t\t\t.editor-post-preview-button__interstitial-message {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-direction: column;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\theight: 100vh;\n\t\t\t\twidth: 100vw;\n\t\t\t}\n\t\t\t@-webkit-keyframes paint {\n\t\t\t\t0% {\n\t\t\t\t\tstroke-dashoffset: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t@-moz-keyframes paint {\n\t\t\t\t0% {\n\t\t\t\t\tstroke-dashoffset: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t@-o-keyframes paint {\n\t\t\t\t0% {\n\t\t\t\t\tstroke-dashoffset: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t@keyframes paint {\n\t\t\t\t0% {\n\t\t\t\t\tstroke-dashoffset: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t.editor-post-preview-button__interstitial-message svg {\n\t\t\t\twidth: 192px;\n\t\t\t\theight: 192px;\n\t\t\t\tstroke: #555d66;\n\t\t\t\tstroke-width: 0.75;\n\t\t\t}\n\t\t\t.editor-post-preview-button__interstitial-message svg .outer,\n\t\t\t.editor-post-preview-button__interstitial-message svg .inner {\n\t\t\t\tstroke-dasharray: 280;\n\t\t\t\tstroke-dashoffset: 280;\n\t\t\t\t-webkit-animation: paint 1.5s ease infinite alternate;\n\t\t\t\t-moz-animation: paint 1.5s ease infinite alternate;\n\t\t\t\t-o-animation: paint 1.5s ease infinite alternate;\n\t\t\t\tanimation: paint 1.5s ease infinite alternate;\n\t\t\t}\n\t\t\tp {\n\t\t\t\ttext-align: center;\n\t\t\t\tfont-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;\n\t\t\t}\n\t\t</style>\n\t\n <div class="editor-post-preview-button__interstitial-message">\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96">\n <path class="outer" d="M48 12c19.9 0 36 16.1 36 36S67.9 84 48 84 12 67.9 12 48s16.1-36 36-36" fill="none" />\n <path class="inner" d="M69.5 46.4c0-3.9-1.4-6.7-2.6-8.8-1.6-2.6-3.1-4.9-3.1-7.5 0-2.9 2.2-5.7 5.4-5.7h.4C63.9 19.2 56.4 16 48 16c-11.2 0-21 5.7-26.7 14.4h2.1c3.3 0 8.5-.4 8.5-.4 1.7-.1 1.9 2.4.2 2.6 0 0-1.7.2-3.7.3L40 67.5l7-20.9L42 33c-1.7-.1-3.3-.3-3.3-.3-1.7-.1-1.5-2.7.2-2.6 0 0 5.3.4 8.4.4 3.3 0 8.5-.4 8.5-.4 1.7-.1 1.9 2.4.2 2.6 0 0-1.7.2-3.7.3l11.5 34.3 3.3-10.4c1.6-4.5 2.4-7.8 2.4-10.5zM16.1 48c0 12.6 7.3 23.5 18 28.7L18.8 35c-1.7 4-2.7 8.4-2.7 13zm32.5 2.8L39 78.6c2.9.8 5.9 1.3 9 1.3 3.7 0 7.3-.6 10.6-1.8-.1-.1-.2-.3-.2-.4l-9.8-26.9zM76.2 36c0 3.2-.6 6.9-2.4 11.4L64 75.6c9.5-5.5 15.9-15.8 15.9-27.6 0-5.5-1.4-10.8-3.9-15.3.1 1 .2 2.1.2 3.3z" fill="none" />\n </svg>\n <p>Generating preview…</p>\n </div>\n '),t.close()}(s.document),((()=>{const e=t.getCurrentPost().status;return"draft"==e||"auto-draft"==e})()?r:a)().then((()=>{s.location=o.href}))})),(0,s.subscribe)((()=>{n()?o.classList.add("is-disabled"):o.classList.remove("is-disabled")})),setInterval((function(){const t=e(),n=(t=>window.Headless.preview_url.replace(window.Headless.post_id_placeholder,`${t}`))(t),s=document.querySelectorAll("[target^=wp-preview-]");s&&s.length&&s.forEach((t=>{t.setAttribute("href",n)})),document.querySelectorAll(".components-snackbar-list .components-snackbar__content a.components-button").forEach((e=>{(e.href.includes("?post="+t)||e.href.includes("?page_id="+t)||e.href.includes("?p="+t))&&(e.href=n,e.target="wp-preview-"+t)}));const i=document.querySelectorAll(".components-menu-group");let a=null;if(i.forEach((t=>{t.querySelector(".editor-preview-dropdown__button-external")&&(a=t)})),!a)return;const r="headless-preview-link";if(a.querySelector("#"+r))return;const l=a.querySelector(".editor-preview-dropdown__button-external"),c=l.querySelector("svg"),d=l.getAttribute("target");o.text=l.textContent,o.append(c),o.target=d,o.href=n,o.id=r,l.style.display="none",a.querySelector('[role="group"]').append(o)}),300)}))})();1 (()=>{"use strict";const t=window.wp.plugins,e=window.wp.editor,n=window.wp.components,s=window.wp.data,i=window.wp.element,a=()=>(0,s.useSelect)(t=>t(e.store).getCurrentPost(),[]),r=window.ReactJSXRuntime,o=({index:t,baseUrl:e,controller:n,onStateChanged:s})=>{const{state:o,reload:l}=(t=>{const[e,n]=(0,i.useState)("idle"),s=a(),r=(0,i.useMemo)(()=>s?.link?new URL(s?.link).pathname:"",[s?.link]);return{state:e,reload:()=>{n("loading"),(async()=>{try{const e=await fetch(((t,e)=>window.Headless.ajax+`?action=${window.Headless.actions.revalidate}&frontend=${t}&path=${e}`)(t,r)),s=await e.json();s.success?n("success"):(console.error(s),n("error"))}catch(t){n("error")}})()}}})(t),d=a(),c=(0,i.useMemo)(()=>{const t=d?.link;if(!t)return"#";const n=new URL(t);return e.replace(/^\/|\/$/g,"")+n.pathname},[d?.link]);return(0,i.useEffect)(()=>{n.add(t,l)},[t]),(0,i.useEffect)(()=>{s(t,o)},[o]),(0,r.jsxs)("div",{title:c,children:[(0,r.jsxs)("a",{href:c,target:"_blank",children:["Frontend ",t]}),"loading"==o&&(0,r.jsx)(r.Fragment,{children:" 🧹"}),"success"==o&&(0,r.jsx)(r.Fragment,{children:" ✅"}),"error"==o&&(0,r.jsx)(r.Fragment,{children:" 🚨"})]})};window.HeadlessAdmin.revalidate_is_active&&(0,t.registerPlugin)("headless-plugin",{icon:()=>null,render:function(){const t=window.Headless.frontends,s=(0,i.useMemo)(()=>(()=>{const t=new Map;return{add:(e,n)=>{t.set(e,n)},run:()=>{t.forEach(t=>{t()})}}})(),[]),[l,d]=(0,i.useState)({}),c="publish"==a()?.status,p=Object.values(l).find(t=>1==t);return(0,r.jsx)(e.PluginDocumentSettingPanel,{name:"palasthotel-headless",title:"Headless",children:c?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("ol",{children:t.map((t,e)=>(0,r.jsx)("li",{children:(0,r.jsx)(o,{baseUrl:t,index:e,controller:s,onStateChanged:(t,e)=>{d(n=>{const s={...n};return s[t]="loading"==e,s})}})},e))}),(0,r.jsx)(n.Button,{variant:"secondary",disabled:p||!c,onClick:()=>{s.run()},children:"Revalidate cache"})]}):(0,r.jsx)("p",{className:"description",children:"Only published contents can be revalidated."})})}}),document.addEventListener("DOMContentLoaded",function(){if(!window.HeadlessAdmin.preview_is_active)return;const t=(0,s.select)("core/editor"),e=t.getCurrentPostId,n=t.isSavingPost,i=(0,s.dispatch)("core/editor"),a=i.autosave,r=i.savePost,o=document.createElement("a");o.className="components-button",o.addEventListener("click",e=>{if(e.preventDefault(),n())return;const s=window.open("about:blank",o.target);s&&(!function(t){let e="";e+='\n\t\t<style>\n\t\t\tbody {\n\t\t\t\tmargin: 0;\n\t\t\t}\n\t\t\t.editor-post-preview-button__interstitial-message {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-direction: column;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\theight: 100vh;\n\t\t\t\twidth: 100vw;\n\t\t\t}\n\t\t\t@-webkit-keyframes paint {\n\t\t\t\t0% {\n\t\t\t\t\tstroke-dashoffset: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t@-moz-keyframes paint {\n\t\t\t\t0% {\n\t\t\t\t\tstroke-dashoffset: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t@-o-keyframes paint {\n\t\t\t\t0% {\n\t\t\t\t\tstroke-dashoffset: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t@keyframes paint {\n\t\t\t\t0% {\n\t\t\t\t\tstroke-dashoffset: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t.editor-post-preview-button__interstitial-message svg {\n\t\t\t\twidth: 192px;\n\t\t\t\theight: 192px;\n\t\t\t\tstroke: #555d66;\n\t\t\t\tstroke-width: 0.75;\n\t\t\t}\n\t\t\t.editor-post-preview-button__interstitial-message svg .outer,\n\t\t\t.editor-post-preview-button__interstitial-message svg .inner {\n\t\t\t\tstroke-dasharray: 280;\n\t\t\t\tstroke-dashoffset: 280;\n\t\t\t\t-webkit-animation: paint 1.5s ease infinite alternate;\n\t\t\t\t-moz-animation: paint 1.5s ease infinite alternate;\n\t\t\t\t-o-animation: paint 1.5s ease infinite alternate;\n\t\t\t\tanimation: paint 1.5s ease infinite alternate;\n\t\t\t}\n\t\t\tp {\n\t\t\t\ttext-align: center;\n\t\t\t\tfont-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;\n\t\t\t}\n\t\t</style>\n\t',e+='\n <div class="editor-post-preview-button__interstitial-message">\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96">\n <path class="outer" d="M48 12c19.9 0 36 16.1 36 36S67.9 84 48 84 12 67.9 12 48s16.1-36 36-36" fill="none" />\n <path class="inner" d="M69.5 46.4c0-3.9-1.4-6.7-2.6-8.8-1.6-2.6-3.1-4.9-3.1-7.5 0-2.9 2.2-5.7 5.4-5.7h.4C63.9 19.2 56.4 16 48 16c-11.2 0-21 5.7-26.7 14.4h2.1c3.3 0 8.5-.4 8.5-.4 1.7-.1 1.9 2.4.2 2.6 0 0-1.7.2-3.7.3L40 67.5l7-20.9L42 33c-1.7-.1-3.3-.3-3.3-.3-1.7-.1-1.5-2.7.2-2.6 0 0 5.3.4 8.4.4 3.3 0 8.5-.4 8.5-.4 1.7-.1 1.9 2.4.2 2.6 0 0-1.7.2-3.7.3l11.5 34.3 3.3-10.4c1.6-4.5 2.4-7.8 2.4-10.5zM16.1 48c0 12.6 7.3 23.5 18 28.7L18.8 35c-1.7 4-2.7 8.4-2.7 13zm32.5 2.8L39 78.6c2.9.8 5.9 1.3 9 1.3 3.7 0 7.3-.6 10.6-1.8-.1-.1-.2-.3-.2-.4l-9.8-26.9zM76.2 36c0 3.2-.6 6.9-2.4 11.4L64 75.6c9.5-5.5 15.9-15.8 15.9-27.6 0-5.5-1.4-10.8-3.9-15.3.1 1 .2 2.1.2 3.3z" fill="none" />\n </svg>\n <p>Generating preview…</p>\n </div>\n ',t.write('\n\t\t<style>\n\t\t\tbody {\n\t\t\t\tmargin: 0;\n\t\t\t}\n\t\t\t.editor-post-preview-button__interstitial-message {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-direction: column;\n\t\t\t\talign-items: center;\n\t\t\t\tjustify-content: center;\n\t\t\t\theight: 100vh;\n\t\t\t\twidth: 100vw;\n\t\t\t}\n\t\t\t@-webkit-keyframes paint {\n\t\t\t\t0% {\n\t\t\t\t\tstroke-dashoffset: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t@-moz-keyframes paint {\n\t\t\t\t0% {\n\t\t\t\t\tstroke-dashoffset: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t@-o-keyframes paint {\n\t\t\t\t0% {\n\t\t\t\t\tstroke-dashoffset: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t@keyframes paint {\n\t\t\t\t0% {\n\t\t\t\t\tstroke-dashoffset: 0;\n\t\t\t\t}\n\t\t\t}\n\t\t\t.editor-post-preview-button__interstitial-message svg {\n\t\t\t\twidth: 192px;\n\t\t\t\theight: 192px;\n\t\t\t\tstroke: #555d66;\n\t\t\t\tstroke-width: 0.75;\n\t\t\t}\n\t\t\t.editor-post-preview-button__interstitial-message svg .outer,\n\t\t\t.editor-post-preview-button__interstitial-message svg .inner {\n\t\t\t\tstroke-dasharray: 280;\n\t\t\t\tstroke-dashoffset: 280;\n\t\t\t\t-webkit-animation: paint 1.5s ease infinite alternate;\n\t\t\t\t-moz-animation: paint 1.5s ease infinite alternate;\n\t\t\t\t-o-animation: paint 1.5s ease infinite alternate;\n\t\t\t\tanimation: paint 1.5s ease infinite alternate;\n\t\t\t}\n\t\t\tp {\n\t\t\t\ttext-align: center;\n\t\t\t\tfont-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif;\n\t\t\t}\n\t\t</style>\n\t\n <div class="editor-post-preview-button__interstitial-message">\n <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96">\n <path class="outer" d="M48 12c19.9 0 36 16.1 36 36S67.9 84 48 84 12 67.9 12 48s16.1-36 36-36" fill="none" />\n <path class="inner" d="M69.5 46.4c0-3.9-1.4-6.7-2.6-8.8-1.6-2.6-3.1-4.9-3.1-7.5 0-2.9 2.2-5.7 5.4-5.7h.4C63.9 19.2 56.4 16 48 16c-11.2 0-21 5.7-26.7 14.4h2.1c3.3 0 8.5-.4 8.5-.4 1.7-.1 1.9 2.4.2 2.6 0 0-1.7.2-3.7.3L40 67.5l7-20.9L42 33c-1.7-.1-3.3-.3-3.3-.3-1.7-.1-1.5-2.7.2-2.6 0 0 5.3.4 8.4.4 3.3 0 8.5-.4 8.5-.4 1.7-.1 1.9 2.4.2 2.6 0 0-1.7.2-3.7.3l11.5 34.3 3.3-10.4c1.6-4.5 2.4-7.8 2.4-10.5zM16.1 48c0 12.6 7.3 23.5 18 28.7L18.8 35c-1.7 4-2.7 8.4-2.7 13zm32.5 2.8L39 78.6c2.9.8 5.9 1.3 9 1.3 3.7 0 7.3-.6 10.6-1.8-.1-.1-.2-.3-.2-.4l-9.8-26.9zM76.2 36c0 3.2-.6 6.9-2.4 11.4L64 75.6c9.5-5.5 15.9-15.8 15.9-27.6 0-5.5-1.4-10.8-3.9-15.3.1 1 .2 2.1.2 3.3z" fill="none" />\n </svg>\n <p>Generating preview…</p>\n </div>\n '),t.close()}(s.document),((()=>{const e=t.getCurrentPost().status;return"draft"==e||"auto-draft"==e})()?r:a)().then(()=>{s.location=o.href}))}),(0,s.subscribe)(()=>{n()?o.classList.add("is-disabled"):o.classList.remove("is-disabled")}),setInterval(function(){const t=e(),n=(t=>window.Headless.preview_url.replace(window.Headless.post_id_placeholder,`${t}`))(t),s=document.querySelectorAll("[target^=wp-preview-]");s&&s.length&&s.forEach(t=>{t.setAttribute("href",n)}),document.querySelectorAll(".components-snackbar-list .components-snackbar__content a.components-button").forEach(e=>{(e.href.includes("?post="+t)||e.href.includes("?page_id="+t)||e.href.includes("?p="+t))&&(e.href=n,e.target="wp-preview-"+t)});const i=Array.from(document.querySelectorAll(".components-menu-group")).find(t=>t.querySelector(".editor-preview-dropdown__button-external"));if(!i)return;const a="headless-preview-link";if(i.querySelector("#"+a))return;const r=i.querySelector(".editor-preview-dropdown__button-external");if(!r)return;const l=r.querySelector("svg"),d=r.getAttribute("target")??"";o.text=r.textContent??"",l&&o.append(l),o.target=d,o.href=n,o.id=a,r.style.display="none",i.querySelector('[role="group"]')?.append(o)},300)})})(); -
headless/trunk/headless.php
r3254930 r3492765 5 5 * Plugin URI: https://github.com/palasthotel/headless 6 6 * Description: Adds features to use WordPress as headless CMS 7 * Version: 2.3.17 * Version: 3.0.1 8 8 * Author: Palasthotel (Edward Bock) <edward.bock@palasthotel.de> 9 9 * Author URI: http://www.palasthotel.de … … 22 22 use Palasthotel\WordPress\Headless\Store\RevalidationDatabase; 23 23 24 if ( ! defined( 'HEADLESS_HEAD_BASE_URL' )) {25 define( 'HEADLESS_HEAD_BASE_URL', '');24 if (!defined("HEADLESS_HEAD_BASE_URL")) { 25 define("HEADLESS_HEAD_BASE_URL", ""); 26 26 } 27 27 28 if ( ! defined( 'HEADLESS_SECRET_TOKEN' )) {29 define( 'HEADLESS_SECRET_TOKEN', "");28 if (!defined("HEADLESS_SECRET_TOKEN")) { 29 define("HEADLESS_SECRET_TOKEN", ""); 30 30 } 31 31 32 if ( ! defined( 'HEADLESS_REST_PARAM' )) {33 define( 'HEADLESS_REST_PARAM', "headless");32 if (!defined("HEADLESS_REST_PARAM")) { 33 define("HEADLESS_REST_PARAM", "headless"); 34 34 } 35 if ( ! defined( 'HEADLESS_REST_VALUE' )) {36 define( 'HEADLESS_REST_VALUE', 'true');35 if (!defined("HEADLESS_REST_VALUE")) { 36 define("HEADLESS_REST_VALUE", "true"); 37 37 } 38 38 39 if ( ! defined( 'HEADLESS_REST_VARIANT_PARAM' )) {40 define( 'HEADLESS_REST_VARIANT_PARAM', "headless_variant");39 if (!defined("HEADLESS_REST_VARIANT_PARAM")) { 40 define("HEADLESS_REST_VARIANT_PARAM", "headless_variant"); 41 41 } 42 if ( ! defined( 'HEADLESS_REST_VARIANT_TEASERS_VALUE' )) {43 define( 'HEADLESS_REST_VARIANT_TEASERS_VALUE', 'teaser');42 if (!defined("HEADLESS_REST_VARIANT_TEASERS_VALUE")) { 43 define("HEADLESS_REST_VARIANT_TEASERS_VALUE", "teaser"); 44 44 } 45 45 46 if ( ! defined( 'HEADLESS_API_KEY_HEADER_KEY' )) {47 define( 'HEADLESS_API_KEY_HEADER_KEY', "");46 if (!defined("HEADLESS_API_KEY_HEADER_KEY")) { 47 define("HEADLESS_API_KEY_HEADER_KEY", ""); 48 48 } 49 if ( ! defined( 'HEADLESS_API_KEY_HEADER_VALUE' )) {50 define( 'HEADLESS_API_KEY_HEADER_VALUE', "");49 if (!defined("HEADLESS_API_KEY_HEADER_VALUE")) { 50 define("HEADLESS_API_KEY_HEADER_VALUE", ""); 51 51 } 52 52 53 53 require_once __DIR__ . "/vendor/autoload.php"; 54 54 55 class Plugin extends Components\Plugin {56 55 class Plugin extends Components\Plugin 56 { 57 57 const DOMAIN = "headless"; 58 58 … … 77 77 78 78 const FILTER_REST_RESPONSE_HEADERS = "headless_rest_response_headers"; 79 const FILTER_REST_RESPONSE_DATA = "headless_rest_response_data";79 const FILTER_REST_RESPONSE_DATA = "headless_rest_response_data"; 80 80 81 const FILTER_FRONTENDS = "headless_frontends";81 const FILTER_FRONTENDS = "headless_frontends"; 82 82 const FILTER_REVALIDATE_BY_PATH_URL = "headless_revalidate_by_path_url"; 83 83 const FILTER_REVALIDATE_BY_TAG_URL = "headless_revalidate_by_tag_url"; … … 106 106 public Log $log; 107 107 108 function onCreate(): void { 108 function onCreate(): void 109 { 110 $this->dbRevalidation = new RevalidationDatabase(); 111 $this->log = new Log($this); 109 112 110 $this->dbRevalidation = new RevalidationDatabase(); 111 $this->log = new Log( $this ); 113 $this->security = new Security($this); 114 $this->headers = new Headers($this); 115 $this->routes = new Routes($this); 116 $this->extensions = new Extensions($this); 117 $this->query = new Query($this); 118 $this->preview = new Preview($this); 119 $this->headquarter = new Headquarter($this); 120 $this->revalidate = new Revalidate($this); 121 $this->gutenberg = new PluginAssets($this); 122 $this->post = new Post($this); 123 $this->dashboard = new Dashboard($this); 124 $this->ajax = new Ajax($this); 125 $this->schedule = new Schedule($this); 112 126 113 $this->security = new Security( $this ); 114 $this->headers = new Headers( $this ); 115 $this->routes = new Routes( $this ); 116 $this->extensions = new Extensions( $this ); 117 $this->query = new Query( $this ); 118 $this->preview = new Preview( $this ); 119 $this->headquarter = new Headquarter( $this ); 120 $this->revalidate = new Revalidate( $this ); 121 $this->gutenberg = new PluginAssets( $this ); 122 $this->post = new Post( $this ); 123 $this->dashboard = new Dashboard( $this ); 124 $this->ajax = new Ajax( $this ); 125 $this->schedule = new Schedule( $this ); 126 127 new Migration( $this ); 128 127 new Migration($this); 129 128 } 130 129 131 public function onSiteActivation() { 130 public function onSiteActivation() 131 { 132 132 parent::onSiteActivation(); 133 133 $this->dbRevalidation->createTables(); 134 135 134 } 136 135 } -
headless/trunk/vendor/composer/installed.php
r3254930 r3492765 4 4 'pretty_version' => 'dev-main', 5 5 'version' => 'dev-main', 6 'reference' => ' a5281d48ac9eb004988aac6b05a2479363adae4c',6 'reference' => '0ed39749f95e81e607472c5bf5ac6bc5948f16d5', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 14 14 'pretty_version' => 'dev-main', 15 15 'version' => 'dev-main', 16 'reference' => ' a5281d48ac9eb004988aac6b05a2479363adae4c',16 'reference' => '0ed39749f95e81e607472c5bf5ac6bc5948f16d5', 17 17 'type' => 'library', 18 18 'install_path' => __DIR__ . '/../../',
Note: See TracChangeset
for help on using the changeset viewer.