Skip to content
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
<?php
/**
* ConvertKit Admin Importer Kit Legacy Forms class.
*
* @package ConvertKit
* @author ConvertKit
*/

/**
* Import and migrate data from Kit Legacy Forms to Kit.
*
* @package ConvertKit
* @author ConvertKit
*/
class ConvertKit_Admin_Importer_ConvertKit_Legacy_Forms extends ConvertKit_Admin_Importer {

/**
* Holds the programmatic name of the importer (lowercase, no spaces).
*
* @since 3.3.5
*
* @var string
*/
public $name = 'convertkit_legacy_forms';

/**
* Holds the title of the importer (for display in the importer list).
*
* @since 3.3.5
*
* @var string
*/
public $title = 'Kit Legacy Forms';

/**
* Holds the shortcode name for ConvertKit Legacy Forms.
*
* @since 3.3.5
*
* @var string
*/
public $shortcode_name = 'convertkit_form';

/**
* Holds the ID attribute names for Kit Legacy Form shortcodes.
*
* Both `form` and `id` are matched because Kit's `[convertkit_form]`
* shortcode has supported both attribute names historically.
*
* @since 3.3.5
*
* @var array
*/
public $shortcode_id_attribute = array( 'form', 'id' );

/**
* Holds the block name for ConvertKit Legacy Forms.
*
* @since 3.3.5
*
* @var string
*/
public $block_name = 'convertkit/form';

/**
* Holds the ID attribute names for Kit Legacy Form blocks.
*
* Both `form` and `id` are matched because Kit's form block has supported
* both attribute names historically.
*
* @since 3.3.5
*
* @var array
*/
public $block_id_attribute = array( 'form', 'id' );

/**
* Constructor
*
* @since 3.3.5
*/
public function __construct() {

// Define a custom description for this importer.
$this->description = __( 'Kit Legacy Forms are being phased out. Use this tool to replace Kit Form shortcodes and blocks using a Legacy Form with a new Kit Form.', 'convertkit' );

// Register this as an importer, if ConvertKit Legacy Forms exist.
add_filter( 'convertkit_get_form_importers', array( $this, 'register' ) );

}

/**
* Returns an array of Kit Legacy Forms form IDs and titles.
*
* @since 3.3.5
*
* @return array
*/
public function get_forms() {

// Query resource class to fetch legacy forms.
$forms = array();
$convertkit_forms = new ConvertKit_Resource_Forms( 'settings' );
if ( $convertkit_forms->exist() ) {
foreach ( $convertkit_forms->get() as $form ) {
// Skip if not a Legacy Form.
if ( ! $convertkit_forms->is_legacy( $form['id'] ) ) {
continue;
}

$forms[ $form['id'] ] = $form['name'];
}
}

return $forms;

}

/**
* Returns an array of legacy Kit form IDs (as strings) for use in
* filtering shortcodes/blocks that reference them.
*
* @since 3.3.5
*
* @return array
*/
private function get_legacy_form_ids() {

$legacy_ids = array();
$convertkit_forms = new ConvertKit_Resource_Forms( 'settings' );

if ( ! $convertkit_forms->exist() ) {
return $legacy_ids;
}

foreach ( $convertkit_forms->get() as $form ) {
if ( ! $convertkit_forms->is_legacy( $form['id'] ) ) {
continue;
}
$legacy_ids[] = (string) $form['id'];
}

return $legacy_ids;

}

/**
* Overrides the parent method to:
* - return form IDs found in both shortcodes AND blocks (the parent only
* handles shortcodes), and
* - filter the result so only legacy form IDs are returned.
*
* @since 3.3.5
*
* @param string $content Content containing Kit Form shortcodes / blocks.
* @return array
*/
public function get_form_ids_from_content( $content ) {

// Get shortcode-derived form IDs from the parent.
$shortcode_ids = parent::get_form_ids_from_content( $content );

// Get block-derived form IDs (parent only handles shortcodes).
$block_ids = $this->get_block_form_ids_from_content( $content );

// Combine and filter to legacy form IDs only.
$all_ids = array_unique( array_merge( $shortcode_ids, $block_ids ) );
$legacy_ids = $this->get_legacy_form_ids();

// Cast both sides to strings for safe comparison.
$all_ids = array_map( 'strval', $all_ids );

return array_values( array_intersect( $all_ids, $legacy_ids ) );

}

/**
* Returns an array of form IDs from convertkit/form blocks in the given
* content. Walks innerBlocks recursively.
*
* @since 3.3.5
*
* @param string $content Content containing Kit Form blocks.
* @return array
*/
private function get_block_form_ids_from_content( $content ) {

return $this->extract_block_form_ids( parse_blocks( $content ) );

}

/**
* Recursively walks blocks (and innerBlocks) and returns an array of form
* IDs from any convertkit/form block's `form` attribute.
*
* @since 3.3.5
*
* @param array $blocks Blocks.
* @return array
*/
private function extract_block_form_ids( $blocks ) {

$form_ids = array();

// Normalise the block ID attribute(s) to an array so we can match
// against multiple attribute names (e.g. both `form` and `id`).
$id_attributes = (array) $this->block_id_attribute;

foreach ( $blocks as $block ) {
if ( ! empty( $block['innerBlocks'] ) ) {
$form_ids = array_merge(
$form_ids,
$this->extract_block_form_ids( $block['innerBlocks'] )
);
}

if ( $block['blockName'] !== $this->block_name ) {
continue;
}

// Record the form ID from the first matching attribute. If a block
// somehow has both `form` and `id` set, they'd be the same value, so
// we stop after the first match to avoid double-counting.
foreach ( $id_attributes as $id_attribute ) {
if ( empty( $block['attrs'][ $id_attribute ] ) ) {
continue;
}
$form_ids[] = (string) $block['attrs'][ $id_attribute ];
break;
}
}

return $form_ids;

}

/**
* Overrides the parent method to only return post IDs whose content
* contains a Kit Form shortcode or block referencing a legacy form ID.
*
* The parent's broad SQL match returns any post containing a
* `[convertkit_form` shortcode or `<!-- wp:convertkit/form` block, which
* for the Kit Legacy Forms importer is too broad: non-legacy uses of these
* shortcodes/blocks are valid and should not appear in the importer UI.
*
* @since 3.3.5
*
* @return array
*/
public function get_forms_in_posts() {

$candidate_post_ids = parent::get_forms_in_posts();

if ( empty( $candidate_post_ids ) ) {
return array();
}

$legacy_ids = $this->get_legacy_form_ids();
if ( empty( $legacy_ids ) ) {
return array();
}

$matched_post_ids = array();
foreach ( $candidate_post_ids as $post_id ) {
$content = get_post_field( 'post_content', $post_id );

// get_form_ids_from_content() (overridden above) returns only legacy IDs
// from both shortcodes and blocks, so any non-empty result means this
// post contains at least one legacy form reference.
$legacy_form_ids_in_content = $this->get_form_ids_from_content( $content );
if ( ! empty( $legacy_form_ids_in_content ) ) {
$matched_post_ids[] = $post_id;
}
}

return $matched_post_ids;

}

}
16 changes: 13 additions & 3 deletions admin/importers/class-convertkit-admin-importer.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,15 @@ abstract class ConvertKit_Admin_Importer {
*/
public $title = '';

/**
* Holds the importer description.
*
* @since 3.3.5
*
* @var string
*/
public $description = '';

/**
* Holds the shortcode name for the third party Form plugin.
*
Expand Down Expand Up @@ -107,9 +116,10 @@ public function register( $importers ) {

// Add this importer to the list of importers.
$importers[ $this->name ] = array(
'name' => $this->name,
'title' => $this->title,
'forms' => $this->get_forms(),
'name' => $this->name,
'title' => $this->title,
'description' => $this->description,
'forms' => $this->get_forms(),
);

return $importers;
Expand Down
1 change: 1 addition & 0 deletions admin/section/class-convertkit-admin-section-tools.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ public function register_notices( $notices ) {
'migrate_activecampaign_configuration_success' => __( 'ActiveCampaign forms migrated successfully.', 'convertkit' ),
'migrate_aweber_configuration_success' => __( 'AWeber forms migrated successfully.', 'convertkit' ),
'migrate_campaignmonitor_configuration_success' => __( 'Campaign Monitor forms migrated successfully.', 'convertkit' ),
'migrate_convertkit_legacy_forms_configuration_success' => __( 'Kit Legacy 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' ),
Expand Down
35 changes: 18 additions & 17 deletions includes/class-wp-convertkit.php
Original file line number Diff line number Diff line change
Expand Up @@ -79,23 +79,24 @@ private function initialize_admin() {
return;
}

$this->classes['admin_bulk_edit'] = new ConvertKit_Admin_Bulk_Edit();
$this->classes['admin_cache_plugins'] = new ConvertKit_Admin_Cache_Plugins();
$this->classes['admin_category'] = new ConvertKit_Admin_Category();
$this->classes['admin_landing_page'] = new ConvertKit_Admin_Landing_Page();
$this->classes['admin_importer_activecampaign'] = new ConvertKit_Admin_Importer_ActiveCampaign();
$this->classes['admin_importer_aweber'] = new ConvertKit_Admin_Importer_AWeber();
$this->classes['admin_importer_campaignmonitor'] = new ConvertKit_Admin_Importer_CampaignMonitor();
$this->classes['admin_importer_mc4wp'] = new ConvertKit_Admin_Importer_MC4WP();
$this->classes['admin_importer_mailpoet'] = new ConvertKit_Admin_Importer_Mailpoet();
$this->classes['admin_importer_newsletter'] = new ConvertKit_Admin_Importer_Newsletter();
$this->classes['admin_post'] = new ConvertKit_Admin_Post();
$this->classes['admin_quick_edit'] = new ConvertKit_Admin_Quick_Edit();
$this->classes['admin_restrict_content'] = new ConvertKit_Admin_Restrict_Content();
$this->classes['admin_settings'] = new ConvertKit_Admin_Settings();
$this->classes['admin_setup_wizard_landing_page'] = new ConvertKit_Admin_Setup_Wizard_Landing_Page();
$this->classes['admin_setup_wizard_plugin'] = new ConvertKit_Admin_Setup_Wizard_Plugin();
$this->classes['admin_setup_wizard_restrict_content'] = new ConvertKit_Admin_Setup_Wizard_Restrict_Content();
$this->classes['admin_bulk_edit'] = new ConvertKit_Admin_Bulk_Edit();
$this->classes['admin_cache_plugins'] = new ConvertKit_Admin_Cache_Plugins();
$this->classes['admin_category'] = new ConvertKit_Admin_Category();
$this->classes['admin_landing_page'] = new ConvertKit_Admin_Landing_Page();
$this->classes['admin_importer_activecampaign'] = new ConvertKit_Admin_Importer_ActiveCampaign();
$this->classes['admin_importer_aweber'] = new ConvertKit_Admin_Importer_AWeber();
$this->classes['admin_importer_campaignmonitor'] = new ConvertKit_Admin_Importer_CampaignMonitor();
$this->classes['admin_importer_convertkit_legacy_forms'] = new ConvertKit_Admin_Importer_ConvertKit_Legacy_Forms();
$this->classes['admin_importer_mc4wp'] = new ConvertKit_Admin_Importer_MC4WP();
$this->classes['admin_importer_mailpoet'] = new ConvertKit_Admin_Importer_Mailpoet();
$this->classes['admin_importer_newsletter'] = new ConvertKit_Admin_Importer_Newsletter();
$this->classes['admin_post'] = new ConvertKit_Admin_Post();
$this->classes['admin_quick_edit'] = new ConvertKit_Admin_Quick_Edit();
$this->classes['admin_restrict_content'] = new ConvertKit_Admin_Restrict_Content();
$this->classes['admin_settings'] = new ConvertKit_Admin_Settings();
$this->classes['admin_setup_wizard_landing_page'] = new ConvertKit_Admin_Setup_Wizard_Landing_Page();
$this->classes['admin_setup_wizard_plugin'] = new ConvertKit_Admin_Setup_Wizard_Plugin();
$this->classes['admin_setup_wizard_restrict_content'] = new ConvertKit_Admin_Setup_Wizard_Restrict_Content();

/**
* Initialize integration classes for the WordPress Administration interface.
Expand Down
14 changes: 9 additions & 5 deletions views/backend/settings/tools.php
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,15 @@

<p class="description">
<?php
printf(
/* translators: %s: Importer title */
esc_html__( 'Automatically replace %s form shortcodes and blocks with Kit form shortcodes and blocks.', 'convertkit' ),
esc_html( $importer['title'] )
);
if ( ! empty( $importer['description'] ) ) {
echo esc_html( $importer['description'] );
} else {
printf(
/* translators: %s: Importer title */
esc_html__( 'Automatically replace %s form shortcodes and blocks with Kit form shortcodes and blocks.', 'convertkit' ),
esc_html( $importer['title'] )
);
}
?>
<br />
</p>
Expand Down
1 change: 1 addition & 0 deletions wp-convertkit.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@
require_once CONVERTKIT_PLUGIN_PATH . '/admin/importers/class-convertkit-admin-importer-activecampaign.php';
require_once CONVERTKIT_PLUGIN_PATH . '/admin/importers/class-convertkit-admin-importer-aweber.php';
require_once CONVERTKIT_PLUGIN_PATH . '/admin/importers/class-convertkit-admin-importer-campaignmonitor.php';
require_once CONVERTKIT_PLUGIN_PATH . '/admin/importers/class-convertkit-admin-importer-convertkit-legacy-forms.php';
require_once CONVERTKIT_PLUGIN_PATH . '/admin/importers/class-convertkit-admin-importer-mc4wp.php';
require_once CONVERTKIT_PLUGIN_PATH . '/admin/importers/class-convertkit-admin-importer-mailpoet.php';
require_once CONVERTKIT_PLUGIN_PATH . '/admin/importers/class-convertkit-admin-importer-newsletter.php';
Expand Down