PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
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 13.7.2 13.8.3 All 506 releases
jetpack / jetpack_vendor / automattic / jetpack-import / src / endpoints / class-attachment.php

class-attachment.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.7, at jetpack_vendor/automattic/jetpack-import/src/endpoints/class-attachment.php

332 lines 9.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 }
332