PATH:
home
/
thebhoeo
/
.trash
/
coming-soon
/
admin
/
includes
<?php /** * V2 Import/Export Functions * * These are wrapper functions for the existing import/export functionality * to work with the new WordPress-native admin system * * @package SeedProd_Lite * @subpackage SeedProd_Lite/admin/includes */ // Exit if accessed directly if ( ! defined( 'ABSPATH' ) ) { exit; } // Include V2 utility functions require_once __DIR__ . '/import-export-utilities.php'; /* ============================================== HELPER FUNCTIONS FOR IMPORT/EXPORT OPERATIONS ============================================== */ /** * Validate AJAX request with nonce and permissions. * * @param string $capability The capability to check. */ function seedprod_lite_v2_validate_ajax_request( $capability = 'export' ) { if ( ! check_ajax_referer( 'seedprod_v2_nonce', 'nonce', false ) ) { wp_send_json_error( __( 'Invalid security token', 'coming-soon' ) ); } if ( ! current_user_can( apply_filters( 'seedprod_import_export', $capability ) ) ) { wp_send_json_error( __( 'Insufficient permissions', 'coming-soon' ) ); } } /** * Initialize WordPress filesystem. * * @return object WP_Filesystem instance. */ function seedprod_lite_v2_init_filesystem() { if ( ! function_exists( 'WP_Filesystem' ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; } // Force direct filesystem method for AJAX requests add_filter( 'filesystem_method', function () { return 'direct'; } ); global $wp_filesystem; WP_Filesystem(); return $wp_filesystem; } /** * Get upload error message. * * @param int $error_code The PHP upload error code. * @return string The error message. */ function seedprod_lite_v2_get_upload_error_message( $error_code ) { switch ( $error_code ) { case UPLOAD_ERR_INI_SIZE: case UPLOAD_ERR_FORM_SIZE: return __( 'File is too large', 'coming-soon' ); case UPLOAD_ERR_PARTIAL: return __( 'File was only partially uploaded', 'coming-soon' ); case UPLOAD_ERR_NO_FILE: return __( 'No file was uploaded', 'coming-soon' ); default: return __( 'File upload failed', 'coming-soon' ); } } /** * Validate that a URL is safe for theme/template import. * * Enforces HTTPS and rejects path traversal. Does not restrict domains * because admins may import from any URL (Dropbox, Google Drive, etc.). * Private-IP SSRF is handled by wp_safe_remote_get() at the call site. * * @param string $url The URL to validate. * @return boolean True if the URL passes safety checks. */ function seedprod_lite_v2_is_allowed_import_url( $url ) { $parsed = wp_parse_url( $url ); if ( empty( $parsed['host'] ) || empty( $parsed['scheme'] ) ) { return false; } // Enforce HTTPS (allow HTTP only in local dev). if ( 'https' !== strtolower( $parsed['scheme'] ) && ! defined( 'SEEDPROD_LOCAL_JS' ) ) { return false; } // Block path traversal sequences. if ( ! empty( $parsed['path'] ) && false !== strpos( $parsed['path'], '..' ) ) { return false; } return true; } /** * Validate ZIP file upload. * * @param string $file_key The $_FILES array key. * @return array File information or sends JSON error. */ function seedprod_lite_v2_validate_zip_upload( $file_key ) { // Check if file was uploaded. if ( ! isset( $_FILES[ $file_key ] ) ) { wp_send_json_error( __( 'No file uploaded', 'coming-soon' ) ); } // Check for upload errors. if ( UPLOAD_ERR_OK !== $_FILES[ $file_key ]['error'] ) { $error_message = seedprod_lite_v2_get_upload_error_message( $_FILES[ $file_key ]['error'] ); wp_send_json_error( $error_message ); } $filename = wp_unslash( $_FILES[ $file_key ]['name'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized $source = $_FILES[ $file_key ]['tmp_name']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash $type = $_FILES[ $file_key ]['type']; // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.MissingUnslash // Validate ZIP file type. $accepted_types = array( 'application/zip', 'application/x-zip-compressed', 'multipart/x-zip', 'application/x-compressed', ); $is_valid_type = in_array( $type, $accepted_types, true ); $has_zip_extension = preg_match( '/\.zip$/i', $filename ); if ( ! $is_valid_type && ! $has_zip_extension ) { wp_send_json_error( __( 'The file you are trying to upload is not a .zip file. Please try again.', 'coming-soon' ) ); } return array( 'filename' => $filename, 'source' => $source, 'type' => $type, ); } /** * Setup import/export directories. * * @param string $folder_name The folder name to create. * @return array Directory paths. */ function seedprod_lite_v2_setup_directories( $folder_name ) { $upload_dir = wp_upload_dir(); $path = trailingslashit( $upload_dir['basedir'] ); $url = trailingslashit( $upload_dir['baseurl'] ); $target_dir = $path . $folder_name; $target_url = $url . $folder_name; // Clean up existing directory. if ( is_dir( $target_dir ) ) { // Directory cleanup handled by V2 function. seedprod_lite_v2_recursive_rmdir( $target_dir ); } // Create fresh directory. wp_mkdir_p( $target_dir ); return array( 'path' => $path, 'url' => $url, 'target_dir' => $target_dir, 'target_url' => $target_url, ); } /** * Extract ZIP file with validation. * * @param string $zip_path Path to ZIP file. * @param string $extract_to Directory to extract to. * @param bool $is_theme Whether this is a theme import. * @return bool|WP_Error True on success, WP_Error on failure. */ function seedprod_lite_v2_extract_zip( $zip_path, $extract_to, $is_theme = true ) { // Validate ZIP contents. $validation = seedprod_lite_v2_validate_import_zip( $zip_path, $is_theme ); if ( is_wp_error( $validation ) ) { return $validation; } if ( ! class_exists( 'ZipArchive' ) ) { return new WP_Error( 'zip_not_available', __( 'ZipArchive class not available. Please contact your host to enable it.', 'coming-soon' ) ); } $zip = new ZipArchive(); if ( true === $zip->open( $zip_path ) ) { $zip->extractTo( $extract_to ); $zip->close(); return true; } return new WP_Error( 'extract_failed', __( 'Failed to extract ZIP file.', 'coming-soon' ) ); } /** * Read JSON file with HTTP/filesystem fallback. * * @param string $file_path Local file path. * @param string $url_path Optional URL for HTTP attempt. * @return object|WP_Error Decoded JSON data or WP_Error. */ function seedprod_lite_v2_read_json_file( $file_path, $url_path = null ) { global $wp_filesystem; $json_data = null; // Try HTTP first if URL provided. if ( $url_path ) { $response = wp_remote_get( $url_path, array( 'sslverify' => false ) ); if ( ! is_wp_error( $response ) && 200 === wp_remote_retrieve_response_code( $response ) ) { $json_data = wp_remote_retrieve_body( $response ); } } // Fallback to direct filesystem reading. if ( ! $json_data && $wp_filesystem && $wp_filesystem->exists( $file_path ) ) { $json_data = $wp_filesystem->get_contents( $file_path ); } if ( ! $json_data ) { return new WP_Error( 'read_failed', __( 'Unable to read data file.', 'coming-soon' ) ); } // Decode and validate JSON. $data = json_decode( $json_data ); if ( null === $data && JSON_ERROR_NONE !== json_last_error() ) { return new WP_Error( 'invalid_json', __( 'Invalid data format.', 'coming-soon' ) ); } return $data; } /** * Process and prepare export data for themes/pages. * * @param array $results Database results. * @param string $type Export type ('theme' or 'page'). * @param mixed $ptype Page type for landing pages. * @return array Processed export data. */ function seedprod_lite_v2_process_export_data( $results, $type = 'theme', $ptype = null ) { $processed_data = array(); $export = array(); $shortcode_exports = array(); // Set export type $export['type'] = ( 'theme' === $type ) ? 'theme-builder' : 'landing-page'; $export['current_home_url'] = home_url(); $export['theme'] = array(); $export['mapped'] = array(); // Check if this is a template (contains placehold.co) $is_template = false; if ( 'theme' === $type ) { foreach ( $results as $v ) { if ( strpos( $v->post_content_filtered, 'placehold.co' ) !== false ) { $is_template = true; break; } } } // Process each result foreach ( $results as $k => $v ) { // Skip if essential data is missing if ( empty( $v->ID ) ) { continue; } $meta = ( 'theme' === $type ) ? get_post_meta( $v->ID ) : wp_json_encode( get_post_meta( $v->ID ) ); if ( 'theme' === $type && $is_template ) { unset( $meta['_seedprod_css'] ); unset( $meta['_seedprod_html'] ); } if ( 'theme' === $type ) { $meta = json_encode( $meta ); // phpcs:ignore } // Handle null/empty content safely $content = ! empty( $v->post_content ) ? $v->post_content : ''; $content_filtered = ! empty( $v->post_content_filtered ) ? $v->post_content_filtered : ''; // Process image filenames if ( 'theme' === $type && $is_template ) { $processed_data[ $k ] = array( 'data' => $content_filtered, 'html' => $content, 'images' => array(), ); } else { $processed_result = seedprod_lite_v2_process_image_filenames( $content_filtered, $content ); // Ensure the result has the expected structure $processed_data[ $k ] = array( 'data' => isset( $processed_result['data'] ) ? $processed_result['data'] : $content_filtered, 'html' => isset( $processed_result['html'] ) ? $processed_result['html'] : $content, 'images' => isset( $processed_result['images'] ) && is_array( $processed_result['images'] ) ? $processed_result['images'] : array(), ); } // Safely get processed data with fallbacks $processed_html = isset( $processed_data[ $k ]['html'] ) ? $processed_data[ $k ]['html'] : ''; $processed_data_content = isset( $processed_data[ $k ]['data'] ) ? $processed_data[ $k ]['data'] : ''; // Build export entry $entry = array( 'order' => $v->menu_order, 'post_content' => base64_encode( $processed_html ), // phpcs:ignore 'post_content_filtered' => base64_encode( $processed_data_content ), // phpcs:ignore 'post_title' => base64_encode( $v->post_title ), // phpcs:ignore 'meta' => base64_encode( $meta ), // phpcs:ignore ); // Add page-specific fields if ( 'page' === $type ) { $entry['post_type'] = base64_encode( $v->post_type ); // phpcs:ignore $entry['post_status'] = base64_encode( $v->post_status ); // phpcs:ignore // Determine page type: check both meta field AND WordPress options // Special pages are identified by options, not meta $actual_ptype = get_post_meta( $v->ID, '_seedprod_page_type', true ); // If no meta, check if this is a special page by checking WordPress options if ( empty( $actual_ptype ) ) { $coming_soon_id = get_option( 'seedprod_coming_soon_page_id' ); $maintenance_id = get_option( 'seedprod_maintenance_mode_page_id' ); $login_id = get_option( 'seedprod_login_page_id' ); $fourohfour_id = get_option( 'seedprod_404_page_id' ); if ( $v->ID == $coming_soon_id ) { $actual_ptype = 'cs'; } elseif ( $v->ID == $maintenance_id ) { $actual_ptype = 'mm'; } elseif ( $v->ID == $login_id ) { $actual_ptype = 'loginp'; } elseif ( $v->ID == $fourohfour_id ) { $actual_ptype = 'p404'; } } $entry['ptype'] = base64_encode( $actual_ptype ?? '' ); // phpcs:ignore } $export['theme'][] = $entry; // Find shortcodes in content (for both themes and landing pages) if ( ! empty( $processed_html ) ) { $post_content_shortcode = base64_decode( base64_encode( $processed_html ) ); // phpcs:ignore $re = '/((\[)(sp_template_part id="){1}[0-9]*["](\]))/m'; preg_match_all( $re, $post_content_shortcode, $matches, PREG_SET_ORDER, 0 ); if ( $matches ) { foreach ( $matches as $t => $val ) { $shortcode_content = $val[0]; $shortcode_page_sc = str_replace( '[sp_template_part id="', '', $shortcode_content ); $shortcode_page_sc = str_replace( '"]', '', $shortcode_page_sc ); $shortcode_exports[ $shortcode_page_sc ] = array( 'id' => $shortcode_page_sc, 'shortcode' => $shortcode_content, ); } } } } return array( 'export' => $export, 'processed_data' => $processed_data, 'shortcode_exports' => $shortcode_exports, ); } /* ============================================== MAIN EXPORT/IMPORT FUNCTIONS ============================================== */ /** * Export theme files (V2 implementation) * Exports all theme templates as a downloadable ZIP file */ function seedprod_lite_v2_export_theme_files() { // Validate request seedprod_lite_v2_validate_ajax_request( 'export' ); // Initialize filesystem $wp_filesystem = seedprod_lite_v2_init_filesystem(); global $wpdb; $tablename = $wpdb->prefix . 'posts'; $meta_tablename = $wpdb->prefix . 'postmeta'; // Get list of theme templates AND pages edited with SeedProd. // phpcs:disable Generic.Formatting.MultipleStatementAlignment.NotSameWarning $sql = 'SELECT DISTINCT p.* FROM ' . $tablename . ' p '; $sql .= "LEFT JOIN $meta_tablename pm ON (pm.post_id = p.ID AND pm.meta_key = '_seedprod_is_theme_template') "; $sql .= "LEFT JOIN $meta_tablename pm2 ON (pm2.post_id = p.ID AND pm2.meta_key = '_seedprod_edited_with_seedprod') "; $sql .= "WHERE p.post_status = 'publish' "; $sql .= 'AND ( '; $sql .= " (p.post_type = 'seedprod' AND pm.meta_value IS NOT NULL) "; // Theme templates. $sql .= " OR (p.post_type = 'page' AND pm2.meta_value = '1') "; // Pages edited with SeedProd. $sql .= ') '; // phpcs:enable Generic.Formatting.MultipleStatementAlignment.NotSameWarning $results = $wpdb->get_results( $sql ); // phpcs:ignore $processed_data = array(); $export = array(); $export['type'] = 'theme-builder'; $export['current_home_url'] = home_url(); $export['theme'] = array(); $export['mapped'] = array(); $shortcode_exports = array(); // Check if this is a template (contains placehold.co) $is_template = false; foreach ( $results as $k => $v ) { if ( strpos( $v->post_content_filtered, 'placehold.co' ) !== false ) { $is_template = true; break; } } // Process each template foreach ( $results as $k => $v ) { // Get post meta $meta = get_post_meta( $v->ID ); if ( $is_template === true ) { unset( $meta['_seedprod_css'] ); unset( $meta['_seedprod_html'] ); } $meta = json_encode( $meta ); // phpcs:ignore $content = $v->post_content; $content_filtered = $v->post_content_filtered; // Process image filenames if not a template if ( $is_template === false ) { // Include helper functions if needed // Image processing handled by V2 function $processed_data[ $k ] = seedprod_lite_v2_process_image_filenames( $content_filtered, $content ); } else { $processed_data[ $k ] = array( 'data' => $content_filtered, 'html' => $content, 'images' => array(), ); } // Determine page category - theme template vs edited page. $page_category = 'theme_template'; $export_post_type = 'seedprod'; $export_post_status = 'publish'; if ( 'page' === $v->post_type ) { $page_category = 'edited_page'; $export_post_type = 'page'; $export_post_status = $v->post_status; } $export['theme'][] = array( 'order' => $v->menu_order, 'post_content' => base64_encode( $processed_data[ $k ]['html'] ), // phpcs:ignore 'post_content_filtered' => base64_encode( $processed_data[ $k ]['data'] ), // phpcs:ignore 'post_title' => base64_encode( $v->post_title ), // phpcs:ignore 'meta' => base64_encode( $meta ), // phpcs:ignore 'page_category' => base64_encode( $page_category ), // phpcs:ignore 'post_type' => base64_encode( $export_post_type ), // phpcs:ignore 'post_status' => base64_encode( $export_post_status ), // phpcs:ignore ); // Find shortcodes in content $post_content_shortcode = base64_decode( base64_encode( $processed_data[ $k ]['html'] ) ); // phpcs:ignore $re = '/((\[)(sp_template_part id="){1}[0-9]*["](\]))/m'; preg_match_all( $re, $post_content_shortcode, $matches, PREG_SET_ORDER, 0 ); if ( $matches ) { foreach ( $matches as $t => $val ) { $shortcode_content = $val[0]; $shortcode_page_sc = str_replace( '[sp_template_part id="', '', $shortcode_content ); $shortcode_page_sc = str_replace( '"]', '', $shortcode_page_sc ); $shortcode_exports[ $shortcode_page_sc ] = array( 'id' => $shortcode_page_sc, 'shortcode' => $shortcode_content, ); } } } // Map shortcodes foreach ( $shortcode_exports as $n => $t ) { $page_id = $t['id']; $sql = "SELECT post_title FROM $tablename WHERE ID = %d"; $fetch_page_title = $wpdb->get_var( $wpdb->prepare( $sql, absint( $page_id ) ) ); // phpcs:ignore $export['mapped'][] = array( 'shortcode' => base64_encode( $t['shortcode'] ), // phpcs:ignore 'page_title' => $fetch_page_title, ); } // Process images for export $export_data_images = array(); foreach ( $processed_data as $p => $v ) { foreach ( $v['images'] as $d => $x ) { $export_data_images[] = $x; } } // Create export directory $upload_dir = wp_upload_dir(); $path = trailingslashit( $upload_dir['basedir'] ); $webpath = trailingslashit( $upload_dir['baseurl'] ); $export_folder = 'seedprod-themes-exports'; $export_path = $path . $export_folder; // Create export folder if it doesn't exist (don't delete - prevents race condition with concurrent exports). if ( ! is_dir( $export_path ) ) { wp_mkdir_p( $export_path ); } // Clean up old files (older than 1 hour) to prevent disk bloat. // This preserves recent exports from other users while still managing disk space. $old_files = glob( $export_path . '/*' ); $one_hour_ago = time() - HOUR_IN_SECONDS; if ( $old_files ) { foreach ( $old_files as $old_file ) { if ( filemtime( $old_file ) < $one_hour_ago ) { wp_delete_file( $old_file ); } } } // Save JSON export file $json_name = 'export_theme.json'; $json_file = $export_path . '/' . $json_name; $export_json = json_encode( $export ); // phpcs:ignore $wp_filesystem->put_contents( $json_file, $export_json, FS_CHMOD_FILE ); // Copy images to export folder // Save images locally and track failed ones $all_failed_images = array(); if ( count( $export_data_images ) > 0 ) { $failed_images = seedprod_lite_v2_save_images_locally( $export_data_images ); $all_failed_images = array_merge( $all_failed_images, $failed_images ); } // Create ZIP file if ( ! class_exists( 'ZipArchive' ) ) { wp_send_json_error( __( 'ZipArchive class not available. Please contact your host to enable it.', 'coming-soon' ) ); } $zip = new ZipArchive(); $zip_name = 'seedprod-theme-export-' . gmdate( 'Y-m-d-H-i-s' ) . '.zip'; $zip_path = $export_path . '/' . $zip_name; if ( $zip->open( $zip_path, ZipArchive::CREATE ) === true ) { // Add all files from export folder to zip seedprod_lite_v2_zipdir( $export_path, $zip, strlen( "$export_path/" ) ); $zip->close(); // Clean up temp files (JSON, images) but keep ZIP for download. $json_file_path = $export_path . '/' . $json_name; if ( file_exists( $json_file_path ) ) { wp_delete_file( $json_file_path ); } // Delete any image files that were copied for export. if ( defined( 'GLOB_BRACE' ) ) { $image_files = glob( $export_path . '/*.{jpg,jpeg,png,gif,webp,svg}', GLOB_BRACE ); if ( $image_files ) { foreach ( $image_files as $image_file ) { wp_delete_file( $image_file ); } } } else { // Fallback for environments without GLOB_BRACE (e.g. WordPress Playground). foreach ( $export_data_images as $image_data ) { if ( ! empty( $image_data['filename'] ) ) { $image_file_path = $export_path . '/' . $image_data['filename']; if ( file_exists( $image_file_path ) ) { wp_delete_file( $image_file_path ); } } } } // Read ZIP and base64-encode for inline delivery. // This ensures downloads work in all environments including WordPress Playground // where the Service Worker intercepts static file requests. $zip_contents = file_get_contents( $zip_path ); // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents if ( false === $zip_contents ) { wp_send_json_error( __( 'Failed to read export file.', 'coming-soon' ) ); } $zip_base64 = base64_encode( $zip_contents ); // phpcs:ignore WordPress.PHP.DiscouragedPHPFunctions.obfuscation_base64_encode unset( $zip_contents ); // Clean up ZIP file since it is delivered inline. wp_delete_file( $zip_path ); // Prepare response data. $response_data = array( 'success' => true, 'message' => __( 'Export completed successfully', 'coming-soon' ), 'filedata' => $zip_base64, 'filename' => $zip_name, ); // Add warning if some images failed to download. if ( ! empty( $all_failed_images ) ) { $response_data['warning'] = sprintf( /* translators: %d: number of failed images */ __( '%d images could not be downloaded and were excluded from the export.', 'coming-soon' ), count( $all_failed_images ) ); $response_data['failed_images'] = count( $all_failed_images ); } wp_send_json_success( $response_data ); } else { wp_send_json_error( __( 'Failed to create ZIP file.', 'coming-soon' ) ); } } /** * Import theme files (V2 implementation) * Imports theme templates from an uploaded ZIP file */ function seedprod_lite_v2_import_theme_files() { // Validate request and permissions seedprod_lite_v2_validate_ajax_request( 'install_themes' ); // Check if user wants to delete existing theme templates $delete_existing = isset( $_POST['delete_existing'] ) && '1' === $_POST['delete_existing']; // Delete existing theme templates if requested if ( $delete_existing ) { seedprod_lite_v2_delete_theme_templates(); } // Validate and get uploaded file $zip_validation = seedprod_lite_v2_validate_zip_upload( 'seedprod_theme_files' ); if ( is_wp_error( $zip_validation ) ) { wp_send_json_error( $zip_validation->get_error_message() ); } // Set script timeout longer set_time_limit( 60 ); // Initialize filesystem seedprod_lite_v2_init_filesystem(); if ( isset( $_FILES['seedprod_theme_files']['name'] ) ) { $filename = wp_unslash( $_FILES['seedprod_theme_files']['name'] ); // phpcs:ignore $source = $_FILES['seedprod_theme_files']['tmp_name']; // phpcs:ignore // Setup directories $filename_import = 'seedprod-themes-imports'; $upload_dir = wp_upload_dir(); $path = trailingslashit( $upload_dir['basedir'] ); $webpath = trailingslashit( $upload_dir['baseurl'] ); $filenoext = basename( $filename_import, '.zip' ); $filenoext = basename( $filenoext, '.ZIP' ); $targetdir = $path . $filenoext; $targetzip = $path . $filename; $webtargetdir = $webpath . $filenoext; // Remove existing import directory if exists if ( is_dir( $targetdir ) ) { // Directory cleanup handled by V2 function seedprod_lite_v2_recursive_rmdir( $targetdir ); } // Create import directory wp_mkdir_p( $targetdir ); // phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- move_uploaded_file is required for handling HTTP file uploads securely. if ( move_uploaded_file( $source, $targetzip ) ) { // Extract ZIP file $extract_result = seedprod_lite_v2_extract_zip( $targetzip, $targetdir, true ); if ( is_wp_error( $extract_result ) ) { seedprod_lite_v2_recursive_rmdir( $targetdir ); wp_send_json_error( $extract_result->get_error_message() ); } // Delete the zip file after extraction wp_delete_file( $targetzip ); $theme_json_data = $targetdir . '/export_theme.json'; $web_theme_json_data = $webtargetdir . '/export_theme.json'; // Read and validate theme.json $data = seedprod_lite_v2_read_json_file( $theme_json_data, $web_theme_json_data ); if ( is_wp_error( $data ) ) { seedprod_lite_v2_recursive_rmdir( $targetdir ); wp_send_json_error( $data->get_error_message() ); } // Validate theme type if ( ! empty( $data->type ) && 'theme-builder' !== $data->type ) { seedprod_lite_v2_recursive_rmdir( $targetdir ); wp_send_json_error( __( 'This does not appear to be a SeedProd theme.', 'coming-soon' ) ); } // Process the import // Process the theme import $warnings = seedprod_lite_v2_theme_import_json( $data ); // Remove the json file for security wp_delete_file( $theme_json_data ); // Clean up import directory seedprod_lite_v2_recursive_rmdir( $targetdir ); wp_send_json_success( array( 'warnings' => is_array( $warnings ) ? $warnings : array() ) ); } else { $message = __( 'There was a problem with the upload. Please try again.', 'coming-soon' ); wp_send_json_error( $message ); } } else { $message = __( 'There was a problem with the upload. Please try again.', 'coming-soon' ); wp_send_json_error( $message ); } } /** * Import theme by URL (V2 implementation) * Downloads and imports a theme from a remote URL */ function seedprod_lite_v2_import_theme_by_url( $theme_url = null ) { // Track if this is a direct AJAX call or internal call. AJAX hook callbacks // receive an empty string (not null) for unset args, so check with empty(). $is_direct_ajax = empty( $theme_url ); // Helper function to handle errors for both AJAX and internal calls $handle_error = function ( $message, $code = 'error' ) use ( $is_direct_ajax ) { if ( $is_direct_ajax ) { wp_send_json_error( $message ); } else { return new WP_Error( $code, $message ); } }; // Check if this is an AJAX request or direct call if ( $is_direct_ajax ) { // Validate request and permissions for AJAX seedprod_lite_v2_validate_ajax_request( 'install_themes' ); } else { // Check permissions for direct call if ( ! current_user_can( apply_filters( 'seedprod_import_export', 'install_themes' ) ) ) { return $handle_error( __( 'Insufficient permissions', 'coming-soon' ), 'insufficient_permissions' ); } } // Check if user wants to delete existing theme templates $delete_existing = isset( $_REQUEST['delete_existing'] ) && '1' === $_REQUEST['delete_existing']; // Delete existing theme templates if requested if ( $delete_existing ) { seedprod_lite_v2_delete_theme_templates(); } // Initialize filesystem seedprod_lite_v2_init_filesystem(); // esc_url_raw() preserves query-string ampersands for signed CDN URLs. $source = isset( $_REQUEST['seedprod_theme_url'] ) ? esc_url_raw( wp_unslash( $_REQUEST['seedprod_theme_url'] ) ) : ''; if ( ! empty( $theme_url ) ) { $source = $theme_url; } if ( empty( $source ) ) { return $handle_error( __( 'No URL provided', 'coming-soon' ), 'no_url' ); } // Enforce HTTPS and reject path traversal to reduce SSRF surface. if ( ! seedprod_lite_v2_is_allowed_import_url( $source ) ) { return $handle_error( __( 'Invalid import URL. Please enter a valid HTTPS URL.', 'coming-soon' ), 'invalid_url' ); } // Download the file from URL. $file_import_url_json = wp_safe_remote_get( $source, array( 'sslverify' => false, 'timeout' => 60, ) ); if ( is_wp_error( $file_import_url_json ) ) { return $handle_error( $file_import_url_json->get_error_message(), $file_import_url_json->get_error_code() ); } // ZipArchive::open() in seedprod_lite_v2_validate_import_zip() below is the // real ZIP check; Content-Type isn't reliable across CDNs. $response_code = (int) wp_remote_retrieve_response_code( $file_import_url_json ); if ( 200 !== $response_code ) { return $handle_error( sprintf( /* translators: %d: HTTP status code returned by the import URL */ __( 'The import URL returned HTTP %d.', 'coming-soon' ), $response_code ), 'http_error' ); } $body = wp_remote_retrieve_body( $file_import_url_json ); if ( empty( $body ) ) { return $handle_error( __( 'Failed to download file content.', 'coming-soon' ), 'download_failed' ); } // Process the filename $url_data = pathinfo( $source ); $filename = $url_data['basename']; // Truncate filename at .zip to remove query parameters (matching old logic) if ( strpos( $filename, '.zip' ) !== false ) { $filename = substr( $filename, 0, strpos( $filename, '.zip' ) + 4 ); } // Ensure it's a ZIP file if ( ! preg_match( '/\.zip$/i', $filename ) ) { return $handle_error( __( 'The file you are trying to upload is not a .zip file. Please try again.', 'coming-soon' ), 'not_zip' ); } // Setup directories $filename_import = 'seedprod-themes-imports'; $upload_dir = wp_upload_dir(); $path = trailingslashit( $upload_dir['basedir'] ); $webpath = trailingslashit( $upload_dir['baseurl'] ); $filenoext = basename( $filename_import, '.zip' ); $filenoext = basename( $filenoext, '.ZIP' ); $targetdir = $path . $filenoext; $targetzip = $path . $filename; $webtargetdir = $webpath . $filenoext; // Remove existing import directory if exists if ( is_dir( $targetdir ) ) { // Clean up existing directory seedprod_lite_v2_recursive_rmdir( $targetdir ); } // Create import directory wp_mkdir_p( $targetdir ); // Save the ZIP file if ( ! file_put_contents( $targetzip, $body ) ) { // phpcs:ignore seedprod_lite_v2_recursive_rmdir( $targetdir ); return $handle_error( __( 'There was a problem saving the file. Please try again.', 'coming-soon' ), 'save_failed' ); } // Validate zip contents $validation_result = seedprod_lite_v2_validate_import_zip( $targetzip, true ); if ( is_wp_error( $validation_result ) ) { wp_delete_file( $targetzip ); seedprod_lite_v2_recursive_rmdir( $targetdir ); return $handle_error( $validation_result->get_error_message(), $validation_result->get_error_code() ); } // Extract ZIP file $extract_result = seedprod_lite_v2_extract_zip( $targetzip, $targetdir, true ); if ( is_wp_error( $extract_result ) ) { wp_delete_file( $targetzip ); seedprod_lite_v2_recursive_rmdir( $targetdir ); return $handle_error( $extract_result->get_error_message(), $extract_result->get_error_code() ); } // Delete the zip file after extraction wp_delete_file( $targetzip ); // Read and validate theme.json $theme_json_path = $targetdir . '/export_theme.json'; $url_path = $webtargetdir . '/export_theme.json'; $data = seedprod_lite_v2_read_json_file( $theme_json_path, $url_path ); if ( is_wp_error( $data ) ) { seedprod_lite_v2_recursive_rmdir( $targetdir ); return $handle_error( $data->get_error_message(), $data->get_error_code() ); } // Validate theme type if ( ! empty( $data->type ) && 'theme-builder' !== $data->type ) { seedprod_lite_v2_recursive_rmdir( $targetdir ); return $handle_error( __( 'This does not appear to be a SeedProd theme.', 'coming-soon' ), 'invalid_theme' ); } // Process the import // Process the theme import $warnings = seedprod_lite_v2_theme_import_json( $data ); $warnings = is_array( $warnings ) ? $warnings : array(); // Remove the json file for security wp_delete_file( $theme_json_path ); // Clean up import directory seedprod_lite_v2_recursive_rmdir( $targetdir ); // Only send JSON if this is a direct AJAX call, not an internal call if ( $is_direct_ajax ) { wp_send_json_success( array( 'warnings' => $warnings ) ); } else { // Return success for internal calls return array( 'success' => true, 'warnings' => $warnings, ); } } /** * Export landing pages (V2 implementation) * Exports all landing pages as a downloadable ZIP file */ function seedprod_lite_v2_export_landing_pages() { // Validate request and permissions try { seedprod_lite_v2_validate_ajax_request( 'export' ); } catch ( Exception $e ) { wp_send_json_error( 'Validation failed: ' . $e->getMessage() ); } // Initialize filesystem try { seedprod_lite_v2_init_filesystem(); } catch ( Exception $e ) { wp_send_json_error( 'Filesystem initialization failed: ' . $e->getMessage() ); } $page_id = isset( $_POST['page_id'] ) ? absint( sanitize_text_field( wp_unslash( $_POST['page_id'] ) ) ) : 0; $ptype = isset( $_POST['ptype'] ) ? sanitize_text_field( wp_unslash( $_POST['ptype'] ) ) : null; global $wpdb; $tablename = $wpdb->prefix . 'posts'; $meta_tablename = $wpdb->prefix . 'postmeta'; // Get list of landing pages - EXACT same logic as dropdown query // Get special page IDs (Coming Soon, Maintenance Mode, Login, 404) $coming_soon_id = get_option( 'seedprod_coming_soon_page_id' ); $maintenance_id = get_option( 'seedprod_maintenance_mode_page_id' ); $login_id = get_option( 'seedprod_login_page_id' ); $fourohfour_id = get_option( 'seedprod_404_page_id' ); $special_page_ids = array(); if ( ! empty( $coming_soon_id ) ) { $special_page_ids[] = $coming_soon_id; } if ( ! empty( $maintenance_id ) ) { $special_page_ids[] = $maintenance_id; } if ( ! empty( $login_id ) ) { $special_page_ids[] = $login_id; } if ( ! empty( $fourohfour_id ) ) { $special_page_ids[] = $fourohfour_id; } $special_pages_condition = ''; if ( ! empty( $special_page_ids ) ) { $special_ids_string = implode( ',', array_map( 'intval', $special_page_ids ) ); $special_pages_condition = " OR p.ID IN ($special_ids_string)"; } $sql = "SELECT p.*, pm.meta_key, pm.meta_value FROM $tablename p "; $sql .= "LEFT JOIN $meta_tablename pm ON (pm.post_id = p.ID AND pm.meta_key = '_seedprod_page_uuid') "; $sql .= "LEFT JOIN $meta_tablename pm4 ON (pm4.post_id = p.ID AND pm4.meta_key = '_seedprod_page_template_type') "; $sql .= "LEFT JOIN $meta_tablename pm5 ON (pm5.post_id = p.ID AND pm5.meta_key = '_seedprod_page') "; $sql .= "WHERE p.post_status != 'trash' "; $sql .= "AND p.post_type IN ('page', 'seedprod') "; $sql .= "AND ((p.post_type = 'page' AND pm4.meta_value = 'lp') OR (p.post_type = 'page' AND pm5.meta_value IS NOT NULL) $special_pages_condition) "; $sql .= "AND (pm.meta_value IS NOT NULL OR pm5.meta_value IS NOT NULL $special_pages_condition) "; if ( 0 !== $page_id ) { $sql .= "AND p.ID = $page_id "; } $sql .= 'GROUP BY p.ID '; $sql .= 'ORDER BY p.post_date DESC'; $results = $wpdb->get_results( $sql ); // phpcs:ignore if ( $wpdb->last_error ) { wp_send_json_error( 'Database error: ' . $wpdb->last_error ); } // Check if we have any pages to export if ( empty( $results ) ) { wp_send_json_error( __( 'No landing pages found to export.', 'coming-soon' ) ); } // Wrap in try-catch to catch any errors try { // Process export data using helper function $export_result = seedprod_lite_v2_process_export_data( $results, 'page', $ptype ); if ( is_wp_error( $export_result ) ) { wp_send_json_error( $export_result->get_error_message() ); } // Validate export result structure if ( ! isset( $export_result['export'] ) || ! isset( $export_result['processed_data'] ) || ! isset( $export_result['shortcode_exports'] ) ) { wp_send_json_error( __( 'Export data structure is invalid.', 'coming-soon' ) ); } $export = $export_result['export']; $processed_data = $export_result['processed_data']; $shortcode_exports = $export_result['shortcode_exports']; } catch ( Exception $e ) { /* translators: %s: Error message from the exception */ wp_send_json_error( sprintf( __( 'Export failed during processing: %s', 'coming-soon' ), $e->getMessage() ) ); } // Process shortcode mapping for landing pages (restored from old logic) foreach ( $shortcode_exports as $k => $sc_val ) { $shortcode_page_id = $sc_val['id']; // Use different variable name to avoid overwriting original $page_id $page_shortcode = $sc_val['shortcode']; $sql = "SELECT p.post_title FROM $tablename p LEFT JOIN $meta_tablename pm ON (pm.post_id = p.ID)"; $sql .= " WHERE p.ID = %d and post_status != 'trash' AND post_type IN ('page','seedprod') AND meta_key = '_seedprod_page_uuid' "; $safe_sql = $wpdb->prepare( $sql, absint( $shortcode_page_id ) ); // phpcs:ignore $page = $wpdb->get_row( $safe_sql ); // phpcs:ignore if ( ! empty( $page ) ) { $export['mapped'][] = array( 'id' => $shortcode_page_id, 'shortcode' => base64_encode( $page_shortcode ),// phpcs:ignore 'page_title' => $page->post_title, ); } } $export_json = wp_json_encode( $export ); // Check if JSON encoding failed if ( false === $export_json ) { $json_error = json_last_error_msg(); wp_send_json_error( __( 'Failed to encode export data. The page data may be too large or contain invalid characters.', 'coming-soon' ) . ' Error: ' . $json_error ); } $files_to_download = array(); global $wp_filesystem; $upload_dir = wp_upload_dir(); $path = trailingslashit( $upload_dir['basedir'] ) . 'seedprod-themes-exports/'; $targetdir = $path; // Create directory if it doesn't exist (don't delete - prevents race condition with concurrent exports). if ( ! is_dir( $targetdir ) ) { if ( ! wp_mkdir_p( $targetdir ) ) { wp_send_json_error( __( 'Failed to create export directory.', 'coming-soon' ) ); } } // Clean up old files (older than 1 hour) to prevent disk bloat. // This preserves recent exports from other users while still managing disk space. $old_files = glob( $targetdir . '*' ); $one_hour_ago = time() - HOUR_IN_SECONDS; if ( $old_files ) { foreach ( $old_files as $old_file ) { if ( filemtime( $old_file ) < $one_hour_ago ) { wp_delete_file( $old_file ); } } } // Save images locally $all_failed_images = array(); // Process images try { foreach ( $processed_data as $k1 => $v1 ) { // Check if images key exists and has items if ( isset( $processed_data[ $k1 ]['images'] ) && is_array( $processed_data[ $k1 ]['images'] ) && count( $processed_data[ $k1 ]['images'] ) > 0 ) { $failed_images = seedprod_lite_v2_save_images_locally( $processed_data[ $k1 ]['images'] ); if ( is_array( $failed_images ) ) { $all_failed_images = array_merge( $all_failed_images, $failed_images ); } } // Only add successfully downloaded images to the zip if ( isset( $processed_data[ $k1 ]['images'] ) && is_array( $processed_data[ $k1 ]['images'] ) ) { foreach ( $processed_data[ $k1 ]['images'] as $image ) { if ( ! isset( $image['filename'] ) ) { continue; } $image_path = trailingslashit( $upload_dir['basedir'] ) . 'seedprod-themes-exports/' . $image['filename']; if ( file_exists( $image_path ) ) { $files_to_download[] = $image['filename']; } } } } } catch ( Exception $e ) { /* translators: %s: Error message from the exception */ wp_send_json_error( sprintf( __( 'Export failed during image processing: %s', 'coming-soon' ), $e->getMessage() ) ); } // Create zip and return download URL try { $zip_result = seedprod_lite_v2_prepare_zip( $files_to_download, $export_json, 'page' ); if ( $zip_result && isset( $zip_result['filedata'] ) ) { wp_send_json_success( $zip_result ); } else { wp_send_json_error( __( 'Export completed but file data not generated.', 'coming-soon' ) ); } } catch ( Exception $e ) { /* translators: %s: Error message from the exception */ wp_send_json_error( sprintf( __( 'Export failed during ZIP creation: %s', 'coming-soon' ), $e->getMessage() ) ); } exit; } /** * Import landing pages (V2 implementation) * Imports landing pages from an uploaded ZIP file */ function seedprod_lite_v2_import_landing_pages() { // Validate request and permissions seedprod_lite_v2_validate_ajax_request( 'install_themes' ); // Validate and get uploaded file $zip_validation = seedprod_lite_v2_validate_zip_upload( 'seedprod_landing_files' ); if ( is_wp_error( $zip_validation ) ) { wp_send_json_error( $zip_validation->get_error_message() ); } // Initialize filesystem // Note: V2 uses direct filesystem access because this is an AJAX handler // and request_filesystem_credentials() doesn't work properly in AJAX context seedprod_lite_v2_init_filesystem(); if ( isset( $_FILES['seedprod_landing_files']['name'] ) ) { $filename = wp_unslash( $_FILES['seedprod_landing_files']['name'] ); // phpcs:ignore $source = $_FILES['seedprod_landing_files']['tmp_name']; // phpcs:ignore // Setup directories $filename_import = 'seedprod-themes-imports'; $upload_dir = wp_upload_dir(); $path = trailingslashit( $upload_dir['basedir'] ); $path_baseurl = trailingslashit( $upload_dir['baseurl'] ); $filenoext = basename( $filename_import, '.zip' ); $filenoext = basename( $filenoext, '.ZIP' ); $targetdir = $path . $filenoext; $targetzip = $path . $filename; $target_url = $path_baseurl . $filenoext; // Remove existing import directory if exists if ( is_dir( $targetdir ) ) { // Directory cleanup handled by V2 function seedprod_lite_v2_recursive_rmdir( $targetdir ); } // Create import directory wp_mkdir_p( $targetdir ); // phpcs:ignore Generic.PHP.ForbiddenFunctions.Found -- move_uploaded_file is required for handling HTTP file uploads securely. if ( move_uploaded_file( $source, $targetzip ) ) { // Validate zip contents $validation_result = seedprod_lite_v2_validate_import_zip( $targetzip, false ); if ( is_wp_error( $validation_result ) ) { wp_delete_file( $targetzip ); seedprod_lite_v2_recursive_rmdir( $targetdir ); wp_send_json_error( $validation_result->get_error_message() ); } // Extract ZIP file $extract_result = seedprod_lite_v2_extract_zip( $targetzip, $targetdir, false ); if ( is_wp_error( $extract_result ) ) { wp_delete_file( $targetzip ); seedprod_lite_v2_recursive_rmdir( $targetdir ); wp_send_json_error( $extract_result->get_error_message() ); } // Delete the zip file after extraction wp_delete_file( $targetzip ); // Read and validate landing page data $theme_json_path = $targetdir . '/export_page.json'; $url_path = $target_url . '/export_page.json'; $data = seedprod_lite_v2_read_json_file( $theme_json_path, $url_path ); if ( is_wp_error( $data ) ) { seedprod_lite_v2_recursive_rmdir( $targetdir ); wp_send_json_error( $data->get_error_message() ); } // Validate landing page type if ( ! empty( $data->type ) && 'landing-page' !== $data->type ) { seedprod_lite_v2_recursive_rmdir( $targetdir ); wp_send_json_error( __( 'This does not appear to be a SeedProd landing page.', 'coming-soon' ) ); } // Process the import // Process the landing page import $import_result = seedprod_lite_v2_landing_import_json( $data ); $warnings = isset( $import_result['warnings'] ) && is_array( $import_result['warnings'] ) ? $import_result['warnings'] : array(); // Remove the json file for security wp_delete_file( $theme_json_path ); // Clean up import directory seedprod_lite_v2_recursive_rmdir( $targetdir ); wp_send_json_success( array( 'warnings' => $warnings ) ); } else { $message = __( 'There was a problem with the upload. Please try again.', 'coming-soon' ); wp_send_json_error( $message ); } } else { wp_send_json_error( __( 'Invalid file upload.', 'coming-soon' ) ); } } /** * Check if theme templates exist (AJAX handler) */ function seedprod_lite_v2_check_existing_theme() { // Validate request seedprod_lite_v2_validate_ajax_request( 'install_themes' ); $has_templates = seedprod_lite_v2_has_theme_templates(); wp_send_json_success( array( 'has_templates' => $has_templates ) ); }
[-] license-functions.php
[edit]
[-] skin.php
[edit]
[-] system-info-functions.php
[edit]
[-] template-functions.php
[edit]
[-] utm-functions.php
[edit]
[-] theme-functions.php
[edit]
[-] setup-wizard-functions.php
[edit]
[-] skin53.php
[edit]
[-] settings-functions.php
[edit]
[-] import-export-functions.php
[edit]
[+]
..
[-] import-export-utilities.php
[edit]
[-] subscriber-functions.php
[edit]
[-] class-seedprod-theme-templates-table.php
[edit]
[-] class-seedprod-landing-pages-table.php
[edit]
[-] review-functions.php
[edit]
[-] debug-functions.php
[edit]
[-] plugin-functions.php
[edit]
[-] edit-with-seedprod-functions.php
[edit]
[-] utility-functions.php
[edit]
[-] product-education-functions.php
[edit]