each ( $forms as $form ) { $this->migrate_form( $form ); } } /** * Get migrated forms. * * @since 1.9.3 * * @param array $old_account_ids Old v2 account ids. * * @return array * @noinspection SqlResolve */ private function get_forms( array $old_account_ids ): array { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching $forms = $wpdb->get_col( $wpdb->prepare( 'SELECT ID FROM ' . $wpdb->posts . ' WHERE post_type = "wpforms" AND post_content REGEXP %s', implode( '|', $old_account_ids ) ) ); if ( empty( $forms ) ) { return []; } $form_ids = array_map( 'absint', $forms ); $form_obj = wpforms()->obj( 'form' ); if ( ! $form_obj ) { return []; } return (array) $form_obj->get( '', [ 'numberposts' => -1, 'orderby' => 'post__in', 'post__in' => $form_ids, 'update_post_meta_cache' => false, 'update_post_term_cache' => false, 'no_found_rows' => true, ] ); } /** * Copy connections from v2 to v3 in proper format. * * @since 1.9.3 * * @param WP_Post $form Form object. */ private function migrate_form( WP_Post $form ) { $this->form_data = wpforms_decode( $form->post_content ); // Nothing to migrate. if ( empty( $this->form_data['providers']['constant-contact'] ) ) { return; } $migrated_connections = $this->form_data['providers'][ Core::SLUG ] ?? []; // All connections were migrated but account migration was interrupted by timeout or an error. if ( count( $this->form_data['providers']['constant-contact'] ) === count( $migrated_connections ) ) { return; } $this->form_data['providers'][ Core::SLUG ] = array_merge( $migrated_connections, $this->get_new_connections() ); $form_obj = wpforms()->obj( 'form' ); if ( ! $form_obj ) { return; } $form_obj->update( $this->form_data['id'], $this->form_data ); } /** * Modify v2 connections to v3. * * @since 1.9.3 * * @return array */ private function get_new_connections(): array { $old_connections = $this->form_data['providers']['constant-contact'] ?? []; $new_connections = []; foreach ( $old_connections as $connection_id => $connection ) { $new_connection_id = str_replace( 'connection_', '', $connection_id ); $connection = wp_parse_args( $connection, [ 'connection_name' => '', 'account_id' => '', 'list_id' => '', 'fields' => [], 'conditional_logic' => '', 'conditional_type' => '', 'conditionals' => [], ] ); // The connection is related to another account, skip it. if ( $this->new_account['id'] !== $connection['account_id'] ) { continue; } reset( $this->lists ); $new_connections[ $new_connection_id ] = [ 'id' => $new_connection_id, 'name' => $connection['connection_name'], 'account_id' => $connection['account_id'], 'action' => 'subscribe', 'list' => $this->lists[ $connection['list_id'] ] ?? key( $this->lists ), 'email' => explode( '.', $connection['fields']['email'] ?? '' )[0], 'fields_meta' => $this->get_connection_custom_fields( $connection['fields'] ), 'conditional_logic' => $connection['conditional_logic'], 'conditional_type' => $connection['conditional_type'], 'conditionals' => $connection['conditionals'], ]; } return $new_connections; } /** * Get custom fields. * * @since 1.9.3 * * @param array $custom_fields Custom fields v2. * * @return array */ private function get_connection_custom_fields( array $custom_fields ): array { $fields_meta = []; $custom_fields = $this->sort_custom_fields( $custom_fields ); foreach ( $custom_fields as $key => $value ) { if ( $key === 'email' ) { continue; } $value_parts = explode( '.', $value ); $field_id = $value_parts[0]; if ( wpforms_is_empty_string( $field_id ) ) { continue; } $fields_meta = $this->update_fields_meta( $fields_meta, $field_id, $key, $value_parts ); } return $fields_meta; } /** * Move $custom_fields['full_name'] at the beginning of the array. * * Thanks to this, if first name and last name are defined, next iterations * of this array will replace full_name - backward compatibility sustained. * * @since 1.9.3 * * @param array $custom_fields Custom fields. * * @return array */ private function sort_custom_fields( array $custom_fields ): array { if ( ! isset( $custom_fields['full_name'] ) || wpforms_is_empty_string( $custom_fields['full_name'] ) ) { return $custom_fields; } $full_name = $custom_fields['full_name']; unset( $custom_fields['full_name'] ); return [ 'full_name' => $full_name ] + $custom_fields; } /** * Update fields meta. * * @since 1.9.3 * * @param array $fields_meta Fields meta. * @param string $field_id Field ID. * @param string $key Key. * @param array $value_parts Value parts. * * @return array */ private function update_fields_meta( array $fields_meta, string $field_id, string $key, array $value_parts ): array { if ( $this->form_data['fields'][ $field_id ]['type'] === 'name' ) { $name_field = $this->handle_name_field( $fields_meta, $field_id, $key, $value_parts ); if ( is_array( $name_field ) ) { return $name_field; } $field_id = $name_field; } $keys_to_rename = [ 'work_phone' => 'phone', 'url' => $this->get_url_field_id(), ]; $new_key = $keys_to_rename[ $key ] ?? $key; $fields_meta[ $this->get_meta_next_index( $fields_meta, $new_key ) ] = [ 'name' => $new_key, 'field_id' => $field_id, ]; return $fields_meta; } /** * Handle name field. * * @since 1.9.3 * * @param array $fields_meta Fields meta. * @param string $field_id Field ID. * @param string $key Key. * @param array $value_parts Value parts. * * @return string|array */ private function handle_name_field( array $fields_meta, string $field_id, string $key, array $value_parts ) { if ( $value_parts[1] === 'value' ) { $value_parts[1] = 'full'; } if ( $key === 'full_name' ) { return $this->update_full_name( $fields_meta, $field_id, $value_parts ); } $field_id .= '.' . $value_parts[1]; return $field_id; } /** * Update full name meta. * * @since 1.9.3 * * @param array $fields_meta Fields meta. * @param string $field_id Field ID. * @param array $value_parts Value parts. * * @return array */ private function update_full_name( array $fields_meta, string $field_id, array $value_parts ): array { $field = $this->form_data['fields'][ $field_id ] ?? []; $is_simple = ! isset( $field['format'] ) || $field['format'] === 'simple'; $first_name_field_id = $is_simple ? $field_id . '.' . $value_parts[1] : $field_id . '.first'; $fields_meta[] = [ 'name' => 'first_name', 'field_id' => $first_name_field_id, ]; $this->first_name_index = count( $fields_meta ) - 1; if ( $is_simple ) { return $fields_meta; } $last_name_field_id = $field_id . '.last'; $fields_meta[] = [ 'name' => 'last_name', 'field_id' => $last_name_field_id, ]; $this->last_name_index = count( $fields_meta ) - 1; return $fields_meta; } /** * Get next index for a custom field. * * @since 1.9.3 * * @param array $fields_meta Fields meta. * @param string $key Key. */ private function get_meta_next_index( array $fields_meta, string $key ): int { if ( $key === 'first_name' ) { return $this->first_name_index ?? count( $fields_meta ); } if ( $key === 'last_name' ) { return $this->last_name_index ?? count( $fields_meta ); } return count( $fields_meta ); } /** * Get URL custom field ID from the new account. * * Returns the id in the new format. * * @since 1.9.3 * * @return string */ private function get_url_field_id(): string { static $field_id; if ( $field_id ) { return $field_id; } $custom_fields = ( new Api( $this->new_account ) )->get_custom_fields( 'custom_field_id', 'name' ); $field_id = $custom_fields['custom_field_1'] ?? $this->register_url_field(); return $field_id; } /** * Get an array of list v2 ids to v3 ids. * * @since 1.9.3 * * @param array $new_account New account data. * @param string $access_token_v2 Access token for v2. * * @return array * * @throws RuntimeException Can't receive v2 lists and finish migration. */ private function get_lists_xhref( array $new_account, string $access_token_v2 ): array { $old_provider = new WPForms_Constant_Contact(); $old_provider->access_token = $access_token_v2; $old_lists = $old_provider->api_lists(); if ( is_wp_error( $old_lists ) ) { throw new RuntimeException( esc_html__( 'Can\'t receive v2 lists and finish migration.', 'wpforms-lite' ) ); } return ( new Api( $new_account ) )->get_contact_list_xrefs( (array) $old_lists ); } /** * Register URL custom field. * * @since 1.9.3 * * @return string */ private function register_url_field(): string { return ( new Api( $this->new_account ) )->register_custom_field( 'Website / URL' ); } }