' . esc_html__( 'Admin Help Docs:', 'admin-help-docs' ) . ' ';
foreach ( $failed as $item ) {
$reason = ( 'unauthorized' === $item[ 'error' ] )
? __( 'invalid API key', 'admin-help-docs' )
: __( 'connection failed', 'admin-help-docs' );
if ( $item[ 'is_current' ] ) {
printf(
/* translators: 1: import title, 2: failure reason */
esc_html__( 'The import "%1$s" could not connect to its remote site (%2$s).', 'admin-help-docs' ),
esc_html( $item[ 'title' ] ),
esc_html( $reason )
);
} else {
printf(
/* translators: 1: import title, 2: failure reason, 3: edit URL */
esc_html__( 'The import "%1$s" could not connect to its remote site (%2$s). %3$s', 'admin-help-docs' ),
esc_html( $item[ 'title' ] ),
esc_html( $reason ),
'' . esc_html__( 'Review import', 'admin-help-docs' ) . ''
);
}
echo ' ';
}
echo '
';
} // End render_failed_import_notice()
/**
* Render the tab
*/
public function render_tab() {
$import_id = isset( $_GET[ 'id' ] ) ? absint( $_GET[ 'id' ] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
$post_title = $import_id ? get_the_title( $import_id ) : '';
$website_url = sanitize_url( get_post_meta( $import_id, 'helpdocs_url', true ) );
$selected_docs = get_post_meta( $import_id, 'helpdocs_docs', true ) ?: [];
$selected_tocs = get_post_meta( $import_id, 'helpdocs_tocs', true ) ?: [];
$all_docs = get_post_meta( $import_id, 'helpdocs_all', true );
$all_tocs = get_post_meta( $import_id, 'helpdocs_all_tocs', true );
$api_key = get_post_meta( $import_id, 'helpdocs_api_key', true );
$table_row_header = '
|
>
|
title ); ?>
|
publish_date ) ) ); ?> |
created_by ); ?> |
' . esc_html( $page_loc ) . '';
}
// 2. Custom URL
if ( ! empty( $loc_array[ 'custom' ] ) ) {
$details[] = '' . esc_html( $loc_array[ 'custom' ] ) . '';
}
// 3. Post Types (Handled serialized remote data)
if ( ! empty( $loc_array[ 'post_types' ] ) ) {
$pt_raw = $loc_array[ 'post_types' ];
$pts = is_serialized( $pt_raw ) ? unserialize( $pt_raw ) : $pt_raw;
$pts = is_array( $pts ) ? $pts : [ $pts ];
if ( ! empty( $pts ) ) {
$clean_pts = array_map( function( $pt ) {
return ucwords( str_replace( [ '-', '_' ], ' ', $pt ) );
}, $pts );
$details[] = 'Types: ' . esc_html( implode( ', ', $clean_pts ) );
}
}
echo '- ';
echo '' . wp_kses_post( $label ) . '';
if ( ! empty( $details ) ) {
echo '
' . wp_kses_post( implode( ' | ', $details ) ) . '';
}
echo ' ';
} ?>
|
>
—
|
|
[] ];
}
if ( empty( $api_key ) ) {
$import_id = isset( $_GET[ 'id' ] ) ? absint( $_GET[ 'id' ] ) : 0; // phpcs:ignore
$api_key = isset( $_POST[ 'helpdocs_api_key' ] ) ? sanitize_text_field( wp_unslash( $_POST[ 'helpdocs_api_key' ] ) ) : (string) get_post_meta( $import_id, 'helpdocs_api_key', true ); // phpcs:ignore
}
$cache_key = 'helpdocs_remote_' . md5( $website_url . $api_key );
if ( isset( self::$request_cache[ $cache_key ] ) ) {
return self::$request_cache[ $cache_key ];
}
$cached = get_transient( $cache_key );
if ( false !== $cached ) {
self::$request_cache[ $cache_key ] = $cached;
return $cached;
}
$timeout = apply_filters( 'helpdocs_import_timeout', is_admin() && wp_doing_ajax() ? 20 : 5, $website_url );
$v2_url = str_replace( home_url(), $website_url, API::api_path() );
$version_found = 'v2';
$response = self::fetch_remote_json( $v2_url, $api_key, $timeout );
$status = wp_remote_retrieve_response_code( $response );
if ( ! is_wp_error( $response ) && 404 === $status ) {
$version_found = 'v1';
$response = self::fetch_remote_json( str_replace( '/v2/', '/v1/', $v2_url ), $api_key, $timeout );
$status = wp_remote_retrieve_response_code( $response );
}
if ( is_wp_error( $response ) ) {
return self::cache_result( $cache_key, [ 'error' => 'connection_failed' ], true, $website_url );
}
if ( 401 === $status ) {
return self::cache_result( $cache_key, [ 'error' => 'unauthorized' ], true, $website_url );
}
$docs = json_decode( wp_remote_retrieve_body( $response ) );
if ( ! empty( $docs ) && is_array( $docs ) ) {
$data = [
'version' => $version_found,
'docs' => $docs,
];
return self::cache_result( $cache_key, $data, false, $website_url );
}
return self::cache_result( $cache_key, [ 'docs' => [] ], true, $website_url );
} // End get_all_import_data()
/**
* Store a lookup result in both the request cache and a transient
*
* @param string $cache_key
* @param array $data
* @param bool $is_failure Failures get a much shorter TTL so a fixed remote recovers quickly
* @param string $website_url
* @return array
*/
private static function cache_result( $cache_key, $data, $is_failure, $website_url ) {
$success_ttl = apply_filters( 'helpdocs_import_cache_duration', 12 * HOUR_IN_SECONDS, $website_url );
$failure_ttl = apply_filters( 'helpdocs_import_failure_cache_duration', 15 * MINUTE_IN_SECONDS, $website_url );
set_transient( $cache_key, $data, $is_failure ? $failure_ttl : $success_ttl );
self::$request_cache[ $cache_key ] = $data;
return $data;
} // End cache_result()
/**
* Force a fresh lookup, bypassing both caches
*
* @param string $website_url
* @param string $api_key
* @return array
*/
public static function refresh_import_data( $website_url, $api_key = '' ) {
$cache_key = 'helpdocs_remote_' . md5( esc_url_raw( trim( (string) $website_url ) ) . $api_key );
unset( self::$request_cache[ $cache_key ] );
delete_transient( $cache_key );
return self::get_all_import_data( $website_url, $api_key );
} // End refresh_import_data()
/**
* Helper to keep the remote request clean
*
* @param string $url
* @param string $api_key
* @param int $timeout
* @return array|\WP_Error
*/
private static function fetch_remote_json( $url, $api_key = '', $timeout = 5 ) {
$args = [
'timeout' => absint( $timeout ),
'httpversion' => '1.1',
'user-agent' => 'AdminHelpDocs Importer; ' . home_url(),
'headers' => [
'Accept' => 'application/json',
],
];
if ( ! empty( $api_key ) ) {
$args[ 'headers' ][ 'X-HelpDocs-API-Key' ] = $api_key;
}
return wp_remote_get( $url, $args );
} // End fetch_remote_json()
/**
* Save import settings
*/
public function save_import_settings() {
if ( Menu::get_current_page() !== Bootstrap::textdomain() || Menu::get_current_tab() !== 'import' ) {
return;
}
if ( ! isset( $_POST[ 'action' ] ) || 'helpdocs_import_docs' !== sanitize_text_field( wp_unslash( $_POST[ 'action' ] ) ) ) {
return;
}
if ( ! isset( $_POST[ 'helpdocs_save_import_nonce' ] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST[ 'helpdocs_save_import_nonce' ] ) ), 'helpdocs_save_import_nonce' ) ) {
return;
}
if ( ! Helpers::user_can_edit() ) {
return;
}
$website_url = isset( $_POST[ 'helpdocs_website_url' ] ) ? sanitize_url( wp_unslash( $_POST[ 'helpdocs_website_url' ] ) ) : '';
if ( empty( $website_url ) ) {
return;
}
$import_id = isset( $_POST[ 'id' ] ) ? absint( $_POST[ 'id' ] ) : 0;
$is_active = isset( $_POST[ 'helpdocs_import_active' ] );
$post_status = $is_active ? 'publish' : 'draft';
$import_title = isset( $_POST[ 'helpdocs_import_title' ] ) ? sanitize_text_field( wp_unslash( $_POST[ 'helpdocs_import_title' ] ) ) : '';
if ( empty( $import_title ) ) {
$import_title = ucfirst( str_ireplace( [ 'http://', 'https://', 'www.' ], '', $website_url ) );
$import_title = untrailingslashit( $import_title );
}
$original_title = $import_title;
$count = 1;
while ( $this->title_exists( $import_title, $import_id ) ) {
$count++;
$import_title = ( $original_title ) . ' (' . $count . ')';
}
$post_data = [
'post_type' => Imports::$post_type,
'post_title' => $import_title,
'post_status' => $post_status,
];
if ( ! $import_id ) {
$import_id = wp_insert_post( $post_data );
} else {
$post_data[ 'ID' ] = $import_id;
wp_update_post( $post_data );
// MIGRATION: Prune the old legacy key now that we're using the post status
delete_post_meta( $import_id, 'helpdocs_enabled' );
}
if ( is_wp_error( $import_id ) || ! $import_id ) {
return;
}
$api_key = isset( $_POST[ 'helpdocs_api_key' ] ) ? sanitize_text_field( wp_unslash( $_POST[ 'helpdocs_api_key' ] ) ) : '';
$all_docs = isset( $_POST[ 'helpdocs_all' ] ) ? '1' : '0';
$all_tocs = isset( $_POST[ 'helpdocs_all_tocs' ] ) ? '1' : '0';
$selected_docs = isset( $_POST[ 'helpdocs_auto_feed' ] ) ? array_map( 'absint', (array) wp_unslash( $_POST[ 'helpdocs_auto_feed' ] ) ) : [];
$selected_tocs = isset( $_POST[ 'helpdocs_tocs' ] ) ? array_map( 'absint', (array) wp_unslash( $_POST[ 'helpdocs_tocs' ] ) ) : [];
// Save Metadata
update_post_meta( $import_id, 'helpdocs_api_key', $api_key );
update_post_meta( $import_id, 'helpdocs_url', $website_url );
update_post_meta( $import_id, 'helpdocs_all', $all_docs );
update_post_meta( $import_id, 'helpdocs_all_tocs', $all_tocs );
update_post_meta( $import_id, 'helpdocs_docs', $selected_docs );
update_post_meta( $import_id, 'helpdocs_tocs', $selected_tocs );
// Redirect with ID and Success Message
$redirect_url = add_query_arg( [
'id' => $import_id,
'import-updated' => 'true',
], wp_get_referer() );
wp_safe_redirect( $redirect_url );
exit;
} // End save_import_settings()
/**
* Helper to check if title already exists for this CPT
*
* @param string $title The title to check
* @param int $post_id The current post ID to exclude from the check
*/
private function title_exists( $title, $post_id ) {
global $wpdb;
return $wpdb->get_var( $wpdb->prepare( // phpcs:ignore
"SELECT ID FROM $wpdb->posts WHERE post_title = %s AND post_type = %s AND ID != %d LIMIT 1",
$title,
Imports::$post_type,
$post_id
) );
} // End title_exists()
/**
* AJAX handler to fetch remote documents
*/
public function ajax_fetch_remote_docs() {
check_ajax_referer( 'helpdocs_import_fetch_nonce', 'nonce' );
if ( ! Helpers::user_can_edit() ) {
wp_send_json_error( [ 'message' => __( 'You do not have permission to fetch remote documents.', 'admin-help-docs' ) ] );
}
$url = isset( $_POST[ 'url' ] ) ? sanitize_url( wp_unslash( $_POST[ 'url' ] ) ) : '';
$api_key = isset( $_POST[ 'api_key' ] ) ? sanitize_text_field( wp_unslash( $_POST[ 'api_key' ] ) ) : '';
if ( empty( $url ) ) {
wp_send_json_error( [ 'message' => __( 'Please enter a valid URL.', 'admin-help-docs' ) ] );
}
$import_data = self::refresh_import_data( $url, $api_key );
$docs = $import_data[ 'docs' ] ?? [];
$version = $import_data[ 'version' ] ?? 'v2';
$error = $import_data[ 'error' ] ?? '';
// If we have a specific error (unauthorized or connection_failed)
if ( ! empty( $error ) ) {
wp_send_json_success( [
'error' => $error,
'docs' => [],
'count' => 0,
'version' => $version
] );
}
if ( empty( $docs ) ) {
wp_send_json_error( [ 'message' => __( 'No documents found at this URL.', 'admin-help-docs' ) ] );
}
ob_start();
$this->render_table_rows( $docs );
$html = ob_get_clean();
wp_send_json_success( [
'html' => $html,
'count' => count( $docs ),
'version' => $version,
'error' => ''
] );
} // End ajax_fetch_remote_docs()
/**
* AJAX handler to import an individual document
*/
public function ajax_import_individual_doc() {
check_ajax_referer( 'helpdocs_import_clone_nonce', 'nonce' );
if ( ! Helpers::user_can_edit() ) {
wp_send_json_error( __( 'You do not have permission to import documents.', 'admin-help-docs' ) );
}
$doc_id = isset( $_POST[ 'doc_id' ] ) ? absint( $_POST[ 'doc_id' ] ) : 0;
$import_id = isset( $_POST[ 'import_id' ] ) ? absint( $_POST[ 'import_id' ] ) : 0;
$url = isset( $_POST[ 'website_url' ] ) ? sanitize_url( wp_unslash( $_POST[ 'website_url' ] ) ) : '';
$api_key = isset( $_POST[ 'api_key' ] ) ? sanitize_text_field( wp_unslash( $_POST[ 'api_key' ] ) ) : '';
if ( ! $doc_id || ! $url ) {
wp_send_json_error( __( 'Invalid request data.', 'admin-help-docs' ) );
}
$import_data = self::refresh_import_data( $url, $api_key );
if ( ! empty( $import_data[ 'error' ] ) ) {
wp_send_json_error( sprintf( __( 'Remote error: %s', 'admin-help-docs' ), $import_data[ 'error' ] ) );
}
// Fetch all remote data and find our specific doc
$remote_docs = $import_data[ 'docs' ] ?? [];
$raw_target = null;
foreach ( (array) $remote_docs as $doc ) {
if ( isset( $doc->ID ) && absint( $doc->ID ) === $doc_id ) {
$raw_target = $doc;
break;
}
}
if ( ! $raw_target ) {
wp_send_json_error( __( 'Could not find the document in the remote feed.', 'admin-help-docs' ) );
}
// Convert the entire stdClass object and all nested objects into associative arrays.
$target_doc = json_decode( json_encode( $raw_target ), true );
// 1. Prepare and Insert the Post
$new_post_id = wp_insert_post( [
'post_title' => sanitize_text_field( $target_doc[ 'title' ] ),
'post_content' => wp_kses_post( $target_doc[ 'content' ] ),
'post_excerpt' => sanitize_text_field( $target_doc[ 'desc' ] ?? '' ),
'post_status' => 'publish',
'post_type' => HelpDocs::$post_type,
] );
if ( is_wp_error( $new_post_id ) ) {
wp_send_json_error( $new_post_id->get_error_message() );
}
// 2. Handle Taxonomies
if ( ! empty( $target_doc[ 'taxonomies' ] ) && is_array( $target_doc[ 'taxonomies' ] ) ) {
foreach ( $target_doc[ 'taxonomies' ] as $taxonomy => $terms ) {
if ( ! taxonomy_exists( $taxonomy ) || empty( $terms ) ) {
continue;
}
$term_ids = [];
foreach ( $terms as $term_data ) {
$term = get_term_by( 'slug', $term_data[ 'slug' ], $taxonomy );
if ( ! $term ) {
$new_term = wp_insert_term( $term_data[ 'name' ], $taxonomy, [ 'slug' => $term_data[ 'slug' ] ] );
if ( ! is_wp_error( $new_term ) ) {
$term_ids[] = (int) $new_term[ 'term_id' ];
}
} else {
$term_ids[] = (int) $term->term_id;
}
}
wp_set_object_terms( $new_post_id, $term_ids, $taxonomy );
}
}
// 3. Normalize Locations
$all_site_locations = HelpDocs::site_locations();
$final_locations = [];
$source_locations = [];
if ( ! empty( $target_doc[ 'locations' ] ) && is_array( $target_doc[ 'locations' ] ) ) {
$source_locations = $target_doc[ 'locations' ];
} elseif ( ! empty( $target_doc[ 'site_location' ] ) ) {
$source_locations = [
[
'site_location' => $target_doc[ 'site_location' ],
'page_location' => $target_doc[ 'page_location' ] ?? '',
'custom' => $target_doc[ 'custom' ] ?? '',
'post_types' => $target_doc[ 'post_types' ] ?? [],
'order' => $target_doc[ 'order' ] ?? 0,
'toc' => $target_doc[ 'toc' ] ?? false,
'css_selector' => $target_doc[ 'css_selector' ] ?? '',
'addt_params' => $target_doc[ 'addt_params' ] ?? false,
]
];
}
foreach ( $source_locations as $loc ) {
// $loc is now guaranteed to be an array because of our json_decode(..., true)
$site_key_encoded = sanitize_text_field( $loc[ 'site_location' ] ?? '' );
$site_key_decoded = base64_decode( $site_key_encoded );
if ( isset( $all_site_locations[ $site_key_decoded ] ) ) {
$clean = [ 'site_location' => $site_key_encoded ];
$allowed_fields = $all_site_locations[ $site_key_decoded ][ 'fields' ] ?? [];
foreach ( $allowed_fields as $field_rule ) {
$field_parts = explode( ':', $field_rule );
$field_name = ( count( $field_parts ) > 1 ) ? end( $field_parts ) : $field_rule;
$val = $loc[ $field_name ] ?? '';
switch ( $field_name ) {
case 'order':
$clean[ $field_name ] = intval( $val );
break;
case 'toc':
case 'addt_params':
$clean[ $field_name ] = ! empty( $val );
break;
case 'post_types':
// Ensure this is saved as a clean array of keys
$pts = is_serialized( $val ) ? unserialize( $val ) : (array) $val;
$clean[ $field_name ] = array_values( array_map( 'sanitize_key', (array) $pts ) );
break;
case 'custom':
$clean[ $field_name ] = esc_url_raw( $val );
break;
case 'page_location':
$clean[ $field_name ] = sanitize_key( $val );
break;
default:
$clean[ $field_name ] = sanitize_text_field( $val );
break;
}
}
$final_locations[] = $clean;
}
}
// 4. Update Meta
update_post_meta( $new_post_id, 'helpdocs_locations', $final_locations );
update_post_meta( $new_post_id, 'helpdocs_view_roles', array_map( 'sanitize_key', (array) ( $target_doc->view_roles ?? [] ) ) );
update_post_meta( $new_post_id, 'helpdocs_imported_doc_id', $doc_id );
update_post_meta( $new_post_id, 'helpdocs_import_id', $import_id );
update_post_meta( $new_post_id, 'helpdocs_imported_from', $url );
update_post_meta( $new_post_id, 'helpdocs_imported_by', get_current_user_id() );
// Compatibility meta for Classic Editor
if ( ! empty( $target_doc->editor_type ) ) {
update_post_meta( $new_post_id, 'classic-editor-remember', sanitize_text_field( $target_doc->editor_type ) );
}
// Support for extra fields added via 'helpdocs_api_doc_object_out' filter
$standard_keys = [ 'ID', 'title', 'created_by', 'publish_date', 'modified_date', 'modified_by', 'desc', 'content', 'taxonomies', 'site_location', 'page_location', 'custom', 'post_types', 'order', 'css_selector', 'addt_params', 'view_roles', 'editor_type' ];
foreach ( $target_doc as $key => $value ) {
if ( ! in_array( $key, $standard_keys ) ) {
update_post_meta( $new_post_id, 'helpdocs_' . sanitize_key( $key ), is_scalar( $value ) ? sanitize_text_field( $value ) : $value );
}
}
Helpers::flush_location_cache();
wp_send_json_success( __( 'Document imported successfully.', 'admin-help-docs' ) );
} // End ajax_import_individual_doc()
/**
* Prevent cloning and unserializing
*/
public function __clone() {}
public function __wakeup() {}
}
ImportEditor::instance();