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 +318 -7 1.0.11.1.0 View file →
@@ -2,11 +2,11 @@
2 2 /**
3 3 * Plugin Name: PatternsWP
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 - * Author: PatternsWP Team
6 + * Author: PatternsWP
7 7 * Author URI: https://thepatternswp.com
8 - * Version: 1.0.1
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
@@ -12,14 +12,325 @@
12 12 * Domain Path: /languages
13 13 */
14 14
15 15 // If this file is called directly, abort.
16 -if ( ! defined( 'WPINC' ) ) {
17 - die;
16 +if (!defined('WPINC')) {
17 + die;
18 18 }
19 19
20 +// Define plugin constants.
21 +define('PWP_PLUGIN_DIR', plugin_dir_path(__FILE__));
22 +define('PWP_PLUGIN_URL', plugin_dir_url(__FILE__));
23 +define('PWP_P_VERSION', '1.1.0');
24 +define('PWP_PLUGIN_FILE', __FILE__);
25 +define('PWP_ABSPATH', dirname(__FILE__) . '/');
26 +define('PWP_VERSION', get_file_data(__FILE__, ['Version'])[0]);
20 27
28 +define('PATTERSWP_PLUGIN_API_URL', 'https://api.lemonsqueezy.com/v1/licenses');
29 +
30 +// Include necessary files.
31 +require_once PWP_PLUGIN_DIR . 'includes/class-patternswp-admin.php';
32 +require_once PWP_PLUGIN_DIR . 'includes/class-patternswp-api.php';
33 +require_once PWP_PLUGIN_DIR . 'includes/lib/class-patterns-license.php';
34 +
21 35 /**
22 - * The core plugin class.
36 + * Enqueue assets for Block Editor.
23 37 */
24 -require plugin_dir_path( __FILE__ ) . 'inc/register-block-patterns.php';
25 -require plugin_dir_path( __FILE__ ) . 'inc/register-block-categories.php';
38 +function patternswp_enqueue_editor_assets() {
39 + $patternswp_api_section = PatternsWP_API_Section::get_instance();
40 +
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 +
59 + wp_enqueue_script(
60 + 'patternswp-editor-scripts',
61 + PWP_PLUGIN_URL . $script_path,
62 + $script_deps,
63 + patternswp_asset_version( $script_path ),
64 + true
65 + );
66 +
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 + );
75 +
76 + wp_localize_script( 'patternswp-editor-scripts', 'patternsWpData', $localize_data );
77 +
78 + patternswp_register_editor_style();
79 + wp_enqueue_style( 'patternswp-editor-styles' );
80 +}
81 +add_action( 'enqueue_block_editor_assets', 'patternswp_enqueue_editor_assets' );
82 +
83 +/**
84 + * Register shared editor stylesheet handle.
85 + */
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 ) {
143 + $asset_path = PWP_ABSPATH . $filepath . '.asset.php';
144 + return file_exists( $asset_path ) ? require $asset_path : array(
145 + 'dependencies' => array(),
146 + 'version' => PWP_VERSION,
147 + );
148 +}
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 +
164 +// Plugin activation hook.
165 +register_activation_hook(__FILE__, 'patternswp_schedule_daily_cron');
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 +
177 +// Plugin deactivation hook.
178 +register_deactivation_hook(__FILE__, 'patternswp_remove_daily_cron');
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 +
186 +/**
187 + * AJAX handler to fetch patterns.
188 + */
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 +
211 + if (!isset($_POST['page']) || !isset($_POST['patternsPerPage'])) {
212 + wp_send_json_error('Missing parameters');
213 + wp_die();
214 + }
215 +
216 + $page = intval($_POST['page']);
217 + $patterns_per_page = intval($_POST['patternsPerPage']);
218 + $search = sanitize_text_field(wp_unslash($_POST['search'] ?? ''));
219 + $category = sanitize_text_field(wp_unslash($_POST['category'] ?? ''));
220 +
221 + $patternswp_api_section = PatternsWP_API_Section::get_instance();
222 +
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' );
227 + wp_die();
228 + }
229 +
230 + if (is_array($localize_data_ajax)) {
231 + wp_send_json_success($localize_data_ajax);
232 + } else {
233 + wp_send_json_error('Failed to fetch patterns');
234 + }
235 +
236 + wp_die();
237 +}
238 +add_action('wp_ajax_fetch_patterns', 'patternswp_fetch_patterns_handler');
239 +
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 +/**
296 + * Add plugin action links.
297 + */
298 +function patternswp_plugin_action_links($links, $file) {
299 + if ($file === plugin_basename(__FILE__)) {
300 + $is_active = PatternsWP_API_Section::get_instance()->is_license_active();
301 +
302 + $support_link = '<a href="https://thepatternswp.com/contact/" target="_blank">Support</a>';
303 + array_unshift($links, $support_link);
304 +
305 + if (!$is_active) {
306 + $upgrade_link = '<a href="https://thepatternswp.com/pricing/" target="_blank">Upgrade to Pro</a>';
307 + array_unshift($links, $upgrade_link);
308 + }
309 + }
310 + return $links;
311 +}
312 +add_filter('plugin_action_links', 'patternswp_plugin_action_links', 10, 2);
313 +
314 +/**
315 + * Add custom plugin meta links.
316 + */
317 +function patternswp_add_plugin_meta_links($links, $file) {
318 + if ($file === plugin_basename(__FILE__)) {
319 + $links[] = '<a href="https://thepatternswp.com/suggest-feature" target="_blank">Suggest a Feature</a>';
320 + }
321 + return $links;
322 +}
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 +}