is_not_paying() ) { add_submenu_page( 'all-in-one-video-gallery', '', '', 'manage_aiovg_options', 'all-in-one-video-gallery-separator-2', '__return_null' ); } } /** * Display import / export page. * * @since 4.5.2 */ public function display_page() { require_once AIOVG_PLUGIN_DIR . 'admin/partials/import-export.php'; } /** * AJAX callback to import videos from a folder. * * @since 4.5.2 */ public function ajax_callback_import_folder() { if ( function_exists( 'set_time_limit' ) ) { set_time_limit( 1200 ); } if ( function_exists( 'ignore_user_abort' ) ) { ignore_user_abort( true ); } check_ajax_referer( 'aiovg_ajax_nonce', 'security' ); // Verify the nonce for security if ( ! current_user_can( 'manage_aiovg_options' ) ) { wp_send_json_error( array( 'error' => esc_html__( 'You do not have sufficient permissions to do this action.', 'all-in-one-video-gallery' ) ) ); } $response = array(); // Sanitize and extract folder path $folder = isset( $_POST['folder'] ) ? sanitize_text_field( wp_unslash( $_POST['folder'] ) ) : ''; $resolved_folder_path = $this->resolve_folder_path( $folder ); if ( is_wp_error( $resolved_folder_path ) ) { wp_send_json_error( array( 'error' => $resolved_folder_path->get_error_message() ) ); } $base_folder_path = $resolved_folder_path; // Sanitize options $include_subfolders = isset( $_POST['include_subfolders'] ) ? (int) $_POST['include_subfolders'] : 0; $slug_strategy = isset( $_POST['slug_strategy'] ) ? sanitize_key( $_POST['slug_strategy'] ) : 'filename'; $set_featured_image = isset( $_POST['set_featured_image'] ) ? (int) $_POST['set_featured_image'] : 0; $enable_downloads = isset( $_POST['enable_downloads'] ) ? (int) $_POST['enable_downloads'] : 0; $enable_comments = isset( $_POST['enable_comments'] ) ? (int) $_POST['enable_comments'] : 0; $email_capture = isset( $_POST['email_capture'] ) ? (int) $_POST['email_capture'] : -1; $access_control = isset( $_POST['access_control'] ) ? (int) $_POST['access_control'] : -1; $restricted_roles = isset( $_POST['restricted_roles'] ) ? array_map( 'sanitize_text_field', $_POST['restricted_roles'] ) : array(); $offset = isset( $_POST['offset'] ) ? (int) $_POST['offset'] : 0; $limit = isset( $_POST['limit'] ) ? (int) $_POST['limit'] : 20; // Taxonomies $categories = array(); if ( isset( $_POST['tax_input'] ) && isset( $_POST['tax_input']['aiovg_categories'] ) ) { $categories = array_map( 'intval', $_POST['tax_input']['aiovg_categories'] ); } $tags = array(); if ( isset( $_POST['tax_input'] ) && isset( $_POST['tax_input']['aiovg_tags'] ) ) { $tags = array_map( 'intval', $_POST['tax_input']['aiovg_tags'] ); } // Unique ID for this import $import_id = md5( $folder ); // Find video files $videos = array(); $matches = array(); $video_extensions = array( 'mp4', 'webm', 'ogv', 'm4v', 'mov' ); $image_extensions = array( 'jpg', 'jpeg', 'png', 'gif', 'webp' ); $current_index = 0; $processed = 0; try { $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $base_folder_path, RecursiveDirectoryIterator::SKIP_DOTS ), RecursiveIteratorIterator::SELF_FIRST ); foreach ( $iterator as $file ) { if ( ! $file->isFile() ) { continue; } // Normalize path once per file $current_folder_path = wp_normalize_path( $file->getPath() ); $file_path = $file->getPathname(); // Skip files from subfolders if not allowed if ( ! $include_subfolders && $current_folder_path !== $base_folder_path ) { continue; } // Skip early if not a video $extension = strtolower( $file->getExtension() ); if ( ! in_array( $extension, $video_extensions, true ) ) { continue; } // Respect offset (counting videos only) if ( $current_index++ < $offset ) { continue; } // Stop when limit is reached if ( $processed >= $limit ) { break; } // Build base name and title $file_name = pathinfo( $file_path, PATHINFO_FILENAME ); $post_title = ucwords( preg_replace( '/[-_]+/', ' ', $file_name ) ); // Add video entry $matches[ $file_name ] = array( 'title' => $post_title, 'video' => $this->make_absolute_url( $file_path ) ); // Try to find matching image in the same folder foreach ( $image_extensions as $image_extension ) { $image_path = $current_folder_path. '/' . $file_name . '.' . $image_extension; if ( file_exists( $image_path ) ) { $matches[ $file_name ]['image'] = $this->make_absolute_url( $image_path ); $matches[ $file_name ]['image_path'] = $image_path; // Already normalized break; } } $processed++; } } catch ( Exception $e ) { wp_send_json_error( array( 'error' => $e->getMessage() ) ); } // Prepare videos for import foreach ( $matches as $name => $data ) { if ( empty( $data['video'] ) ) { continue; } $videos[] = array( 'title' => $data['title'], 'src' => $data['video'], 'image' => isset( $data['image'] ) ? $data['image'] : '', 'image_path' => isset( $data['image_path'] ) ? $data['image_path'] : '' ); } // Import videos $imported_data = $this->import_videos_from_folder( $videos, array( 'slug_strategy' => $slug_strategy, 'categories' => $categories, 'tags' => $tags, 'comment_status' => ( ! empty( $enable_comments ) ? 'open' : 'closed' ), 'set_featured_image' => $set_featured_image, 'enable_downloads' => $enable_downloads, 'email_capture' => $email_capture, 'access_control' => $access_control, 'restricted_roles' => $restricted_roles, 'import_id' => $import_id ) ); // Calculate new offset $new_offset = $offset + $processed; // Batch processing if ( $current_index > $new_offset ) { $message = sprintf( esc_html__( '%s videos imported, %s excluded as duplicates, and %s failed to import.', 'all-in-one-video-gallery' ), '%%imported%%', '%%skipped%%', '%%failed%%' ); $response = array( 'step' => 'process', 'message' => $message, 'offset' => $new_offset, 'imported' => $imported_data['imported'], 'skipped' => $imported_data['skipped'], 'failed' => $imported_data['failed'] ); wp_send_json_success( $response ); } // Build the final response $message = sprintf( esc_html__( '%s videos imported, %s excluded as duplicates, and %s failed to import.', 'all-in-one-video-gallery' ), '%%imported%%', '%%skipped%%', '%%failed%%' ); $message .= sprintf( ' %s', esc_url( admin_url( 'edit.php?post_type=aiovg_videos&aiovg_filter=imported&import_id=' . $import_id ) ), esc_html__( 'Click here to view the imported videos', 'all-in-one-video-gallery' ) ); $response = array( 'step' => 'done', 'message' => $message, 'offset' => $new_offset, 'imported' => $imported_data['imported'], 'skipped' => $imported_data['skipped'], 'failed' => $imported_data['failed'] ); wp_send_json_success( $response ); // Send successful JSON response } /** * AJAX callback to import videos from a csv file. * * @since 4.5.2 */ public function ajax_callback_import_csv() { if ( function_exists( 'set_time_limit' ) ) { set_time_limit( 1200 ); } if ( function_exists( 'ignore_user_abort' ) ) { ignore_user_abort( true ); } check_ajax_referer( 'aiovg_ajax_nonce', 'security' ); // Verify the nonce for security if ( ! current_user_can( 'manage_aiovg_options' ) ) { wp_send_json_error( array( 'error' => esc_html__( 'You do not have sufficient permissions to do this action.', 'all-in-one-video-gallery' ) ) ); } // Sanitize and extract parameters $csv_file = isset( $_POST['csv_file'] ) ? esc_url_raw( $_POST['csv_file'] ) : ''; $zip_file = isset( $_POST['zip_file'] ) ? sanitize_text_field( wp_unslash( $_POST['zip_file'] ) ) : ''; $folder = isset( $_POST['folder'] ) ? sanitize_text_field( wp_unslash( $_POST['folder'] ) ) : ''; $columns_separator = isset( $_POST['columns_separator'] ) ? sanitize_text_field( $_POST['columns_separator'] ) : ','; $values_separator = isset( $_POST['values_separator'] ) ? sanitize_text_field( $_POST['values_separator'] ) : ';'; $add_new_term = isset( $_POST['add_new_term'] ) ? (int) $_POST['add_new_term'] : 0; $add_new_user = isset( $_POST['add_new_user'] ) ? (int) $_POST['add_new_user'] : 0; $set_featured_image = isset( $_POST['set_featured_image'] ) ? (int) $_POST['set_featured_image'] : 0; $csv_columns = isset( $_POST['csv_columns'] ) ? array_map( 'sanitize_text_field', $_POST['csv_columns'] ) : array(); $offset = isset( $_POST['offset'] ) ? (int) $_POST['offset'] : 0; $limit = isset( $_POST['limit'] ) ? (int) $_POST['limit'] : 20; $response = $this->parse_csv( $csv_file, array( 'csv_columns' => $csv_columns, 'columns_separator' => $columns_separator, 'offset' => $offset, 'limit' => $limit ) ); if ( is_wp_error( $response ) ) { wp_send_json_error( array( 'error' => $response->get_error_message() ) ); } $videos = $response['videos']; $has_more = $response['has_more']; $new_offset = $response['new_offset']; if ( ! empty( $folder ) ) { $folder = $this->resolve_folder_path( $folder ); if ( is_wp_error( $folder ) ) { wp_send_json_error( array( 'error' => $folder->get_error_message() ) ); } } elseif ( ! empty( $zip_file ) ) { $folder = $this->resolve_import_directory( $zip_file ); if ( is_wp_error( $folder ) ) { wp_send_json_error( array( 'error' => $folder->get_error_message() ) ); } } // Unique ID for this import $import_id = md5( $csv_file ); // Import videos $video_settings = aiovg_get_option( 'aiovg_video_settings' ); $imported_data = $this->import_videos_from_csv( $videos, array( 'folder' => $folder, 'offset' => $offset, 'values_separator' => $values_separator, 'add_new_term' => $add_new_term, 'add_new_user' => $add_new_user, 'set_featured_image' => $set_featured_image, 'comment_status' => ( (int) $video_settings['has_comments'] > 0 ) ? 'open' : 'closed', 'import_id' => $import_id ) ); // Batch processing if ( $has_more ) { if ( count( $imported_data['updated'] ) > 0 ) { $message = sprintf( esc_html__( '%s videos imported, %s updated, %s excluded as duplicates, and %s failed to import.', 'all-in-one-video-gallery' ), '%%imported%%', '%%updated%%', '%%skipped%%', '%%failed%%' ); } else { $message = sprintf( esc_html__( '%s videos imported, %s excluded as duplicates, and %s failed to import.', 'all-in-one-video-gallery' ), '%%imported%%', '%%skipped%%', '%%failed%%' ); } $response = array( 'step' => 'process', 'message' => $message, 'folder' => $folder, 'offset' => $new_offset, 'imported' => $imported_data['imported'], 'updated' => $imported_data['updated'], 'skipped' => $imported_data['skipped'], 'failed' => $imported_data['failed'], 'errors' => $imported_data['errors'] ); wp_send_json_success( $response ); } // Delete the attachments before returning the final response $maybe_attachments = array( $csv_file, $zip_file ); foreach ( $maybe_attachments as $file_path ) { if ( empty( $file_path ) ) continue; // Only delete if it's an attachment $attachment_id = attachment_url_to_postid( $file_path ); if ( $attachment_id ) { wp_delete_attachment( $attachment_id, true ); } } // Build the final response if ( count( $imported_data['updated'] ) > 0 ) { $message = sprintf( esc_html__( '%s videos imported, %s updated, %s excluded as duplicates, and %s failed to import.', 'all-in-one-video-gallery' ), '%%imported%%', '%%updated%%', '%%skipped%%', '%%failed%%' ); $message .= sprintf( ' %s', esc_url( admin_url( 'edit.php?post_type=aiovg_videos&aiovg_filter=imported&import_id=' . $import_id ) ), esc_html__( 'Click here to view the imported videos', 'all-in-one-video-gallery' ) ); } else { $message = sprintf( esc_html__( '%s videos imported, %s excluded as duplicates, and %s failed to import.', 'all-in-one-video-gallery' ), '%%imported%%', '%%skipped%%', '%%failed%%' ); $message .= sprintf( ' %s', esc_url( admin_url( 'edit.php?post_type=aiovg_videos&aiovg_filter=imported&import_id=' . $import_id ) ), esc_html__( 'Click here to view the imported videos', 'all-in-one-video-gallery' ) ); } $response = array( 'step' => 'done', 'message' => $message, 'folder' => $folder, 'offset' => $new_offset, 'imported' => $imported_data['imported'], 'updated' => $imported_data['updated'], 'skipped' => $imported_data['skipped'], 'failed' => $imported_data['failed'], 'errors' => $imported_data['errors'] ); wp_send_json_success( $response ); // Send successful JSON response } /** * AJAX callback to get CSV column mapping fields. * * @since 4.5.2 */ public function ajax_callback_get_csv_columns() { check_ajax_referer( 'aiovg_ajax_nonce', 'security' ); // Verify the nonce for security if ( ! current_user_can( 'manage_aiovg_options' ) ) { wp_send_json_error( array( 'error' => esc_html__( 'You do not have sufficient permissions to do this action.', 'all-in-one-video-gallery' ) ) ); } // Sanitize and extract parameters. $csv_file = isset( $_POST['csv_file'] ) ? esc_url_raw( $_POST['csv_file'] ) : ''; $columns_separator = isset( $_POST['columns_separator'] ) ? sanitize_text_field( $_POST['columns_separator'] ) : ','; $response = $this->parse_csv( $csv_file, array( 'columns_separator' => $columns_separator, 'headers_only' => true ) ); if ( is_wp_error( $response ) ) { wp_send_json_error( array( 'error' => $response->get_error_message() ) ); } $csv_headers = $response['headers']; $csv_columns = $this->get_csv_column_mapping_fields(); $html = ''; foreach ( $csv_headers as $index => $header ) { $html .= '
'; $html .= sprintf( '', esc_attr( $index ), esc_html( $header ) ); $html .= sprintf( ''; $html .= '
'; } // Send successful JSON response wp_send_json_success( array( 'html' => $html ) ); } /** * AJAX callback to export videos to a CSV file. * * @since 4.5.2 */ public function ajax_callback_export_csv() { if ( function_exists( 'set_time_limit' ) ) { set_time_limit( 1200 ); } if ( function_exists( 'ignore_user_abort' ) ) { ignore_user_abort( true ); } check_ajax_referer( 'aiovg_ajax_nonce', 'security' ); // Verify the nonce for security if ( ! current_user_can( 'manage_aiovg_options' ) ) { wp_send_json_error( array( 'error' => esc_html__( 'You do not have sufficient permissions to do this action.', 'all-in-one-video-gallery' ) ) ); } // Sanitize and extract parameters $offset = isset( $_POST['offset'] ) ? (int) $_POST['offset'] : 0; $limit = isset( $_POST['limit'] ) ? (int) $_POST['limit'] : 200; $total = isset( $_POST['total'] ) ? (int) $_POST['total'] : 0; $timestamp = isset( $_POST['timestamp'] ) ? sanitize_text_field( $_POST['timestamp'] ) : date( 'YmdHis' ); $file_name = isset( $_POST['file_name'] ) ? sanitize_file_name( wp_unslash( $_POST['file_name'] ) ) : ''; $csv_columns = $this->get_csv_column_mapping_fields(); $csv_rows = array(); // Ensure export directory exists $upload_dir = wp_upload_dir(); $upload_basedir = trailingslashit( $upload_dir['basedir'] ); $upload_baseurl = trailingslashit( $upload_dir['baseurl'] ); $export_dir = $upload_basedir . 'all-in-one-video-gallery/exports/'; $export_url = $upload_baseurl . 'all-in-one-video-gallery/exports/'; if ( ! file_exists( $export_dir ) ) { if ( ! wp_mkdir_p( $export_dir ) ) { wp_send_json_error( array( 'error' => __( 'Sorry, we were unable to create the export directory. Please check folder permissions and try again.', 'all-in-one-video-gallery' ) ) ); } } // Query videos $args = array( 'post_type' => 'aiovg_videos', 'post_status' => 'publish', 'posts_per_page' => $limit, 'offset' => $offset, 'orderby' => 'ID', 'order' => 'ASC' ); if ( $offset > 0 ) { $args['no_found_rows'] = true; // Only first batch needs total count } $aiovg_query = new WP_Query( $args ); if ( ! $aiovg_query->have_posts() ) { wp_send_json_error( array( 'error' => __( 'Sorry, no videos were found to export.', 'all-in-one-video-gallery' ) ) ); } // Get total count on first batch if ( $offset === 0 ) { $total = $aiovg_query->found_posts; } // Loop through videos and build CSV rows global $post; while ( $aiovg_query->have_posts() ) { $aiovg_query->the_post(); $post_id = get_the_ID(); $post_meta = get_post_meta( $post_id ); $row = array(); foreach ( $csv_columns as $field => $column ) { switch ( $field ) { case 'post_id': $row[] = $post_id; break; case 'post_title': $row[] = $post->post_title; break; case 'post_content': $row[] = $post->post_content; break; case 'post_excerpt': $row[] = $post->post_excerpt; break; case 'aiovg_categories': case 'aiovg_tags': $terms = array(); $terms_ids = wp_get_object_terms( get_the_ID(), $field, array( 'fields' => 'ids' ) ); foreach ( $terms_ids as $term_id ) { $term_parents_list = get_term_parents_list( $term_id, $field, array( 'separator' => '>', 'link' => false ) ); $terms[] = rtrim( $term_parents_list, '>' ); } $row[] = implode( ';', $terms ); break; case 'video': $type = isset( $post_meta['type'] ) ? sanitize_text_field( $post_meta['type'][0] ) : 'default'; if ( in_array( $type, array( 'youtube', 'vimeo', 'dailymotion', 'rumble', 'facebook' ) ) ) { $row[] = isset( $post_meta[ $type ] ) ? aiovg_sanitize_url( $post_meta[ $type ][0] ) : ''; } elseif ( 'adaptive' == $type ) { $src = isset( $post_meta['hls'] ) ? aiovg_sanitize_url( $post_meta['hls'][0] ) : ''; if ( empty( $src ) ) { $src = isset( $post_meta['dash'] ) ? aiovg_sanitize_url( $post_meta['dash'][0] ) : ''; } $row[] = $src; } elseif ( 'embedcode' == $type ) { try { add_filter( 'wp_kses_allowed_html', 'aiovg_allow_iframe_script_tags' ); $row[] = isset( $post_meta['embedcode'] ) ? wp_kses_post( $post_meta['embedcode'][0] ) : ''; } finally { remove_filter( 'wp_kses_allowed_html', 'aiovg_allow_iframe_script_tags' ); } } else { $mp4 = isset( $post_meta['mp4'] ) ? aiovg_sanitize_url( $post_meta['mp4'][0] ) : ''; $sources = isset( $post_meta['sources'] ) ? maybe_unserialize( $post_meta['sources'][0] ) : array(); if ( empty( $sources ) ) { $row[] = wp_json_encode( array( 'src' => $mp4, 'file' => $this->get_exported_filename( $mp4, $post_id ) ) ); } else { $videos = array(); $videos[] = array( 'src' => $mp4, 'file' => $this->get_exported_filename( $mp4, $post_id ), 'quality' => ( isset( $post_meta['quality_level'] ) ? sanitize_text_field( $post_meta['quality_level'][0] ) : '' ) ); foreach ( $sources as $source ) { if ( ! empty( $source['src'] ) && ! empty( $source['quality'] ) ) { $src = aiovg_sanitize_url( $source['src'] ); $videos[] = array( 'src' => $src, 'file' => $this->get_exported_filename( $src, $post_id ), 'quality' => sanitize_text_field( $source['quality'] ) ); } } $row[] = wp_json_encode( $videos ); } } break; case 'image': $image = isset( $post_meta['image'] ) ? aiovg_sanitize_url( $post_meta['image'][0] ) : ''; $image_alt = isset( $post_meta['image_alt'] ) ? sanitize_text_field( $post_meta['image_alt'][0] ) : ''; $row[] = wp_json_encode( array( 'src' => $image, 'file' => $this->get_exported_filename( $image, $post_id ), 'alt' => $image_alt ) ); break; case 'track': $tracks = array(); if ( ! empty( $post_meta['track'] ) ) { foreach ( $post_meta['track'] as $track ) { $track = maybe_unserialize( $track ); if ( ! empty( $track['src'] ) ) { $src = aiovg_sanitize_url( $track['src'] ); $tracks[] = array( 'src' => $src, 'file' => $this->get_exported_filename( $src, $post_id ), 'label' => isset( $track['label'] ) ? sanitize_text_field( $track['label'] ) : '', 'srclang' => isset( $track['srclang'] ) ? sanitize_text_field( $track['srclang'] ) : '' ); } } } $row[] = wp_json_encode( $tracks ); break; case 'chapter': $chapters = array(); if ( ! empty( $post_meta['chapter'] ) ) { foreach ( $post_meta['chapter'] as $chapter ) { $chapters[] = maybe_unserialize( $chapter ); } } $row[] = wp_json_encode( $chapters ); break; case 'duration': $row[] = isset( $post_meta['duration'] ) ? sanitize_text_field( $post_meta['duration'][0] ) : ''; break; case 'views': case 'likes': case 'dislikes': case 'download': case 'featured': $row[] = isset( $post_meta[ $field ] ) ? (int) $post_meta[ $field ][0] : 0; break; case 'email_capture': $row[] = isset( $post_meta['email_capture'] ) ? (int) $post_meta['email_capture'][0] : -1; break; case 'access_control': $row[] = isset( $post_meta['access_control'] ) ? (int) $post_meta['access_control'][0] : -1; break; case 'restricted_roles': $restricted_roles = isset( $post_meta['restricted_roles'] ) ? maybe_unserialize( $post_meta['restricted_roles'][0] ) : array(); $restricted_roles = is_array( $restricted_roles ) ? array_map( 'sanitize_text_field', $restricted_roles ) : array(); $row[] = implode( ';', $restricted_roles ); break; case 'post_date': $row[] = get_the_date( 'Y-m-d H:i:s' ); break; case 'post_status': $row[] = $post->post_status; break; case 'comment_status': $row[] = $post->comment_status; break; case 'post_author': $row[] = get_the_author_meta( 'user_email' ); break; default: $row[] = isset( $post_meta[ $field ] ) ? sanitize_text_field( $post_meta[ $field ][0] ) : ''; } } $csv_rows[] = $row; } wp_reset_postdata(); // Calculate new offset $new_offset = $offset + count( $aiovg_query->posts ); // Generate file name on first batch if ( empty( $file_name ) ) { $file_name = 'aiovg-videos-' . $timestamp . '.csv'; } $file_path = trailingslashit( $export_dir ) . $file_name; $file_url = trailingslashit( $export_url ) . $file_name; // Open file in append mode (or write mode if first batch) $handle = fopen( $file_path, $offset === 0 ? 'w' : 'a' ); if ( false === $handle ) { wp_send_json_error( array( 'error' => __( 'Sorry, we were unable to open the CSV file for writing. Please check file permissions and try again.', 'all-in-one-video-gallery' ) ) ); } // Write header row only for first batch if ( $offset === 0 ) { fputcsv( $handle, array_values( $csv_columns ) ); } // Write CSV rows foreach ( $csv_rows as $row ) { fputcsv( $handle, $row ); } fclose( $handle ); // Batch processing if ( $new_offset < $total ) { $message = sprintf( __( '%d out of %d videos processed', 'all-in-one-video-gallery' ), $new_offset, $total ); $response = array( 'step' => 'process', 'message' => $message, 'offset' => $new_offset, 'total' => $total, 'file_name' => $file_name ); wp_send_json_success( $response ); } // Schedule cleanup $this->schedule_export_directory_cleanup(); // Build the final response $message = sprintf( __( 'Your CSV export is complete. %s video records processed successfully. Click here to download the CSV file.', 'all-in-one-video-gallery' ), $new_offset, esc_url( $file_url ) ); $response = array( 'step' => 'done', 'message' => $message, 'offset' => $new_offset, 'total' => $total, 'file_name' => $file_name ); wp_send_json_success( $response ); // Send successful JSON response } /** * AJAX callback to export videos to ZIP files. * * @since 4.5.2 */ public function ajax_callback_export_zip() { if ( function_exists( 'set_time_limit' ) ) { set_time_limit( 1200 ); } if ( function_exists( 'ignore_user_abort' ) ) { ignore_user_abort( true ); } check_ajax_referer( 'aiovg_ajax_nonce', 'security' ); // Verify the nonce for security if ( ! current_user_can( 'manage_aiovg_options' ) ) { wp_send_json_error( array( 'error' => esc_html__( 'You do not have sufficient permissions to do this action.', 'all-in-one-video-gallery' ) ) ); } if ( ! class_exists( 'ZipArchive' ) ) { wp_send_json_error( array( 'error' => __( 'ZIP creation is not supported on your server. This feature requires the PHP "ZipArchive" class. Please ask your hosting provider to enable the PHP ZIP extension, or upgrade PHP to a version that includes it.', 'all-in-one-video-gallery' ) ) ); } // Sanitize and extract parameters $offset = isset( $_POST['offset'] ) ? (int) $_POST['offset'] : 0; $limit = isset( $_POST['limit'] ) ? (int) $_POST['limit'] : 50; $total = isset( $_POST['total'] ) ? (int) $_POST['total'] : 0; $zip_index = isset( $_POST['zip_index'] ) ? (int) $_POST['zip_index'] : 1; $timestamp = isset( $_POST['timestamp'] ) ? sanitize_text_field( $_POST['timestamp'] ) : date( 'YmdHis' ); $max_size = apply_filters( 'aiovg_export_max_zip_size', 1024 * 1024 * 1024 ); // 1GB default $zip_files = array(); $skipped = array(); // Ensure export directory exists $upload_dir = wp_upload_dir(); $upload_basedir = trailingslashit( $upload_dir['basedir'] ); $upload_baseurl = trailingslashit( $upload_dir['baseurl'] ); $export_dir = $upload_basedir . 'all-in-one-video-gallery/exports/'; $export_url = $upload_baseurl . 'all-in-one-video-gallery/exports/'; if ( ! file_exists( $export_dir ) ) { if ( ! wp_mkdir_p( $export_dir ) ) { wp_send_json_error( array( 'error' => __( 'Sorry, we were unable to create the export directory. Please check folder permissions and try again.', 'all-in-one-video-gallery' ) ) ); } } // Query videos $args = array( 'post_type' => 'aiovg_videos', 'post_status' => 'publish', 'posts_per_page' => $limit, 'offset' => $offset, 'fields' => 'ids', 'orderby' => 'ID', 'order' => 'ASC', 'update_post_term_cache' => false, 'update_post_meta_cache' => false ); if ( $offset > 0 ) { $args['no_found_rows'] = true; // Only first batch needs total count } $aiovg_query = new WP_Query( $args ); if ( ! $aiovg_query->have_posts() ) { wp_send_json_error( array( 'error' => __( 'Sorry, no videos were found to export. Please check your selection and try again.', 'all-in-one-video-gallery' ) ) ); } // Get total count on first batch if ( $offset === 0 ) { $total = $aiovg_query->found_posts; } // Process posts and create ZIPs $added_count = 0; $added_size = 0; $zip_name = 'aiovg-videos-' . $timestamp . '-' . $zip_index . '.zip'; $zip_path = trailingslashit( $export_dir ) . $zip_name; $zip = new ZipArchive(); if ( $zip->open( $zip_path, ZipArchive::CREATE ) !== TRUE ) { wp_send_json_error( array( 'error' => __( 'Sorry, we were unable to create the ZIP file. Please check folder permissions and try again.', 'all-in-one-video-gallery' ) ) ); } foreach ( $aiovg_query->posts as $post_id ) { $files = array(); // Get main video file(s) $type = get_post_meta( $post_id, 'type', true ); if ( 'default' == $type ) { $mp4 = get_post_meta( $post_id, 'mp4', true ); if ( ! empty( $mp4 ) && $this->is_file_local( $mp4 ) ) { $files[] = $mp4; } $sources = maybe_unserialize( get_post_meta( $post_id, 'sources', true ) ); if ( ! empty( $sources ) && is_array( $sources ) ) { foreach ( $sources as $source ) { if ( ! empty( $source['src'] ) && $this->is_file_local( $source['src'] ) ) { $files[] = $source['src']; } } } } // Get image file $image = get_post_meta( $post_id, 'image', true ); if ( ! empty( $image ) && $this->is_file_local( $image ) ) { $files[] = $image; } // Get subtitle files $tracks = get_post_meta( $post_id, 'track' ); if ( ! empty( $tracks ) && is_array( $tracks ) ) { foreach ( $tracks as $track ) { $track = maybe_unserialize( $track ); if ( ! empty( $track['src'] ) && $this->is_file_local( $track['src'] ) ) { $files[] = $track['src']; } } } // No files found for this video if ( empty( $files ) ) { continue; } // Move subtitle, image files to the top of the list and video files to the bottom for efficient ZIP compression. // Remove duplicate entries just in case. $files = array_reverse( array_unique( $files ) ); // Process each file foreach ( $files as $file_url ) { // Convert URL to absolute path if ( substr( $file_url, 0, strlen( $upload_baseurl ) ) === $upload_baseurl ) { $file_path = $upload_basedir . substr( $file_url, strlen( $upload_baseurl ) ); } else { $file_path = ABSPATH . str_replace( site_url( '/' ), '', $file_url ); } if ( ! file_exists( $file_path ) ) { continue; } $file_size = filesize( $file_path ); // Skip single files larger than max size if ( $file_size > $max_size ) { $skipped[] = $file_url; continue; } // If adding this file exceeds max ZIP size, close current ZIP and start a new one if ( ( $added_size + $file_size ) > $max_size ) { $zip->close(); $zip_files[] = trailingslashit( $export_url ) . $zip_name; // Schedule cleanup $this->schedule_export_directory_cleanup(); // Start new ZIP $zip_index++; $added_size = 0; $zip_name = 'aiovg-videos-' . $timestamp . '-' . $zip_index . '.zip'; $zip_path = trailingslashit( $export_dir ) . $zip_name; $zip = new ZipArchive(); if ( $zip->open( $zip_path, ZipArchive::CREATE ) !== TRUE ) { wp_send_json_error( array( 'error' => __( 'Sorry, we were unable to create the ZIP file. Please check folder permissions and try again.', 'all-in-one-video-gallery' ) ) ); } } $zip->addFile( $file_path, $this->get_exported_filename( $file_path, $post_id, true ) ); $added_size += $file_size; $added_count++; } } // Close last ZIP if files were added if ( $added_count > 0 ) { $zip->close(); $zip_files[] = trailingslashit( $export_url ) . $zip_name; // Schedule cleanup $this->schedule_export_directory_cleanup(); } wp_reset_postdata(); // Calculate new offset $new_offset = $offset + count( $aiovg_query->posts ); // Batch processing if ( $new_offset < $total ) { $message = sprintf( __( '%d out of %d videos processed', 'all-in-one-video-gallery' ), $new_offset, $total ); $response = array( 'step' => 'process', 'message' => $message, 'offset' => $new_offset, 'total' => $total, 'zip_index' => $zip_index, 'zip_files' => $zip_files, 'skipped' => $skipped ); wp_send_json_success( $response ); } // Build the final response $message = sprintf( __( 'Your ZIP export is complete. %s video records processed, and %s skipped.', 'all-in-one-video-gallery' ), '%%exported%%', '%%skipped%%' ); $response = array( 'step' => 'done', 'message' => $message, 'offset' => $new_offset, 'total' => $total, 'zip_index' => $zip_index, 'zip_files' => $zip_files, 'skipped' => $skipped ); wp_send_json_success( $response ); // Send successful JSON response } /** * AJAX callback to export email leads as a downloadable CSV file. * * @since 4.7.6 */ public function ajax_callback_export_email_leads_csv() { if ( function_exists( 'set_time_limit' ) ) { set_time_limit( 1200 ); } if ( function_exists( 'ignore_user_abort' ) ) { ignore_user_abort( true ); } check_ajax_referer( 'aiovg_ajax_nonce', 'security' ); if ( ! current_user_can( 'manage_aiovg_options' ) ) { wp_send_json_error( array( 'error' => esc_html__( 'You do not have sufficient permissions to do this action.', 'all-in-one-video-gallery' ) ) ); } $video_id = isset( $_GET['video_id'] ) ? absint( $_GET['video_id'] ) : 0; $date_from = isset( $_GET['date_from'] ) ? sanitize_text_field( wp_unslash( $_GET['date_from'] ) ) : ''; $date_to = isset( $_GET['date_to'] ) ? sanitize_text_field( wp_unslash( $_GET['date_to'] ) ) : ''; $leads = aiovg_email_capture_get_leads( array( 'video_id' => $video_id, 'date_from' => $date_from, 'date_to' => $date_to ) ); if ( $video_id > 0 ) { $post = get_post( $video_id ); $slug = ( $post && ! empty( $post->post_name ) ) ? $post->post_name : 'video-' . $video_id; $file_name = 'aiovg-emails-' . $slug . '-' . date( 'Y-m-d' ) . '.csv'; } else { $file_name = 'aiovg-emails-' . date( 'Y-m-d' ) . '.csv'; } aiovg_email_capture_generate_csv( $leads, $file_name ); } /** * Hooked cleanup function to delete all files inside the export directory. * * @since 4.5.2 */ public function cleanup_export_directory() { // Ensure WP_Filesystem is available if ( ! isset( $GLOBALS['wp_filesystem'] ) || ! is_object( $GLOBALS['wp_filesystem'] ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; WP_Filesystem(); } global $wp_filesystem; // Path to export directory $uploads = wp_upload_dir(); $export_dir = trailingslashit( $uploads['basedir'] ) . 'all-in-one-video-gallery/exports/'; if ( $wp_filesystem->is_dir( $export_dir ) ) { // Get all items (files and subdirectories) inside the folder $items = $wp_filesystem->dirlist( $export_dir ); foreach ( $items as $item ) { $path = trailingslashit( $export_dir ) . $item['name']; // Recursive delete: removes files and subfolders $wp_filesystem->delete( $path, true ); } } } /** * Parse CSV data with mapping, offset, and limit support. * * @since 4.5.2 * @access private * @param string $csv_file Path or attachment URL of the CSV file. * @param array $attributes Import settings. * @return array|WP_Error Parsed CSV data. */ private function parse_csv( $csv_file, $attributes = array() ) { if ( empty( $csv_file ) ) { return new WP_Error( 'invalid_csv', __( 'Sorry, the selected file is not a valid CSV. Please upload a valid CSV file.', 'all-in-one-video-gallery' ) ); } // Default arguments. $attributes = wp_parse_args( $attributes, array( 'columns_separator' => ',', 'csv_columns' => array(), 'headers_only' => false, 'offset' => 0, 'limit' => 0 ) ); $response = array( 'headers' => array(), 'videos' => array(), 'has_more' => false, 'new_offset' => $attributes['offset'] ); $csv_row_number = 0; // Resolve CSV file path. $attachment_id = attachment_url_to_postid( $csv_file ); $csv_file_path = $attachment_id ? get_attached_file( $attachment_id ) : ''; if ( ! $csv_file_path || ! file_exists( $csv_file_path ) ) { return new WP_Error( 'invalid_csv', __( 'Sorry, the selected file is not a valid CSV. Please upload a valid CSV file.', 'all-in-one-video-gallery' ) ); } if ( ! is_readable( $csv_file_path ) ) { chmod( $csv_file_path, 0744 ); if ( ! is_readable( $csv_file_path ) ) { return new WP_Error( 'csv_not_readable', __( 'Sorry, we could not read the selected CSV file. Please check the file and try again.', 'all-in-one-video-gallery' ) ); } } if ( function_exists( 'wp_check_filetype' ) ) { $filetype = wp_check_filetype( $csv_file_path ); if ( empty( $filetype['ext'] ) || 'csv' !== strtolower( $filetype['ext'] ) ) { return new WP_Error( 'invalid_file_type', __( 'Sorry, this file type is not supported. Please upload a valid CSV file.', 'all-in-one-video-gallery' ) ); } } if ( false === ( $handle = fopen( $csv_file_path, 'r' ) ) ) { return new WP_Error( 'csv_open_failed', __( 'Sorry, we were unable to open the selected CSV file. Please check the file and try again.', 'all-in-one-video-gallery' ) ); } // Get headers $headers = fgetcsv( $handle, 0, $attributes['columns_separator'] ); $csv_row_number++; if ( empty( $headers ) || ! is_array( $headers ) ) { fclose( $handle ); return new WP_Error( 'invalid_csv_headers', __( 'Sorry, the CSV file does not contain the required column headers. Please check the file and try again.', 'all-in-one-video-gallery' ) ); } $response['headers'] = array_map( 'trim', $headers ); if ( true === $attributes['headers_only'] ) { fclose( $handle ); return $response; } // Skip until offset for ( $i = 0; $i < $attributes['offset']; $i++ ) { if ( feof( $handle ) ) break; fgetcsv( $handle, 0, $attributes['columns_separator'] ); $csv_row_number++; } // Read CSV rows while ( ! feof( $handle ) ) { if ( $attributes['limit'] > 0 && count( $response['videos'] ) >= $attributes['limit'] ) { // Reached limit, stop reading further rows break; } $row = fgetcsv( $handle, 0, $attributes['columns_separator'] ); $csv_row_number++; if ( $row === false ) { if ( ! feof( $handle ) ) { // Malformed row $response['videos'][] = array( 'number' => $csv_row_number, 'error' => __( 'Malformed CSV row', 'all-in-one-video-gallery' ) ); continue; } else { break; } } $post = array( 'number' => $csv_row_number ); // Include empty rows if ( ! array_filter( $row, 'strlen' ) ) { $response['videos'][] = $post; continue; } // Map columns foreach ( $attributes['csv_columns'] as $i => $field ) { if ( ! empty( $field ) && isset( $row[ $i ] ) ) { $value = trim( $row[ $i ] ); $decoded = json_decode( $value, true ); if ( json_last_error() === JSON_ERROR_NONE && is_array( $decoded ) ) { $value = $decoded; } else { $value = htmlspecialchars_decode( $value ); } $post[ $field ] = $value; } } $post = apply_filters( 'aiovg_parse_csv_row', $post, $attributes ); $response['videos'][] = $post; } // Peek one extra row to set has_more if needed if ( ! feof( $handle ) && $attributes['limit'] > 0 ) { while ( ( $next_row = fgetcsv( $handle, 0, $attributes['columns_separator'] ) ) !== false ) { if ( array_filter( $next_row, 'strlen' ) ) { $response['has_more'] = true; break; } } } fclose( $handle ); // Calculate next offset for batch import $response['new_offset'] = $csv_row_number - 1; return $response; } /** * Resolve import directory from a ZIP file or a folder path. * * @since 4.5.2 * @access private * @param string $zip_file Path or attachment URL of the ZIP file, or folder path. * @return string|WP_Error Extracted directory path or validated folder path, or WP_Error on failure. */ private function resolve_import_directory( $zip_file = '' ) { if ( empty( $zip_file ) ) { return new WP_Error( 'invalid_zip', __( 'Sorry, no ZIP file or folder path was provided. Please select a ZIP file or enter a folder path to continue.', 'all-in-one-video-gallery' ) ); } // CASE 1: Folder path $folder_path = $this->resolve_folder_path( $zip_file ); if ( ! is_wp_error( $folder_path ) ) { // Valid folder return $folder_path; } // If folder validation failed with 'invalid_folder', treat it as ZIP if ( $folder_path->get_error_code() !== 'invalid_folder' ) { // Any other error (e.g., permissions, invalid location) — stop return $folder_path; } // CASE 2: ZIP attachment $attachment_id = attachment_url_to_postid( $zip_file ); $zip_file_path = $attachment_id ? get_attached_file( $attachment_id ) : ''; if ( empty( $zip_file_path ) || ! is_file( $zip_file_path ) ) { return new WP_Error( 'invalid_zip', __( 'Sorry, the selected ZIP file could not be found or read. Please check the file and try again.', 'all-in-one-video-gallery' ) ); } // Ensure WP_Filesystem is available if ( ! isset( $GLOBALS['wp_filesystem'] ) || ! is_object( $GLOBALS['wp_filesystem'] ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; WP_Filesystem(); } $uploads = wp_upload_dir(); $zip_file_name = basename( $zip_file_path ); $folder_name = substr( $zip_file_name, 0, strrpos( $zip_file_name, '.' ) ); $default_path = trailingslashit( $uploads['basedir'] ) . 'all-in-one-video-gallery/imports/' . sanitize_file_name( $folder_name ); $extract_path = apply_filters( 'aiovg_import_directory_path', $default_path, array( 'zip_file' => $zip_file ) ); if ( ! wp_mkdir_p( $extract_path ) ) { return new WP_Error( 'mkdir_failed', __( 'Sorry, we were unable to create the import directory. Please check your folder permissions and try again.', 'all-in-one-video-gallery' ) ); } // Create protection files safely inside the extract path $htaccess_file = trailingslashit( $extract_path ) . '.htaccess'; $webconfig_file = trailingslashit( $extract_path ) . 'web.config'; $htaccess_rules = "Deny from all\n"; $webconfig_rules = "\n" . "\n" . "\t\n" . "\t\t\n" . "\t\t\t\n" . "\t\t\n" . "\t\n" . ""; if ( false === file_put_contents( $htaccess_file, $htaccess_rules ) ) { return new WP_Error( 'file_write_failed', __( 'We were unable to complete a required file operation. Please make sure your server allows writing files.', 'all-in-one-video-gallery' ) ); } if ( false === file_put_contents( $webconfig_file, $webconfig_rules ) ) { return new WP_Error( 'file_write_failed', __( 'We were unable to complete a required file operation. Please make sure your server allows writing files.', 'all-in-one-video-gallery' ) ); } // Unzip $unzip_result = unzip_file( $zip_file_path, $extract_path ); if ( is_wp_error( $unzip_result ) ) { // Remove protection files before returning unlink( $htaccess_file ); unlink( $webconfig_file ); return $unzip_result; } // Keep only files allowed by WordPress MIME validation $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $extract_path, RecursiveDirectoryIterator::SKIP_DOTS ), RecursiveIteratorIterator::CHILD_FIRST ); foreach ( $iterator as $file ) { if ( $file->isFile() ) { $filetype = wp_check_filetype( $file->getFilename() ); if ( empty( $filetype['type'] ) ) { unlink( $file->getPathname() ); } } } // Security cleanup unlink( $htaccess_file ); unlink( $webconfig_file ); return untrailingslashit( $extract_path ); } /** * Validate a folder path inside the WordPress directory. * * @since 4.5.2 * @access private * @param string $folder The folder path to validate (relative or absolute). * @return string|WP_Error Validated and normalized absolute folder path, or WP_Error on failure. */ private function resolve_folder_path( $folder ) { if ( empty( $folder ) ) { return new WP_Error( 'invalid_folder', __( 'Sorry, the folder path provided is invalid. Please check the path and try again.', 'all-in-one-video-gallery' ) ); } // Resolve the absolute path $folder_path = realpath( trailingslashit( ABSPATH ) . ltrim( $folder, '/' ) ); if ( ! $folder_path || ! is_dir( $folder_path ) ) { return new WP_Error( 'invalid_folder', __( 'Sorry, the folder path provided is invalid. Please check the path and try again.', 'all-in-one-video-gallery' ) ); } // Normalize and remove trailing slash $folder_path = untrailingslashit( wp_normalize_path( $folder_path ) ); // Ensure folder is inside the WordPress directory if ( strpos( $folder_path, wp_normalize_path( ABSPATH ) ) !== 0 ) { return new WP_Error( 'invalid_folder_location', __( 'Sorry, the folder must be located inside your WordPress directory. Please choose a valid folder and try again.', 'all-in-one-video-gallery' ) ); } return $folder_path; } /** * Import videos from folder. * * @since 4.5.2 * @access private * @param array $videos Array of videos to import. * @param array $attributes Import settings. * @return array $response Array containing import results. */ private function import_videos_from_folder( $videos, $attributes = array() ) { $response = array( 'total' => count( $videos ), 'imported' => array(), 'skipped' => array(), 'failed' => array() ); if ( empty( $videos ) ) { return $response; } // Pre import wp_defer_term_counting( true ); wp_defer_comment_counting( true ); // Import videos foreach ( $videos as $video ) { // Check if the video post already exists $post_id = $this->is_video_exists( $video['src'] ); $video_src = aiovg_sanitize_url( $video['src'] ); if ( ! empty( $post_id ) ) { $response['skipped'][] = $video_src; continue; } // Insert post $args = array( 'post_type' => 'aiovg_videos', 'post_title' => wp_strip_all_tags( $video['title'] ), 'post_status' => 'publish', 'comment_status' => $attributes['comment_status'] ); if ( 'random' === $attributes['slug_strategy'] ) { $args['post_name'] = $this->generate_random_video_slug( $video_src ); } $post_id = wp_insert_post( $args ); // Insert post meta if ( ! is_wp_error( $post_id ) && ! empty( $post_id ) ) { // Categories if ( ! empty( $attributes['categories'] ) ) { wp_set_object_terms( $post_id, $attributes['categories'], 'aiovg_categories' ); } // Tags if ( ! empty( $attributes['tags'] ) ) { wp_set_object_terms( $post_id, $attributes['tags'], 'aiovg_tags' ); } // Meta $meta = array( 'type' => 'default', 'mp4' => $video_src, 'duration' => '', 'featured' => 0, 'views' => 0, 'likes' => 0, 'dislikes' => 0, 'download' => $attributes['enable_downloads'], 'email_capture' => $attributes['email_capture'], 'access_control' => $attributes['access_control'], 'restricted_roles' => $attributes['restricted_roles'], 'import_id' => $attributes['import_id'] ); // Image if ( ! empty( $video['image'] ) ) { $meta['image'] = aiovg_sanitize_url( $video['image'] ); if ( ! empty( $attributes['set_featured_image'] ) && ! empty( $video['image_path'] ) ) { $image_id = $this->create_attachment( $post_id, sanitize_text_field( $video['image_path'] ), $meta['image'] ); if ( ! empty( $image_id ) ) { $meta['image_id'] = $image_id; set_post_thumbnail( $post_id, $image_id ); } } } // Insert post meta in bulk $this->add_post_meta_bulk( $post_id, $meta ); $response['imported'][] = $video_src; // Hook for developers do_action( 'aiovg_video_imported', $post_id, $attributes['import_id'] ); } else { $response['failed'][] = $video_src; } } // Post Import wp_defer_term_counting( false ); wp_defer_comment_counting( false ); return $response; } /** * Import videos from CSV. * * @since 4.5.2 * @access private * @param array $videos Array of videos to import. * @param array $attributes Import settings. * @return array $response Array containing import results. */ private function import_videos_from_csv( $videos, $attributes = array() ) { $response = array( 'total' => count( $videos ), 'imported' => array(), 'updated' => array(), 'skipped' => array(), 'failed' => array(), 'errors' => array() ); if ( empty( $videos ) ) { return $response; } // Pre import wp_defer_term_counting( true ); wp_defer_comment_counting( true ); // Import videos foreach ( $videos as $row ) { $csv_row_number = (int) $row['number']; if ( isset( $row['error'] ) && ! empty( $row['error'] ) ) { $response['failed'][] = $csv_row_number; $response['errors'][] = sprintf( __( 'Line #%d: %s', 'all-in-one-video-gallery' ), $csv_row_number, esc_html( $row['error'] ) ); continue; } if ( empty( $row['post_title'] ) || empty( $row['video'] ) ) { $response['failed'][] = $csv_row_number; $response['errors'][] = sprintf( __( 'Line #%d: Invalid video title or source.', 'all-in-one-video-gallery' ), $csv_row_number ); continue; } // Parse video data $post_title = wp_strip_all_tags( $row['post_title'] ); $video = $row['video']; $sources = array(); if ( is_array( $video ) ) { // Multiple videos array if ( ! isset( $video['src'] ) ) { $sources = $video; $video = array_shift( $sources ); } } else { $video = array( 'src' => $video ); } if ( empty( $video['src'] ) ) { $response['failed'][] = $csv_row_number; $response['errors'][] = sprintf( __( 'Line #%d: %s - Invalid video source.', 'all-in-one-video-gallery' ), $csv_row_number, $post_title ); continue; } // $video['src'] is a simple filename (e.g. video.mp4) if ( ! empty( $attributes['folder'] ) && preg_match( '/^[a-zA-Z0-9_\-]+\.[a-zA-Z0-9]+$/i', $video['src'] ) ) { $local_file_path = $attributes['folder'] . '/' . sanitize_file_name( $video['src'] ); if ( file_exists( $local_file_path ) ) { $video['src'] = $this->make_absolute_url( $local_file_path ); } } // Check if the video post already exists $type = $this->resolve_video_type( $video['src'] ); $post_id = isset( $row['post_id'] ) ? (int) $row['post_id'] : 0; if ( $post_id > 0 ) { unset( $row['post_id'] ); $post = get_post( $post_id ); if ( $post && 'aiovg_videos' === $post->post_type ) { $row['post_id'] = $post_id; } } if ( ! isset( $row['post_id'] ) ) { $post_id = $this->is_video_exists( $video['src'], $type ); if ( ! empty( $post_id ) ) { $response['skipped'][] = $csv_row_number; $response['errors'][] = sprintf( __( 'Line #%d: %s - Video already exists.', 'all-in-one-video-gallery' ), $csv_row_number, $post_title ); continue; } } // Insert / Update post $is_existing_post = false; $args = array( 'post_type' => 'aiovg_videos', 'post_title' => $post_title ); if ( isset( $row['post_content'] ) && ! empty( $row['post_content'] ) ) { $args['post_content'] = wp_kses_post( $row['post_content'] ); } if ( isset( $row['post_excerpt'] ) && ! empty( $row['post_excerpt'] ) ) { $args['post_excerpt'] = wp_kses_post( $row['post_excerpt'] ); } if ( isset( $row['post_date'] ) && ! empty( $row['post_date'] ) ) { $timestamp = strtotime( $row['post_date'] ); if ( false !== $timestamp ) { $args['post_date'] = gmdate( 'Y-m-d H:i:s', $timestamp ); } } if ( isset( $row['post_status'] ) && ! empty( $row['post_status'] ) ) { $args['post_status'] = sanitize_text_field( $row['post_status'] ); } if ( isset( $row['post_author'] ) && ! empty( $row['post_author'] ) ) { $post_author = $this->resolve_user_id( $row['post_author'], $attributes ); if ( ! is_wp_error( $post_author ) ) { $args['post_author'] = (int) $post_author; } else { $response['errors'][] = sprintf( __( 'Line #%d: %s - %s', 'all-in-one-video-gallery' ), $csv_row_number, $post_title, $post_author->get_error_message() ); } } if ( isset( $row['comment_status'] ) && 'open' === strtolower( $row['comment_status'] ) ) { $args['comment_status'] = 'open'; } if ( isset( $row['post_id'] ) ) { // Update the existing video post $post_id = $row['post_id']; $args['ID'] = $row['post_id']; wp_update_post( $args ); $is_existing_post = true; } else { if ( ! isset( $row['post_status'] ) || empty( $row['post_status'] ) ) { $args['post_status'] = 'publish'; } if ( ! isset( $row['comment_status'] ) || empty( $row['comment_status'] ) ) { $args['comment_status'] = $attributes['comment_status']; } $post_id = wp_insert_post( $args ); } // Insert post meta if ( ! is_wp_error( $post_id ) && ! empty( $post_id ) ) { // Categories if ( isset( $row['aiovg_categories'] ) ) { wp_set_object_terms( $post_id, null, 'aiovg_categories' ); if ( ! empty( $row['aiovg_categories'] ) ) { $categories = $this->resolve_term_ids( $row['aiovg_categories'], 'aiovg_categories', $attributes ); if ( ! is_wp_error( $categories ) ) { if ( ! empty( $categories ) ) { wp_set_object_terms( $post_id, $categories, 'aiovg_categories' ); } } else { $response['errors'][] = sprintf( __( 'Line #%d: %s - %s', 'all-in-one-video-gallery' ), $csv_row_number, $post_title, $categories->get_error_message() ); } } } // Tags if ( isset( $row['aiovg_tags'] ) ) { wp_set_object_terms( $post_id, null, 'aiovg_tags' ); if ( ! empty( $row['aiovg_tags'] ) ) { $tags = $this->resolve_term_ids( $row['aiovg_tags'], 'aiovg_tags', $attributes ); if ( ! is_wp_error( $tags ) ) { if ( ! empty( $tags ) ) { wp_set_object_terms( $post_id, $tags, 'aiovg_tags' ); } } else { $response['errors'][] = sprintf( __( 'Line #%d: %s - %s', 'all-in-one-video-gallery' ), $csv_row_number, $post_title, $tags->get_error_message() ); } } } // Subtitles if ( isset( $row['track'] ) ) { delete_post_meta( $post_id, 'track' ); if ( is_array( $row['track'] ) ) { foreach ( $row['track'] as $source ) { if ( ! empty( $source['src'] ) ) { $src = $source['src']; if ( ! empty( $attributes['folder'] ) && preg_match( '/^[a-zA-Z0-9_\-]+\.[a-zA-Z0-9]+$/i', $src ) ) { // $src is a simple filename (e.g. captions.vtt) $local_file_path = $attributes['folder'] . '/' . sanitize_file_name( $src ); if ( file_exists( $local_file_path ) ) { $src = $this->make_absolute_url( $local_file_path ); } } if ( ! empty( $attributes['folder'] ) && isset( $source['file'] ) && ! empty( $source['file'] ) ) { $local_file_path = $attributes['folder'] . '/' . sanitize_file_name( $source['file'] ); if ( file_exists( $local_file_path ) ) { $src = $this->make_absolute_url( $local_file_path ); } } $track = array( 'src' => aiovg_sanitize_url( $src ), 'label' => '', 'srclang' => '' ); if ( isset( $source['label'] ) && ! empty( $source['label'] ) ) { $track['label'] = sanitize_text_field( $source['label'] ); } if ( isset( $source['srclang'] ) && ! empty( $source['srclang'] ) ) { $track['srclang'] = sanitize_text_field( $source['srclang'] ); } add_post_meta( $post_id, 'track', $track ); } } } } // Chapters if ( isset( $row['chapter'] ) ) { delete_post_meta( $post_id, 'chapter' ); if ( is_array( $row['chapter'] ) ) { foreach ( $row['chapter'] as $chapter ) { if ( ! empty( $chapter['time'] ) && ! empty( $chapter['label'] ) ) { $chapter = array( 'time' => sanitize_text_field( $chapter['time'] ), 'label' => sanitize_text_field( $chapter['label'] ) ); add_post_meta( $post_id, 'chapter', $chapter ); } } } } // Import ID $import_id = sanitize_text_field( $attributes['import_id'] ); if ( ! metadata_exists( 'post', $post_id, 'import_id' ) ) { add_post_meta( $post_id, 'import_id', $import_id ); } else { // Meta exists — check if same value $existing = get_post_meta( $post_id, 'import_id' ); if ( ! in_array( $import_id, $existing ) ) { add_post_meta( $post_id, 'import_id', $import_id ); } } // Meta $meta = array(); // Type $meta['type'] = $type; // Video if ( in_array( $type, array( 'youtube', 'vimeo', 'dailymotion', 'rumble', 'facebook' ) ) ) { $meta[ $type ] = aiovg_sanitize_url( $video['src'] ); } elseif ( in_array( $type, array( 'hls', 'dash' ) ) ) { $meta['type'] = 'adaptive'; $meta[ $type ] = aiovg_sanitize_url( $video['src'] ); } elseif ( 'embedcode' == $type ) { try { add_filter( 'wp_kses_allowed_html', 'aiovg_allow_iframe_script_tags' ); $meta[ $type ] = wp_kses_post( $video['src'] ); } finally { remove_filter( 'wp_kses_allowed_html', 'aiovg_allow_iframe_script_tags' ); } } else { $src = $video['src']; if ( ! empty( $attributes['folder'] ) && isset( $video['file'] ) && ! empty( $video['file'] ) ) { $local_file_path = $attributes['folder'] . '/' . sanitize_file_name( $video['file'] ); if ( file_exists( $local_file_path ) ) { $src = $this->make_absolute_url( $local_file_path ); } } $meta['mp4'] = aiovg_sanitize_url( $src ); // Quality level if ( isset( $video['quality'] ) && ! empty( $video['quality'] ) ) { $meta['quality_level'] = sanitize_text_field( $video['quality'] ); } } // Sources $videos = array(); foreach ( $sources as $source ) { if ( ! empty( $source['src'] ) && ! empty( $source['quality'] ) ) { $src = $source['src']; if ( ! empty( $attributes['folder'] ) && preg_match( '/^[a-zA-Z0-9_\-]+\.[a-zA-Z0-9]+$/i', $src ) ) { // $src is a simple filename (e.g. video.mp4) $local_file_path = $attributes['folder'] . '/' . sanitize_file_name( $src ); if ( file_exists( $local_file_path ) ) { $src = $this->make_absolute_url( $local_file_path ); } } if ( ! empty( $attributes['folder'] ) && isset( $source['file'] ) && ! empty( $source['file'] ) ) { $local_file_path = $attributes['folder'] . '/' . sanitize_file_name( $source['file'] ); if ( file_exists( $local_file_path ) ) { $src = $this->make_absolute_url( $local_file_path ); } } $videos[] = array( 'src' => aiovg_sanitize_url( $src ), 'quality' => sanitize_text_field( $source['quality'] ) ); } } $meta['sources'] = $videos; // Parse image data $image = ''; $image_path = ''; $image_id = 0; $image_alt = ''; if ( isset( $row['image'] ) ) { if ( is_array( $row['image'] ) ) { if ( isset( $row['image']['src'] ) && ! empty( $row['image']['src'] ) ) { $image = $row['image']['src']; } if ( ! empty( $attributes['folder'] ) && isset( $row['image']['file'] ) && ! empty( $row['image']['file'] ) ) { $local_file_path = $attributes['folder'] . '/' . sanitize_file_name( $row['image']['file'] ); if ( file_exists( $local_file_path ) ) { $image = $this->make_absolute_url( $local_file_path ); $image_path = $local_file_path; } } if ( isset( $row['image']['alt'] ) && ! empty( $row['image']['alt'] ) ) { $image_alt = $row['image']['alt']; } } else { $image = $row['image']; } // $image is a simple filename (e.g. image.jpg) if ( ! empty( $image ) && ! empty( $attributes['folder'] ) && preg_match( '/^[a-zA-Z0-9_\-]+\.[a-zA-Z0-9]+$/i', $image ) ) { $local_file_path = $attributes['folder'] . '/' . sanitize_file_name( $image ); if ( file_exists( $local_file_path ) ) { $image = $this->make_absolute_url( $local_file_path ); $image_path = $local_file_path; } } } $meta['image'] = ! empty( $image ) ? aiovg_sanitize_url( $image ) : ''; $meta['image_alt'] = ! empty( $image_alt ) ? sanitize_text_field( $image_alt ) : ''; // Featured Image if ( ! empty( $attributes['set_featured_image'] ) ) { if ( ! empty( $meta['image'] ) ) { if ( ! empty( $image_path ) ) { $image_id = $this->create_attachment( $post_id, $image_path, $meta['image'] ); } else { $image_id = aiovg_create_attachment_from_external_image_url( $meta['image'], $post_id ); } } if ( ! empty( $image_id ) ) { $meta['image_id'] = $image_id; set_post_thumbnail( $post_id, $image_id ); } } // Duration if ( isset( $row['duration'] ) ) { $meta['duration'] = ''; if ( ! empty( $row['duration'] ) ) { $meta['duration'] = sanitize_text_field( $row['duration'] ); } } // Views if ( isset( $row['views'] ) ) { $meta['views'] = 0; if ( ! empty( $row['views'] ) ) { $meta['views'] = (int) $row['views']; } } // Likes if ( isset( $row['likes'] ) ) { $meta['likes'] = 0; if ( ! empty( $row['likes'] ) ) { $meta['likes'] = (int) $row['likes']; } } // Dislikes if ( isset( $row['dislikes'] ) ) { $meta['dislikes'] = 0; if ( ! empty( $row['dislikes'] ) ) { $meta['dislikes'] = (int) $row['dislikes']; } } // Download if ( isset( $row['download'] ) ) { $meta['download'] = 0; if ( ! empty( $row['download'] ) ) { $meta['download'] = (int) $row['download']; } } // Featured if ( isset( $row['featured'] ) ) { $meta['featured'] = 0; if ( ! empty( $row['featured'] ) ) { $meta['featured'] = (int) $row['featured']; } } // Email Capture if ( isset( $row['email_capture'] ) ) { $meta['email_capture'] = -1; if ( '' !== (string) $row['email_capture'] ) { $meta['email_capture'] = (int) $row['email_capture']; } } // Access Control if ( isset( $row['access_control'] ) ) { $meta['access_control'] = -1; if ( ! empty( $row['access_control'] ) ) { $meta['access_control'] = (int) $row['access_control']; } } // Restricted Roles if ( isset( $row['restricted_roles'] ) ) { $meta['restricted_roles'] = array(); if ( ! empty( $row['restricted_roles'] ) ) { $separator = ! empty( $attributes['values_separator'] ) ? $attributes['values_separator'] : ';'; $restricted_roles = array_filter( array_map( 'trim', explode( $separator, $row['restricted_roles'] ) ) ); $meta['restricted_roles'] = array_map( 'sanitize_text_field', $restricted_roles ); } } // Insert / Update post meta in bulk if ( $is_existing_post ) { $this->update_post_meta_bulk( $post_id, $meta ); $response['updated'][] = $csv_row_number; } else { $this->add_post_meta_bulk( $post_id, $meta ); $response['imported'][] = $csv_row_number; } // Hook for developers do_action( 'aiovg_video_imported', $post_id, $attributes['import_id'] ); } else { $response['failed'][] = $csv_row_number; $response['errors'][] = sprintf( __( 'Line #%d: %s - %s', 'all-in-one-video-gallery' ), $csv_row_number, $post_title, $post_id->get_error_message() ); } } // Post Import wp_defer_term_counting( false ); wp_defer_comment_counting( false ); return $response; } /** * Check if the video already exists. * * @since 4.5.2 * @access private * @param string $src The video source (URL or embed code). * @param string $type The video source type. * @return mixed Video post ID if exists, false if not. */ private function is_video_exists( $src, $type = 'default' ) { if ( empty( $src ) || empty( $type ) ) { return false; } $type = sanitize_key( $type ); $args = array( 'post_type' => 'aiovg_videos', 'post_status' => 'any', 'posts_per_page' => 1, 'fields' => 'ids', 'no_found_rows' => true, 'update_post_term_cache' => false, 'update_post_meta_cache' => false ); $meta_queries = array(); // Match by video type $meta_queries[] = array( 'key' => 'type', 'value' => ( in_array( $type, array( 'hls', 'dash' ) ) ? 'adaptive' : $type ), 'compare' => '=' ); // Match by source URL if ( 'embedcode' === $type ) { try { add_filter( 'wp_kses_allowed_html', 'aiovg_allow_iframe_script_tags' ); $meta_queries[] = array( 'key' => 'embedcode', 'value' => wp_kses_post( $src ), 'compare' => '=' ); } finally { remove_filter( 'wp_kses_allowed_html', 'aiovg_allow_iframe_script_tags' ); } } else { $meta_queries[] = array( 'key' => ( 'default' == $type ? 'mp4' : $type ), 'value' => aiovg_sanitize_url( $src ), 'compare' => '=' ); } // Assign the meta query with 'AND' relation if multiple clauses exist. if ( count( $meta_queries ) > 1 ) { $meta_queries['relation'] = 'AND'; } $args['meta_query'] = $meta_queries; $aiovg_query = new WP_Query( $args ); if ( $aiovg_query->have_posts() ) { // Since 'fields' is 'ids', $aiovg_query->posts is an array of post IDs. return $aiovg_query->posts[0]; } return false; } /** * Generate a secure, deterministic video post slug. * * @since 4.7.0 * @access private * @param string $video_src Absolute or relative video file path. * @return string */ private function generate_random_video_slug( $video_src ) { // Normalize path for consistency across environments $normalized = wp_normalize_path( $video_src ); // Add site-specific salt $hash = hash( 'sha256', $normalized . '|' . wp_salt( 'aiovg_video_slug' ) ); // Short, URL-safe slug return substr( $hash, 0, 18 ); } /** * Resolve video type. * * @since 4.5.2 * @access private * @param string $src Video source (URL, relative path, or embed code). * @return string Video type ('default', 'youtube', 'vimeo', etc., or 'embedcode'). */ private function resolve_video_type( $src ) { if ( empty( $src ) ) { return 'default'; } // Normalize source $src = trim( $src ); // Check for any HTML markup if ( preg_match( '/<\s*\w+(?:\b[^>]*)?>/i', $src ) ) { return 'embedcode'; } // Check known video platforms if ( false !== stripos( $src, 'youtube.com' ) || false !== stripos( $src, 'youtu.be' ) ) { return 'youtube'; } elseif ( false !== stripos( $src, 'vimeo.com' ) ) { return 'vimeo'; } elseif ( false !== stripos( $src, 'dailymotion.com' ) ) { return 'dailymotion'; } elseif ( false !== stripos( $src, 'rumble.com' ) ) { return 'rumble'; } elseif ( false !== stripos( $src, 'facebook.com' ) ) { return 'facebook'; } else { $file_type = wp_check_filetype( $src ); if ( 'm3u8' == $file_type['ext'] ) { return 'hls'; } if ( 'mpd' == $file_type['ext'] ) { return 'dash'; } } // Default fallback return 'default'; } /** * Resolve term IDs from imported data for a given taxonomy. * * Supports hierarchical chains like "Parent > Child > Subchild". * Can optionally create new terms if they don't exist. * * @since 4.5.2 * @access private * @param array $terms Imported terms array. * @param string $taxonomy Taxonomy name. * @param array $attributes Import settings (expects 'values_separator' and 'add_new_term'). * @return array|WP_Error Array of term IDs, or WP_Error on failure. */ private function resolve_term_ids( $terms, $taxonomy, $attributes ) { $term_ids = array(); if ( empty( $terms ) ) { return $term_ids; // No terms provided, return empty array } $separator = ! empty( $attributes['values_separator'] ) ? $attributes['values_separator'] : ';'; $terms_raw = array_filter( array_map( 'trim', explode( $separator, $terms ) ) ); foreach ( $terms_raw as $term_item ) { // Numeric term IDs if ( is_numeric( $term_item ) && get_term( (int) $term_item, $taxonomy ) ) { $term_ids[] = (int) $term_item; continue; } // Hierarchical chain $chain = array_filter( array_map( 'trim', explode( '>', $term_item ) ) ); if ( empty( $chain ) ) { continue; } $parent_id = 0; // For hierarchy foreach ( $chain as $term_name ) { $term_name = htmlspecialchars( sanitize_text_field( $term_name ) ); // Handle & etc. // Check if term exists $term = term_exists( $term_name, $taxonomy, $parent_id ); if ( $term ) { $parent_id = (int) $term['term_id']; } else { if ( ! empty( $attributes['add_new_term'] ) ) { $new_term = wp_insert_term( $term_name, $taxonomy, array( 'parent' => $parent_id ) ); if ( is_wp_error( $new_term ) ) { return new WP_Error( 'term_insert_failed', sprintf( __( 'Failed to create term "%s" in taxonomy "%s".', 'all-in-one-video-gallery' ), $term_name, $taxonomy ) ); } $parent_id = (int) $new_term['term_id']; } else { return new WP_Error( 'term_not_found', sprintf( __( 'Term "%s" not found in taxonomy "%s" and adding new terms is disabled.', 'all-in-one-video-gallery' ), $term_name, $taxonomy ) ); } } // Append each term in the chain if not already added if ( $parent_id && ! in_array( $parent_id, $term_ids, true ) ) { $term_ids[] = $parent_id; } } } return $term_ids; } /** * Resolve user ID from a username or email, optionally creating a new user. * * @since 4.5.2 * @access private * @param string $post_author WordPress username or email address. * @param array $attributes Import settings (expects 'add_new_user' boolean). * @return int|WP_Error User ID on success, WP_Error on failure. */ private function resolve_user_id( $post_author, $attributes = array() ) { if ( empty( $post_author ) ) { return new WP_Error( 'invalid_user', __( 'No username or email provided.', 'all-in-one-video-gallery' ) ); } $post_author = sanitize_text_field( $post_author ); // Case 1: Numeric user ID if ( is_numeric( $post_author ) ) { $user = get_user_by( 'ID', (int) $post_author ); if ( $user ) { return $user->ID; } return new WP_Error( 'invalid_user', __( 'User ID does not exist.', 'all-in-one-video-gallery' ) ); } // Case 2: Email if ( is_email( $post_author ) ) { $email = sanitize_email( $post_author ); $user = get_user_by( 'email', $email ); if ( $user ) { return $user->ID; } if ( ! empty( $attributes['add_new_user'] ) ) { $password = wp_generate_password( 12, true ); $new_user_id = wp_create_user( $email, $password, $email ); if ( is_wp_error( $new_user_id ) ) { return $new_user_id; } return $new_user_id; } return new WP_Error( 'invalid_user', sprintf( __( 'User with email "%s" does not exist.', 'all-in-one-video-gallery' ), $email ) ); } // Case 3: Username $user_id = username_exists( $post_author ); if ( $user_id ) { return $user_id; } return new WP_Error( 'invalid_user', sprintf( __( 'User "%s" does not exist.', 'all-in-one-video-gallery' ), $post_author ) ); } /** * Create a WordPress attachment from a local file if it doesn't already exist. * * @since 4.5.2 * @access private * @param int $post_id The post ID to attach the media to (e.g., a video post). * @param string $file_path Absolute path to the local file. * @param string $file_url Optional. Public URL of the file (for checking duplicates by URL). * @return int|false Attachment ID on success, false on failure. */ private function create_attachment( $post_id, $file_path, $file_url = '' ) { if ( ! file_exists( $file_path ) ) { return false; // File doesn't exist } if ( $file_url ) { // Check via WordPress native function (uploads directory) if ( $existing_id = attachment_url_to_postid( $file_url ) ) { return $existing_id; } // Check via plugin meta (for external or imported files) $existing = get_posts( array( 'post_type' => 'attachment', 'post_status' => 'inherit', 'meta_key' => 'aiovg_source_url', 'meta_value' => $file_url, 'fields' => 'ids', 'posts_per_page' => 1 ) ); if ( ! empty( $existing ) ) { return $existing[0]; } } // Include required WordPress files if ( ! function_exists( 'wp_generate_attachment_metadata' ) ) { require_once ABSPATH . 'wp-admin/includes/image.php'; require_once ABSPATH . 'wp-admin/includes/file.php'; require_once ABSPATH . 'wp-admin/includes/media.php'; } // Prepare attachment data $file_name = basename( $file_path ); $file_type = wp_check_filetype( $file_name, null ); // Default to a safe image MIME type if detection fails $mime_type = ! empty( $file_type['type'] ) ? $file_type['type'] : 'image/jpeg'; $attachment = array( 'guid' => $file_url, 'post_mime_type' => $mime_type, 'post_title' => sanitize_file_name( preg_replace( '/\.[^.]+$/', '', $file_name ) ), 'post_content' => '', 'post_status' => 'inherit' ); // Insert attachment into WordPress $attach_id = wp_insert_attachment( $attachment, $file_path, $post_id ); if ( ! $attach_id || is_wp_error( $attach_id ) ) { return false; } // Generate metadata (thumbnails, sizes) $attach_data = wp_generate_attachment_metadata( $attach_id, $file_path ); wp_update_attachment_metadata( $attach_id, $attach_data ); // Save source URL for plugin-specific imports if ( $file_url ) { update_post_meta( $attach_id, 'aiovg_source_url', esc_url_raw( $file_url ) ); } return $attach_id; } /** * Insert multiple post meta at once. * * @since 4.5.2 * @access private * @param int $post_id Post ID. * @param array $data Associative array of meta keys and values. */ private function add_post_meta_bulk( $post_id, $data ) { global $wpdb; $meta_table = _get_meta_table( 'post' ); // Basic sanity check. if ( empty( $post_id ) || empty( $data ) ) { return; } // Bulk insert all new meta entries. $placeholders = array(); $values = array(); foreach ( $data as $key => $value ) { $placeholders[] = "(%d, %s, %s)"; $values[] = $post_id; $values[] = $key; $values[] = maybe_serialize( $value ); } $sql = "INSERT INTO $meta_table (`post_id`, `meta_key`, `meta_value`) VALUES "; $sql .= implode( ',', $placeholders ); $wpdb->query( $wpdb->prepare( $sql, $values ) ); } /** * Update multiple post meta entries at once. * * @since 4.5.2 * @access private * @param int $post_id Post ID. * @param array $data Associative array of meta keys and values. * @return void */ private function update_post_meta_bulk( $post_id, $data ) { global $wpdb; $meta_table = _get_meta_table( 'post' ); // Basic sanity check. if ( empty( $post_id ) || empty( $data ) ) { return; } // Determine if the DB engine supports transactions (InnoDB). $supports_transactions = false; $engine_result = $wpdb->get_row( $wpdb->prepare( "SHOW TABLE STATUS WHERE Name = %s", $meta_table ) ); if ( $engine_result && isset( $engine_result->Engine ) && strtoupper( $engine_result->Engine ) === 'INNODB' ) { $supports_transactions = true; } // Begin transaction if supported. if ( $supports_transactions ) { $wpdb->query( 'START TRANSACTION' ); } try { // Step 1: Delete existing meta for the given keys. $meta_keys = array_keys( $data ); $placeholders = implode( ', ', array_fill( 0, count( $meta_keys ), '%s' ) ); $sql_delete = "DELETE FROM {$meta_table} WHERE post_id = %d AND meta_key IN ($placeholders)"; $wpdb->query( $wpdb->prepare( $sql_delete, array_merge( array( $post_id ), $meta_keys ) ) ); // Step 2: Bulk insert all new meta entries. $placeholders = array(); $values = array(); foreach ( $data as $key => $value ) { $placeholders[] = "(%d, %s, %s)"; $values[] = $post_id; $values[] = $key; $values[] = maybe_serialize( $value ); } $sql_insert = "INSERT INTO {$meta_table} (`post_id`, `meta_key`, `meta_value`) VALUES "; $sql_insert .= implode( ',', $placeholders ); $wpdb->query( $wpdb->prepare( $sql_insert, $values ) ); // Commit if all went well. if ( $supports_transactions ) { $wpdb->query( 'COMMIT' ); } } catch ( Exception $e ) { // Rollback only works if engine supports transactions. if ( $supports_transactions ) { $wpdb->query( 'ROLLBACK' ); } } } /** * Get CSV column mapping fields. * * @since 4.5.2 * @access private * @return array $fields CSV column mapping fields. */ private function get_csv_column_mapping_fields() { $fields = array( 'post_id' => __( 'Video ID (existing video)', 'all-in-one-video-gallery' ), 'post_title' => __( 'Video Title', 'all-in-one-video-gallery' ), 'video' => __( 'Video File', 'all-in-one-video-gallery' ), 'image' => __( 'Poster Image', 'all-in-one-video-gallery' ), 'post_content' => __( 'Video Description', 'all-in-one-video-gallery' ), 'post_excerpt' => __( 'Video Excerpt', 'all-in-one-video-gallery' ), 'aiovg_categories' => __( 'Video Categories', 'all-in-one-video-gallery' ), 'aiovg_tags' => __( 'Video Tags', 'all-in-one-video-gallery' ), 'track' => __( 'Subtitles', 'all-in-one-video-gallery' ), 'chapter' => __( 'Chapters', 'all-in-one-video-gallery' ), 'duration' => __( 'Video Duration', 'all-in-one-video-gallery' ), 'views' => __( 'Views Count', 'all-in-one-video-gallery' ), 'likes' => __( 'Likes Count', 'all-in-one-video-gallery' ), 'dislikes' => __( 'Dislikes Count', 'all-in-one-video-gallery' ), 'download' => __( 'Enable Download', 'all-in-one-video-gallery' ), 'featured' => __( 'Featured', 'all-in-one-video-gallery' ), 'email_capture' => __( 'Email & Lead Capture', 'all-in-one-video-gallery' ), 'access_control' => __( 'Access Control', 'all-in-one-video-gallery' ), 'restricted_roles' => __( 'Allowed User Roles', 'all-in-one-video-gallery' ), 'comment_status' => __( 'Comment Status', 'all-in-one-video-gallery' ), 'post_status' => __( 'Video Status', 'all-in-one-video-gallery' ), 'post_author' => __( 'Video Author', 'all-in-one-video-gallery' ), 'post_date' => __( 'Published Date', 'all-in-one-video-gallery' ) ); return $fields; } /** * Check if a given URL is local to the WordPress site. * * @since 4.5.2 * @access private * @param string $url The URL to check. * @return bool True if local, false if external. */ private function is_file_local( $url ) { // Get the hostname of the WordPress site. $site_host = wp_parse_url( get_home_url(), PHP_URL_HOST ); // Get the hostname from the URL to be checked. $file_host = wp_parse_url( $url, PHP_URL_HOST ); // If there is no host, it's a relative path and thus local. if ( null === $file_host ) { return true; } // Compare the hostnames (case-insensitive). return strcasecmp( $site_host, $file_host ) === 0; } /** * Generate a filename for exported local files. * * @since 4.5.2 * @access private * @param string $file_url The file URL. * @param int $post_id The post ID. * @param bool|null $is_local Optional. Whether the file is local. If null, it will be determined automatically. * @return string|false Generated filename or false if external URL. */ private function get_exported_filename( $file_url, $post_id, $is_local = null ) { if ( $is_local === null ) { $is_local = $this->is_file_local( $file_url ); } if ( $is_local ) { return "$post_id-" . basename( $file_url ); } return false; } /** * Converts an absolute file system path to a site-relative URL. * * @since 4.5.2 * @access private * @param string $absolute_path Absolute file path. * @return string File URL or empty string if path is invalid. */ private function make_absolute_url( $absolute_path ) { $absolute_path = wp_normalize_path( $absolute_path ); $abspath = wp_normalize_path( ABSPATH ); if ( stripos( $absolute_path, $abspath ) === 0 ) { $relative_path = ltrim( substr( $absolute_path, strlen( $abspath ) ), '/' ); return site_url( $relative_path ); } return ''; } /** * Schedule cleanup of the export directory contents. * * @since 4.5.2 * @access private */ private function schedule_export_directory_cleanup() { // Unschedule previous cleanup if exists $timestamp = wp_next_scheduled( 'aiovg_cleanup_export_directory' ); if ( $timestamp ) { wp_unschedule_event( $timestamp, 'aiovg_cleanup_export_directory' ); } // Schedule new single-event cleanup $cleanup_after = apply_filters( 'aiovg_cleanup_export_directory_time', DAY_IN_SECONDS ); wp_schedule_single_event( time() + $cleanup_after, 'aiovg_cleanup_export_directory' ); } }