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 +229 -39 1.0.51.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.5
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.5');
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,52 +35,180 @@
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' => [],
55 - 'patternCategories' => $patternswp_api_section->get_patternswp_category_type()
56 - ];
67 + $localize_data = array(
68 + 'isLicenseActive' => $patternswp_api_section->is_license_active(),
69 + 'externalPatterns' => array(),
70 + 'patternCategories' => $patternswp_api_section->get_patternswp_category_type(),
71 + 'patternsNonce' => wp_create_nonce( 'patternswp_nonce' ),
72 + 'libraryComplete' => $patternswp_api_section->is_catalog_ready(),
73 + 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
74 + );
57 75
58 - wp_localize_script('patternswp-editor-scripts', 'patternsWpData', $localize_data);
76 + wp_localize_script( 'patternswp-editor-scripts', 'patternsWpData', $localize_data );
59 77
60 - $style_asset = patternswp_get_asset_file('build/block-pattern-inserter-editor-styles');
61 - 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' );
62 80 }
63 -add_action('enqueue_block_editor_assets', 'patternswp_enqueue_editor_assets');
81 +add_action( 'enqueue_block_editor_assets', 'patternswp_enqueue_editor_assets' );
64 82
65 83 /**
66 - * Get asset file data.
84 + * Register shared editor stylesheet handle.
67 85 */
68 -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 ) {
69 143 $asset_path = PWP_ABSPATH . $filepath . '.asset.php';
70 - 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 + );
71 148 }
72 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 +
73 164 // Plugin activation hook.
74 -register_activation_hook(__FILE__, function() {});
165 +register_activation_hook(__FILE__, 'patternswp_schedule_daily_cron');
75 166
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' );
171 + }
172 + if ( ! wp_next_scheduled( 'patternswp_library_warm' ) ) {
173 + wp_schedule_single_event( time() + 3, 'patternswp_library_warm' );
174 + }
175 +}
176 +
76 177 // Plugin deactivation hook.
77 -register_deactivation_hook(__FILE__, function() {});
178 +register_deactivation_hook(__FILE__, 'patternswp_remove_daily_cron');
78 179
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' );
184 +}
185 +
79 186 /**
80 187 * AJAX handler to fetch patterns.
81 188 */
82 -function fetch_patterns_handler() {
189 +function patternswp_fetch_patterns_handler() {
190 +
191 + // Verify nonce
192 + if (!isset($_POST['nonce'])) {
193 + wp_send_json_error('Missing nonce');
194 + wp_die();
195 + }
196 +
197 + $nonce = sanitize_text_field(wp_unslash($_POST['nonce']));
198 +
199 + if (!wp_verify_nonce($nonce, 'patternswp_nonce')) {
200 + wp_send_json_error('Invalid nonce');
201 + wp_die();
202 + }
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 +
83 211 if (!isset($_POST['page']) || !isset($_POST['patternsPerPage'])) {
84 212 wp_send_json_error('Missing parameters');
85 213 wp_die();
86 214 }
@@ -89,21 +217,18 @@
89 217 $patterns_per_page = intval($_POST['patternsPerPage']);
90 218 $search = sanitize_text_field(wp_unslash($_POST['search'] ?? ''));
91 219 $category = sanitize_text_field(wp_unslash($_POST['category'] ?? ''));
92 220
93 - $cache_key = 'patternswp_' . md5("$page-$patterns_per_page-$search-$category");
94 - $cached_data = get_transient($cache_key);
221 + $patternswp_api_section = PatternsWP_API_Section::get_instance();
95 222
96 - if ($cached_data !== false) {
97 - 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' );
98 227 wp_die();
99 228 }
100 229
101 - $patternswp_api_section = new PatternsWP_API_Section();
102 - $localize_data_ajax = $patternswp_api_section->get_patternswp_pattern($page, $patterns_per_page, $search, $category);
103 -
104 230 if (is_array($localize_data_ajax)) {
105 - set_transient($cache_key, $localize_data_ajax, 3600);
106 231 wp_send_json_success($localize_data_ajax);
107 232 } else {
108 233 wp_send_json_error('Failed to fetch patterns');
109 234 }
@@ -109,18 +234,71 @@
109 234 }
110 235
111 236 wp_die();
112 237 }
113 -add_action('wp_ajax_fetch_patterns', 'fetch_patterns_handler');
114 -add_action('wp_ajax_nopriv_fetch_patterns', 'fetch_patterns_handler');
238 +add_action('wp_ajax_fetch_patterns', 'patternswp_fetch_patterns_handler');
115 239
116 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 +/**
117 296 * Add plugin action links.
118 297 */
119 298 function patternswp_plugin_action_links($links, $file) {
120 299 if ($file === plugin_basename(__FILE__)) {
121 - $get_license_data = get_option('patternswp_plugin_license_data');
122 - $is_active = isset($get_license_data['activated']) ? $get_license_data['activated'] : false;
300 + $is_active = PatternsWP_API_Section::get_instance()->is_license_active();
123 301
124 302 $support_link = '<a href="https://thepatternswp.com/contact/" target="_blank">Support</a>';
125 303 array_unshift($links, $support_link);
126 304
@@ -138,9 +316,21 @@
138 316 */
139 317 function patternswp_add_plugin_meta_links($links, $file) {
140 318 if ($file === plugin_basename(__FILE__)) {
141 319 $links[] = '<a href="https://thepatternswp.com/suggest-feature" target="_blank">Suggest a Feature</a>';
142 - $links[] = '<a href="https://wordpress.org/support/plugin/patternswp/reviews/?filter=5" target="_blank">Rate Us</a>';
143 320 }
144 321 return $links;
145 322 }
146 -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 +}