get_admin_css() ); wp_register_script( 'a1tools-import-export', false, array( 'jquery' ), A1TOOLS_VERSION, true ); wp_enqueue_script( 'a1tools-import-export' ); wp_localize_script( 'a1tools-import-export', 'a1toolsIE', array( 'ajaxUrl' => admin_url( 'admin-ajax.php' ), 'noncePages' => wp_create_nonce( 'a1tools_get_pages' ), 'nonceExport' => wp_create_nonce( 'a1tools_export' ), 'nonceImport' => wp_create_nonce( 'a1tools_import' ), ) ); wp_add_inline_script( 'a1tools-import-export', $this->get_admin_js() ); } // ========================================================================= // AJAX: Get Pages by Author // ========================================================================= /** * AJAX handler — return pages filtered by author. */ public function ajax_get_pages_by_author() { check_ajax_referer( 'a1tools_get_pages', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Permission denied.', 'a1-tools' ) ) ); } $author_id = isset( $_POST['author_id'] ) ? absint( $_POST['author_id'] ) : 0; $args = array( 'post_type' => 'page', 'post_status' => array( 'publish', 'draft', 'private' ), 'numberposts' => -1, 'orderby' => 'title', 'order' => 'ASC', ); if ( $author_id > 0 ) { $args['author'] = $author_id; } $posts = get_posts( $args ); $pages = array(); foreach ( $posts as $post ) { $elementor_data = get_post_meta( $post->ID, '_elementor_data', true ); $pages[] = array( 'id' => $post->ID, 'title' => $post->post_title, 'status' => $post->post_status, 'date' => get_the_date( 'Y-m-d', $post ), 'has_elementor' => ! empty( $elementor_data ), ); } wp_send_json_success( array( 'pages' => $pages ) ); } // ========================================================================= // AJAX: Export Pages // ========================================================================= /** * AJAX handler — export selected pages as JSON or WXR. */ public function ajax_export_pages() { check_ajax_referer( 'a1tools_export', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Permission denied.', 'a1-tools' ) ) ); } $page_ids = isset( $_POST['page_ids'] ) ? array_map( 'absint', (array) $_POST['page_ids'] ) : array(); $format = isset( $_POST['format'] ) ? sanitize_text_field( wp_unslash( $_POST['format'] ) ) : 'json'; if ( empty( $page_ids ) ) { wp_send_json_error( array( 'message' => __( 'No pages selected.', 'a1-tools' ) ) ); } if ( ! in_array( $format, array( 'json', 'wxr' ), true ) ) { wp_send_json_error( array( 'message' => __( 'Invalid format.', 'a1-tools' ) ) ); } $site_name = sanitize_file_name( get_bloginfo( 'name' ) ); $date = gmdate( 'Y-m-d' ); if ( 'json' === $format ) { $data = $this->export_json( $page_ids ); $filename = "a1tools-export-{$site_name}-{$date}.json"; $content = wp_json_encode( $data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE ); $mime = 'application/json'; } else { $content = $this->export_wxr( $page_ids ); $filename = "a1tools-export-{$site_name}-{$date}.xml"; $mime = 'text/xml'; } wp_send_json_success( array( 'filename' => $filename, 'content' => $content, 'mime' => $mime, ) ); } // ========================================================================= // AJAX: Import Pages // ========================================================================= /** * AJAX handler — import pages from uploaded file. */ public function ajax_import_pages() { check_ajax_referer( 'a1tools_import', 'nonce' ); if ( ! current_user_can( 'manage_options' ) ) { wp_send_json_error( array( 'message' => __( 'Permission denied.', 'a1-tools' ) ) ); } if ( empty( $_FILES['import_file'] ) ) { wp_send_json_error( array( 'message' => __( 'No file uploaded.', 'a1-tools' ) ) ); } $file = $_FILES['import_file']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized -- file array handled by validate_upload() $validation = $this->validate_upload( $file ); if ( is_wp_error( $validation ) ) { wp_send_json_error( array( 'message' => $validation->get_error_message() ) ); } $duplicate_handling = isset( $_POST['duplicate_handling'] ) ? sanitize_text_field( wp_unslash( $_POST['duplicate_handling'] ) ) : 'skip'; if ( ! in_array( $duplicate_handling, array( 'skip', 'create_new', 'update_existing' ), true ) ) { $duplicate_handling = 'skip'; } $author_id = isset( $_POST['author_id'] ) ? absint( $_POST['author_id'] ) : 0; $parent_page_id = isset( $_POST['parent_page_id'] ) ? absint( $_POST['parent_page_id'] ) : 0; $ext = strtolower( pathinfo( $file['name'], PATHINFO_EXTENSION ) ); if ( 'json' === $ext ) { $results = $this->import_json( $file['tmp_name'], $duplicate_handling, $author_id, $parent_page_id ); } elseif ( 'xml' === $ext ) { $results = $this->import_wxr( $file['tmp_name'], $duplicate_handling, $author_id, $parent_page_id ); } else { wp_send_json_error( array( 'message' => __( 'Unsupported file format. Use .json or .xml.', 'a1-tools' ) ) ); return; } if ( is_wp_error( $results ) ) { wp_send_json_error( array( 'message' => $results->get_error_message() ) ); return; } wp_send_json_success( $results ); } // ========================================================================= // Export Helpers // ========================================================================= /** * Build export data for a single page. * * @param int $post_id The page ID. * @return array|null Page data array or null on failure. */ private function get_post_export_data( $post_id ) { $post = get_post( $post_id ); if ( ! $post || 'page' !== $post->post_type ) { return null; } $data = array( 'post_title' => $post->post_title, 'post_content' => $post->post_content, 'post_excerpt' => $post->post_excerpt, 'post_status' => $post->post_status, 'post_name' => $post->post_name, 'post_parent' => $post->post_parent, 'menu_order' => $post->menu_order, 'comment_status' => $post->comment_status, 'ping_status' => $post->ping_status, 'post_date' => $post->post_date, 'post_date_gmt' => $post->post_date_gmt, 'meta' => array(), ); // Capture all post meta (skip internal keys). $skip_keys = array( '_edit_lock', '_edit_last' ); $all_meta = get_post_meta( $post_id ); foreach ( $all_meta as $key => $values ) { if ( in_array( $key, $skip_keys, true ) ) { continue; } $data['meta'][ $key ] = maybe_unserialize( $values[0] ); } return $data; } /** * Generate JSON export data. * * @param array $post_ids Array of page IDs. * @return array Export data structure. */ private function export_json( $post_ids ) { $export = array( 'version' => A1TOOLS_VERSION, 'export_date' => gmdate( 'Y-m-d H:i:s' ), 'site_url' => site_url(), 'site_name' => get_bloginfo( 'name' ), 'format' => 'a1tools-json', 'pages' => array(), ); foreach ( $post_ids as $post_id ) { $page_data = $this->get_post_export_data( $post_id ); if ( $page_data ) { $export['pages'][] = $page_data; } } return $export; } /** * Generate WXR (WordPress eXtended RSS) export string. * * @param array $post_ids Array of page IDs. * @return string WXR XML string. */ private function export_wxr( $post_ids ) { $site_url = site_url(); $site_name = get_bloginfo( 'name' ); $xml = '' . "\n"; $xml .= 'esc_xml( $site_name ) . '' . "\n"; $xml .= ' ' . esc_url( $site_url ) . '' . "\n"; $xml .= ' A1 Tools Page Export' . "\n"; $xml .= ' 1.2' . "\n"; $xml .= ' ' . esc_url( $site_url ) . '' . "\n"; $xml .= ' ' . esc_url( home_url() ) . '' . "\n"; $xml .= ' A1 Tools ' . $this->esc_xml( A1TOOLS_VERSION ) . '' . "\n"; foreach ( $post_ids as $post_id ) { $page_data = $this->get_post_export_data( $post_id ); if ( ! $page_data ) { continue; } $author_login = get_the_author_meta( 'login', get_post_field( 'post_author', $post_id ) ); $xml .= ' ' . "\n"; $xml .= ' ' . $this->esc_xml( $page_data['post_title'] ) . '' . "\n"; $xml .= ' ' . "\n"; $xml .= ' ' . "\n"; $xml .= ' ' . "\n"; $xml .= ' ' . $this->esc_xml( $page_data['post_date'] ) . '' . "\n"; $xml .= ' ' . $this->esc_xml( $page_data['post_date_gmt'] ) . '' . "\n"; $xml .= ' ' . $this->esc_xml( $page_data['post_name'] ) . '' . "\n"; $xml .= ' ' . $this->esc_xml( $page_data['post_status'] ) . '' . "\n"; $xml .= ' ' . absint( $page_data['post_parent'] ) . '' . "\n"; $xml .= ' ' . intval( $page_data['menu_order'] ) . '' . "\n"; $xml .= ' page' . "\n"; $xml .= ' ' . $this->esc_xml( $page_data['comment_status'] ) . '' . "\n"; foreach ( $page_data['meta'] as $meta_key => $meta_value ) { $serialized = maybe_serialize( $meta_value ); $xml .= ' ' . "\n"; $xml .= ' ' . "\n"; $xml .= ' ' . "\n"; $xml .= ' ' . "\n"; } $xml .= ' ' . "\n"; } $xml .= '' . "\n"; $xml .= '' . "\n"; return $xml; } /** * XML-escape a string. Uses esc_xml() if available (WP 5.5+), else esc_html(). * * @param string $value The string to escape. * @return string Escaped string. */ private function esc_xml( $value ) { if ( function_exists( 'esc_xml' ) ) { return esc_xml( $value ); } return esc_html( $value ); } // ========================================================================= // Import Helpers // ========================================================================= /** * Validate an uploaded file. * * @param array $file The $_FILES entry. * @return true|WP_Error True on success, WP_Error on failure. */ private function validate_upload( $file ) { if ( $file['error'] !== UPLOAD_ERR_OK ) { return new WP_Error( 'upload_error', __( 'File upload failed.', 'a1-tools' ) ); } $ext = strtolower( pathinfo( $file['name'], PATHINFO_EXTENSION ) ); if ( ! in_array( $ext, array( 'json', 'xml' ), true ) ) { return new WP_Error( 'invalid_type', __( 'Only .json and .xml files are allowed.', 'a1-tools' ) ); } if ( $file['size'] > 50 * 1024 * 1024 ) { return new WP_Error( 'file_too_large', __( 'File exceeds maximum size of 50 MB.', 'a1-tools' ) ); } if ( ! is_readable( $file['tmp_name'] ) ) { return new WP_Error( 'not_readable', __( 'Uploaded file is not readable.', 'a1-tools' ) ); } if ( 'json' === $ext ) { $content = file_get_contents( $file['tmp_name'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents $decoded = json_decode( $content, true ); if ( null === $decoded ) { return new WP_Error( 'invalid_json', __( 'File contains invalid JSON.', 'a1-tools' ) ); } if ( ! isset( $decoded['format'] ) || 'a1tools-json' !== $decoded['format'] ) { return new WP_Error( 'invalid_format', __( 'This JSON file was not exported by A1 Tools.', 'a1-tools' ) ); } } if ( 'xml' === $ext ) { $content = file_get_contents( $file['tmp_name'] ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents if ( strpos( $content, 'xmlns:wp=' ) === false ) { return new WP_Error( 'invalid_wxr', __( 'This XML file does not appear to be a valid WordPress export.', 'a1-tools' ) ); } } return true; } /** * Import pages from a JSON file. * * @param string $file_path Path to the JSON file. * @param string $duplicate_handling How to handle duplicates: skip, create_new, update_existing. * @param int $author_id Author ID to assign to imported pages (0 = keep default). * @param int $parent_page_id Parent page ID to assign to all imported pages (0 = keep original). * @return array|WP_Error Results array or error. */ private function import_json( $file_path, $duplicate_handling, $author_id = 0, $parent_page_id = 0 ) { $content = file_get_contents( $file_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents $data = json_decode( $content, true ); if ( ! isset( $data['pages'] ) || ! is_array( $data['pages'] ) ) { return new WP_Error( 'invalid_data', __( 'No pages found in import file.', 'a1-tools' ) ); } return $this->process_import( $data['pages'], $duplicate_handling, $author_id, $parent_page_id ); } /** * Import pages from a WXR (WordPress XML) file. * * @param string $file_path Path to the XML file. * @param string $duplicate_handling How to handle duplicates. * @param int $author_id Author ID to assign to imported pages (0 = keep default). * @param int $parent_page_id Parent page ID to assign to all imported pages (0 = keep original). * @return array|WP_Error Results array or error. */ private function import_wxr( $file_path, $duplicate_handling, $author_id = 0, $parent_page_id = 0 ) { $xml_content = file_get_contents( $file_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents libxml_use_internal_errors( true ); $xml = simplexml_load_string( $xml_content ); if ( false === $xml ) { return new WP_Error( 'xml_parse_error', __( 'Failed to parse XML file.', 'a1-tools' ) ); } $namespaces = $xml->getNamespaces( true ); $wp_ns = isset( $namespaces['wp'] ) ? $namespaces['wp'] : 'http://wordpress.org/export/1.2/'; $content_ns = isset( $namespaces['content'] ) ? $namespaces['content'] : 'http://purl.org/rss/1.0/modules/content/'; $excerpt_ns = isset( $namespaces['excerpt'] ) ? $namespaces['excerpt'] : 'http://wordpress.org/export/1.2/excerpt/'; $pages = array(); foreach ( $xml->channel->item as $item ) { $wp = $item->children( $wp_ns ); $content = $item->children( $content_ns ); $excerpt = $item->children( $excerpt_ns ); // Only import pages. if ( isset( $wp->post_type ) && 'page' !== (string) $wp->post_type ) { continue; } $page_data = array( 'post_title' => (string) $item->title, 'post_content' => (string) $content->encoded, 'post_excerpt' => isset( $excerpt->encoded ) ? (string) $excerpt->encoded : '', 'post_status' => (string) $wp->status, 'post_name' => (string) $wp->post_name, 'post_parent' => (int) $wp->post_parent, 'menu_order' => (int) $wp->menu_order, 'comment_status' => (string) $wp->comment_status, 'post_date' => (string) $wp->post_date, 'post_date_gmt' => (string) $wp->post_date_gmt, 'meta' => array(), ); if ( isset( $wp->postmeta ) ) { foreach ( $wp->postmeta as $meta ) { $meta_key = (string) $meta->meta_key; $meta_value = (string) $meta->meta_value; $page_data['meta'][ $meta_key ] = maybe_unserialize( $meta_value ); } } $pages[] = $page_data; } if ( empty( $pages ) ) { return new WP_Error( 'no_pages', __( 'No pages found in the import file.', 'a1-tools' ) ); } return $this->process_import( $pages, $duplicate_handling, $author_id, $parent_page_id ); } /** * Process an array of page data for import. * * @param array $pages Array of page data arrays. * @param string $duplicate_handling How to handle duplicates. * @param int $author_id Author ID to assign to imported pages (0 = keep default). * @param int $parent_page_id Parent page ID to assign to all imported pages (0 = keep original). * @return array Results summary. */ private function process_import( $pages, $duplicate_handling, $author_id = 0, $parent_page_id = 0 ) { $results = array( 'imported' => 0, 'updated' => 0, 'skipped' => 0, 'errors' => 0, 'details' => array(), ); foreach ( $pages as $page_data ) { $result = $this->import_single_page( $page_data, $duplicate_handling, $author_id, $parent_page_id ); $results['details'][] = $result; switch ( $result['action'] ) { case 'imported': $results['imported']++; break; case 'updated': $results['updated']++; break; case 'skipped': $results['skipped']++; break; default: $results['errors']++; break; } } return $results; } /** * Find an existing page by slug or title. * * @param array $page_data The page data array. * @return int Existing page ID or 0 if not found. */ private function find_existing_page( $page_data ) { // Try by slug first. if ( ! empty( $page_data['post_name'] ) ) { $existing = get_page_by_path( $page_data['post_name'], OBJECT, 'page' ); if ( $existing ) { return $existing->ID; } } // Fallback: query by title. if ( ! empty( $page_data['post_title'] ) ) { $query = new WP_Query( array( 'post_type' => 'page', 'title' => $page_data['post_title'], 'posts_per_page' => 1, 'fields' => 'ids', 'post_status' => array( 'publish', 'draft', 'private', 'pending', 'trash' ), ) ); if ( $query->have_posts() ) { return $query->posts[0]; } } return 0; } /** * Import a single page. * * @param array $page_data The page data array. * @param string $duplicate_handling How to handle duplicates. * @param int $author_id Author ID to assign (0 = keep default). * @param int $parent_page_id Parent page ID to assign (0 = keep original from export data). * @return array Result with action, title, and message. */ private function import_single_page( $page_data, $duplicate_handling, $author_id = 0, $parent_page_id = 0 ) { $title = isset( $page_data['post_title'] ) ? $page_data['post_title'] : ''; $existing_id = $this->find_existing_page( $page_data ); // Handle duplicates. if ( $existing_id > 0 ) { if ( 'skip' === $duplicate_handling ) { return array( 'action' => 'skipped', 'title' => $title, 'message' => sprintf( /* translators: %s: page title */ __( 'Page "%s" already exists. Skipped.', 'a1-tools' ), $title ), ); } if ( 'update_existing' === $duplicate_handling ) { $update_data = array( 'ID' => $existing_id, 'post_title' => $title, 'post_content' => isset( $page_data['post_content'] ) ? $page_data['post_content'] : '', 'post_excerpt' => isset( $page_data['post_excerpt'] ) ? $page_data['post_excerpt'] : '', 'post_status' => isset( $page_data['post_status'] ) ? $page_data['post_status'] : 'draft', 'post_name' => isset( $page_data['post_name'] ) ? $page_data['post_name'] : '', 'menu_order' => isset( $page_data['menu_order'] ) ? intval( $page_data['menu_order'] ) : 0, 'comment_status' => isset( $page_data['comment_status'] ) ? $page_data['comment_status'] : 'closed', ); if ( $author_id > 0 ) { $update_data['post_author'] = $author_id; } // Apply parent page override if specified, otherwise keep original from export. if ( $parent_page_id > 0 ) { $update_data['post_parent'] = $parent_page_id; } elseif ( isset( $page_data['post_parent'] ) ) { $update_data['post_parent'] = intval( $page_data['post_parent'] ); } $result = wp_update_post( $update_data, true ); if ( is_wp_error( $result ) ) { return array( 'action' => 'error', 'title' => $title, 'message' => $result->get_error_message(), ); } $this->import_page_meta( $existing_id, $page_data ); return array( 'action' => 'updated', 'title' => $title, 'post_id' => $existing_id, 'message' => sprintf( /* translators: %s: page title */ __( 'Page "%s" updated successfully.', 'a1-tools' ), $title ), ); } // 'create_new' — fall through to insert below. } // Insert new page. $post_data = array( 'post_title' => $title, 'post_content' => isset( $page_data['post_content'] ) ? $page_data['post_content'] : '', 'post_excerpt' => isset( $page_data['post_excerpt'] ) ? $page_data['post_excerpt'] : '', 'post_status' => isset( $page_data['post_status'] ) ? $page_data['post_status'] : 'draft', 'post_type' => 'page', 'post_name' => isset( $page_data['post_name'] ) ? $page_data['post_name'] : '', 'menu_order' => isset( $page_data['menu_order'] ) ? intval( $page_data['menu_order'] ) : 0, 'comment_status' => isset( $page_data['comment_status'] ) ? $page_data['comment_status'] : 'closed', ); if ( $author_id > 0 ) { $post_data['post_author'] = $author_id; } // Apply parent page override if specified, otherwise keep original from export. if ( $parent_page_id > 0 ) { $post_data['post_parent'] = $parent_page_id; } elseif ( isset( $page_data['post_parent'] ) ) { $post_data['post_parent'] = intval( $page_data['post_parent'] ); } // Let WordPress auto-generate slug if creating duplicate. if ( $existing_id > 0 && 'create_new' === $duplicate_handling ) { $post_data['post_name'] = ''; } $new_id = wp_insert_post( $post_data, true ); if ( is_wp_error( $new_id ) ) { return array( 'action' => 'error', 'title' => $title, 'message' => $new_id->get_error_message(), ); } $this->import_page_meta( $new_id, $page_data ); return array( 'action' => 'imported', 'title' => $title, 'post_id' => $new_id, 'message' => sprintf( /* translators: %s: page title */ __( 'Page "%s" imported successfully.', 'a1-tools' ), $title ), ); } /** * Import meta data for a page with Elementor double-serialization fix. * * @param int $post_id The page ID. * @param array $page_data The page data array containing a 'meta' key. */ private function import_page_meta( $post_id, $page_data ) { if ( empty( $page_data['meta'] ) || ! is_array( $page_data['meta'] ) ) { return; } $skip_keys = array( '_edit_lock', '_edit_last' ); foreach ( $page_data['meta'] as $meta_key => $meta_value ) { if ( in_array( $meta_key, $skip_keys, true ) ) { continue; } // CRITICAL: Elementor _elementor_data is stored as a JSON string. // WordPress's update_post_meta() internally calls wp_unslash() which // strips backslashes from the JSON. We must use wp_slash() to counteract this. if ( '_elementor_data' === $meta_key ) { if ( is_array( $meta_value ) ) { $meta_value = wp_json_encode( $meta_value ); } update_post_meta( $post_id, $meta_key, wp_slash( $meta_value ) ); } else { update_post_meta( $post_id, $meta_key, $meta_value ); } } } // ========================================================================= // Admin Page Render // ========================================================================= /** * Build the HTML for the page checkbox list. * * @param array $posts Array of WP_Post objects. * @return string HTML string. */ private function build_page_list_html( $posts ) { if ( empty( $posts ) ) { return '

' . esc_html__( 'No pages found.', 'a1-tools' ) . '

'; } $html = '
'; foreach ( $posts as $post ) { $elementor_data = get_post_meta( $post->ID, '_elementor_data', true ); $has_elementor = ! empty( $elementor_data ); $badge = $has_elementor ? ' Elementor' : ''; $status = ''; if ( 'publish' !== $post->post_status ) { $status = ' ' . esc_html( $post->post_status ) . ''; } $title = ! empty( $post->post_title ) ? esc_html( $post->post_title ) : esc_html__( '(no title)', 'a1-tools' ); $html .= ''; } $html .= '
'; return $html; } /** * Render the Import/Export admin page. */ public function render_page() { // Pre-load all pages server-side for instant display. $all_pages = get_posts( array( 'post_type' => 'page', 'post_status' => array( 'publish', 'draft', 'private' ), 'numberposts' => -1, 'orderby' => 'title', 'order' => 'ASC', ) ); $page_count = count( $all_pages ); ?>

'a1tools_export_author', 'id' => 'a1tools-export-author', 'show_option_all' => __( 'All Authors', 'a1-tools' ), ) ); ?>
build_page_list_html( $all_pages ); ?>


'a1tools_import_author', 'id' => 'a1tools-import-author', ) ); ?>

'a1tools_import_parent', 'id' => 'a1tools-import-parent', 'show_option_none' => esc_html__( '(no parent)', 'a1-tools' ), 'option_none_value' => '0', 'sort_column' => 'menu_order, post_title', ) ); ?>

No pages found for this author.

'; } var html = '
'; $.each(pages, function(i, p) { var badge = p.has_elementor ? ' Elementor' : ''; var sBadge = p.status !== 'publish' ? ' ' + p.status + '' : ''; html += ''; }); html += '
'; return html; } /* ── Author filter change → AJAX reload ──────────────────── */ $('#a1tools-export-author').on('change', function() { var authorId = $(this).val() || 0; // Show spinner, hide current list. $('#a1tools-page-list').hide(); $('#a1tools-page-list-spinner').show(); $('#a1tools-page-list-controls').hide(); $.post(s.ajaxUrl, { action: 'a1tools_get_pages_by_author', nonce: s.noncePages, author_id: authorId }, function(res) { $('#a1tools-page-list-spinner').hide(); if (res.success && res.data.pages.length > 0) { $('#a1tools-page-list').html(buildPageListHtml(res.data.pages)).show(); $('#a1tools-page-list-controls').show(); $('#a1tools-page-count').text(res.data.pages.length + (res.data.pages.length === 1 ? ' page' : ' pages')); } else { $('#a1tools-page-list').html('

No pages found for this author.

').show(); $('#a1tools-page-list-controls').hide(); } }).fail(function() { $('#a1tools-page-list-spinner').hide(); $('#a1tools-page-list').html('

Failed to load pages. Please refresh and try again.

').show(); }); }); /* ── Select / Deselect All ───────────────────────────────── */ $('#a1tools-select-all').on('click', function() { $('#a1tools-page-list input[type="checkbox"]').prop('checked', true); }); $('#a1tools-deselect-all').on('click', function() { $('#a1tools-page-list input[type="checkbox"]').prop('checked', false); }); /* ── Export ───────────────────────────────────────────────── */ $('#a1tools-export-btn').on('click', function() { var ids = []; $('#a1tools-page-list input[type="checkbox"]:checked').each(function() { ids.push($(this).val()); }); if (ids.length === 0) { alert('Please select at least one page to export.'); return; } var $btn = $(this), $st = $('#a1tools-export-status'), $prog = $('#a1tools-export-progress'), $fill = $('#a1tools-export-progress-fill'), $text = $('#a1tools-export-progress-text'); $btn.prop('disabled', true); $st.text('').removeClass('a1tools-success a1tools-error'); $fill.css('width', '0%').addClass('a1tools-indeterminate'); $text.text('Preparing export for ' + ids.length + (ids.length === 1 ? ' page' : ' pages') + '...'); $prog.show(); $.post(s.ajaxUrl, { action: 'a1tools_export_pages', nonce: s.nonceExport, page_ids: ids, format: $('#a1tools-export-format').val() }, function(res) { $btn.prop('disabled', false); $fill.removeClass('a1tools-indeterminate'); if (res.success) { $fill.css('width', '100%'); $text.text('Download starting...'); var blob = new Blob([res.data.content], { type: res.data.mime }); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = res.data.filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); $text.text('Exported ' + ids.length + (ids.length === 1 ? ' page' : ' pages') + ' successfully!'); $st.text('').addClass('a1tools-success'); setTimeout(function() { $prog.fadeOut(); }, 4000); } else { $fill.css('width', '100%').css('background', '#d63638'); $text.text(res.data.message || 'Export failed.'); $st.text('').addClass('a1tools-error'); setTimeout(function() { $prog.fadeOut(function() { $fill.css('background', ''); }); }, 5000); } }).fail(function() { $btn.prop('disabled', false); $fill.removeClass('a1tools-indeterminate').css('width', '100%').css('background', '#d63638'); $text.text('Export failed. Please try again.'); setTimeout(function() { $prog.fadeOut(function() { $fill.css('background', ''); }); }, 5000); }); }); /* ── Import ───────────────────────────────────────────────── */ $('#a1tools-import-btn').on('click', function() { var fileInput = $('#a1tools-import-file')[0]; if (!fileInput.files || !fileInput.files[0]) { alert('Please select a file to import.'); return; } var $btn = $(this), $st = $('#a1tools-import-status'), $prog = $('#a1tools-import-progress'), $fill = $('#a1tools-import-progress-fill'), $text = $('#a1tools-import-progress-text'); $btn.prop('disabled', true); $st.text('').removeClass('a1tools-success a1tools-error'); $('#a1tools-import-results').hide(); $fill.css('width', '0%').css('background', '').addClass('a1tools-indeterminate'); $text.text('Uploading and importing pages...'); $prog.show(); var fd = new FormData(); fd.append('action', 'a1tools_import_pages'); fd.append('nonce', s.nonceImport); fd.append('import_file', fileInput.files[0]); fd.append('duplicate_handling', $('#a1tools-duplicate-handling').val()); fd.append('author_id', $('#a1tools-import-author').val()); fd.append('parent_page_id', $('#a1tools-import-parent').val() || '0'); $.ajax({ url: s.ajaxUrl, type: 'POST', data: fd, processData: false, contentType: false, success: function(res) { $btn.prop('disabled', false); $fill.removeClass('a1tools-indeterminate'); if (res.success) { var d = res.data; var total = d.imported + d.updated + d.skipped + d.errors; $fill.css('width', '100%'); $text.text('Processed ' + total + (total === 1 ? ' page' : ' pages') + ' successfully!'); var summary = '

'; summary += 'Imported: ' + d.imported + '  |  '; summary += 'Updated: ' + d.updated + '  |  '; summary += 'Skipped: ' + d.skipped + '  |  '; summary += 'Errors: ' + d.errors; summary += '

'; $('#a1tools-import-summary').html(summary); var tbl = ''; $.each(d.details, function(i, r) { var cls = r.action === 'error' ? ' style="color:#d63638;"' : ''; tbl += ''; tbl += ''; tbl += ''; tbl += ''; tbl += ''; }); tbl += '
PageActionDetails
' + $('').text(r.title).html() + '' + r.action + '' + $('').text(r.message).html() + '
'; $('#a1tools-import-details').html(tbl); $('#a1tools-import-results').show(); setTimeout(function() { $prog.fadeOut(); }, 3000); } else { $fill.css('width', '100%').css('background', '#d63638'); $text.text(res.data.message || 'Import failed.'); setTimeout(function() { $prog.fadeOut(function() { $fill.css('background', ''); }); }, 5000); } }, error: function() { $btn.prop('disabled', false); $fill.removeClass('a1tools-indeterminate').css('width', '100%').css('background', '#d63638'); $text.text('Import failed. Please try again.'); setTimeout(function() { $prog.fadeOut(function() { $fill.css('background', ''); }); }, 5000); } }); }); })(jQuery); JS; } }