PluginProbe
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages / 3.4.5
Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages v3.4.5
3.4.5 3.4.4 3.4.3 3.4.2 3.4.1 3.4.0 3.3.9 3.3.8 3.3.7 3.3.6 3.3.5 3.3.4 3.3.3 3.3.2 3.3.1 2.2.0 2.2.1 2.2.2 2.2.3 2.2.4 2.2.5 2.2.6 2.2.7 2.2.8 2.2.9 All 198 releases
convertkit / includes / class-convertkit-gutenberg.php

class-convertkit-gutenberg.php in Kit (formerly ConvertKit) – Email Newsletter, Email Marketing, Membership, Subscribers and Landing Pages 3.4.5, at includes/class-convertkit-gutenberg.php

479 lines 14.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * ConvertKit Gutenberg class.
4 *
5 * @package ConvertKit
6 * @author ConvertKit
7 */
8
9 /**
10 * Registers blocks defined in the `convertkit_blocks` filter in Gutenberg.
11 *
12 * @package ConvertKit
13 * @author ConvertKit
14 */
15 class ConvertKit_Gutenberg {
16
17 /**
18 * Constructor
19 *
20 * @since 1.9.6
21 */
22 public function __construct() {
23
24 // Register Gutenberg Block Categories and Blocks.
25 if ( get_bloginfo( 'version' ) >= 5.8 ) {
26 // Filter changed in 5.8.
27 add_filter( 'block_categories_all', array( $this, 'add_block_categories' ), 10, 2 );
28 } else {
29 add_filter( 'block_categories', array( $this, 'add_block_categories' ), 10, 2 );
30 }
31
32 // Register Gutenberg Blocks.
33 add_action( 'init', array( $this, 'add_blocks' ) );
34
35 // Register Gutenberg Plugin Sidebars.
36 add_action( 'init', array( $this, 'add_plugin_sidebars' ) );
37
38 // Register REST API routes.
39 add_action( 'rest_api_init', array( $this, 'register_routes' ) );
40
41 // Delete duplicate Post Meta rows before WordPress updates them.
42 add_filter( 'update_post_metadata', array( $this, 'maybe_delete_duplicate_post_meta' ), 10, 5 );
43
44 }
45
46 /**
47 * Deletes duplicate Post Meta rows for the Plugin's Post Meta key, before WordPress updates them,
48 * which have been created by third party Plugins, migrations, or other code.
49 *
50 * @since 3.4.5
51 *
52 * @param null $check Whether to short circuit the update.
53 * @param int $post_id Post ID.
54 * @param string $meta_key Meta key.
55 * @param mixed $meta_value Meta value.
56 * @param mixed $prev_value Previous meta value to update.
57 * @return null|bool Whether to short circuit the update.
58 */
59 public function maybe_delete_duplicate_post_meta( $check, $post_id, $meta_key, $meta_value, $prev_value ) {
60
61 // Bail if this isn't the Plugin's Post Meta key.
62 if ( $meta_key !== ConvertKit_Post::POST_META_KEY ) {
63 return $check;
64 }
65
66 // Bail if a previous value is specified, as WordPress will only update matching rows.
67 if ( ! empty( $prev_value ) ) {
68 return $check;
69 }
70
71 // Bail if no duplicate rows exist.
72 $convertkit_post = new ConvertKit_Post( $post_id );
73 $rows = $convertkit_post->get_meta_rows();
74 if ( count( $rows ) < 2 ) {
75 return $check;
76 }
77
78 // Delete all but the most recent row.
79 $row = array_pop( $rows );
80 foreach ( $rows as $duplicate_row ) {
81 delete_metadata_by_mid( 'post', $duplicate_row->meta_id );
82 }
83
84 // Restore the Post in the Member Content cache, as deleting the above rows removed it.
85 $value = maybe_unserialize( $row->meta_value );
86 WP_ConvertKit()->get_class( 'restrict_content_cache' )->on_meta_change( 0, $post_id, $meta_key, $value );
87
88 // If the remaining row already holds the value WordPress is about to store, tell WordPress
89 // the update succeeded, as there's nothing to change. Without this, WordPress' own check for
90 // a single unchanged row returns false, and the block editor still cannot save the Post.
91 if ( $value === $meta_value ) {
92 return true;
93 }
94
95 return $check;
96
97 }
98
99 /**
100 * Register REST API routes.
101 *
102 * @since 3.1.0
103 */
104 public function register_routes() {
105
106 // Register route to refresh resources andreturn all blocks registered by the Plugin,
107 // when the user clicks the refresh button in the Gutenberg editor.
108 register_rest_route(
109 'kit/v1',
110 '/blocks',
111 array(
112 'methods' => WP_REST_Server::READABLE,
113
114 // Return blocks.
115 'callback' => function () {
116 // Refresh Forms.
117 $forms = new ConvertKit_Resource_Forms( 'block_edit' );
118 $result = $forms->refresh();
119 if ( is_wp_error( $result ) ) {
120 // Return blocks without refreshing other resources.
121 return rest_ensure_response( convertkit_get_blocks() );
122 }
123
124 // Refresh Posts.
125 $posts = new ConvertKit_Resource_Posts( 'block_edit' );
126 $result = $posts->refresh();
127 if ( is_wp_error( $result ) ) {
128 // Return blocks without refreshing other resources.
129 return rest_ensure_response( convertkit_get_blocks() );
130 }
131
132 // Refresh Products.
133 $products = new ConvertKit_Resource_Products( 'block_edit' );
134 $result = $products->refresh();
135 if ( is_wp_error( $result ) ) {
136 // Return blocks without refreshing other resources.
137 return rest_ensure_response( convertkit_get_blocks() );
138 }
139
140 // Return blocks, which will now include the refreshed resources.
141 return rest_ensure_response( convertkit_get_blocks() );
142 },
143
144 // Only refresh resources for users who can edit posts.
145 'permission_callback' => function () {
146 return current_user_can( 'edit_posts' );
147 },
148 )
149 );
150
151 }
152
153 /**
154 * Registers the ConvertKit Block Category.
155 *
156 * @since 1.9.6
157 *
158 * @param array $categories Block Categories.
159 * @param WP_Post $post WordPress Post.
160 * @return array Block Categories
161 */
162 public function add_block_categories( $categories, $post ) {
163
164 // Define block categories.
165 $categories = array_merge(
166 $categories,
167 array(
168 array(
169 'slug' => 'convertkit',
170 'title' => 'Kit',
171 ),
172 )
173 );
174
175 /**
176 * Adds block categories to the default Gutenberg Block Categories
177 *
178 * @since 1.9.6
179 *
180 * @param array $categories Block Categories
181 * @param WP_Post $post WordPress Post
182 */
183 $categories = apply_filters( 'convertkit_admin_gutenberg_add_block_categories', $categories, $post );
184
185 // Return filtered results.
186 return $categories;
187
188 }
189
190 /**
191 * Registers Blocks, so that they can be used in the Gutenberg Editor
192 *
193 * @since 1.9.6
194 */
195 public function add_blocks() {
196
197 // Bail if Gutenberg isn't available.
198 if ( ! function_exists( 'register_block_type' ) ) {
199 return;
200 }
201
202 // Get blocks.
203 $blocks = convertkit_get_blocks();
204
205 // Bail if no blocks are available.
206 if ( ! count( $blocks ) ) {
207 return;
208 }
209
210 // Determine the block API version to use for registering blocks.
211 $block_api_version = $this->get_block_api_version();
212
213 // Get registered blocks.
214 $registered_blocks = array_keys( WP_Block_Type_Registry::get_instance()->get_all_registered() );
215
216 // Iterate through blocks, registering them.
217 foreach ( $blocks as $block => $properties ) {
218
219 // Skip if this block has already been registered.
220 if ( in_array( 'convertkit/' . $block, $registered_blocks, true ) ) {
221 continue;
222 }
223
224 // Register block.
225 register_block_type(
226 CONVERTKIT_PLUGIN_PATH . '/includes/blocks/v' . (string) $block_api_version . '/' . $block,
227 array(
228 'attributes' => $properties['attributes'],
229 'editor_script' => 'convertkit-gutenberg',
230 'render_callback' => array(
231 $properties['render_callback'][0],
232 $properties['render_callback'][1],
233 ),
234 )
235 );
236
237 }
238
239 // Enqueue block scripts and styles in the editor view.
240 add_action( 'admin_enqueue_scripts', array( $this, 'enqueue_scripts' ) );
241 add_action( 'enqueue_block_assets', array( $this, 'enqueue_styles' ) );
242
243 // Enqueue block scripts and styles in the editor and frontend views.
244 add_action( 'enqueue_block_assets', array( $this, 'enqueue_scripts_editor_and_frontend' ) );
245 add_action( 'enqueue_block_assets', array( $this, 'enqueue_styles_editor_and_frontend' ) );
246
247 }
248
249 /**
250 * Registers post meta for any registered plugin sidebars using register_post_meta(),
251 * so data is saved when using the Gutenberg editor.
252 *
253 * @since 3.3.0
254 */
255 public function add_plugin_sidebars() {
256
257 // Get plugin sidebars.
258 $plugin_sidebars = convertkit_get_plugin_sidebars();
259
260 // Bail if no plugin sidebars are available.
261 if ( ! count( $plugin_sidebars ) ) {
262 return;
263 }
264
265 foreach ( $plugin_sidebars as $plugin_sidebar ) {
266 register_post_meta(
267 '',
268 $plugin_sidebar['meta_key'],
269 array(
270 'show_in_rest' => array(
271 'schema' => array(
272 'type' => 'object',
273 'properties' => $plugin_sidebar['attributes'],
274 ),
275 ),
276 'single' => true,
277 'type' => 'object',
278 'default' => $plugin_sidebar['default_values'],
279 'sanitize_callback' => function ( $meta ) use ( $plugin_sidebar ) {
280
281 // If the value is not an array, return the default values.
282 if ( ! is_array( $meta ) ) {
283 return $plugin_sidebar['default_values'];
284 }
285
286 // Iterate through the attributes and sanitize the meta.
287 foreach ( $plugin_sidebar['attributes'] as $key => $attribute ) {
288 $meta[ $key ] = sanitize_text_field( $meta[ $key ] ?? $attribute['default'] );
289 }
290
291 // If a Form or Landing Page was specified, request a review.
292 // This can safely be called multiple times, as the review request
293 // class will ensure once a review request is dismissed by the user,
294 // it is never displayed again.
295 if ( $meta['form'] || $meta['landing_page'] ) {
296 WP_ConvertKit()->get_class( 'review_request' )->request_review();
297 }
298
299 // Return the sanitized meta.
300 return $meta;
301
302 },
303 'auth_callback' => function () use ( $plugin_sidebar ) {
304
305 return current_user_can( $plugin_sidebar['minimum_capability'] );
306
307 },
308 )
309 );
310 }
311
312 }
313
314 /**
315 * Determines the block API version to use for registering blocks.
316 *
317 * @since 3.2.0
318 *
319 * @return int Block API version.
320 */
321 public function get_block_api_version() {
322
323 // Determine the block API version to use for registering blocks.
324 // WordPress supports Version 3 from WordPress 6.3:
325 // https://developer.wordpress.org/block-editor/reference-guides/block-api/block-api-versions/.
326 $block_api_version = ( version_compare( get_bloginfo( 'version' ), '6.3', '>=' ) ? 3 : 2 );
327
328 /**
329 * Determine the block API version to use for registering blocks.
330 *
331 * @since 3.1.4
332 *
333 * @param int $block_api_version Block API version.
334 * @return int Block API version.
335 */
336 $block_api_version = apply_filters( 'convertkit_gutenberg_block_api_version', $block_api_version );
337
338 return absint( $block_api_version );
339
340 }
341
342 /**
343 * Enqueues scripts for Gutenberg blocks in the editor view.
344 *
345 * @since 1.9.6
346 */
347 public function enqueue_scripts() {
348
349 // Bail if request isn't for the Admin or a Frontend Editor.
350 if ( ! WP_ConvertKit()->is_admin_or_frontend_editor() ) {
351 return;
352 }
353
354 // Get settings.
355 $settings = new ConvertKit_Settings();
356
357 // Get blocks and block toolbar buttons.
358 $blocks = convertkit_get_blocks();
359 $block_formatters = convertkit_get_block_formatters();
360 $pre_publish_actions = convertkit_get_pre_publish_actions();
361 $plugin_sidebars = convertkit_get_plugin_sidebars();
362
363 // Enqueue Gutenberg Javascript, and set the blocks data.
364 wp_enqueue_script( 'convertkit-gutenberg', CONVERTKIT_PLUGIN_URL . 'resources/backend/js/gutenberg.js', array( 'jquery' ), CONVERTKIT_PLUGIN_VERSION, true );
365 wp_localize_script( 'convertkit-gutenberg', 'convertkit_blocks', $blocks );
366
367 // If pre-publish actions are available, set the data.
368 if ( count( $pre_publish_actions ) ) {
369 wp_localize_script( 'convertkit-gutenberg', 'convertkit_pre_publish_actions', $pre_publish_actions );
370 }
371
372 // If plugin sidebars are available, set the data.
373 if ( count( $plugin_sidebars ) ) {
374 wp_localize_script( 'convertkit-gutenberg', 'convertkit_plugin_sidebars', $plugin_sidebars );
375 }
376
377 // Set the Gutenberg data.
378 wp_localize_script(
379 'convertkit-gutenberg',
380 'convertkit_gutenberg',
381 array(
382 'ajaxurl' => rest_url( 'kit/v1/blocks' ),
383 'block_api_version' => $this->get_block_api_version(),
384 'get_blocks_nonce' => wp_create_nonce( 'wp_rest' ),
385 'refresh_resources_url' => rest_url( 'kit/v1/resources/refresh/' ),
386 'refresh_resources_nonce' => wp_create_nonce( 'wp_rest' ),
387 )
388 );
389
390 // Enqueue Gutenberg Block Toolbar Javascript, and set the block toolbar buttons data.
391 wp_enqueue_script( 'convertkit-gutenberg-block-formatters', CONVERTKIT_PLUGIN_URL . 'resources/backend/js/gutenberg-block-formatters.js', array( 'jquery' ), CONVERTKIT_PLUGIN_VERSION, true );
392 wp_localize_script( 'convertkit-gutenberg-block-formatters', 'convertkit_block_formatters', $block_formatters );
393
394 /**
395 * Enqueue any additional scripts for Gutenberg blocks that have been registered.
396 *
397 * @since 1.9.6.5
398 *
399 * @param array $blocks ConvertKit Blocks.
400 * @param array $block_formatters ConvertKit Block Formatters.
401 */
402 do_action( 'convertkit_gutenberg_enqueue_scripts', $blocks, $block_formatters );
403
404 }
405
406 /**
407 * Enqueues styles for Gutenberg blocks in the editor view.
408 *
409 * Use wp_enqueue_style() hooked to the enqueue_block_assets hook for frontend styles:
410 * https://developer.wordpress.org/block-editor/how-to-guides/block-tutorial/applying-styles-with-stylesheets/
411 *
412 * @since 1.9.6.9
413 */
414 public function enqueue_styles() {
415
416 // Bail if request isn't for the Admin.
417 if ( ! is_admin() ) {
418 return;
419 }
420
421 /**
422 * Enqueue styles for Gutenberg blocks that have been registered.
423 *
424 * @since 1.9.6.9
425 */
426 do_action( 'convertkit_gutenberg_enqueue_styles' );
427
428 }
429
430 /**
431 * Enqueues scripts for Gutenberg blocks in both the editor and frontend view,
432 * if the Plugin's Disable JavaScript option is not enabled.
433 *
434 * @since 1.9.7.6
435 */
436 public function enqueue_scripts_editor_and_frontend() {
437
438 // Don't load scripts if the Disable JS option is on.
439 $settings = new ConvertKit_Settings();
440 if ( $settings->scripts_disabled() ) {
441 return;
442 }
443
444 /**
445 * Enqueues scripts for Gutenberg blocks in both the editor and frontend view,
446 * if the Plugin's Disable JavaScript option is not enabled.
447 *
448 * @since 1.9.7.6
449 */
450 do_action( 'convertkit_gutenberg_enqueue_scripts_editor_and_frontend' );
451
452 }
453
454 /**
455 * Enqueues styles for Gutenberg blocks in both the editor and frontend view,
456 * if the Plugin's Disable CSS option is not enabled.
457 *
458 * @since 1.9.7.6
459 */
460 public function enqueue_styles_editor_and_frontend() {
461
462 // Don't load styles if the Disable CSS option is on.
463 $settings = new ConvertKit_Settings();
464 if ( $settings->css_disabled() ) {
465 return;
466 }
467
468 /**
469 * Enqueues styles for Gutenberg blocks in both the editor and frontend view,
470 * if the Plugin's Disable CSS option is not enabled.
471 *
472 * @since 1.9.7.6
473 */
474 do_action( 'convertkit_gutenberg_enqueue_styles_editor_and_frontend' );
475
476 }
477
478 }
479