id values where the notice should render
*
* @return array
*/
public static function known_sources() : array {
$sources = [
'wp-help' => [
'post_type' => 'wp-help',
'plugin_label' => __( 'WP Help', 'admin-help-docs' ),
'doc_label' => __( 'WP Help Documents', 'admin-help-docs' ),
'pages' => [ 'wp-help-documents' ],
'screens' => [ 'edit-wp-help' ],
'notice_message' => __( 'WP Help has not been updated in a couple of years. You can migrate your existing docs to Admin Help Docs in a couple of clicks, no need to recreate them.', 'admin-help-docs' ),
],
];
return apply_filters( 'helpdocs_migrate_known_sources', $sources );
} // End known_sources()
/**
* Get post types eligible as a migration source (excludes our own types and non-public built-ins)
*
* @return array Associative array of post_type => label
*/
public static function eligible_post_types() : array {
$excluded = [
HelpDocs::$post_type,
Imports::$post_type,
'attachment',
'revision',
'nav_menu_item',
'wp_block',
'wp_template',
'wp_template_part',
'wp_navigation',
'wp_font_family',
'wp_font_face',
'wp_global_styles',
'customize_changeset',
'oembed_cache',
'user_request',
];
$label_overrides = [];
foreach ( self::known_sources() as $source ) {
if ( ! empty( $source[ 'doc_label' ] ) ) {
$label_overrides[ $source[ 'post_type' ] ] = $source[ 'doc_label' ];
}
}
$post_types = get_post_types( [ 'show_ui' => true ], 'objects' );
$eligible = [];
foreach ( $post_types as $post_type ) {
if ( in_array( $post_type->name, $excluded, true ) ) {
continue;
}
$eligible[ $post_type->name ] = $label_overrides[ $post_type->name ] ?? $post_type->label;
}
return apply_filters( 'helpdocs_migrate_eligible_post_types', $eligible );
} // End eligible_post_types()
/**
* Render migration notices for any known source plugin that's active and on a matching screen
*
* @return void
*/
public function render_migration_notices() {
$current_page = isset( $_GET[ 'page' ] ) ? sanitize_text_field( wp_unslash( $_GET[ 'page' ] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$screen = get_current_screen();
$screen_id = $screen ? $screen->id : '';
foreach ( self::known_sources() as $key => $source ) {
if ( ! post_type_exists( $source[ 'post_type' ] ) ) {
continue;
}
$matches_page = in_array( $current_page, $source[ 'pages' ] ?? [], true );
$matches_screen = in_array( $screen_id, $source[ 'screens' ] ?? [], true );
if ( ! $matches_page && ! $matches_screen ) {
continue;
}
if ( get_user_meta( get_current_user_id(), 'helpdocs_dismissed_migrate_notice_' . $key, true ) ) {
continue;
}
$migrate_url = add_query_arg( [ 'source_post_type' => $source[ 'post_type' ] ], Bootstrap::tab_url( 'migrate' ) );
$message = $source[ 'notice_message' ] ?? sprintf(
/* translators: %s: plugin name */
__( '%s docs can be migrated to Admin Help Docs in a couple of clicks, no need to recreate them.', 'admin-help-docs' ),
$source[ 'plugin_label' ]
);
echo '
';
}
} // End render_migration_notices()
/**
* Enqueue the notice-dismiss script on screens where a migration notice may render
*
* @return void
*/
public function enqueue_notice_script() {
if ( ! self::current_screen_has_notice() ) {
return;
}
$text_domain = Bootstrap::textdomain();
$version = Bootstrap::script_version();
wp_enqueue_style( $text_domain . '-migrate', Bootstrap::url( 'inc/tabs/css/migrate.css' ), [ $text_domain . '-docs' ], $version );
wp_enqueue_script( $text_domain . '-migrate-notice', Bootstrap::url( 'inc/tabs/js/migrate-notice.js' ), [ 'jquery' ], $version, true );
wp_localize_script( $text_domain . '-migrate-notice', 'helpdocs_migrate_notice', [
'nonce' => wp_create_nonce( 'helpdocs_migrate_nonce' ),
] );
} // End enqueue_notice_script()
/**
* Whether the current screen matches any known source's notice targets
*
* @return bool
*/
public static function current_screen_has_notice() : bool {
$current_page = isset( $_GET[ 'page' ] ) ? sanitize_text_field( wp_unslash( $_GET[ 'page' ] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$screen = get_current_screen();
$screen_id = $screen ? $screen->id : '';
foreach ( self::known_sources() as $source ) {
if ( ! post_type_exists( $source[ 'post_type' ] ) ) {
continue;
}
if ( in_array( $current_page, $source[ 'pages' ] ?? [], true ) || in_array( $screen_id, $source[ 'screens' ] ?? [], true ) ) {
return true;
}
}
return false;
} // End current_screen_has_notice()
/**
* AJAX: dismiss a migration notice for the current user
*
* @return void
*/
public function ajax_dismiss_notice() {
check_ajax_referer( 'helpdocs_migrate_nonce', 'nonce' );
$key = isset( $_POST[ 'source_key' ] ) ? sanitize_key( wp_unslash( $_POST[ 'source_key' ] ) ) : '';
if ( $key ) {
update_user_meta( get_current_user_id(), 'helpdocs_dismissed_migrate_notice_' . $key, 1 );
}
wp_send_json_success();
} // End ajax_dismiss_notice()
/**
* Render the tab
*/
public function render_tab() {
$text_domain = Bootstrap::textdomain();
$version = Bootstrap::script_version();
wp_enqueue_style( $text_domain . '-migrate', Bootstrap::url( 'inc/tabs/css/migrate.css' ), [ $text_domain . '-docs' ], $version );
wp_enqueue_script( $text_domain . '-migrate', Bootstrap::url( 'inc/tabs/js/migrate.js' ), [ 'jquery' ], $version, true );
wp_localize_script( $text_domain . '-migrate', 'helpdocs_migrate', [
'ajax_url' => admin_url( 'admin-ajax.php' ),
'nonce' => wp_create_nonce( 'helpdocs_migrate_nonce' ),
'docs_url' => Bootstrap::tab_url( 'documentation' ),
'i18n' => [
'no_posts' => __( 'No posts found for this post type.', 'admin-help-docs' ),
'already_migrated' => __( 'Migrated', 'admin-help-docs' ),
'migrating' => __( 'Migrating…', 'admin-help-docs' ),
'loading' => __( 'Loading posts…', 'admin-help-docs' ),
'error' => __( 'Something went wrong loading these posts. Please try again.', 'admin-help-docs' ),
'done' => __( 'Migration completed successfully. Migrated %migrated%, skipped %skipped%.', 'admin-help-docs' ),
'go_to_docs' => __( 'Go to Main Documentation Page!', 'admin-help-docs' ),
'select_all' => __( 'Select All', 'admin-help-docs' ),
'deselect_all' => __( 'Deselect All', 'admin-help-docs' ),
],
] );
$preselect = isset( $_GET[ 'source_post_type' ] ) ? sanitize_key( wp_unslash( $_GET[ 'source_post_type' ] ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$post_types = self::eligible_post_types();
?>
__( 'Insufficient permissions.', 'admin-help-docs' ) ] );
}
$source_post_type = isset( $_POST[ 'source_post_type' ] ) ? sanitize_key( wp_unslash( $_POST[ 'source_post_type' ] ) ) : '';
if ( ! $source_post_type || ! array_key_exists( $source_post_type, self::eligible_post_types() ) ) {
wp_send_json_error( [ 'message' => __( 'Invalid post type.', 'admin-help-docs' ) ] );
}
$source_posts = get_posts( [
'post_type' => $source_post_type,
'post_status' => [ 'publish', 'draft' ],
'posts_per_page' => -1,
'orderby' => 'title',
'order' => 'ASC',
] );
$already_migrated = self::get_imported_source_ids( $source_post_type );
$rows = [];
foreach ( $source_posts as $source_post ) {
$rows[] = [
'id' => $source_post->ID,
'title' => get_the_title( $source_post ),
'imported' => in_array( $source_post->ID, $already_migrated, true ),
];
}
wp_send_json_success( [ 'posts' => $rows ] );
} // End ajax_list_posts()
/**
* AJAX: import the selected posts into Admin Help Docs
*
* @return void
*/
public function ajax_import_posts() {
check_ajax_referer( 'helpdocs_migrate_nonce', 'nonce' );
if ( ! current_user_can( 'edit_posts' ) ) {
wp_send_json_error( [ 'message' => __( 'Insufficient permissions.', 'admin-help-docs' ) ] );
}
$source_post_type = isset( $_POST[ 'source_post_type' ] ) ? sanitize_key( wp_unslash( $_POST[ 'source_post_type' ] ) ) : '';
$source_ids = isset( $_POST[ 'post_ids' ] ) ? array_map( 'absint', (array) wp_unslash( $_POST[ 'post_ids' ] ) ) : [];
$eligible = self::eligible_post_types();
if ( ! $source_post_type || ! array_key_exists( $source_post_type, $eligible ) || empty( $source_ids ) ) {
wp_send_json_error( [ 'message' => __( 'Nothing to import.', 'admin-help-docs' ) ] );
}
$folder_term_id = self::get_or_create_folder_term( $eligible[ $source_post_type ] );
global $wpdb;
$imported = 0;
$skipped = 0;
$last_id = 0;
foreach ( $source_ids as $source_id ) {
$source_post = get_post( $source_id );
if ( ! $source_post || $source_post_type !== $source_post->post_type ) {
$skipped++;
continue;
}
if ( self::already_imported( $source_post_type, $source_id ) ) {
$skipped++;
continue;
}
$new_id = wp_insert_post( [
'post_type' => HelpDocs::$post_type,
'post_title' => $source_post->post_title,
'post_content' => wp_kses_post( $source_post->post_content ),
'post_excerpt' => $source_post->post_excerpt,
'post_status' => 'publish',
], true );
if ( is_wp_error( $new_id ) ) {
$skipped++;
continue;
}
$wpdb->update(
$wpdb->posts,
[
'post_date' => $source_post->post_date,
'post_date_gmt' => $source_post->post_date_gmt,
'post_modified' => $source_post->post_modified,
'post_modified_gmt' => $source_post->post_modified_gmt,
],
[ 'ID' => $new_id ]
);
clean_post_cache( $new_id );
update_post_meta( $new_id, 'helpdocs_locations', [
[
'site_location' => base64_encode( 'main' ),
'page_location' => '',
'custom' => '',
'post_types' => [],
'order' => '',
'css_selector' => '',
],
] );
if ( $folder_term_id ) {
wp_set_object_terms( $new_id, [ $folder_term_id ], Folders::$taxonomy, false );
}
update_post_meta( $new_id, 'helpdocs_migrated_from', $source_post_type );
update_post_meta( $new_id, 'helpdocs_migrated_at', current_time( 'mysql' ) );
update_post_meta( $new_id, 'helpdocs_migrated_from_id', $source_id );
do_action( 'helpdocs_migrate_post_imported', $new_id, $source_post );
$last_id = $new_id;
$imported++;
}
wp_send_json_success( [
'imported' => $imported,
'skipped' => $skipped,
'last_id' => $last_id,
] );
} // End ajax_import_posts()
/**
* Check whether a given source post has already been migrated
*
* @param string $source_post_type
* @param int $source_id
* @return bool
*/
public static function already_imported( $source_post_type, $source_id ) : bool {
return in_array( absint( $source_id ), self::get_imported_source_ids( $source_post_type ), true );
} // End already_imported()
/**
* Get the source IDs already migrated for a given source post type
*
* @param string $source_post_type
* @return array
*/
public static function get_imported_source_ids( $source_post_type ) : array {
global $wpdb;
$ids = $wpdb->get_col( $wpdb->prepare(
"SELECT pm2.meta_value FROM {$wpdb->postmeta} pm1
INNER JOIN {$wpdb->postmeta} pm2 ON pm1.post_id = pm2.post_id
WHERE pm1.meta_key = %s AND pm1.meta_value = %s AND pm2.meta_key = %s",
'helpdocs_migrated_from',
$source_post_type,
'helpdocs_migrated_from_id'
) );
return array_map( 'absint', $ids );
} // End get_imported_source_ids()
/**
* Get the folder term for a source post type's label, creating it if it doesn't exist
*
* @param string $folder_name
* @return int|null Term ID, or null on failure
*/
public static function get_or_create_folder_term( $folder_name ) {
$existing = get_term_by( 'name', $folder_name, Folders::$taxonomy );
if ( $existing ) {
return $existing->term_id;
}
$created = wp_insert_term( $folder_name, Folders::$taxonomy );
if ( is_wp_error( $created ) ) {
return null;
}
return $created[ 'term_id' ];
} // End get_or_create_folder_term()
}
Migrate::instance();