get_file_params(); $nonce = $request->get_header( 'X-WP-Nonce' ); if ( empty( $nonce ) ) { $nonce = isset( $_POST['_wpnonce'] ) ? sanitize_key( $_POST['_wpnonce'] ) : ''; } if ( ! wp_verify_nonce( $nonce, 'wp_rest' ) ) { return new WP_REST_Response( array( 'success' => false, 'message' => esc_html__( 'Invalid nonce', 'search-replace-wpcode' ), ), 403 ); } if ( empty( $files['file'] ) ) { return new WP_REST_Response( array( 'success' => false, 'message' => esc_html__( 'No file uploaded', 'search-replace-wpcode' ), ), 400 ); } $file = $files['file']; $media_id = isset( $_POST['media_id'] ) ? absint( $_POST['media_id'] ) : 0; // Let's grab the path of the current image using the media_id. $attachment = get_post( $media_id ); $old_file_path = get_attached_file( $media_id, true ); // Validate file type. $original_mime_type = get_post_mime_type( $media_id ); $file_mime_type = $file['type']; // Get allowed mime types. $allowed_mime_types = get_allowed_mime_types(); // Check if the uploaded file type is allowed. $is_allowed = false; foreach ( $allowed_mime_types as $ext => $mime ) { if ( $mime === $file_mime_type ) { $is_allowed = true; break; } } if ( ! $is_allowed ) { return new WP_REST_Response( array( 'success' => false, 'message' => esc_html__( 'File type not allowed. Please upload a file with a supported format.', 'search-replace-wpcode' ), ), 400 ); } // For images, check if we're replacing an image with a non-image. if ( strpos( $original_mime_type, 'image/' ) === 0 && strpos( $file_mime_type, 'image/' ) !== 0 ) { return new WP_REST_Response( array( 'success' => false, 'message' => esc_html__( 'You cannot replace an image with a non-image file. Please upload an image file.', 'search-replace-wpcode' ), ), 200 ); } // Let's first delete all the thumbnails for the old image. $metadata = wp_get_attachment_metadata( $media_id ); $backup_sizes = get_post_meta( $media_id, '_wp_attachment_backup_sizes', true ); wp_delete_attachment_files( $media_id, $metadata, $backup_sizes, $old_file_path ); // Let's upload the new file in the same directory as the old file with the same exact name. if ( ! function_exists( 'wp_handle_upload' ) ) { require_once ABSPATH . 'wp-admin/includes/file.php'; } $this->old_file_path = $old_file_path; // Let's grab the original upload time from the post publish date. $time = strtotime( $attachment->post_date ); // Time should be formatted in yyyy/mm so that we use the same directory. $time = gmdate( 'Y/m', $time ); // Let's move the file to the new location using wp_handle_upload. $move_file = wp_handle_upload( $file, array( 'test_form' => false, 'unique_filename_callback' => array( $this, 'unique_filename_callback' ), ), $time ); if ( ! $move_file || isset( $move_file['error'] ) ) { return new WP_REST_Response( array( 'success' => false, 'message' => $move_file['error'] ?? esc_html__( 'An error occurred while uploading the file', 'search-replace-wpcode' ), ), 200 ); } $new_file_path = $move_file['file']; // Let's make sure the media file is included before calling generate attachment metada. require_once ABSPATH . 'wp-admin/includes/image.php'; add_filter( 'big_image_size_threshold', '__return_false' ); // Let's update the attachment metadata. $attachment_data = wp_generate_attachment_metadata( $media_id, $new_file_path ); wp_update_attachment_metadata( $media_id, $attachment_data ); // Add post meta to mark this has been replaced. update_post_meta( $media_id, '_wsrw_replaced', time() ); $new_media = wp_get_attachment_image_src( $media_id, 'large' ); $message = '

' . esc_html__( 'File uploaded successfully', 'search-replace-wpcode' ) . '

'; $message .= '

' . esc_html__( 'Please note that the source file has been replaced. If you see the old file, please clear your browser cache.', 'search-replace-wpcode' ) . '

'; $response = array( 'success' => true, 'message' => $message, ); if ( wp_attachment_is_image( $media_id ) ) { $response['image_url'] = $new_media[0] ?? wp_get_attachment_url( $media_id ); } return new WP_REST_Response( $response, 200 ); } /** * Register the REST API routes. */ public function register_routes() { register_rest_route( 'wsrw/v1', '/upload-image', array( 'methods' => 'POST', 'callback' => array( $this, 'handle_image_upload' ), 'permission_callback' => function () { return current_user_can( 'upload_files' ); }, ) ); } /** * Get the new filename for the uploaded file. * * @param string $old_file_path The path to the old file. * @param string $new_filename The new filename. * * @return string */ protected function get_new_filename( $old_file_path, $new_filename ) { // By default, use the original filename. return basename( $old_file_path ); } /** * Get the file extension to use. * * @param string $old_file_path The path to the old file. * @param string $new_file_path The path to the new file. * * @return string */ protected function get_file_extension( $old_file_path, $new_file_path ) { // Always use the original extension in base class. return pathinfo( $old_file_path, PATHINFO_EXTENSION ); } /** * Add a button to the edit media modal fields area. * * @param array $form_fields The form fields. * @param WP_Post $post The post object. * * @return array */ public function add_button_to_edit_media_modal_fields_area( $form_fields, $post ) { if ( ! $this->can_user_replace_image( $post ) ) { return $form_fields; } if ( ! wp_attachment_is_image( $post ) ) { return $form_fields; } $form_fields['wsrw-replace-button'] = array( 'label' => '', 'input' => 'html', 'html' => '' . esc_html_x( 'Replace Source File', 'action for a single image', 'search-replace-wpcode' ) . '', 'show_in_modal' => true, 'show_in_edit' => false, 'helps' => esc_html__( 'Directly replace the original file\'s source without creating a duplicate.', 'search-replace-wpcode' ), ); return $form_fields; } /** * Get the URL for the replace page. * * @param WP_Post $post The post object. * * @return string */ public static function get_replace_page_url( $post ) { return wp_nonce_url( admin_url( 'admin.php?page=wsrw-search-replace&view=replace_media&media_id=' . $post->ID ), 'wsrw_replace_media' ); } /** * Add a button to the media row actions. * * @param array $actions The actions array. * @param WP_Post $post The post object. * * @return array */ public function add_button_to_media_row_actions( $actions, $post ) { if ( ! $this->can_user_replace_image( $post ) ) { return $actions; } $actions['wsrw-replace'] = '' . esc_html_x( 'Replace Source File', 'action in the list of attachments', 'search-replace-wpcode' ) . ''; return $actions; } /** * Permissions check for a specific attachment * * @param WP_Post $post The post object to check for. * * @return bool */ public function can_user_replace_image( $post ) { return current_user_can( 'upload_files' ) && current_user_can( 'edit_post', $post->ID ); } /** * Add meta boxes for the replace media page. * * @param WP_Post $post The post object. * * @return void */ public function add_meta_boxes( $post ) { if ( ! $this->can_user_replace_image( $post ) ) { return; } add_meta_box( 'wsrw-replace', __( 'Replace Source File', 'search-replace-wpcode' ), array( $this, 'replace_meta_box', ), 'attachment', 'side', 'low' ); } /** * Output the meta box for the replace media page. * * @param WP_Post $post The post object. * * @return void */ public function replace_meta_box( $post ) { ?>

old_file_path ) ) { $new_filename = $this->get_new_filename( $this->old_file_path, $filename ); $filename_without_ext = pathinfo( $new_filename, PATHINFO_FILENAME ); $new_ext = $this->get_file_extension( $this->old_file_path, $filename . $ext ); return $filename_without_ext . '.' . $new_ext; } else { return $filename; } } }