PluginProbe
PatternsWP – Gutenberg Block Patterns & Page Templates Library / 1.1.0
PatternsWP – Gutenberg Block Patterns & Page Templates Library v1.1.0
1.1.0 trunk 1.0.0 1.0.1 1.0.10 1.0.2 1.0.3 1.0.4 1.0.5 1.0.6 1.0.7 1.0.8 1.0.9
← All changes | patternswp.php +210 -48 1.0.61.1.0 View file →
@@ -4,9 +4,9 @@
4 4 * Plugin URI: https://thepatternswp.com
5 5 * Description: A growing library of ready-made block patterns can help you build websites faster in no time.
6 6 * Author: PatternsWP
7 7 * Author URI: https://thepatternswp.com
8 - * Version: 1.0.6
8 + * Version: 1.1.0
9 9 * License: GPL-2.0+
10 10 * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
11 11 * Text Domain: patternswp
12 12 * Domain Path: /languages
@@ -19,9 +19,9 @@
19 19
20 20 // Define plugin constants.
21 21 define('PWP_PLUGIN_DIR', plugin_dir_path(__FILE__));
22 22 define('PWP_PLUGIN_URL', plugin_dir_url(__FILE__));
23 -define('PWP_P_VERSION', '1.0.6');
23 +define('PWP_P_VERSION', '1.1.0');
24 24 define('PWP_PLUGIN_FILE', __FILE__);
25 25 define('PWP_ABSPATH', dirname(__FILE__) . '/');
26 26 define('PWP_VERSION', get_file_data(__FILE__, ['Version'])[0]);
27 27
@@ -35,66 +35,159 @@
35 35 /**
36 36 * Enqueue assets for Block Editor.
37 37 */
38 38 function patternswp_enqueue_editor_assets() {
39 - $get_license_data = get_option('patternswp_plugin_license_data');
40 - $is_active = isset($get_license_data['activated']) ? $get_license_data['activated'] : false;
39 + $patternswp_api_section = PatternsWP_API_Section::get_instance();
41 40
42 - $script_asset = patternswp_get_asset_file('build/patternswp-editor');
41 + $script_path = 'assets/js/patternswp-editor.js';
42 + $script_deps = array(
43 + 'lodash',
44 + 'wp-a11y',
45 + 'wp-block-editor',
46 + 'wp-blocks',
47 + 'wp-components',
48 + 'wp-compose',
49 + 'wp-data',
50 + 'wp-dom-ready',
51 + 'wp-element',
52 + 'wp-i18n',
53 + 'wp-notices',
54 + 'wp-plugins',
55 + 'wp-editor',
56 + 'wp-primitives',
57 + );
58 +
43 59 wp_enqueue_script(
44 - 'patternswp-editor-scripts',
45 - PWP_PLUGIN_URL . 'build/patternswp-editor.js',
46 - array_merge($script_asset['dependencies'], ['wp-api']),
47 - $script_asset['version'],
60 + 'patternswp-editor-scripts',
61 + PWP_PLUGIN_URL . $script_path,
62 + $script_deps,
63 + patternswp_asset_version( $script_path ),
48 64 true
49 65 );
50 66
51 - $patternswp_api_section = new PatternsWP_API_Section();
52 - $localize_data = [
53 - 'isLicenseActive' => $is_active,
54 - 'externalPatterns' => [],
67 + $localize_data = array(
68 + 'isLicenseActive' => $patternswp_api_section->is_license_active(),
69 + 'externalPatterns' => array(),
55 70 'patternCategories' => $patternswp_api_section->get_patternswp_category_type(),
56 - 'patternsNonce' => wp_create_nonce('patternswp_nonce'),
57 - ];
71 + 'patternsNonce' => wp_create_nonce( 'patternswp_nonce' ),
72 + 'libraryComplete' => $patternswp_api_section->is_catalog_ready(),
73 + 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
74 + );
58 75
59 - wp_localize_script('patternswp-editor-scripts', 'patternsWpData', $localize_data);
76 + wp_localize_script( 'patternswp-editor-scripts', 'patternsWpData', $localize_data );
60 77
61 - $style_asset = patternswp_get_asset_file('build/block-pattern-inserter-editor-styles');
62 - wp_enqueue_style('patternswp-editor-styles', PWP_PLUGIN_URL . 'build/style-patternswp-editor-styles.css', [], $style_asset['version']);
78 + patternswp_register_editor_style();
79 + wp_enqueue_style( 'patternswp-editor-styles' );
63 80 }
64 -add_action('enqueue_block_editor_assets', 'patternswp_enqueue_editor_assets');
81 +add_action( 'enqueue_block_editor_assets', 'patternswp_enqueue_editor_assets' );
65 82
66 83 /**
67 - * Get asset file data.
84 + * Register shared editor stylesheet handle.
68 85 */
69 -function patternswp_get_asset_file($filepath) {
86 +function patternswp_register_editor_style() {
87 + $style_path = 'assets/css/patternswp-editor.css';
88 + wp_register_style(
89 + 'patternswp-editor-styles',
90 + PWP_PLUGIN_URL . $style_path,
91 + array( 'wp-components' ),
92 + patternswp_asset_version( $style_path )
93 + );
94 +}
95 +
96 +/**
97 + * Load styles into the iframed editor canvas (block content lives there in WP 6.3+).
98 + * enqueue_block_editor_assets alone only reaches the parent chrome / modal portals.
99 + */
100 +function patternswp_enqueue_canvas_styles() {
101 + if ( ! is_admin() ) {
102 + return;
103 + }
104 + patternswp_register_editor_style();
105 + wp_enqueue_style( 'patternswp-editor-styles' );
106 +}
107 +add_action( 'enqueue_block_assets', 'patternswp_enqueue_canvas_styles' );
108 +
109 +/**
110 + * Register the PatternsWP Pattern Library launcher block.
111 + *
112 + * Renders nothing on the front end — it is an editor-only entry point
113 + * for browsing and inserting patterns (similar to a pattern inserter).
114 + */
115 +function patternswp_register_blocks() {
116 + patternswp_register_editor_style();
117 +
118 + register_block_type(
119 + 'patternswp/library',
120 + array(
121 + 'api_version' => 3,
122 + 'title' => __( 'PatternsWP Pattern Library', 'patternswp' ),
123 + 'description' => __( 'Browse the PatternsWP pattern library and insert patterns into your page.', 'patternswp' ),
124 + 'category' => 'widgets',
125 + 'icon' => 'layout',
126 + 'keywords' => array( 'patternswp', 'patterns', 'library', 'templates', 'blocks' ),
127 + 'supports' => array(
128 + 'html' => false,
129 + 'multiple' => true,
130 + 'reusable' => false,
131 + ),
132 + 'editor_style' => 'patternswp-editor-styles',
133 + 'render_callback' => '__return_empty_string',
134 + )
135 + );
136 +}
137 +add_action( 'init', 'patternswp_register_blocks' );
138 +
139 +/**
140 + * Get asset file data (legacy build/*.asset.php support).
141 + */
142 +function patternswp_get_asset_file( $filepath ) {
70 143 $asset_path = PWP_ABSPATH . $filepath . '.asset.php';
71 - return file_exists($asset_path) ? require_once $asset_path : ['dependencies' => [], 'version' => PWP_VERSION];
144 + return file_exists( $asset_path ) ? require $asset_path : array(
145 + 'dependencies' => array(),
146 + 'version' => PWP_VERSION,
147 + );
72 148 }
73 149
150 +/**
151 + * Cache-busting version for ready-to-use assets.
152 + *
153 + * @param string $relative_path Path relative to the plugin root.
154 + * @return string
155 + */
156 +function patternswp_asset_version( $relative_path ) {
157 + $absolute = PWP_PLUGIN_DIR . ltrim( $relative_path, '/' );
158 + if ( file_exists( $absolute ) ) {
159 + return (string) filemtime( $absolute );
160 + }
161 + return PWP_VERSION;
162 +}
163 +
74 164 // Plugin activation hook.
75 -register_activation_hook(__FILE__, 'patternswp_schedule_hourly_cron');
165 +register_activation_hook(__FILE__, 'patternswp_schedule_daily_cron');
76 166
77 -function patternswp_schedule_hourly_cron() {
78 - if ( !wp_next_scheduled( 'patternswp_hourly_transient_load' ) ) {
79 - wp_schedule_event( time(), 'hourly', 'patternswp_hourly_transient_load' );
167 +function patternswp_schedule_daily_cron() {
168 + wp_clear_scheduled_hook( 'patternswp_hourly_transient_load' );
169 + if ( ! wp_next_scheduled( 'patternswp_daily_transient_load' ) ) {
170 + wp_schedule_event( time() + DAY_IN_SECONDS, 'daily', 'patternswp_daily_transient_load' );
80 171 }
172 + if ( ! wp_next_scheduled( 'patternswp_library_warm' ) ) {
173 + wp_schedule_single_event( time() + 3, 'patternswp_library_warm' );
174 + }
81 175 }
82 176
83 177 // Plugin deactivation hook.
84 -register_deactivation_hook(__FILE__, 'patternswp_remove_hourly_cron');
178 +register_deactivation_hook(__FILE__, 'patternswp_remove_daily_cron');
85 179
86 -function patternswp_remove_hourly_cron() {
87 - $timestamp = wp_next_scheduled( 'patternswp_hourly_transient_load' );
88 - if ( $timestamp ) {
89 - wp_unschedule_event( $timestamp, 'patternswp_hourly_transient_load' );
90 - }
180 +function patternswp_remove_daily_cron() {
181 + wp_clear_scheduled_hook( 'patternswp_hourly_transient_load' );
182 + wp_clear_scheduled_hook( 'patternswp_daily_transient_load' );
183 + wp_clear_scheduled_hook( 'patternswp_library_warm' );
91 184 }
92 185
93 186 /**
94 187 * AJAX handler to fetch patterns.
95 188 */
96 -function fetch_patterns_handler() {
189 +function patternswp_fetch_patterns_handler() {
97 190
98 191 // Verify nonce
99 192 if (!isset($_POST['nonce'])) {
100 193 wp_send_json_error('Missing nonce');
@@ -105,10 +198,17 @@
105 198
106 199 if (!wp_verify_nonce($nonce, 'patternswp_nonce')) {
107 200 wp_send_json_error('Invalid nonce');
108 201 wp_die();
109 - }
202 + }
110 203
204 + if (!current_user_can('edit_posts')) {
205 + wp_send_json_error('Forbidden');
206 + wp_die();
207 + }
208 +
209 + patternswp_release_ajax_lock();
210 +
111 211 if (!isset($_POST['page']) || !isset($_POST['patternsPerPage'])) {
112 212 wp_send_json_error('Missing parameters');
113 213 wp_die();
114 214 }
@@ -117,21 +217,18 @@
117 217 $patterns_per_page = intval($_POST['patternsPerPage']);
118 218 $search = sanitize_text_field(wp_unslash($_POST['search'] ?? ''));
119 219 $category = sanitize_text_field(wp_unslash($_POST['category'] ?? ''));
120 220
121 - $cache_key = 'patternswp_' . md5("$page-$patterns_per_page-$search-$category");
122 - $cached_data = get_transient($cache_key);
221 + $patternswp_api_section = PatternsWP_API_Section::get_instance();
123 222
124 - if ($cached_data !== false) {
125 - wp_send_json_success($cached_data);
223 + try {
224 + $localize_data_ajax = $patternswp_api_section->query_library_page($page, $patterns_per_page, $search, $category);
225 + } catch ( \Throwable $e ) {
226 + wp_send_json_error( 'Failed to fetch patterns' );
126 227 wp_die();
127 228 }
128 229
129 - $patternswp_api_section = new PatternsWP_API_Section();
130 - $localize_data_ajax = $patternswp_api_section->get_patternswp_pattern($page, $patterns_per_page, $search, $category);
131 -
132 230 if (is_array($localize_data_ajax)) {
133 - set_transient($cache_key, $localize_data_ajax, 3600);
134 231 wp_send_json_success($localize_data_ajax);
135 232 } else {
136 233 wp_send_json_error('Failed to fetch patterns');
137 234 }
@@ -137,18 +234,71 @@
137 234 }
138 235
139 236 wp_die();
140 237 }
141 -add_action('wp_ajax_fetch_patterns', 'fetch_patterns_handler');
142 -add_action('wp_ajax_nopriv_fetch_patterns', 'fetch_patterns_handler');
238 +add_action('wp_ajax_fetch_patterns', 'patternswp_fetch_patterns_handler');
143 239
144 240 /**
241 + * AJAX: continue downloading the catalog in short bursts.
242 + */
243 +function patternswp_warm_library_handler() {
244 + if (!isset($_POST['nonce'])) {
245 + wp_send_json_error('Missing nonce');
246 + }
247 +
248 + $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
249 + if (!wp_verify_nonce($nonce, 'patternswp_nonce')) {
250 + wp_send_json_error('Invalid nonce');
251 + }
252 +
253 + if (!current_user_can('edit_posts')) {
254 + wp_send_json_error('Forbidden');
255 + }
256 +
257 + patternswp_release_ajax_lock();
258 +
259 + $api = PatternsWP_API_Section::get_instance();
260 + $mode = isset($_POST['mode']) ? sanitize_key(wp_unslash($_POST['mode'])) : 'sync';
261 + $force = ! empty($_POST['force']);
262 +
263 + try {
264 + if ( 'check' === $mode ) {
265 + $status = $api->maybe_pull_remote_updates( $force );
266 + } else {
267 + $status = $api->warm_library_for_request();
268 + }
269 + } catch ( \Throwable $e ) {
270 + wp_send_json_error('Failed to refresh patterns');
271 + }
272 +
273 + wp_send_json_success(is_array($status) ? $status : array());
274 +}
275 +add_action('wp_ajax_patternswp_warm_library', 'patternswp_warm_library_handler');
276 +
277 +/**
278 + * Warm the library after install or plugin update.
279 + */
280 +function patternswp_maybe_upgrade_library() {
281 + if (!is_admin()) {
282 + return;
283 + }
284 +
285 + $installed = get_option('patternswp_installed_version', '');
286 + if ($installed === PWP_P_VERSION) {
287 + return;
288 + }
289 +
290 + update_option('patternswp_installed_version', PWP_P_VERSION, false);
291 + PatternsWP_API_Section::get_instance()->maybe_schedule_warm();
292 +}
293 +add_action('admin_init', 'patternswp_maybe_upgrade_library', 5);
294 +
295 +/**
145 296 * Add plugin action links.
146 297 */
147 298 function patternswp_plugin_action_links($links, $file) {
148 299 if ($file === plugin_basename(__FILE__)) {
149 - $get_license_data = get_option('patternswp_plugin_license_data');
150 - $is_active = isset($get_license_data['activated']) ? $get_license_data['activated'] : false;
300 + $is_active = PatternsWP_API_Section::get_instance()->is_license_active();
151 301
152 302 $support_link = '<a href="https://thepatternswp.com/contact/" target="_blank">Support</a>';
153 303 array_unshift($links, $support_link);
154 304
@@ -166,9 +316,21 @@
166 316 */
167 317 function patternswp_add_plugin_meta_links($links, $file) {
168 318 if ($file === plugin_basename(__FILE__)) {
169 319 $links[] = '<a href="https://thepatternswp.com/suggest-feature" target="_blank">Suggest a Feature</a>';
170 - $links[] = '<a href="https://wordpress.org/support/plugin/patternswp/reviews/?filter=5" target="_blank">Rate Us</a>';
171 320 }
172 321 return $links;
173 322 }
174 -add_filter('plugin_row_meta', 'patternswp_add_plugin_meta_links', 10, 2);
323 +add_filter('plugin_row_meta', 'patternswp_add_plugin_meta_links', 10, 2);
324 +
325 +/**
326 + * Let long catalog AJAX run without blocking other editor requests.
327 + */
328 +function patternswp_release_ajax_lock() {
329 + if ( function_exists( 'session_status' ) && PHP_SESSION_ACTIVE === session_status() ) {
330 + session_write_close();
331 + return;
332 + }
333 + if ( session_id() ) {
334 + session_write_close();
335 + }
336 +}