From fc4b1969a1fb17c6a9dca17fec98b17159c9b5d9 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Tue, 24 Feb 2026 11:44:55 +0800 Subject: [PATCH 01/12] Block Editor: Register Post Settings Panel --- includes/class-convertkit-gutenberg.php | 185 +++++++++++++++++++++++ resources/backend/js/gutenberg.js | 193 +++++++++++++++++++++++- 2 files changed, 377 insertions(+), 1 deletion(-) diff --git a/includes/class-convertkit-gutenberg.php b/includes/class-convertkit-gutenberg.php index e7286322d..bd83add04 100644 --- a/includes/class-convertkit-gutenberg.php +++ b/includes/class-convertkit-gutenberg.php @@ -29,6 +29,10 @@ public function __construct() { add_filter( 'block_categories', array( $this, 'add_block_categories' ), 10, 2 ); } + // Register Gutenberg Post Settings. + add_action( 'init', array( $this, 'register_post_settings' ) ); + add_action( 'convertkit_gutenberg_enqueue_scripts', array( $this, 'register_post_settings_fields' ) ); + // Register Gutenberg Blocks. add_action( 'init', array( $this, 'add_blocks' ) ); @@ -37,6 +41,187 @@ public function __construct() { } + /** + * Registers the post meta key for storing post settings for the block editor. + * + * @since 3.3.0 + */ + public function register_post_settings() { + + register_post_meta( + '', + '_wp_convertkit_settings', + array( + 'show_in_rest' => array( + 'schema' => array( + 'type' => 'object', + 'properties' => array( + 'form' => array( + 'type' => 'string', + 'default' => '-1', + ), + 'landing_page' => array( + 'type' => 'string', + 'default' => '0', + ), + 'tag' => array( + 'type' => 'string', + 'default' => '0', + ), + 'restrict_content' => array( + 'type' => 'string', + 'default' => '0', + ), + ), + ), + ), + 'single' => true, + 'type' => 'object', + 'default' => array( + 'form' => '-1', + 'landing_page' => '0', + 'tag' => '0', + 'restrict_content' => '0', + ), + 'sanitize_callback' => function ( $value ) { + + if ( ! is_array( $value ) ) { + return array( + 'form' => '-1', + 'landing_page' => '0', + 'tag' => '0', + 'restrict_content' => '0', + ); + } + + return array( + 'form' => sanitize_text_field( $value['form'] ?? '-1' ), + 'landing_page' => sanitize_text_field( $value['landing_page'] ?? '0' ), + 'tag' => sanitize_text_field( $value['tag'] ?? '0' ), + 'restrict_content' => sanitize_text_field( $value['restrict_content'] ?? '0' ), + ); + + }, + 'auth_callback' => function () { + + return current_user_can( 'edit_posts' ); + + }, + ) + ); + + } + + /** + * Registers the post settings fields for the block editor. + */ + public function register_post_settings_fields() { + + // Load resource classes. + $convertkit_forms = new ConvertKit_Resource_Forms( 'post_settings' ); + $convertkit_landing_pages = new ConvertKit_Resource_Landing_Pages( 'post_settings' ); + $convertkit_tags = new ConvertKit_Resource_Tags( 'post_settings' ); + $convertkit_products = new ConvertKit_Resource_Products( 'post_settings' ); + + // Get Forms. + $forms = array( + '-1' => esc_html__( 'Default', 'convertkit' ), + '0' => esc_html__( 'None', 'convertkit' ), + ); + if ( $convertkit_forms->exist() ) { + foreach ( $convertkit_forms->get() as $form ) { + // Legacy forms don't include a `format` key, so define them as inline. + $forms[ absint( $form['id'] ) ] = sprintf( + '%s [%s]', + sanitize_text_field( $form['name'] ), + ( ! empty( $form['format'] ) ? sanitize_text_field( $form['format'] ) : 'inline' ) + ); + } + } + + // Get Landing Pages. + $landing_pages = array( + '0' => esc_html__( 'None', 'convertkit' ), + ); + if ( $convertkit_landing_pages->exist() ) { + foreach ( $convertkit_landing_pages->get() as $landing_page ) { + $landing_pages[ absint( $landing_page['id'] ) ] = sanitize_text_field( $landing_page['name'] ); + } + } + + // Get Tags. + $tags = array( + '0' => esc_html__( 'None', 'convertkit' ), + ); + if ( $convertkit_tags->exist() ) { + foreach ( $convertkit_tags->get() as $tag ) { + $tags[ absint( $tag['id'] ) ] = sanitize_text_field( $tag['name'] ); + } + } + + // Get Products. + $restrict_content = array( + '0' => esc_html__( 'Don\'t restrict content to member-only', 'convertkit' ), + ); + if ( $convertkit_forms->exist() ) { + foreach ( $convertkit_forms->get() as $form ) { + // Legacy forms don't include a `format` key, so define them as inline. + $restrict_content[ 'form_' . absint( $form['id'] ) ] = sprintf( + '%s [%s]', + sanitize_text_field( $form['name'] ), + ( ! empty( $form['format'] ) ? sanitize_text_field( $form['format'] ) : 'inline' ) + ); + } + } + if ( $convertkit_tags->exist() ) { + foreach ( $convertkit_tags->get() as $tag ) { + $restrict_content[ 'tag_' . absint( $tag['id'] ) ] = sanitize_text_field( $tag['name'] ); + } + } + if ( $convertkit_products->exist() ) { + foreach ( $convertkit_products->get() as $product ) { + $restrict_content[ 'product_' . $product['id'] ] = sanitize_text_field( $product['name'] ); + } + } + + // Define post settings fields. + wp_localize_script( + 'convertkit-gutenberg', + 'convertkit_post_settings', + array( + 'form' => array( + 'label' => __( 'Form', 'convertkit' ), + 'type' => 'select', + 'description' => __( 'Select a form to display on this post.', 'convertkit' ), + 'values' => $forms, + 'default_value' => '-1', + ), + 'landing_page' => array( + 'label' => __( 'Landing Page', 'convertkit' ), + 'type' => 'select', + 'description' => __( 'Select a landing page to display on this post.', 'convertkit' ), + 'values' => $landing_pages, + 'default_value' => '0', + ), + 'tag' => array( + 'label' => __( 'Tag', 'convertkit' ), + 'type' => 'select', + 'description' => __( 'Select a tag to apply to visitors of this page who are subscribed.', 'convertkit' ), + 'values' => $tags, + 'default_value' => '0', + ), + 'restrict_content' => array( + 'label' => __( 'Restrict Content', 'convertkit' ), + 'type' => 'select', + 'description' => __( 'Select a content to restrict to members only.', 'convertkit' ), + 'values' => $restrict_content, + 'default_value' => '0', + ), + ) + ); + + } + /** * Register REST API routes. * diff --git a/resources/backend/js/gutenberg.js b/resources/backend/js/gutenberg.js index 258fe1fff..a3ececc01 100644 --- a/resources/backend/js/gutenberg.js +++ b/resources/backend/js/gutenberg.js @@ -19,8 +19,15 @@ if (convertKitGutenbergEnabled()) { convertKitGutenbergRegisterBlock(convertkit_blocks[block]); } - // Register ConvertKit Pre-publish actions in Gutenberg if we're editing a Post. if (convertKitEditingPostInGutenberg()) { + // Register Post Meta settings in Gutenberg if we're editing a Post. + if (typeof convertkit_post_settings !== 'undefined') { + convertKitGutenbergRegisterPostSettingsPanel( + convertkit_post_settings + ); + } + + // Register ConvertKit Pre-publish actions in Gutenberg if we're editing a Post. if (typeof convertkit_pre_publish_actions !== 'undefined') { convertKitGutenbergRegisterPrePublishActions( convertkit_pre_publish_actions @@ -912,6 +919,190 @@ function convertKitGutenbergRegisterBlock(block) { ); } +/** + * Registers post settings in Gutenberg's document sidebar panel. + * + * @since 3.3.0 + * + * @param {Object} postSettings Post settings configuration. + */ +function convertKitGutenbergRegisterPostSettingsPanel(fields) { + (function (plugins, editPost, element, components, data) { + // Define some constants for the various items we'll use. + const el = element.createElement; + const { registerPlugin } = plugins; + const { PluginDocumentSettingPanel } = editPost; + const { + Button, + Icon, + TextControl, + SelectControl, + ToggleControl, + Flex, + FlexItem, + PanelBody, + PanelRow, + ProgressBar, + } = components; + const { useSelect, useDispatch } = data; + + /** + * Returns a PluginDocumentSettingPanel for this plugin, containing + * post-level settings. + * + * @since 3.3.0 + */ + const RenderPanel = function () { + const meta = useSelect(function (wpSelect) { + return wpSelect('core/editor').getEditedPostAttribute('meta') || {}; + }, []); + + const { editPost: wpEditPost } = useDispatch('core/editor'); + + const settings = meta._wp_convertkit_post_meta || {}; + + /** + * Updates a single key within the _wp_convertkit_post_meta object. + * + * @since 3.3.0 + * + * @param {string} key Sub-key within the _wp_convertkit_post_meta object. + * @param {string} value Value to assign to the sub-key. + */ + const updateSetting = function (key, value) { + wpEditPost({ + meta: { + _wp_convertkit_post_meta: Object.assign( + {}, + settings, + { [key]: value } + ), + }, + }); + }; + + /** + * Return a field element for the settings panel. + * + * @since 3.3.0 + * + * @param {Object} props Block properties. + * @param {Object} field Field attributes. + * @param {string} attribute Attribute name to store the field's data in. + * @return {Object} Field element. + */ + const getField = function (field, key) { + + // Define some field properties shared across all field types. + const fieldProperties = { + key: 'convertkit_post_settings_' + key, + label: field.label, + help: field.description, + value: settings[key] || field.default_value || '', + + // Add __next40pxDefaultSize and __nextHasNoMarginBottom properties, + // preventing deprecation notices in the block editor and opt in to the new styles + // from 7.0. + __next40pxDefaultSize: true, + __nextHasNoMarginBottom: true, + + onChange(value) { + updateSetting(key, value); + }, + }; + + const fieldOptions = []; + + // Define additional Field Properties and the Field Element, + // depending on the Field Type (select, textarea, text etc). + switch (field.type) { + case 'select': + // Build options for