From f64c65248859eed7c868c56d3c2a565ded296d84 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Wed, 29 Jul 2026 21:51:54 +0800 Subject: [PATCH 01/11] Remove Legacy Forms from Settings Dropdowns --- .../class-convertkit-block-form-builder.php | 29 ++- .../blocks/class-convertkit-block-form.php | 30 ++- includes/class-convertkit-resource-forms.php | 71 ++++++- ...onvertkit-plugin-sidebar-post-settings.php | 34 +++- resources/backend/js/gutenberg.js | 81 ++++++++ .../PageBlockFormBuilderCest.php | 185 +++++++++--------- .../blocks-shortcodes/PageBlockFormCest.php | 153 ++++++++++----- .../forms/general/EditFormLinkCest.php | 47 ----- .../EndToEnd/forms/general/WidgetFormCest.php | 78 -------- .../forms/post-types/BlockEditorFormCest.php | 140 ++++++++----- .../post-types/ClassicEditorFormCest.php | 52 ----- .../general/other/SelectOptionOrderCest.php | 144 ++++++++++++++ .../other/ContactForm7FormCest.php | 29 --- .../integrations/other/ForminatorCest.php | 29 --- .../integrations/wlm/WishListMemberCest.php | 64 ------ tests/Integration/ResourceFormsTest.php | 85 ++++++++ views/backend/subscription-dropdown-field.php | 25 ++- 17 files changed, 750 insertions(+), 526 deletions(-) diff --git a/includes/blocks/class-convertkit-block-form-builder.php b/includes/blocks/class-convertkit-block-form-builder.php index 16833a0fd..3d0e83cfb 100644 --- a/includes/blocks/class-convertkit-block-form-builder.php +++ b/includes/blocks/class-convertkit-block-form-builder.php @@ -511,17 +511,27 @@ public function get_supports() { */ public function get_fields() { - // Get Kit Forms. - $forms = new ConvertKit_Resource_Forms( 'block_form_builder' ); - $forms_options = array(); + // Get Kit Forms. Non-legacy forms populate the sidebar dropdown; + // legacy forms are exposed separately as a fallback so the sidebar can + // keep displaying a previously-saved legacy form as the current + // selection without offering other legacy forms as new choices. + $forms = new ConvertKit_Resource_Forms( 'block_form_builder' ); + $forms_options = array(); + $forms_legacy_options = array(); if ( $forms->exist() ) { foreach ( $forms->get() as $form ) { - // Legacy forms don't include a `format` key, so define them as inline. - $forms_options[ $form['id'] ] = sprintf( + $label = sprintf( '%s [%s]', sanitize_text_field( $form['name'] ), + // Legacy forms don't include a `format` key, so define them as inline. ( ! empty( $form['format'] ) ? sanitize_text_field( $form['format'] ) : 'inline' ) ); + + if ( ! empty( $form['format'] ) ) { + $forms_options[ $form['id'] ] = $label; + } else { + $forms_legacy_options[ $form['id'] ] = $label; + } } } @@ -569,10 +579,11 @@ public function get_fields() { ), ), 'form_id' => array( - 'label' => __( 'Form', 'convertkit' ), - 'type' => 'select', - 'description' => __( 'The Kit form to add the subscriber to. Useful if you want to send an incentive email.', 'convertkit' ), - 'values' => $forms_options, + 'label' => __( 'Form', 'convertkit' ), + 'type' => 'select', + 'description' => __( 'The Kit form to add the subscriber to. Useful if you want to send an incentive email.', 'convertkit' ), + 'values' => $forms_options, + 'legacy_values' => $forms_legacy_options, ), 'tag_id' => array( 'label' => __( 'Tag', 'convertkit' ), diff --git a/includes/blocks/class-convertkit-block-form.php b/includes/blocks/class-convertkit-block-form.php index f5e121ba8..59acd50cf 100644 --- a/includes/blocks/class-convertkit-block-form.php +++ b/includes/blocks/class-convertkit-block-form.php @@ -262,30 +262,42 @@ public function get_supports() { */ public function get_fields() { - // Get ConvertKit Forms. + // Get ConvertKit Forms. Non-legacy forms populate the sidebar dropdown; + // legacy forms are exposed separately as a fallback so the sidebar can + // keep displaying a previously-saved legacy form as the current + // selection without offering other legacy forms as new choices. $forms = array(); + $legacy_forms = array(); $convertkit_forms = new ConvertKit_Resource_Forms( 'block_edit' ); 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( + $label = sprintf( '%s [%s]', sanitize_text_field( $form['name'] ), + // Legacy forms don't include a `format` key, so define them as inline. ( ! empty( $form['format'] ) ? sanitize_text_field( $form['format'] ) : 'inline' ) ); + + if ( ! empty( $form['format'] ) ) { + $forms[ absint( $form['id'] ) ] = $label; + } else { + $legacy_forms[ absint( $form['id'] ) ] = $label; + } } } return array( 'form' => array( - 'label' => __( 'Form', 'convertkit' ), - 'type' => 'resource', - 'resource' => 'forms', - 'values' => $forms, - 'data' => array( + 'label' => __( 'Form', 'convertkit' ), + 'type' => 'resource', + 'resource' => 'forms', + 'values' => $forms, + 'legacy_values' => $legacy_forms, + 'data' => array( // Used by resources/backend/js/gutenberg-block-form.js to determine the selected form's format // (modal, slide in, sticky bar) and output a message in the block editor for the preview to explain - // why some formats cannot be previewed. + // why some formats cannot be previewed. Includes legacy forms so the preview code can still find + // them when a saved block references a legacy form. 'forms' => ( $convertkit_forms->exist() ? $convertkit_forms->get() : array() ), ), ), diff --git a/includes/class-convertkit-resource-forms.php b/includes/class-convertkit-resource-forms.php index 966361277..277636756 100644 --- a/includes/class-convertkit-resource-forms.php +++ b/includes/class-convertkit-resource-forms.php @@ -153,6 +153,42 @@ public function non_inline_exist() { } + /** + * Returns all non-legacy forms (any v4 format: inline, modal, slide in + * or sticky bar) based on the sort order. Legacy forms lack a `format` + * key and are therefore excluded. + * + * @since 3.3.7 + * + * @return bool|array + */ + public function get_non_legacy() { + + // If the ConvertKit WordPress Libraries are < 1.3.6 (e.g. loaded by an outdated + // addon), or a WordPress site updates this Plugin before other ConvertKit Plugins, + // get_by() won't be available and will cause an E_ERROR, crashing the site. + // @see https://wordpress.org/support/topic/error-1795/. + if ( ! method_exists( $this, 'get_by' ) ) { // @phpstan-ignore-line Older WordPress Libraries won't have this function. + return false; + } + + return $this->get_by( 'format', array( 'inline', 'modal', 'slide in', 'sticky bar' ) ); + + } + + /** + * Returns whether any non-legacy forms exist in the options table. + * + * @since 3.3.7 + * + * @return bool + */ + public function non_legacy_exist() { + + return (bool) $this->get_non_legacy(); + + } + /** * Determines if the given Form ID is a legacy Form or Landing Page. * @@ -196,7 +232,7 @@ public function is_legacy( $id ) { public function get_select_field_all( $name, $id, $css_classes, $selected_option, $prepend_options = false, $attributes = false, $description = false ) { return $this->get_select_field( - $this->get(), + $this->get_forms_for_select_field( $selected_option ), $name, $id, $css_classes, @@ -224,7 +260,7 @@ public function get_select_field_all( $name, $id, $css_classes, $selected_option public function output_select_field_all( $name, $id, $css_classes, $selected_option, $prepend_options = false, $attributes = false, $description = false ) { $this->output_select_field( - $this->get(), + $this->get_forms_for_select_field( $selected_option ), $name, $id, $css_classes, @@ -236,6 +272,37 @@ public function output_select_field_all( $name, $id, $css_classes, $selected_opt } + /** + * Returns the array of forms to display in an "all forms" dropdown: every + * non-legacy form, plus the currently-selected legacy form (if any) so + * existing saved legacy assignments continue to render as selected in the + * UI without exposing other legacy forms as new selection options. + * + * @since 3.3.7 + * + * @param string|int $selected_option Currently-selected form ID. + * @return array + */ + private function get_forms_for_select_field( $selected_option ) { + + $forms = $this->get_non_legacy(); + if ( ! is_array( $forms ) ) { + $forms = array(); + } + + // If the currently-selected form is a legacy form, append it so the + // dropdown shows the saved value as selected. + if ( $selected_option && $this->is_legacy( $selected_option ) ) { + $legacy_form = $this->get_by_id( (int) $selected_option ); + if ( $legacy_form ) { + $forms[] = $legacy_form; + } + } + + return $forms; + + } + /** * Returns a exist() ) { - foreach ( $convertkit_landing_pages->get() as $landing_page ) { - if ( isset( $convertkit_landing_page['url'] ) ) { - ?> - - get_non_legacy(); + if ( is_array( $non_legacy_landing_pages ) ) { + foreach ( $non_legacy_landing_pages as $landing_page ) { + ?> + + get_landing_page(); + if ( $current_landing_page && $convertkit_landing_pages->is_legacy( $current_landing_page ) ) { + // Pre-1.9.6 storage: the saved value is the legacy URL itself. + if ( is_string( $current_landing_page ) && strstr( $current_landing_page, 'http' ) ) { + ?> + + get_by_id( (int) $current_landing_page ); + if ( $legacy_landing_page ) { ?> - + landing_pages->exist() ) { +// If no non-legacy Landing Pages exist on the Kit account, show the user how to add +// one, with an option to refresh this page so that they can then select it. Legacy +// landing pages are intentionally excluded here; the wizard only offers v4 pages +// for new selections. +if ( ! $this->landing_pages->non_legacy_exist() ) { ?>

From 6ebee8db04ac3eefefe13d231c19d3b3838b7217 Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Thu, 30 Jul 2026 14:21:24 +0800 Subject: [PATCH 03/11] Tests: Fix selecting existing block --- .../PageBlockFormBuilderCest.php | 18 +++++++--------- .../blocks-shortcodes/PageBlockFormCest.php | 21 +++++-------------- tests/Support/Helper/WPGutenberg.php | 21 +++++++++++++++++++ 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/tests/EndToEnd/forms/blocks-shortcodes/PageBlockFormBuilderCest.php b/tests/EndToEnd/forms/blocks-shortcodes/PageBlockFormBuilderCest.php index 2b8614277..39d2b02d5 100644 --- a/tests/EndToEnd/forms/blocks-shortcodes/PageBlockFormBuilderCest.php +++ b/tests/EndToEnd/forms/blocks-shortcodes/PageBlockFormBuilderCest.php @@ -52,11 +52,11 @@ public function testFormBuilderBlockSidebarFormDropdownExcludesLegacyForms(EndTo ); // The block sidebar's Form select is at #convertkit_form_builder_form_id. - $I->waitForElementVisible('#convertkit_form_builder_form_id'); - $I->dontSeeElementInDOM('#convertkit_form_builder_form_id option[value="' . $_ENV['CONVERTKIT_API_LEGACY_FORM_ID'] . '"]'); + $I->waitForElementVisible('select#convertkit_form_builder_form_id'); + $I->dontSeeElementInDOM('select#convertkit_form_builder_form_id option[value="' . $_ENV['CONVERTKIT_API_LEGACY_FORM_ID'] . '"]'); $I->dontSee($_ENV['CONVERTKIT_API_LEGACY_FORM_NAME'] . ' [inline]', '#convertkit_form_builder_form_id'); - // Save page to avoid alert when _passed() deactivates the Plugin. + // Save page to avoid alert box when _passed() runs to deactivate the Plugin. $I->publishGutenbergPage($I); } @@ -98,15 +98,13 @@ public function testFormBuilderBlockPreservesSelectedLegacyForm(EndToEndTester $ // Open the page's edit screen. $I->amOnAdminPage('post.php?post=' . $pageID . '&action=edit'); - // Select the Form Builder block so the sidebar renders. - $I->waitForElementVisible('.wp-block-convertkit-form-builder'); - $I->click('.wp-block-convertkit-form-builder'); - $I->waitForElementVisible('#convertkit_form_builder_form_id'); + // Select the Form Builder block in the Document Overview sidebar. + $I->selectGutenbergBlockInDocumentOverview($I, 'Kit Form Builder'); - // The legacy form should appear as an option and be selected. - $I->seeElementInDOM('#convertkit_form_builder_form_id option[value="' . $_ENV['CONVERTKIT_API_LEGACY_FORM_ID'] . '"]'); + // Assert the legacy form is selected. + $I->waitForElementVisible('select#convertkit_form_builder_form_id'); $I->seeOptionIsSelected( - '#convertkit_form_builder_form_id', + 'select#convertkit_form_builder_form_id', $_ENV['CONVERTKIT_API_LEGACY_FORM_NAME'] . ' [inline]' ); } diff --git a/tests/EndToEnd/forms/blocks-shortcodes/PageBlockFormCest.php b/tests/EndToEnd/forms/blocks-shortcodes/PageBlockFormCest.php index aa9e40f68..30f4f8a0d 100644 --- a/tests/EndToEnd/forms/blocks-shortcodes/PageBlockFormCest.php +++ b/tests/EndToEnd/forms/blocks-shortcodes/PageBlockFormCest.php @@ -925,26 +925,15 @@ public function testFormBlockPreservesSelectedLegacyForm(EndToEndTester $I) // Load the page's edit screen. $I->amOnAdminPage('post.php?post=' . $pageID . '&action=edit'); - // Wait for the block editor to finish loading, then open the block - // sidebar by clicking the Kit Form block. Once selected, the block - // sidebar renders with the Form dropdown showing the saved value. - $I->waitForElementVisible('.wp-block-convertkit-form'); - $I->click('.wp-block-convertkit-form'); - $I->waitForElementVisible('#convertkit_form_form'); + // Select the Form Builder block in the Document Overview sidebar. + $I->selectGutenbergBlockInDocumentOverview($I, 'Kit Form'); - // The legacy form should be present as an option AND be the selected - // value in the sidebar dropdown. - $I->seeElementInDOM('#convertkit_form_form option[value="' . $_ENV['CONVERTKIT_API_LEGACY_FORM_ID'] . '"]'); + // Assert the legacy form is selected. + $I->waitForElementVisible('select#convertkit_form_form'); $I->seeOptionIsSelected( - '#convertkit_form_form', + 'select#convertkit_form_form', $_ENV['CONVERTKIT_API_LEGACY_FORM_NAME'] . ' [inline]' ); - - // Visit the page on the frontend and confirm the legacy form still - // renders — proving that a saved legacy assignment continues to - // work at render time. - $I->amOnPage('/?p=' . $pageID); - $I->seeInSource('

'); } /** diff --git a/tests/Support/Helper/WPGutenberg.php b/tests/Support/Helper/WPGutenberg.php index f1d122f65..87ec1042d 100644 --- a/tests/Support/Helper/WPGutenberg.php +++ b/tests/Support/Helper/WPGutenberg.php @@ -353,6 +353,27 @@ public function selectGutenbergBlockInEditor($I, $blockProgrammaticName) } } + /** + * Selects a block in the Document Overview sidebar. + * + * @since 3.3.7 + * + * @param EndToEndTester $I EndToEnd Tester. + * @param string $blockName Block Name (e.g. 'Kit Form Builder'). + */ + public function selectGutenbergBlockInDocumentOverview($I, $blockName) + { + // Open the Document Overview sidebar. + $I->waitForElementVisible('button.editor-document-tools__document-overview-toggle'); + $I->click('button.editor-document-tools__document-overview-toggle'); + $I->waitForElementVisible('.interface-interface-skeleton__secondary-sidebar[aria-label="Document Overview"]'); + $I->wait(2); + + // Select the block. + $I->click($blockName, '.interface-interface-skeleton__secondary-sidebar[aria-label="Document Overview"]'); + $I->waitForElementVisible('.interface-interface-skeleton__sidebar[aria-label="Editor settings"]'); + } + /** * Asserts that the given block is available in the Gutenberg block library. * From 1b8961edd1c28cce62b02597339b52bc1b422f5c Mon Sep 17 00:00:00 2001 From: Tim Carr Date: Thu, 30 Jul 2026 14:37:21 +0800 Subject: [PATCH 04/11] Move legacy form dropdown tests to own file --- .../general/other/LegacyFormDropdownCest.php | 192 ++++++++++++++++++ .../general/other/SelectOptionOrderCest.php | 144 ------------- 2 files changed, 192 insertions(+), 144 deletions(-) create mode 100644 tests/EndToEnd/general/other/LegacyFormDropdownCest.php diff --git a/tests/EndToEnd/general/other/LegacyFormDropdownCest.php b/tests/EndToEnd/general/other/LegacyFormDropdownCest.php new file mode 100644 index 000000000..8d323cf90 --- /dev/null +++ b/tests/EndToEnd/general/other/LegacyFormDropdownCest.php @@ -0,0 +1,192 @@ +activateKitPlugin($I); + $I->activateThirdPartyPlugin($I, 'classic-editor'); + $I->setupKitPlugin($I); + $I->setupKitPluginResources($I); + } + + /** + * Test that legacy Kit forms are excluded from Form dropdown options across + * the Post metabox, the Category term edit screen and the Plugin's General + * Settings screen. Legacy forms continue to work if already saved to a + * post/setting, but no longer appear as a selectable choice in dropdowns. + * + * @since 3.3.7 + * + * @param EndToEndTester $I Tester. + */ + public function testLegacyFormsExcludedFromDropdowns(EndToEndTester $I) + { + $legacyOption = '