$patternswp_api_section->is_license_active(), 'externalPatterns' => array(), 'patternCategories' => $patternswp_api_section->get_patternswp_category_type(), 'patternsNonce' => wp_create_nonce( 'patternswp_nonce' ), 'libraryComplete' => $patternswp_api_section->is_catalog_ready(), 'ajaxUrl' => admin_url( 'admin-ajax.php' ), ); wp_localize_script( 'patternswp-editor-scripts', 'patternsWpData', $localize_data ); patternswp_register_editor_style(); wp_enqueue_style( 'patternswp-editor-styles' ); } add_action( 'enqueue_block_editor_assets', 'patternswp_enqueue_editor_assets' ); /** * Register shared editor stylesheet handle. */ function patternswp_register_editor_style() { $style_path = 'assets/css/patternswp-editor.css'; wp_register_style( 'patternswp-editor-styles', PWP_PLUGIN_URL . $style_path, array( 'wp-components' ), patternswp_asset_version( $style_path ) ); } /** * Load styles into the iframed editor canvas (block content lives there in WP 6.3+). * enqueue_block_editor_assets alone only reaches the parent chrome / modal portals. */ function patternswp_enqueue_canvas_styles() { if ( ! is_admin() ) { return; } patternswp_register_editor_style(); wp_enqueue_style( 'patternswp-editor-styles' ); } add_action( 'enqueue_block_assets', 'patternswp_enqueue_canvas_styles' ); /** * Register the PatternsWP Pattern Library launcher block. * * Renders nothing on the front end — it is an editor-only entry point * for browsing and inserting patterns (similar to a pattern inserter). */ function patternswp_register_blocks() { patternswp_register_editor_style(); register_block_type( 'patternswp/library', array( 'api_version' => 3, 'title' => __( 'PatternsWP Pattern Library', 'patternswp' ), 'description' => __( 'Browse the PatternsWP pattern library and insert patterns into your page.', 'patternswp' ), 'category' => 'widgets', 'icon' => 'layout', 'keywords' => array( 'patternswp', 'patterns', 'library', 'templates', 'blocks' ), 'supports' => array( 'html' => false, 'multiple' => true, 'reusable' => false, ), 'editor_style' => 'patternswp-editor-styles', 'render_callback' => '__return_empty_string', ) ); } add_action( 'init', 'patternswp_register_blocks' ); /** * Get asset file data (legacy build/*.asset.php support). */ function patternswp_get_asset_file( $filepath ) { $asset_path = PWP_ABSPATH . $filepath . '.asset.php'; return file_exists( $asset_path ) ? require $asset_path : array( 'dependencies' => array(), 'version' => PWP_VERSION, ); } /** * Cache-busting version for ready-to-use assets. * * @param string $relative_path Path relative to the plugin root. * @return string */ function patternswp_asset_version( $relative_path ) { $absolute = PWP_PLUGIN_DIR . ltrim( $relative_path, '/' ); if ( file_exists( $absolute ) ) { return (string) filemtime( $absolute ); } return PWP_VERSION; } // Plugin activation hook. register_activation_hook(__FILE__, 'patternswp_schedule_daily_cron'); function patternswp_schedule_daily_cron() { wp_clear_scheduled_hook( 'patternswp_hourly_transient_load' ); if ( ! wp_next_scheduled( 'patternswp_daily_transient_load' ) ) { wp_schedule_event( time() + DAY_IN_SECONDS, 'daily', 'patternswp_daily_transient_load' ); } if ( ! wp_next_scheduled( 'patternswp_library_warm' ) ) { wp_schedule_single_event( time() + 3, 'patternswp_library_warm' ); } } // Plugin deactivation hook. register_deactivation_hook(__FILE__, 'patternswp_remove_daily_cron'); function patternswp_remove_daily_cron() { wp_clear_scheduled_hook( 'patternswp_hourly_transient_load' ); wp_clear_scheduled_hook( 'patternswp_daily_transient_load' ); wp_clear_scheduled_hook( 'patternswp_library_warm' ); } /** * AJAX handler to fetch patterns. */ function patternswp_fetch_patterns_handler() { // Verify nonce if (!isset($_POST['nonce'])) { wp_send_json_error('Missing nonce'); wp_die(); } $nonce = sanitize_text_field(wp_unslash($_POST['nonce'])); if (!wp_verify_nonce($nonce, 'patternswp_nonce')) { wp_send_json_error('Invalid nonce'); wp_die(); } if (!current_user_can('edit_posts')) { wp_send_json_error('Forbidden'); wp_die(); } patternswp_release_ajax_lock(); if (!isset($_POST['page']) || !isset($_POST['patternsPerPage'])) { wp_send_json_error('Missing parameters'); wp_die(); } $page = intval($_POST['page']); $patterns_per_page = intval($_POST['patternsPerPage']); $search = sanitize_text_field(wp_unslash($_POST['search'] ?? '')); $category = sanitize_text_field(wp_unslash($_POST['category'] ?? '')); $patternswp_api_section = PatternsWP_API_Section::get_instance(); try { $localize_data_ajax = $patternswp_api_section->query_library_page($page, $patterns_per_page, $search, $category); } catch ( \Throwable $e ) { wp_send_json_error( 'Failed to fetch patterns' ); wp_die(); } if (is_array($localize_data_ajax)) { wp_send_json_success($localize_data_ajax); } else { wp_send_json_error('Failed to fetch patterns'); } wp_die(); } add_action('wp_ajax_fetch_patterns', 'patternswp_fetch_patterns_handler'); /** * AJAX: continue downloading the catalog in short bursts. */ function patternswp_warm_library_handler() { if (!isset($_POST['nonce'])) { wp_send_json_error('Missing nonce'); } $nonce = sanitize_text_field(wp_unslash($_POST['nonce'])); if (!wp_verify_nonce($nonce, 'patternswp_nonce')) { wp_send_json_error('Invalid nonce'); } if (!current_user_can('edit_posts')) { wp_send_json_error('Forbidden'); } patternswp_release_ajax_lock(); $api = PatternsWP_API_Section::get_instance(); $mode = isset($_POST['mode']) ? sanitize_key(wp_unslash($_POST['mode'])) : 'sync'; $force = ! empty($_POST['force']); try { if ( 'check' === $mode ) { $status = $api->maybe_pull_remote_updates( $force ); } else { $status = $api->warm_library_for_request(); } } catch ( \Throwable $e ) { wp_send_json_error('Failed to refresh patterns'); } wp_send_json_success(is_array($status) ? $status : array()); } add_action('wp_ajax_patternswp_warm_library', 'patternswp_warm_library_handler'); /** * Warm the library after install or plugin update. */ function patternswp_maybe_upgrade_library() { if (!is_admin()) { return; } $installed = get_option('patternswp_installed_version', ''); if ($installed === PWP_P_VERSION) { return; } update_option('patternswp_installed_version', PWP_P_VERSION, false); PatternsWP_API_Section::get_instance()->maybe_schedule_warm(); } add_action('admin_init', 'patternswp_maybe_upgrade_library', 5); /** * Add plugin action links. */ function patternswp_plugin_action_links($links, $file) { if ($file === plugin_basename(__FILE__)) { $is_active = PatternsWP_API_Section::get_instance()->is_license_active(); $support_link = 'Support'; array_unshift($links, $support_link); if (!$is_active) { $upgrade_link = 'Upgrade to Pro'; array_unshift($links, $upgrade_link); } } return $links; } add_filter('plugin_action_links', 'patternswp_plugin_action_links', 10, 2); /** * Add custom plugin meta links. */ function patternswp_add_plugin_meta_links($links, $file) { if ($file === plugin_basename(__FILE__)) { $links[] = 'Suggest a Feature'; } return $links; } add_filter('plugin_row_meta', 'patternswp_add_plugin_meta_links', 10, 2); /** * Let long catalog AJAX run without blocking other editor requests. */ function patternswp_release_ajax_lock() { if ( function_exists( 'session_status' ) && PHP_SESSION_ACTIVE === session_status() ) { session_write_close(); return; } if ( session_id() ) { session_write_close(); } }