Create New Item
×
Item Type
File
Folder
Item Name
×
Search file in folder and subfolders...
File Manager
/
wp-content
/
plugins
/
jetpack
/
jetpack_vendor
/
automattic
/
jetpack-import
/
src
/
endpoints
Advanced Search
Upload
New Item
Settings
Back
Back Up
Advanced Editor
Save
<?php /** * Attachments REST route * * @package automattic/jetpack-import */ namespace Automattic\Jetpack\Import\Endpoints; use WP_Error; use WP_REST_Request; use WP_REST_Response; /** * Class Attachment */ class Attachment extends \WP_REST_Attachments_Controller { /** * Base class */ use Import; /** * The Import ID add a new item to the schema. */ use Import_ID; /** * Whether the controller supports batching. Default false. * * @var false */ protected $allow_batch = false; /** * Constructor. */ public function __construct() { parent::__construct( 'attachment' ); // @see add_term_meta $this->import_id_meta_type = 'post'; } /** * Registers the routes for the objects of the controller. * * @see WP_REST_Terms_Controller::register_rest_route() */ public function register_routes() { register_rest_route( self::$rest_namespace, '/' . $this->rest_base, $this->get_route_options() ); register_rest_route( self::$rest_namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array( 'args' => array( 'id' => array( 'description' => __( 'Unique identifier for the attachment.', 'jetpack-import' ), 'type' => 'integer', ), ), array( 'methods' => \WP_REST_Server::EDITABLE, 'callback' => array( $this, 'update_item' ), 'permission_callback' => array( $this, 'update_item_permissions_check' ), 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ), ), 'allow_batch' => array( 'v1' => true ), 'schema' => array( $this, 'get_public_item_schema' ), ) ); register_rest_route( self::$rest_namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)/post-process', array( 'methods' => \WP_REST_Server::CREATABLE, 'callback' => array( $this, 'post_process_item' ), 'permission_callback' => array( $this, 'import_permissions_callback' ), 'args' => array( 'id' => array( 'description' => __( 'Unique identifier for the attachment.', 'jetpack-import' ), 'type' => 'integer', ), 'action' => array( 'type' => 'string', 'enum' => array( 'create-image-subsizes' ), 'required' => true, ), ), ) ); } /** * Create a single attachment. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, WP_Error object on failure. */ public function create_item( $request ) { // Set the WP_IMPORTING constant to prevent sync notifications $this->set_importing(); $file_info = $this->get_file_info( $request ); $attachment = $this->get_attachment_by_file_info( $file_info ); if ( $attachment ) { $response = $this->prepare_attachment_for_response( $attachment, $request ); if ( \is_wp_error( $response ) ) { return $response; } return new WP_Error( 'attachment_exists', __( 'The attachment already exists.', 'jetpack-import' ), array( 'status' => 409, 'attachment' => $response, 'attachment_id' => $attachment->ID, ) ); } $this->set_upload_dir( $request ); // Disable scaled image generation. add_filter( 'big_image_size_threshold', '__return_false' ); return parent::create_item( $request ); } /** * Updates a single attachment. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. */ public function update_item( $request ) { $response = parent::update_item( $request ); return $this->add_import_id_metadata( $request, $response ); } /** * Adds the schema from additional fields to a schema array. * * The type of object is inferred from the passed schema. * * @param array $schema Schema array. * @return array Modified Schema array. */ public function add_additional_fields_schema( $schema ) { // Validate the upload_date, used for placing the uploaded file in the correct upload directory. $schema['properties']['upload_date'] = array( 'description' => __( 'The date for the upload directory of the attachment.', 'jetpack-import' ), 'type' => array( 'string', 'null' ), 'pattern' => '^\d{4}\/\d{2}$', 'required' => false, ); // The unique identifier is only required for PUT requests. return $this->add_unique_identifier_to_schema( $schema, isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] === 'PUT' ); } /** * Performs post-processing on an attachment. * * @param WP_REST_Request $request Full details about the request. * @return WP_REST_Response|WP_Error Response object on success, WP_Error object on failure. */ public function post_process_item( $request ) { require_once ABSPATH . 'wp-admin/includes/image.php'; \wp_update_image_subsizes( $request['id'] ); $request['context'] = 'edit'; return $this->prepare_item_for_response( \get_post( $request['id'] ), $request ); } /** * Add a filter that rewrites the upload path. * * @param WP_REST_Request $request Full details about the request. * * @return void * @throws \Exception If the date is invalid. */ protected function set_upload_dir( $request ) { if ( ! $request->get_param( 'upload_date' ) ) { return; } add_filter( 'upload_dir', static function ( $data ) use ( $request ) { $date = $request->get_param( 'upload_date' ); $fields_to_rewrite = array( 'path', 'url', 'subdir' ); foreach ( $fields_to_rewrite as $field ) { $data[ $field ] = preg_replace( '/\d{4}\/\d{2}$/', $date, $data[ $field ] ); } return $data; } ); } /** * Prepares a single attachment for create or update. This function overrides the parent function * * @param WP_REST_Request $request Request object. * @return \stdClass|WP_Error Post object. */ protected function prepare_item_for_database( $request ) { $prepared_attachment = parent::prepare_item_for_database( $request ); // date_gmt is equal to the date by default, so we need to override it. if ( $request->get_param( 'date_gmt' ) ) { $prepared_attachment->post_date_gmt = $request->get_param( 'date_gmt' ); } return $prepared_attachment; } /** * Retrieve the filename and MIME type from the request headers. * * @param WP_REST_Request $request Full details about the request. * * @return array An associative array containing the filename and MIME type. */ protected function get_file_info( $request ) { // Get the filename from the Content-Disposition header. $filename_header = $request->get_header( 'content_disposition' ); $filename = self::get_filename_from_disposition( (array) $filename_header ); $post_date_gmt = $request->get_param( 'date_gmt' ); // Get the MIME type from the Content-Type header. $mime_type = $request->get_header( 'content_type' ); return array( 'filename' => $filename, 'mime_type' => $mime_type, 'post_date_gmt' => $post_date_gmt, ); } /** * Retrieve attachment metadata by file information. * * This function retrieves attachment metadata for a given file based on its filename, MIME type, and creation date. * * @param array $fileinfo An associative array containing information about the file. The array must contain the following keys: * - 'filename': The name of the file. * - 'mime_type': The MIME type of the file (e.g. 'image/jpeg'). * - 'date': The creation date of the file (e.g. '2022-01-01 12:00:00'). * * @return mixed An associative array containing metadata for the attachment, or false if no attachment was found. */ protected function get_attachment_by_file_info( $fileinfo ) { // Make sure all required variables are set and not empty if ( empty( $fileinfo['filename'] ) || empty( $fileinfo['mime_type'] ) ) { return false; } $original_filename = $fileinfo['filename']; $mime_type = $fileinfo['mime_type']; $post_date_gmt = $fileinfo['post_date_gmt']; // From WordPress 5.3, we introduced the scaled image feature, so we'll also need to check for the scaled filename. // https://make.wordpress.org/core/2019/10/09/introducing-handling-of-big-images-in-wordpress-5-3/ $extension_pos = strrpos( $original_filename, '.' ); $scaled_filename = substr( $original_filename, 0, $extension_pos ) . '-scaled' . substr( $original_filename, $extension_pos ); $filename_check_array = array( $original_filename, $scaled_filename ); $args = array( 'post_type' => 'attachment', 'post_mime_type' => $mime_type, 'date_query' => array( array( 'after' => $post_date_gmt, 'before' => $post_date_gmt, 'inclusive' => true, 'column' => 'post_date_gmt', ), ), 'posts_per_page' => 1, ); $args['meta_query'] = array( 'relation' => 'OR' ); foreach ( $filename_check_array as $filename ) { $args['meta_query'][] = array( 'key' => '_wp_attached_file', 'value' => preg_quote( $filename, '/' ), 'compare' => 'REGEXP', ); } $attachments = \get_posts( $args ); if ( ! empty( $attachments ) ) { // Return the first attachment data found return $attachments[0]; } return false; } /** * Prepares an attachment object for REST API response and returns the resulting data as an array. * * @param object $attachment The attachment object to be prepared for response. * @param object $request The REST API request object. * * @return array|WP_Error The prepared data as an array, or a WP_Error object if there was an error preparing the data. */ private function prepare_attachment_for_response( $attachment, $request ) { // Prepare attachment data for response $response = $this->prepare_item_for_response( $attachment, $request ); // Check if there was an error preparing the data if ( \is_wp_error( $response ) ) { return $response; } return (array) $response->get_data(); } }