PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 All 508 releases
← All changes | jetpack_vendor/automattic/jetpack-import/src/endpoints/class-attachment.php +331 -0 16.2-beta → 16.3 View file →
@@ -1,0 +1,331 @@
1 +<?php
2 +/**
3 + * Attachments REST route
4 + *
5 + * @package automattic/jetpack-import
6 + */
7 +
8 +namespace Automattic\Jetpack\Import\Endpoints;
9 +
10 +use WP_Error;
11 +use WP_REST_Request;
12 +use WP_REST_Response;
13 +
14 +if ( ! defined( 'ABSPATH' ) ) {
15 + exit( 0 );
16 +}
17 +
18 +/**
19 + * Class Attachment
20 + */
21 +class Attachment extends \WP_REST_Attachments_Controller {
22 +
23 + /**
24 + * Base class
25 + */
26 + use Import;
27 +
28 + /**
29 + * The Import ID add a new item to the schema.
30 + */
31 + use Import_ID;
32 +
33 + /**
34 + * Whether the controller supports batching. Default false.
35 + *
36 + * @var false
37 + */
38 + protected $allow_batch = false;
39 +
40 + /**
41 + * Constructor.
42 + */
43 + public function __construct() {
44 + parent::__construct( 'attachment' );
45 +
46 + // @see add_term_meta
47 + $this->import_id_meta_type = 'post';
48 + }
49 +
50 + /**
51 + * Registers the routes for the objects of the controller.
52 + *
53 + * @see WP_REST_Terms_Controller::register_rest_route()
54 + */
55 + public function register_routes() {
56 + register_rest_route(
57 + self::$rest_namespace,
58 + '/' . $this->rest_base,
59 + $this->get_route_options()
60 + );
61 +
62 + register_rest_route(
63 + self::$rest_namespace,
64 + '/' . $this->rest_base . '/(?P<id>[\d]+)',
65 + array(
66 + 'args' => array(
67 + 'id' => array(
68 + 'description' => __( 'Unique identifier for the attachment.', 'jetpack-import' ),
69 + 'type' => 'integer',
70 + ),
71 + ),
72 + array(
73 + 'methods' => \WP_REST_Server::EDITABLE,
74 + 'callback' => array( $this, 'update_item' ),
75 + 'permission_callback' => array( $this, 'update_item_permissions_check' ),
76 + 'args' => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ),
77 + ),
78 + 'allow_batch' => array( 'v1' => true ),
79 + 'schema' => array( $this, 'get_public_item_schema' ),
80 + )
81 + );
82 +
83 + register_rest_route(
84 + self::$rest_namespace,
85 + '/' . $this->rest_base . '/(?P<id>[\d]+)/post-process',
86 + array(
87 + 'methods' => \WP_REST_Server::CREATABLE,
88 + 'callback' => array( $this, 'post_process_item' ),
89 + 'permission_callback' => array( $this, 'import_permissions_callback' ),
90 + 'args' => array(
91 + 'id' => array(
92 + 'description' => __( 'Unique identifier for the attachment.', 'jetpack-import' ),
93 + 'type' => 'integer',
94 + ),
95 + 'action' => array(
96 + 'type' => 'string',
97 + 'enum' => array( 'create-image-subsizes' ),
98 + 'required' => true,
99 + ),
100 + ),
101 + )
102 + );
103 + }
104 +
105 + /**
106 + * Create a single attachment.
107 + *
108 + * @param WP_REST_Request $request Full details about the request.
109 + * @return WP_REST_Response|WP_Error Response object on success, WP_Error object on failure.
110 + */
111 + public function create_item( $request ) {
112 + // Set the WP_IMPORTING constant to prevent sync notifications
113 + $this->set_importing();
114 + $file_info = $this->get_file_info( $request );
115 + $attachment = $this->get_attachment_by_file_info( $file_info );
116 + if ( $attachment ) {
117 + $response = $this->prepare_attachment_for_response( $attachment, $request );
118 +
119 + if ( \is_wp_error( $response ) ) {
120 + return $response;
121 + }
122 +
123 + return new WP_Error(
124 + 'attachment_exists',
125 + __( 'The attachment already exists.', 'jetpack-import' ),
126 + array(
127 + 'status' => 409,
128 + 'attachment' => $response,
129 + 'attachment_id' => $attachment->ID,
130 + )
131 + );
132 + }
133 +
134 + $this->set_upload_dir( $request );
135 + // Disable scaled image generation.
136 + add_filter( 'big_image_size_threshold', '__return_false' );
137 + return parent::create_item( $request );
138 + }
139 +
140 + /**
141 + * Updates a single attachment.
142 + *
143 + * @param WP_REST_Request $request Full details about the request.
144 + * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure.
145 + */
146 + public function update_item( $request ) {
147 + $response = parent::update_item( $request );
148 +
149 + return $this->add_import_id_metadata( $request, $response );
150 + }
151 +
152 + /**
153 + * Adds the schema from additional fields to a schema array.
154 + *
155 + * The type of object is inferred from the passed schema.
156 + *
157 + * @param array $schema Schema array.
158 + * @return array Modified Schema array.
159 + */
160 + public function add_additional_fields_schema( $schema ) {
161 + // Validate the upload_date, used for placing the uploaded file in the correct upload directory.
162 + $schema['properties']['upload_date'] = array(
163 + 'description' => __( 'The date for the upload directory of the attachment.', 'jetpack-import' ),
164 + 'type' => array( 'string', 'null' ),
165 + 'pattern' => '^\d{4}\/\d{2}$',
166 + 'required' => false,
167 + );
168 +
169 + // The unique identifier is only required for PUT requests.
170 + return $this->add_unique_identifier_to_schema( $schema, isset( $_SERVER['REQUEST_METHOD'] ) && $_SERVER['REQUEST_METHOD'] === 'PUT' );
171 + }
172 +
173 + /**
174 + * Performs post-processing on an attachment.
175 + *
176 + * @param WP_REST_Request $request Full details about the request.
177 + * @return WP_REST_Response|WP_Error Response object on success, WP_Error object on failure.
178 + */
179 + public function post_process_item( $request ) {
180 + require_once ABSPATH . 'wp-admin/includes/image.php';
181 +
182 + \wp_update_image_subsizes( $request['id'] );
183 + $request['context'] = 'edit';
184 +
185 + return $this->prepare_item_for_response( \get_post( $request['id'] ), $request );
186 + }
187 +
188 + /**
189 + * Add a filter that rewrites the upload path.
190 + *
191 + * @param WP_REST_Request $request Full details about the request.
192 + *
193 + * @return void
194 + * @throws \Exception If the date is invalid.
195 + */
196 + protected function set_upload_dir( $request ) {
197 +
198 + if ( ! $request->get_param( 'upload_date' ) ) {
199 + return;
200 + }
201 +
202 + add_filter(
203 + 'upload_dir',
204 + static function ( $data ) use ( $request ) {
205 + $date = $request->get_param( 'upload_date' );
206 + $fields_to_rewrite = array( 'path', 'url', 'subdir' );
207 + foreach ( $fields_to_rewrite as $field ) {
208 + $data[ $field ] = preg_replace( '/\d{4}\/\d{2}$/', $date, $data[ $field ] );
209 + }
210 +
211 + return $data;
212 + }
213 + );
214 + }
215 +
216 + /**
217 + * Prepares a single attachment for create or update. This function overrides the parent function
218 + *
219 + * @param WP_REST_Request $request Request object.
220 + * @return \stdClass|WP_Error Post object.
221 + */
222 + protected function prepare_item_for_database( $request ) {
223 + $prepared_attachment = parent::prepare_item_for_database( $request );
224 + // date_gmt is equal to the date by default, so we need to override it.
225 + if ( $request->get_param( 'date_gmt' ) ) {
226 + $prepared_attachment->post_date_gmt = $request->get_param( 'date_gmt' );
227 + }
228 + return $prepared_attachment;
229 + }
230 +
231 + /**
232 + * Retrieve the filename and MIME type from the request headers.
233 + *
234 + * @param WP_REST_Request $request Full details about the request.
235 + *
236 + * @return array An associative array containing the filename and MIME type.
237 + */
238 + protected function get_file_info( $request ) {
239 + // Get the filename from the Content-Disposition header.
240 + $filename_header = $request->get_header( 'content_disposition' );
241 + $filename = self::get_filename_from_disposition( (array) $filename_header );
242 + $post_date_gmt = $request->get_param( 'date_gmt' );
243 +
244 + // Get the MIME type from the Content-Type header.
245 + $mime_type = $request->get_header( 'content_type' );
246 +
247 + return array(
248 + 'filename' => $filename,
249 + 'mime_type' => $mime_type,
250 + 'post_date_gmt' => $post_date_gmt,
251 + );
252 + }
253 +
254 + /**
255 + * Retrieve attachment metadata by file information.
256 + *
257 + * This function retrieves attachment metadata for a given file based on its filename, MIME type, and creation date.
258 + *
259 + * @param array $fileinfo An associative array containing information about the file. The array must contain the following keys:
260 + * - 'filename': The name of the file.
261 + * - 'mime_type': The MIME type of the file (e.g. 'image/jpeg').
262 + * - 'date': The creation date of the file (e.g. '2022-01-01 12:00:00').
263 + *
264 + * @return mixed An associative array containing metadata for the attachment, or false if no attachment was found.
265 + */
266 + protected function get_attachment_by_file_info( $fileinfo ) {
267 + // Make sure all required variables are set and not empty
268 + if ( empty( $fileinfo['filename'] ) || empty( $fileinfo['mime_type'] ) ) {
269 + return false;
270 + }
271 + $original_filename = $fileinfo['filename'];
272 + $mime_type = $fileinfo['mime_type'];
273 + $post_date_gmt = $fileinfo['post_date_gmt'];
274 + // From WordPress 5.3, we introduced the scaled image feature, so we'll also need to check for the scaled filename.
275 + // https://make.wordpress.org/core/2019/10/09/introducing-handling-of-big-images-in-wordpress-5-3/
276 + $extension_pos = strrpos( $original_filename, '.' );
277 + $scaled_filename = substr( $original_filename, 0, $extension_pos ) . '-scaled' . substr( $original_filename, $extension_pos );
278 + $filename_check_array = array( $original_filename, $scaled_filename );
279 +
280 + $args = array(
281 + 'post_type' => 'attachment',
282 + 'post_mime_type' => $mime_type,
283 + 'date_query' => array(
284 + array(
285 + 'after' => $post_date_gmt,
286 + 'before' => $post_date_gmt,
287 + 'inclusive' => true,
288 + 'column' => 'post_date_gmt',
289 + ),
290 + ),
291 + 'posts_per_page' => 1,
292 + );
293 +
294 + $args['meta_query'] = array( 'relation' => 'OR' );
295 + foreach ( $filename_check_array as $filename ) {
296 + $args['meta_query'][] = array(
297 + 'key' => '_wp_attached_file',
298 + 'value' => preg_quote( $filename, '/' ),
299 + 'compare' => 'REGEXP',
300 + );
301 + }
302 +
303 + $attachments = \get_posts( $args );
304 +
305 + if ( ! empty( $attachments ) ) {
306 + // Return the first attachment data found
307 + return $attachments[0];
308 + }
309 + return false;
310 + }
311 +
312 + /**
313 + * Prepares an attachment object for REST API response and returns the resulting data as an array.
314 + *
315 + * @param object $attachment The attachment object to be prepared for response.
316 + * @param object $request The REST API request object.
317 + *
318 + * @return array|WP_Error The prepared data as an array, or a WP_Error object if there was an error preparing the data.
319 + */
320 + private function prepare_attachment_for_response( $attachment, $request ) {
321 + // Prepare attachment data for response
322 + $response = $this->prepare_item_for_response( $attachment, $request );
323 +
324 + // Check if there was an error preparing the data
325 + if ( \is_wp_error( $response ) ) {
326 + return $response;
327 + }
328 +
329 + return (array) $response->get_data();
330 + }
331 +}