Last active
November 4, 2024 09:48
-
-
Save codersantosh/d1740e1fb832df684601aab2e0451786 to your computer and use it in GitHub Desktop.
Automatic registration of patterns within the "patterns" folder for active plugins, similar to how it works for themes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php // phpcs:ignore Class file names should be based on the class name with "class-" prepended. | |
| // Exit if accessed directly. | |
| if ( ! defined( 'ABSPATH' ) ) { | |
| exit; | |
| } | |
| /** | |
| * WP_Plugin_Pattern Class | |
| * | |
| * Handles the management of block patterns for the plugin. | |
| */ | |
| final class WP_Plugin_Pattern { | |
| /** | |
| * Cache hash used for caching block patterns. | |
| * | |
| * @var string | |
| */ | |
| private $cache_hash; | |
| /** | |
| * Plugin directory path. | |
| * | |
| * @var string | |
| */ | |
| private $plugin_dir; | |
| /** | |
| * Plugin version. | |
| * | |
| * @var string | |
| */ | |
| private $plugin_version; | |
| /** | |
| * Constructor method to set the plugin directory, version, and cache hash. | |
| * | |
| * @param string $plugin_dir Plugin directory path. | |
| * @param string $plugin_version Plugin version. | |
| */ | |
| public function __construct( $plugin_dir, $plugin_version ) { | |
| $this->plugin_dir = rtrim( $plugin_dir, '/' ); // Ensure no trailing slash. | |
| $this->cache_hash = md5( $this->plugin_dir ); | |
| $this->plugin_version = $plugin_version; | |
| } | |
| /** | |
| * Gets block pattern data for the plugin. | |
| * | |
| * @return array Block pattern data. | |
| */ | |
| public function get_block_patterns() { | |
| $can_use_cached = ! wp_is_development_mode( 'plugin' ); | |
| $pattern_data = $this->get_pattern_cache(); | |
| if ( is_array( $pattern_data ) ) { | |
| if ( $can_use_cached ) { | |
| return $pattern_data; | |
| } | |
| // Clear pattern cache if in development mode. | |
| $this->delete_pattern_cache(); | |
| } | |
| $dirpath = $this->plugin_dir . '/patterns/'; | |
| $pattern_data = array(); | |
| if ( ! file_exists( $dirpath ) ) { | |
| if ( $can_use_cached ) { | |
| $this->set_pattern_cache( $pattern_data ); | |
| } | |
| return $pattern_data; | |
| } | |
| $files = glob( $dirpath . '*.php' ); | |
| if ( ! $files ) { | |
| if ( $can_use_cached ) { | |
| $this->set_pattern_cache( $pattern_data ); | |
| } | |
| return $pattern_data; | |
| } | |
| $default_headers = array( | |
| 'title' => 'Title', | |
| 'slug' => 'Slug', | |
| 'description' => 'Description', | |
| 'viewportWidth' => 'Viewport Width', | |
| 'inserter' => 'Inserter', | |
| 'categories' => 'Categories', | |
| 'keywords' => 'Keywords', | |
| 'blockTypes' => 'Block Types', | |
| 'postTypes' => 'Post Types', | |
| 'templateTypes' => 'Template Types', | |
| ); | |
| $properties_to_parse = array( | |
| 'categories', | |
| 'keywords', | |
| 'blockTypes', | |
| 'postTypes', | |
| 'templateTypes', | |
| ); | |
| foreach ( $files as $file ) { | |
| $pattern = get_file_data( $file, $default_headers ); | |
| if ( empty( $pattern['slug'] ) ) { | |
| _doing_it_wrong( | |
| __FUNCTION__, | |
| sprintf( | |
| /* translators: 1: file name. */ | |
| __( 'Could not register file "%s" as a block pattern ("Slug" field missing)' ), | |
| $file | |
| ), | |
| '6.0.0' | |
| ); | |
| continue; | |
| } | |
| if ( ! preg_match( '/^[A-z0-9\/_-]+$/', $pattern['slug'] ) ) { | |
| _doing_it_wrong( | |
| __FUNCTION__, | |
| sprintf( | |
| /* translators: 1: file name; 2: slug value found. */ | |
| __( 'Could not register file "%1$s" as a block pattern (invalid slug "%2$s")' ), | |
| $file, | |
| $pattern['slug'] | |
| ), | |
| '6.0.0' | |
| ); | |
| } | |
| // Title is a required property. | |
| if ( ! $pattern['title'] ) { | |
| _doing_it_wrong( | |
| __FUNCTION__, | |
| sprintf( | |
| /* translators: 1: file name. */ | |
| __( 'Could not register file "%s" as a block pattern ("Title" field missing)' ), | |
| $file | |
| ), | |
| '6.0.0' | |
| ); | |
| continue; | |
| } | |
| // Parse comma-separated properties as arrays. | |
| foreach ( $properties_to_parse as $property ) { | |
| if ( ! empty( $pattern[ $property ] ) ) { | |
| $pattern[ $property ] = array_filter( wp_parse_list( (string) $pattern[ $property ] ) ); | |
| } else { | |
| unset( $pattern[ $property ] ); | |
| } | |
| } | |
| // Parse integer properties. | |
| $property = 'viewportWidth'; | |
| if ( ! empty( $pattern[ $property ] ) ) { | |
| $pattern[ $property ] = (int) $pattern[ $property ]; | |
| } else { | |
| unset( $pattern[ $property ] ); | |
| } | |
| // Parse boolean properties. | |
| $property = 'inserter'; | |
| if ( ! empty( $pattern[ $property ] ) ) { | |
| $pattern[ $property ] = in_array( | |
| strtolower( $pattern[ $property ] ), | |
| array( 'yes', 'true' ), | |
| true | |
| ); | |
| } else { | |
| unset( $pattern[ $property ] ); | |
| } | |
| $key = str_replace( $dirpath, '', $file ); | |
| $pattern_data[ $key ] = $pattern; | |
| } | |
| if ( $can_use_cached ) { | |
| $this->set_pattern_cache( $pattern_data ); | |
| } | |
| return $pattern_data; | |
| } | |
| /** | |
| * Gets block pattern cache for the plugin. | |
| * | |
| * @return array|false Cached pattern data or false if not found. | |
| */ | |
| private function get_pattern_cache() { | |
| $pattern_data = get_site_transient( $this->get_cache_key() ); | |
| if ( is_array( $pattern_data ) && $pattern_data['version'] === $this->plugin_version ) { | |
| return $pattern_data['patterns']; | |
| } | |
| return false; | |
| } | |
| /** | |
| * Sets block pattern cache for the plugin. | |
| * | |
| * @param array $patterns Block pattern data to cache. | |
| */ | |
| private function set_pattern_cache( array $patterns ) { | |
| $pattern_data = array( | |
| 'version' => $this->plugin_version, | |
| 'patterns' => $patterns, | |
| ); | |
| $cache_expiration = (int) apply_filters( 'wp_plugin_files_cache_ttl', DAY_IN_SECONDS ); | |
| set_site_transient( $this->get_cache_key(), $pattern_data, $cache_expiration ); | |
| } | |
| /** | |
| * Clears block pattern cache for the plugin. | |
| */ | |
| public function delete_pattern_cache() { | |
| delete_site_transient( $this->get_cache_key() ); | |
| } | |
| /** | |
| * Generates the cache key based on the plugin's cache hash. | |
| * | |
| * @return string Cache key for block patterns. | |
| */ | |
| private function get_cache_key() { | |
| return 'wp_plugin_files_patterns-' . $this->cache_hash; | |
| } | |
| } | |
| /** | |
| * Register any patterns that active plugins may provide under their | |
| * `./patterns/` directory. | |
| * | |
| * @since 1.0.0 | |
| * @access private | |
| */ | |
| function _register_plugin_block_patterns() { | |
| if ( ! function_exists( 'get_plugin_data' ) ) { | |
| require_once ABSPATH . 'wp-admin/includes/plugin.php'; | |
| } | |
| /* | |
| * Retrieve the list of active plugins. | |
| */ | |
| $plugins = get_option( 'active_plugins' ); | |
| $registry = WP_Block_Patterns_Registry::get_instance(); | |
| foreach ( $plugins as $plugin_file ) { | |
| // Construct the full path to the plugin directory. | |
| $plugin_dir = WP_PLUGIN_DIR . '/' . dirname( $plugin_file ); | |
| $dirpath = $plugin_dir . '/patterns/'; | |
| // Check if the patterns directory exists. | |
| if ( ! is_dir( $dirpath ) ) { | |
| continue; // Skip this plugin if the patterns directory does not exist. | |
| } | |
| // Get the plugin metadata, such as version and text domain. | |
| $plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin_file ); | |
| $plugin_version = $plugin_data['Version']; | |
| $text_domain = $plugin_data['TextDomain']; | |
| // Initialize the WP_Plugin_Pattern class with the full plugin path and version. | |
| $plugin_pattern_manager = new WP_Plugin_Pattern( $plugin_dir, $plugin_version ); | |
| // Get the block patterns from the plugin. | |
| $patterns = $plugin_pattern_manager->get_block_patterns(); | |
| foreach ( $patterns as $file => $pattern_data ) { | |
| // Check if the pattern is already registered. | |
| if ( $registry->is_registered( $pattern_data['slug'] ) ) { | |
| continue; | |
| } | |
| $file_path = $dirpath . $file; | |
| // Ensure the pattern file exists. | |
| if ( ! file_exists( $file_path ) ) { | |
| _doing_it_wrong( | |
| __FUNCTION__, | |
| sprintf( | |
| /* translators: %s: file name. */ | |
| __( 'Could not register file "%s" as a block pattern as the file does not exist.' ), | |
| $file | |
| ), | |
| '1.0.0' | |
| ); | |
| $plugin_pattern_manager->delete_pattern_cache(); | |
| continue; | |
| } | |
| $pattern_data['filePath'] = $file_path; | |
| // Translate the pattern metadata. | |
| // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain,WordPress.WP.I18n.LowLevelTranslationFunction | |
| $pattern_data['title'] = translate_with_gettext_context( $pattern_data['title'], 'Pattern title', $text_domain ); | |
| if ( ! empty( $pattern_data['description'] ) ) { | |
| // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralText,WordPress.WP.I18n.NonSingularStringLiteralDomain,WordPress.WP.I18n.LowLevelTranslationFunction | |
| $pattern_data['description'] = translate_with_gettext_context( $pattern_data['description'], 'Pattern description', $text_domain ); | |
| } | |
| // Register the block pattern. | |
| register_block_pattern( $pattern_data['slug'], $pattern_data ); | |
| } | |
| } | |
| } | |
| // Hook the function to the 'init' action. | |
| add_action( 'init', '_register_plugin_block_patterns' ); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment