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
patternswp / patternswp.php

patternswp.php in PatternsWP – Gutenberg Block Patterns & Page Templates Library 1.1.0, at patternswp.php

337 lines 10.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: PatternsWP
4 * Plugin URI: https://thepatternswp.com
5 * Description: A growing library of ready-made block patterns can help you build websites faster in no time.
6 * Author: PatternsWP
7 * Author URI: https://thepatternswp.com
8 * Version: 1.1.0
9 * License: GPL-2.0+
10 * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
11 * Text Domain: patternswp
12 * Domain Path: /languages
13 */
14
15 // If this file is called directly, abort.
16 if (!defined('WPINC')) {
17 die;
18 }
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]);
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
35 /**
36 * Enqueue assets for Block Editor.
37 */
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 }
337