diff --git a/admin/importers/class-convertkit-admin-importer-mailpoet.php b/admin/importers/class-convertkit-admin-importer-mailpoet.php
new file mode 100644
index 000000000..27dc10954
--- /dev/null
+++ b/admin/importers/class-convertkit-admin-importer-mailpoet.php
@@ -0,0 +1,82 @@
+get_results(
+ "SELECT id, name FROM {$wpdb->prefix}mailpoet_forms WHERE deleted_at IS NULL"
+ );
+
+ if ( empty( $results ) ) {
+ return array();
+ }
+
+ $forms = array();
+ foreach ( $results as $form ) {
+ $forms[ $form->id ] = $form->name;
+ }
+
+ return $forms;
+
+ }
+
+}
diff --git a/admin/importers/class-convertkit-admin-importer-newsletter.php b/admin/importers/class-convertkit-admin-importer-newsletter.php
new file mode 100644
index 000000000..a99d34e9e
--- /dev/null
+++ b/admin/importers/class-convertkit-admin-importer-newsletter.php
@@ -0,0 +1,49 @@
+get_form_ids_in_posts();
+
+ }
+
+}
diff --git a/admin/importers/class-convertkit-admin-importer.php b/admin/importers/class-convertkit-admin-importer.php
index 17e86d60c..f9abfc724 100644
--- a/admin/importers/class-convertkit-admin-importer.php
+++ b/admin/importers/class-convertkit-admin-importer.php
@@ -28,9 +28,9 @@ abstract class ConvertKit_Admin_Importer {
*
* @since 3.1.0
*
- * @var string
+ * @var bool|string
*/
- public $shortcode_id_attribute = '';
+ public $shortcode_id_attribute = false;
/**
* Holds the block name for the third party Form plugin.
@@ -46,9 +46,9 @@ abstract class ConvertKit_Admin_Importer {
*
* @since 3.1.6
*
- * @var string
+ * @var bool|string
*/
- public $block_id_attribute = '';
+ public $block_id_attribute = false;
/**
* Returns an array of third party form IDs and titles.
@@ -171,13 +171,20 @@ public function replace_shortcodes_in_posts( $third_party_form_id, $form_id ) {
*/
public function replace_shortcodes_in_content( $content, $third_party_form_id, $form_id ) {
- $pattern = '/\[' // Start regex with an opening square bracket.
+ // If there's no shortcode ID attribute, match shortcodes with or without any attribute.
+ if ( ! $this->shortcode_id_attribute ) {
+ $pattern = '/\[' // Start regex with an opening square bracket.
. preg_quote( $this->shortcode_name, '/' ) // Match the shortcode name, escaping any regex special chars.
- . '[^\]]*?' // Match any characters that are not a closing square bracket, non-greedy.
- . '\b' . preg_quote( $this->shortcode_id_attribute, '/' ) // Match the id attribute word boundary and escape as needed.
- . '\s*=\s*' // Match optional whitespace around an equals sign.
- . '(?:"' . preg_quote( (string) $third_party_form_id, '/' ) . '"|' . preg_quote( (string) $third_party_form_id, '/' ) . ')' // Match the form ID, quoted or unquoted.
. '[^\]]*?\]/i'; // Match any other characters (non-greedy) up to the closing square bracket, case-insensitive.
+ } else {
+ $pattern = '/\[' // Start regex with an opening square bracket.
+ . preg_quote( $this->shortcode_name, '/' ) // Match the shortcode name, escaping any regex special chars.
+ . '[^\]]*?' // Match any characters that are not a closing square bracket, non-greedy.
+ . '\b' . preg_quote( $this->shortcode_id_attribute, '/' ) // Match the id attribute word boundary and escape as needed.
+ . '\s*=\s*' // Match optional whitespace around an equals sign.
+ . '(?:"' . preg_quote( (string) $third_party_form_id, '/' ) . '"|' . preg_quote( (string) $third_party_form_id, '/' ) . ')' // Match the form ID, quoted or unquoted.
+ . '[^\]]*?\]/i'; // Match any other characters (non-greedy) up to the closing square bracket, case-insensitive.
+ }
return preg_replace(
$pattern,
@@ -204,6 +211,14 @@ public function get_form_ids_in_posts() {
return array();
}
+ // If the shortcode or block ID attribute is not set, the third party Plugin doesn't use IDs
+ // and only has one form.
+ if ( ! $this->shortcode_id_attribute && ! $this->block_id_attribute ) {
+ return array(
+ __( 'Default Form', 'convertkit' ),
+ );
+ }
+
// Iterate through Posts, extracting the Form IDs from the third party form shortcodes.
$form_ids = array();
foreach ( $post_ids as $post_id ) {
@@ -227,6 +242,24 @@ public function get_form_ids_in_posts() {
*/
public function get_form_ids_from_content( $content ) {
+ // If there's no shortcode ID attribute, match shortcodes with or without any attribute and treat any match as a single "form".
+ if ( ! $this->shortcode_id_attribute ) {
+ $pattern = '/\[' // Start regex with an opening square bracket.
+ . preg_quote( $this->shortcode_name, '/' ) // Match the shortcode name, escaping any regex special chars.
+ . '(?:\s+[^\]]*)?' // Optionally match any attributes (key/value pairs), non-greedy.
+ . '[^\]]*?\]/i'; // Match up to closing bracket, case-insensitive.
+
+ preg_match_all( $pattern, $content, $matches );
+
+ // If we matched at least one occurrence, just return an array with a single 0 (default/non-ID form).
+ if ( ! empty( $matches[0] ) ) {
+ return array( 0 );
+ }
+
+ return array();
+ }
+
+ // Legacy: Extract where attribute is required.
$pattern = '/\[' // Start regex with an opening square bracket.
. preg_quote( $this->shortcode_name, '/' ) // Match the shortcode name, escaping any regex special chars.
. '(?:\s+[^\]]*)?' // Optionally match any attributes (key/value pairs), non-greedy.
@@ -365,14 +398,18 @@ private function recursively_convert_blocks( $blocks, $third_party_form_id, $for
continue;
}
- // Skip if the attribute doesn't exist i.e. the block was not configured.
- if ( ! array_key_exists( $this->block_id_attribute, $block['attrs'] ) ) {
- continue;
- }
-
- // Skip if the third party form ID doesn't exist within the third party form block's attribute.
- if ( stripos( $block['attrs'][ $this->block_id_attribute ], (string) $third_party_form_id ) === false ) {
- continue;
+ // If the block ID attribute is not set, the third party Plugin doesn't use IDs,
+ // so there's no need to check the $third_party_form_id matches the block attribute.
+ if ( $this->block_id_attribute ) {
+ // Skip if the attribute doesn't exist i.e. the block was not configured.
+ if ( ! array_key_exists( $this->block_id_attribute, $block['attrs'] ) ) {
+ continue;
+ }
+
+ // Skip if the third party form ID doesn't exist within the third party form block's attribute.
+ if ( stripos( $block['attrs'][ $this->block_id_attribute ], (string) $third_party_form_id ) === false ) {
+ continue;
+ }
}
// Replace third party form block with Kit form block.
diff --git a/admin/section/class-convertkit-admin-section-tools.php b/admin/section/class-convertkit-admin-section-tools.php
index b13956391..b2d6f78e5 100644
--- a/admin/section/class-convertkit-admin-section-tools.php
+++ b/admin/section/class-convertkit-admin-section-tools.php
@@ -53,12 +53,14 @@ public function register_notices( $notices ) {
return array_merge(
$notices,
array(
- 'import_configuration_upload_error' => __( 'An error occured uploading the configuration file.', 'convertkit' ),
- 'import_configuration_invalid_file_type' => __( 'The uploaded configuration file isn\'t valid.', 'convertkit' ),
- 'import_configuration_empty' => __( 'The uploaded configuration file contains no settings.', 'convertkit' ),
- 'import_configuration_success' => __( 'Configuration imported successfully.', 'convertkit' ),
- 'migrate_aweber_configuration_success' => __( 'AWeber forms migrated successfully.', 'convertkit' ),
- 'migrate_mc4wp_configuration_success' => __( 'MC4WP forms migrated successfully.', 'convertkit' ),
+ 'import_configuration_upload_error' => __( 'An error occured uploading the configuration file.', 'convertkit' ),
+ 'import_configuration_invalid_file_type' => __( 'The uploaded configuration file isn\'t valid.', 'convertkit' ),
+ 'import_configuration_empty' => __( 'The uploaded configuration file contains no settings.', 'convertkit' ),
+ 'import_configuration_success' => __( 'Configuration imported successfully.', 'convertkit' ),
+ 'migrate_aweber_configuration_success' => __( 'AWeber forms migrated successfully.', 'convertkit' ),
+ 'migrate_mc4wp_configuration_success' => __( 'MC4WP forms migrated successfully.', 'convertkit' ),
+ 'migrate_mailpoet_configuration_success' => __( 'MailPoet forms migrated successfully.', 'convertkit' ),
+ 'migrate_newsletter_configuration_success' => __( 'Newsletter forms migrated successfully.', 'convertkit' ),
)
);
@@ -79,6 +81,8 @@ private function maybe_perform_actions() {
$this->maybe_import_configuration();
$this->maybe_migrate_aweber_configuration();
$this->maybe_migrate_mc4wp_configuration();
+ $this->maybe_migrate_mailpoet_configuration();
+ $this->maybe_migrate_newsletter_configuration();
}
@@ -390,6 +394,78 @@ private function maybe_migrate_mc4wp_configuration() {
}
+ /**
+ * Replaces Mailpoet Form Shortcodes and Blocks with Kit Form Shortcodes and Blocks, if the user submitted the
+ * Mailpoet Migrate Configuration section.
+ *
+ * @since 3.1.6
+ */
+ private function maybe_migrate_mailpoet_configuration() {
+
+ // Bail if nonce verification fails.
+ if ( ! isset( $_REQUEST['_convertkit_settings_tools_nonce'] ) ) {
+ return;
+ }
+
+ if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_convertkit_settings_tools_nonce'] ), 'convertkit-settings-tools' ) ) {
+ return;
+ }
+
+ // Bail if no Mailpoet Form IDs were submitted.
+ if ( ! isset( $_REQUEST['_wp_convertkit_integration_mailpoet_settings'] ) ) {
+ return;
+ }
+
+ // Initialise the importer.
+ $mailpoet = new ConvertKit_Admin_Importer_Mailpoet();
+
+ // Iterate through the Mailpoet Form IDs and replace the shortcodes with the Kit Form Shortcodes.
+ foreach ( array_map( 'sanitize_text_field', wp_unslash( $_REQUEST['_wp_convertkit_integration_mailpoet_settings'] ) ) as $mailpoet_form_id => $kit_form_id ) {
+ $mailpoet->replace_blocks_in_posts( (int) $mailpoet_form_id, (int) $kit_form_id );
+ $mailpoet->replace_shortcodes_in_posts( (int) $mailpoet_form_id, (int) $kit_form_id );
+ }
+
+ // Redirect to Tools screen.
+ $this->redirect_with_success_notice( 'migrate_mailpoet_configuration_success' );
+
+ }
+
+ /**
+ * Replaces Newsletter Form Blocks and Shortcodes with Kit Form Blocks and Shortcodes, if the user submitted the
+ * Newsletter Migrate Configuration section.
+ *
+ * @since 3.1.6
+ */
+ private function maybe_migrate_newsletter_configuration() {
+
+ // Bail if nonce verification fails.
+ if ( ! isset( $_REQUEST['_convertkit_settings_tools_nonce'] ) ) {
+ return;
+ }
+
+ if ( ! wp_verify_nonce( sanitize_key( $_REQUEST['_convertkit_settings_tools_nonce'] ), 'convertkit-settings-tools' ) ) {
+ return;
+ }
+
+ // Bail if no Newsletter Form IDs were submitted.
+ if ( ! isset( $_REQUEST['_wp_convertkit_integration_newsletter_settings'] ) ) {
+ return;
+ }
+
+ // Initialise the importer.
+ $newsletter = new ConvertKit_Admin_Importer_Newsletter();
+
+ // Iterate through the Newsletter Form IDs and replace the blocks and shortcodes with the Kit Form blocks and shortcodes.
+ foreach ( array_map( 'sanitize_text_field', wp_unslash( $_REQUEST['_wp_convertkit_integration_newsletter_settings'] ) ) as $newsletter_form_id => $kit_form_id ) {
+ $newsletter->replace_blocks_in_posts( (int) $newsletter_form_id, (int) $kit_form_id );
+ $newsletter->replace_shortcodes_in_posts( (int) $newsletter_form_id, (int) $kit_form_id );
+ }
+
+ // Redirect to Tools screen.
+ $this->redirect_with_success_notice( 'migrate_newsletter_configuration_success' );
+
+ }
+
/**
* Outputs the Debug Log and System Info view.
*
@@ -412,8 +488,10 @@ public function render() {
$forms = new ConvertKit_Resource_Forms();
// Get Importers.
- $aweber = new ConvertKit_Admin_Importer_AWeber();
- $mc4wp = new ConvertKit_Admin_Importer_MC4WP();
+ $aweber = new ConvertKit_Admin_Importer_AWeber();
+ $mc4wp = new ConvertKit_Admin_Importer_MC4WP();
+ $mailpoet = new ConvertKit_Admin_Importer_Mailpoet();
+ $newsletter = new ConvertKit_Admin_Importer_Newsletter();
// Output view.
require_once CONVERTKIT_PLUGIN_PATH . '/views/backend/settings/tools.php';
diff --git a/tests/EndToEnd/general/plugin-screens/PluginSettingsToolsImporterMailPoetCest.php b/tests/EndToEnd/general/plugin-screens/PluginSettingsToolsImporterMailPoetCest.php
new file mode 100644
index 000000000..781348f14
--- /dev/null
+++ b/tests/EndToEnd/general/plugin-screens/PluginSettingsToolsImporterMailPoetCest.php
@@ -0,0 +1,292 @@
+ Kit > Tools > Import sections for the MailPoet third party form plugin.
+ *
+ * @since 3.1.6
+ */
+class PluginSettingsToolsImporterMailPoetCest
+{
+ /**
+ * Run common actions before running the test functions in this class.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ */
+ public function _before(EndToEndTester $I)
+ {
+ // Activate Plugins.
+ $I->activateKitPlugin($I);
+ }
+
+ /**
+ * Test that MailPoet Form Shortcodes are replaced with Kit Form Shortcodes when the Tools > MailPoet: Migrate Configuration is configured.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ */
+ public function testMailPoetImportWithShortcodes(EndToEndTester $I)
+ {
+ // Setup Plugin.
+ $I->setupKitPlugin($I);
+ $I->setupKitPluginResources($I);
+
+ // Create MailPoet Forms.
+ $mailPoetFormIDs = $this->_createMailPoetForms($I);
+
+ // Insert MailPoet Form Shortcodes into Pages.
+ $pageIDs = $this->_createPagesWithMailPoetFormShortcodes($I, $mailPoetFormIDs);
+
+ // Navigate to the Tools screen.
+ $I->loadKitSettingsToolsScreen($I);
+
+ // Select the Kit Forms to replace the MailPoet Forms.
+ foreach ($mailPoetFormIDs as $mailPoetFormID) {
+ $I->selectOption('_wp_convertkit_integration_mailpoet_settings[' . $mailPoetFormID . ']', $_ENV['CONVERTKIT_API_FORM_ID']);
+ }
+
+ // Click the Migrate button.
+ $I->click('Migrate');
+
+ // Confirm success message displays.
+ $I->waitForElementVisible('.notice-success');
+ $I->see('MailPoet forms migrated successfully.');
+
+ // View the Pages, to confirm Kit Forms now display.
+ foreach ($pageIDs as $pageID) {
+ $I->amOnPage('?p=' . $pageID);
+ $I->seeElementInDOM('form[data-sv-form]');
+ }
+ }
+
+ /**
+ * Test that MailPoet Blocks are replaced with Kit Blocks when the Tools > MailPoet: Migrate Configuration is configured.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ */
+ public function testMailPoetImportWithBlocks(EndToEndTester $I)
+ {
+ // Setup Plugin.
+ $I->setupKitPlugin($I);
+ $I->setupKitPluginResources($I);
+
+ // Create MailPoet Forms.
+ $mailPoetFormIDs = $this->_createMailPoetForms($I);
+
+ // Insert MailPoet Blocks into Pages.
+ $pageIDs = $this->_createPagesWithMailPoetBlocks($I, $mailPoetFormIDs);
+
+ // Navigate to the Tools screen.
+ $I->loadKitSettingsToolsScreen($I);
+
+ // Select the Kit Forms to replace the MailPoet Forms.
+ foreach ($mailPoetFormIDs as $mailPoetFormID) {
+ $I->selectOption('_wp_convertkit_integration_mailpoet_settings[' . $mailPoetFormID . ']', $_ENV['CONVERTKIT_API_FORM_ID']);
+ }
+
+ // Click the Migrate button.
+ $I->click('Migrate');
+
+ // Confirm success message displays.
+ $I->waitForElementVisible('.notice-success');
+ $I->see('MailPoet forms migrated successfully.');
+
+ // Test each Page.
+ foreach ($pageIDs as $pageID) {
+ $I->amOnPage('?p=' . $pageID);
+
+ // Check Kit Form block is displayed.
+ $I->seeElementInDOM('form[data-sv-form]');
+
+ // Confirm special characters have not been stripped.
+ $I->seeInSource('!@£$%^&*()_+~!@£$%^&*()_+\\');
+ }
+ }
+
+ /**
+ * Test that the MailPoet: Migrate Configuration section is not displayed when no MailPoet Forms exist.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ */
+ public function testMailPoetImportWhenNoMailPoetForms(EndToEndTester $I)
+ {
+ // Setup Plugin.
+ $I->setupKitPlugin($I);
+ $I->setupKitPluginResources($I);
+
+ // Navigate to the Tools screen.
+ $I->loadKitSettingsToolsScreen($I);
+
+ // Confirm no MailPoet: Migrate Configuration section is displayed.
+ $I->dontSeeElementInDOM('#import-mailpoet');
+ }
+
+ /**
+ * Test that the MailPoet: Migrate Configuration section is not displayed when MailPoet Forms exist,
+ * but no Pages, Posts or Custom Posts contain MailPoet Form Shortcodes.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ */
+ public function testMailPoetImportWhenNoMailPoetShortcodesInContent(EndToEndTester $I)
+ {
+ // Setup Plugin.
+ $I->setupKitPlugin($I);
+ $I->setupKitPluginResources($I);
+
+ // Create MailPoet Forms.
+ $mailPoetFormIDs = $this->_createMailPoetForms($I);
+
+ // Navigate to the Tools screen.
+ $I->loadKitSettingsToolsScreen($I);
+
+ // Confirm no MailPoet: Migrate Configuration section is displayed, as there are no
+ // MailPoet Form Shortcodes in the content.
+ $I->dontSeeElementInDOM('#import-mailpoet');
+ }
+
+ /**
+ * Test that the MailPoet: Migrate Configuration section is not displayed when no Kit Forms exist.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ */
+ public function testMailPoetImportWhenNoKitForms(EndToEndTester $I)
+ {
+ // Setup Plugin.
+ $I->setupKitPluginCredentialsNoData($I);
+ $I->setupKitPluginResourcesNoData($I);
+
+ // Navigate to the Tools screen.
+ $I->loadKitSettingsToolsScreen($I);
+
+ // Confirm no MailPoet: Migrate Configuration section is displayed, as there are no
+ // MailPoet Form Shortcodes in the content.
+ $I->dontSeeElementInDOM('#import-mailpoet');
+ }
+
+ /**
+ * Create MailPoet Forms.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ * @return array
+ */
+ private function _createMailPoetForms(EndToEndTester $I)
+ {
+ return array(
+ $I->haveInDatabase(
+ 'wp_mailpoet_forms',
+ [
+ 'name' => 'MailPoet Form #1',
+ ]
+ ),
+ $I->haveInDatabase(
+ 'wp_mailpoet_forms',
+ [
+ 'name' => 'MailPoet Form #2',
+ ]
+ ),
+ );
+ }
+
+ /**
+ * Create Pages with MailPoet Form Shortcodes.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ * @param array $mailPoetFormIDs MailPoet Form IDs.
+ * @return array
+ */
+ private function _createPagesWithMailPoetFormShortcodes(EndToEndTester $I, $mailPoetFormIDs)
+ {
+ $pageIDs = array();
+
+ foreach ($mailPoetFormIDs as $mailPoetFormID) {
+ $pageIDs[] = $I->havePostInDatabase(
+ [
+ 'post_type' => 'page',
+ 'post_status' => 'publish',
+ 'post_title' => 'Page with MailPoet Form #' . $mailPoetFormID,
+ 'post_content' => '[mailpoet_form id="' . $mailPoetFormID . '"]',
+ 'meta_input' => [
+ '_wp_convertkit_post_meta' => [
+ 'form' => '0',
+ 'landing_page' => '',
+ 'tag' => '',
+ ],
+ ],
+ ]
+ );
+ }
+
+ return $pageIDs;
+ }
+
+ /**
+ * Create Pages with MailPoet Blocks.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ * @param array $mailPoetFormIDs MailPoet Form IDs.
+ * @return array
+ */
+ private function _createPagesWithMailPoetBlocks(EndToEndTester $I, $mailPoetFormIDs)
+ {
+ $pageIDs = array();
+
+ foreach ($mailPoetFormIDs as $mailPoetFormID) {
+ $pageIDs[] = $I->havePostInDatabase(
+ [
+ 'post_type' => 'page',
+ 'post_status' => 'publish',
+ 'post_title' => 'Page with MailPoet Block #' . $mailPoetFormID,
+ 'post_content' => '
Some content with characters !@£$%^&*()_+~!@£$%^&*()_+\\\
',
+
+ // Configure Kit Plugin to not display a default Form, so we test against the Kit Form in the content.
+ 'meta_input' => [
+ '_wp_convertkit_post_meta' => [
+ 'form' => '0',
+ 'landing_page' => '',
+ 'tag' => '',
+ ],
+ ],
+ ]
+ );
+ }
+
+ return $pageIDs;
+ }
+
+ /**
+ * Deactivate and reset Plugin(s) after each test, if the test passes.
+ * We don't use _after, as this would provide a screenshot of the Plugin
+ * deactivation and not the true test error.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ */
+ public function _passed(EndToEndTester $I)
+ {
+ // Clear the table of any existing MailPoet Forms.
+ $I->truncateDbTable('wp_mailpoet_forms');
+ $I->deactivateKitPlugin($I);
+ $I->resetKitPlugin($I);
+ }
+}
diff --git a/tests/EndToEnd/general/plugin-screens/PluginSettingsToolsImporterNewsletterCest.php b/tests/EndToEnd/general/plugin-screens/PluginSettingsToolsImporterNewsletterCest.php
new file mode 100644
index 000000000..43c25a6e0
--- /dev/null
+++ b/tests/EndToEnd/general/plugin-screens/PluginSettingsToolsImporterNewsletterCest.php
@@ -0,0 +1,208 @@
+ Kit > Tools > Import sections for the Newsletter third party form plugin.
+ *
+ * @since 3.1.6
+ */
+class PluginSettingsToolsImporterNewsletterCest
+{
+ /**
+ * Run common actions before running the test functions in this class.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ */
+ public function _before(EndToEndTester $I)
+ {
+ // Activate Plugins.
+ $I->activateKitPlugin($I);
+ }
+
+ /**
+ * Test that Newsletter Form Shortcodes are replaced with Kit Form Shortcodes when the Tools > Newsletter: Migrate Configuration is configured.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ */
+ public function testNewsletterImportWithShortcodes(EndToEndTester $I)
+ {
+ // Setup Plugin.
+ $I->setupKitPlugin($I);
+ $I->setupKitPluginResources($I);
+
+ // Insert Newsletter Form Shortcode into Page.
+ $pageID = $this->_createPageWithNewsletterFormShortcodes($I);
+
+ // Navigate to the Tools screen.
+ $I->loadKitSettingsToolsScreen($I);
+
+ // Select the Kit Form to replace the Newsletter Forms.
+ $I->selectOption('_wp_convertkit_integration_newsletter_settings[0]', $_ENV['CONVERTKIT_API_FORM_ID']);
+
+ // Click the Migrate button.
+ $I->click('Migrate');
+
+ // Confirm success message displays.
+ $I->waitForElementVisible('.notice-success');
+ $I->see('Newsletter forms migrated successfully.');
+
+ // View the Page, to confirm Kit Forms now display.
+ $I->amOnPage('?p=' . $pageID);
+ $I->seeElementInDOM('form[data-sv-form]');
+ }
+
+ /**
+ * Test that Newsletter Blocks are replaced with Kit Blocks when the Tools > Newsletter: Migrate Configuration is configured.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ */
+ public function testNewsletterImportWithBlocks(EndToEndTester $I)
+ {
+ // Setup Plugin.
+ $I->setupKitPlugin($I);
+ $I->setupKitPluginResources($I);
+
+ // Insert Newsletter Block into Page.
+ $pageID = $this->_createPageWithNewsletterBlock($I);
+
+ // Navigate to the Tools screen.
+ $I->loadKitSettingsToolsScreen($I);
+
+ // Select the Kit Form to replace the Newsletter Form.
+ $I->selectOption('_wp_convertkit_integration_newsletter_settings[0]', $_ENV['CONVERTKIT_API_FORM_ID']);
+
+ // Click the Migrate button.
+ $I->click('Migrate');
+
+ // Confirm success message displays.
+ $I->waitForElementVisible('.notice-success');
+ $I->see('Newsletter forms migrated successfully.');
+
+ // View the Page, to confirm Kit Form block now displays.
+ $I->amOnPage('?p=' . $pageID);
+ $I->seeElementInDOM('form[data-sv-form]');
+ }
+
+ /**
+ * Test that the Newsletter: Migrate Configuration section is not displayed when Newsletter Forms exist,
+ * but no Pages, Posts or Custom Posts contain Newsletter Form Shortcodes.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ */
+ public function testNewsletterImportWhenNoNewsletterShortcodesInContent(EndToEndTester $I)
+ {
+ // Setup Plugin.
+ $I->setupKitPlugin($I);
+ $I->setupKitPluginResources($I);
+
+ // Navigate to the Tools screen.
+ $I->loadKitSettingsToolsScreen($I);
+
+ // Confirm no Newsletter: Migrate Configuration section is displayed, as there are no
+ // Newsletter Form Shortcodes in the content.
+ $I->dontSeeElementInDOM('#import-newsletter');
+ }
+
+ /**
+ * Test that the Newsletter: Migrate Configuration section is not displayed when no Kit Forms exist.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ */
+ public function testNewsletterImportWhenNoKitForms(EndToEndTester $I)
+ {
+ // Setup Plugin.
+ $I->setupKitPluginCredentialsNoData($I);
+ $I->setupKitPluginResourcesNoData($I);
+
+ // Navigate to the Tools screen.
+ $I->loadKitSettingsToolsScreen($I);
+
+ // Confirm no Newsletter: Migrate Configuration section is displayed, as there are no
+ // Newsletter Form Shortcodes in the content.
+ $I->dontSeeElementInDOM('#import-newsletter');
+ }
+
+ /**
+ * Create Page with Newsletter Form Shortcodes.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ * @return int
+ */
+ private function _createPageWithNewsletterFormShortcodes(EndToEndTester $I)
+ {
+ return $I->havePostInDatabase(
+ [
+ 'post_type' => 'page',
+ 'post_status' => 'publish',
+ 'post_title' => 'Page with Newsletter Form',
+ 'post_content' => '[newsletter_form]',
+ 'meta_input' => [
+ '_wp_convertkit_post_meta' => [
+ 'form' => '0',
+ 'landing_page' => '',
+ 'tag' => '',
+ ],
+ ],
+ ]
+ );
+ }
+
+ /**
+ * Create Page with Newsletter Block.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ * @return int
+ */
+ private function _createPageWithNewsletterBlock(EndToEndTester $I)
+ {
+ return $I->havePostInDatabase(
+ [
+ 'post_type' => 'page',
+ 'post_status' => 'publish',
+ 'post_title' => 'Page with Newsletter Block',
+ 'post_content' => '',
+
+ // Configure Kit Plugin to not display a default Form, so we test against the Kit Form in the content.
+ 'meta_input' => [
+ '_wp_convertkit_post_meta' => [
+ 'form' => '0',
+ 'landing_page' => '',
+ 'tag' => '',
+ ],
+ ],
+ ]
+ );
+ }
+
+ /**
+ * Deactivate and reset Plugin(s) after each test, if the test passes.
+ * We don't use _after, as this would provide a screenshot of the Plugin
+ * deactivation and not the true test error.
+ *
+ * @since 3.1.6
+ *
+ * @param EndToEndTester $I Tester.
+ */
+ public function _passed(EndToEndTester $I)
+ {
+ $I->deactivateKitPlugin($I);
+ $I->resetKitPlugin($I);
+ }
+}
diff --git a/tests/Integration/ImporterTest.php b/tests/Integration/ImporterTest.php
index 886c2a761..580d6dca3 100644
--- a/tests/Integration/ImporterTest.php
+++ b/tests/Integration/ImporterTest.php
@@ -389,4 +389,311 @@ public function testMC4WPReplaceBlocksInContent()
$this->importer->replace_blocks_in_content( parse_blocks( $content ), 4410, $_ENV['CONVERTKIT_API_FORM_ID'] )
);
}
+
+ /**
+ * Test that the get_form_ids_from_content() method returns MailPoet form shortcode Form IDs
+ * ignoring any other shortcodes.
+ *
+ * @since 3.1.6
+ */
+ public function testGetMailPoetFormIDsFromContent()
+ {
+ // Initialize the class we want to test.
+ $this->importer = new \ConvertKit_Admin_Importer_Mailpoet();
+
+ // Confirm initialization didn't result in an error.
+ $this->assertNotInstanceOf(\WP_Error::class, $this->importer);
+
+ // Define the content to test.
+ $content = '[mailpoet_form id="10"] some content [mailpoet_form id="11"] some other content [aweber formid="12"] different shortcode to ignore';
+
+ // Extract form IDs from content.
+ $form_ids = $this->importer->get_form_ids_from_content( $content );
+
+ // Assert the correct number of form IDs are returned.
+ $this->assertEquals( 2, count( $form_ids ) );
+ $this->assertEquals( 10, $form_ids[0] );
+ $this->assertEquals( 11, $form_ids[1] );
+ }
+
+ /**
+ * Test that the replace_shortcodes_in_content() method replaces the MailPoet form shortcode with the Kit form shortcode.
+ *
+ * @since 3.1.6
+ */
+ public function testMailPoetReplaceShortcodesInContent()
+ {
+ // Initialize the class we want to test.
+ $this->importer = new \ConvertKit_Admin_Importer_Mailpoet();
+
+ // Confirm initialization didn't result in an error.
+ $this->assertNotInstanceOf(\WP_Error::class, $this->importer);
+
+ // Define the shortcodes to test.
+ $shortcodes = [
+ '[mailpoet_form id="10"]',
+ '[mailpoet_form id=10]',
+ ];
+
+ // Test each shortcode is replaced with the Kit form shortcode.
+ foreach ( $shortcodes as $shortcode ) {
+ $this->assertEquals(
+ '[convertkit_form id="' . $_ENV['CONVERTKIT_API_FORM_ID'] . '"]',
+ $this->importer->replace_shortcodes_in_content( $shortcode, 10, $_ENV['CONVERTKIT_API_FORM_ID'] )
+ );
+
+ // Prepend and append some content.
+ $content = 'Some content before the shortcode: ' . $shortcode . ' Some content after the shortcode.';
+ $this->assertEquals(
+ 'Some content before the shortcode: [convertkit_form id="' . $_ENV['CONVERTKIT_API_FORM_ID'] . '"] Some content after the shortcode.',
+ $this->importer->replace_shortcodes_in_content( $content, 10, $_ENV['CONVERTKIT_API_FORM_ID'] )
+ );
+
+ // Prepend and append some content and duplicate the shortcode.
+ $content = 'Some content before the shortcode: ' . $shortcode . ' Some content after the shortcode: ' . $shortcode;
+ $this->assertEquals(
+ 'Some content before the shortcode: [convertkit_form id="' . $_ENV['CONVERTKIT_API_FORM_ID'] . '"] Some content after the shortcode: [convertkit_form id="' . $_ENV['CONVERTKIT_API_FORM_ID'] . '"]',
+ $this->importer->replace_shortcodes_in_content( $content, 10, $_ENV['CONVERTKIT_API_FORM_ID'] )
+ );
+ }
+ }
+
+ /**
+ * Test that the replace_shortcodes_in_content() method ignores non-MailPoet shortcodes.
+ *
+ * @since 3.1.6
+ */
+ public function testMailPoetReplaceShortcodesInContentIgnoringOtherShortcodes()
+ {
+ // Initialize the class we want to test.
+ $this->importer = new \ConvertKit_Admin_Importer_Mailpoet();
+
+ // Confirm initialization didn't result in an error.
+ $this->assertNotInstanceOf(\WP_Error::class, $this->importer);
+
+ // Define the shortcodes to test.
+ $shortcodes = [
+ '[convertkit_form id="' . $_ENV['CONVERTKIT_API_FORM_ID'] . '"]',
+ '[a_random_shortcode]',
+ ];
+
+ // Test each shortcode is ignored.
+ foreach ( $shortcodes as $shortcode ) {
+ $this->assertEquals(
+ $shortcode,
+ $this->importer->replace_shortcodes_in_content( $shortcode, 10, $_ENV['CONVERTKIT_API_FORM_ID'] )
+ );
+ }
+ }
+
+ /**
+ * Test that the replace_blocks_in_post() method replaces the third party form block with the Kit form block,
+ * and special characters are not stripped when the Post is saved.
+ *
+ * @since 3.1.6
+ */
+ public function testMailPoetReplaceBlocksInPost()
+ {
+ // Initialize the class we want to test.
+ $this->importer = new \ConvertKit_Admin_Importer_Mailpoet();
+
+ // Confirm initialization didn't result in an error.
+ $this->assertNotInstanceOf(\WP_Error::class, $this->importer);
+
+ // Create a Post with a MailPoet form block and HTML block, as if the user already created this post.
+ $postID = $this->factory->post->create(
+ [
+ 'post_type' => 'page',
+ 'post_status' => 'publish',
+ 'post_title' => 'MailPoet: Replace Blocks in Post',
+ 'post_content' => str_replace( '\\', '\\\\', '' . $this->html_block ),
+ ]
+ );
+
+ // Replace the blocks in the post.
+ $this->importer->replace_blocks_in_post( $postID, 4410, $_ENV['CONVERTKIT_API_FORM_ID'] );
+
+ // Test the block is replaced with the Kit form block, and special characters are not stripped.
+ $this->assertEquals(
+ '' . $this->html_block,
+ get_post_field( 'post_content', $postID )
+ );
+ }
+
+ /**
+ * Test that the replace_blocks_in_content() method replaces the third party form block with the Kit form block,
+ * and special characters are not stripped.
+ *
+ * @since 3.1.6
+ */
+ public function testMailPoetReplaceBlocksInContent()
+ {
+ // Initialize the class we want to test.
+ $this->importer = new \ConvertKit_Admin_Importer_Mailpoet();
+
+ // Confirm initialization didn't result in an error.
+ $this->assertNotInstanceOf(\WP_Error::class, $this->importer);
+
+ // Define the blocks to test.
+ $content = '' . $this->html_block;
+
+ // Test the block is replaced with the Kit form block.
+ $this->assertEquals(
+ '' . $this->html_block,
+ $this->importer->replace_blocks_in_content( parse_blocks( $content ), 4410, $_ENV['CONVERTKIT_API_FORM_ID'] )
+ );
+ }
+
+ /**
+ * Test that the get_form_ids_from_content() method returns a single Newsletter form shortcode,
+ * ignoring any other shortcodes.
+ *
+ * @since 3.1.6
+ */
+ public function testGetNewsletterFormFromContent()
+ {
+ // Initialize the class we want to test.
+ $this->importer = new \ConvertKit_Admin_Importer_Newsletter();
+
+ // Confirm initialization didn't result in an error.
+ $this->assertNotInstanceOf(\WP_Error::class, $this->importer);
+
+ // Define the content to test.
+ $content = '[newsletter_form] some content [newsletter_form] some other content [aweber formid="12"] different shortcode to ignore';
+
+ // Extract forms from content.
+ // Only one form should be returned as the Newsletter Plugin does not use IDs.
+ $form_ids = $this->importer->get_form_ids_from_content( $content );
+
+ // Assert the correct number of form IDs are returned.
+ $this->assertEquals( 1, count( $form_ids ) );
+ $this->assertEquals( 0, $form_ids[0] );
+ }
+
+ /**
+ * Test that the replace_shortcodes_in_content() method replaces the Newsletter form shortcode with the Kit form shortcode.
+ *
+ * @since 3.1.6
+ */
+ public function testNewsletterReplaceShortcodesInContent()
+ {
+ // Initialize the class we want to test.
+ $this->importer = new \ConvertKit_Admin_Importer_Newsletter();
+
+ // Confirm initialization didn't result in an error.
+ $this->assertNotInstanceOf(\WP_Error::class, $this->importer);
+
+ // Define the shortcodes to test.
+ $shortcodes = [
+ '[newsletter_form]',
+ ];
+
+ // Test each shortcode is replaced with the Kit form shortcode.
+ foreach ( $shortcodes as $shortcode ) {
+ $this->assertEquals(
+ '[convertkit_form id="' . $_ENV['CONVERTKIT_API_FORM_ID'] . '"]',
+ $this->importer->replace_shortcodes_in_content( $shortcode, 0, $_ENV['CONVERTKIT_API_FORM_ID'] )
+ );
+
+ // Prepend and append some content.
+ $content = 'Some content before the shortcode: ' . $shortcode . ' Some content after the shortcode.';
+ $this->assertEquals(
+ 'Some content before the shortcode: [convertkit_form id="' . $_ENV['CONVERTKIT_API_FORM_ID'] . '"] Some content after the shortcode.',
+ $this->importer->replace_shortcodes_in_content( $content, 0, $_ENV['CONVERTKIT_API_FORM_ID'] )
+ );
+
+ // Prepend and append some content and duplicate the shortcode.
+ $content = 'Some content before the shortcode: ' . $shortcode . ' Some content after the shortcode: ' . $shortcode;
+ $this->assertEquals(
+ 'Some content before the shortcode: [convertkit_form id="' . $_ENV['CONVERTKIT_API_FORM_ID'] . '"] Some content after the shortcode: [convertkit_form id="' . $_ENV['CONVERTKIT_API_FORM_ID'] . '"]',
+ $this->importer->replace_shortcodes_in_content( $content, 0, $_ENV['CONVERTKIT_API_FORM_ID'] )
+ );
+ }
+ }
+
+ /**
+ * Test that the replace_shortcodes_in_content() method ignores non-MailPoet shortcodes.
+ *
+ * @since 3.1.6
+ */
+ public function testNewsletterReplaceShortcodesInContentIgnoringOtherShortcodes()
+ {
+ // Initialize the class we want to test.
+ $this->importer = new \ConvertKit_Admin_Importer_Newsletter();
+
+ // Confirm initialization didn't result in an error.
+ $this->assertNotInstanceOf(\WP_Error::class, $this->importer);
+
+ // Define the shortcodes to test.
+ $shortcodes = [
+ '[convertkit_form id="' . $_ENV['CONVERTKIT_API_FORM_ID'] . '"]',
+ '[a_random_shortcode]',
+ ];
+
+ // Test each shortcode is ignored.
+ foreach ( $shortcodes as $shortcode ) {
+ $this->assertEquals(
+ $shortcode,
+ $this->importer->replace_shortcodes_in_content( $shortcode, 0, $_ENV['CONVERTKIT_API_FORM_ID'] )
+ );
+ }
+ }
+
+ /**
+ * Test that the replace_blocks_in_post() method replaces the third party form block with the Kit form block,
+ * and special characters are not stripped when the Post is saved.
+ *
+ * @since 3.1.6
+ */
+ public function testNewsletterReplaceBlocksInPost()
+ {
+ // Initialize the class we want to test.
+ $this->importer = new \ConvertKit_Admin_Importer_Newsletter();
+
+ // Confirm initialization didn't result in an error.
+ $this->assertNotInstanceOf(\WP_Error::class, $this->importer);
+
+ // Create a Post with a MailPoet form block and HTML block, as if the user already created this post.
+ $postID = $this->factory->post->create(
+ [
+ 'post_type' => 'page',
+ 'post_status' => 'publish',
+ 'post_title' => 'Newsletter: Replace Blocks in Post',
+ 'post_content' => str_replace( '\\', '\\\\', '' . $this->html_block ),
+ ]
+ );
+
+ // Replace the blocks in the post.
+ $this->importer->replace_blocks_in_post( $postID, 0, $_ENV['CONVERTKIT_API_FORM_ID'] );
+
+ // Test the block is replaced with the Kit form block, and special characters are not stripped.
+ $this->assertEquals(
+ '' . $this->html_block,
+ get_post_field( 'post_content', $postID )
+ );
+ }
+
+ /**
+ * Test that the replace_blocks_in_content() method replaces the third party form block with the Kit form block,
+ * and special characters are not stripped.
+ *
+ * @since 3.1.6
+ */
+ public function testNewsletterReplaceBlocksInContent()
+ {
+ // Initialize the class we want to test.
+ $this->importer = new \ConvertKit_Admin_Importer_Newsletter();
+
+ // Confirm initialization didn't result in an error.
+ $this->assertNotInstanceOf(\WP_Error::class, $this->importer);
+
+ // Define the blocks to test.
+ $content = '' . $this->html_block;
+
+ // Test the block is replaced with the Kit form block.
+ $this->assertEquals(
+ '' . $this->html_block,
+ $this->importer->replace_blocks_in_content( parse_blocks( $content ), 0, $_ENV['CONVERTKIT_API_FORM_ID'] )
+ );
+ }
}
diff --git a/tests/Support/Data/dump.sql b/tests/Support/Data/dump.sql
index 1931c2ea0..ab2895aff 100644
--- a/tests/Support/Data/dump.sql
+++ b/tests/Support/Data/dump.sql
@@ -97,6 +97,19 @@ CREATE TABLE `wp_links` (
KEY `link_visible` (`link_visible`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
+DROP TABLE IF EXISTS `wp_mailpoet_forms`;
+CREATE TABLE `wp_mailpoet_forms` (
+ `id` int unsigned NOT NULL AUTO_INCREMENT,
+ `name` varchar(90) COLLATE utf8mb4_unicode_520_ci NOT NULL,
+ `status` varchar(20) COLLATE utf8mb4_unicode_520_ci NOT NULL DEFAULT 'enabled',
+ `body` longtext COLLATE utf8mb4_unicode_520_ci,
+ `settings` longtext COLLATE utf8mb4_unicode_520_ci,
+ `styles` longtext COLLATE utf8mb4_unicode_520_ci,
+ `created_at` timestamp NULL DEFAULT NULL,
+ `updated_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ `deleted_at` timestamp NULL DEFAULT NULL,
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
DROP TABLE IF EXISTS `wp_options`;
CREATE TABLE `wp_options` (
diff --git a/views/backend/settings/tools.php b/views/backend/settings/tools.php
index 597fb4d72..a6e05be82 100644
--- a/views/backend/settings/tools.php
+++ b/views/backend/settings/tools.php
@@ -212,6 +212,116 @@
has_forms_in_posts() && $mailpoet->has_forms() && $forms->exist() ) {
+ ?>
+