PluginProbe
Datafeedr API / 1.0.125
Datafeedr API v1.0.125
1.4.3 1.4.2 1.0.125 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.2 1.0.20 1.0.21 1.0.22 1.0.23 1.0.24 1.0.25 1.0.26 1.0.27 1.0.28 1.0.29 1.0.3 1.0.30 1.0.31 1.0.32 All 182 releases
datafeedr-api / classes / class-datafeedr-image-importer.php

class-datafeedr-image-importer.php in Datafeedr API 1.0.125, at classes/class-datafeedr-image-importer.php

751 lines 17.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 /**
4 * Class Datafeedr_Image_Importer.
5 *
6 * Handles importing images from URLs and assigning image data.
7 *
8 * Usage:
9 *
10 * $url = 'http://via.placeholder.com/350x150';
11 *
12 * $args = array(
13 * 'title' => 'My Image Name',
14 * 'file_name' => 'my-image-name',
15 * 'post_id' => 123,
16 * 'description' => 'Description of this image.',
17 * 'caption' => 'Caption for this image.',
18 * 'alt_text' => 'Alt text for this image.',
19 * 'user_id' => 1,
20 * 'is_post_thumbnail' => true,
21 * 'user_agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0',
22 * 'timeout' => 30,
23 * );
24 *
25 * $image = new Datafeedr_Image_Importer( $url, $args );
26 *
27 * $attachment_id = $image->import();
28 *
29 * if ( is_wp_error( $attachment_id ) ) {
30 * // Handle error.
31 * } else {
32 * // Success! Do something with $attachment_id.
33 * }
34 *
35 * @since 1.0.71
36 */
37 class Datafeedr_Image_Importer {
38
39 use Datafeedr_Timer;
40
41 /**
42 * Image URL.
43 *
44 * @since 1.0.71
45 * @access protected
46 * @var string $url
47 */
48 protected $url;
49
50 /**
51 * An array of arguments for storing image in database.
52 *
53 * @see $this->default_args()
54 * @since 1.0.71
55 * @access protected
56 * @var array $args
57 */
58 protected $args;
59
60 /**
61 * The response of wp_remote_get( $this->url ) or WP_Error on failure.
62 *
63 * @since 1.0.71
64 * @access protected
65 * @var WP_Error|array $response
66 */
67 protected $response;
68
69 /**
70 * The ID created for the new attachment.
71 *
72 * @since 1.0.71
73 * @access protected
74 * @var integer $attachment_id
75 */
76 protected $attachment_id;
77
78 /**
79 * A copy of the WP_Error encountered during import.
80 *
81 * @since 1.0.74
82 * @access protected
83 * @var WP_Error $wp_error
84 */
85 protected $wp_error = null;
86
87 /**
88 * Datafeedr_Image_Importer constructor.
89 *
90 * Sets the $this->url and $this->args properties.
91 *
92 * @param string $url Image URL.
93 * @param array $args Optional. See $this->default_args().
94 *
95 * @since 1.0.71
96 */
97 public function __construct( $url, $args = array() ) {
98 $this->set_url( $url );
99 $this->set_args( $args );
100 }
101
102 /**
103 * Sets the $this->url property.
104 *
105 * @param string $url Image URL.
106 *
107 * @since 1.0.71
108 */
109 protected function set_url( $url ) {
110 $url = ( "//" === substr( $url, 0, 2 ) ) ? 'http:' . $url : $url;
111 $this->url = apply_filters( 'datafeedr_image_importer/url', $url, $this );
112 }
113
114 /**
115 * Sets the $this->args property.
116 *
117 * This merges the $defaults with the values passed into the constructor.
118 *
119 * @param array $args
120 *
121 * @since 1.0.71
122 */
123 protected function set_args( $args ) {
124 $args = wp_parse_args( $args, $this->default_args() );
125 $this->args = apply_filters( 'datafeedr_image_importer/args', $args, $this );
126 }
127
128 /**
129 * Default args to use for importing image.
130 *
131 * @return array
132 * @since 1.0.71
133 */
134 protected function default_args() {
135
136 $args = array(
137
138 /**
139 * This is the ID of the post we want to attach this image to. If we do not
140 * want this image to be attached to a post, leave this set to 0.
141 */
142 'post_id' => 0,
143
144 /**
145 * This is name of the file name the image will have once it is stored on
146 * on the server in the WordPress uploads directory.
147 */
148 'file_name' => preg_replace( '/\.[^.]+$/', '', basename( $this->url ) ),
149
150 /**
151 * This is the ID of the User this image will be associated with.
152 */
153 'user_id' => $this->get_admin_user_id(),
154
155 /**
156 * This is the title of the image (which is different than the file name).
157 */
158 'title' => preg_replace( '/\.[^.]+$/', '', basename( $this->url ) ),
159
160 /**
161 * The description of the image.
162 */
163 'description' => '',
164
165 /**
166 * The caption for the image.
167 */
168 'caption' => '',
169
170 /**
171 * The alt text. Text to display if image cannot be loaded.
172 */
173 'alt_text' => '',
174
175 /**
176 * Whether this image should be set as the post's thumbail. If the post_id
177 * is 0, this setting will be ignored.
178 */
179 'is_post_thumbnail' => false,
180
181 /**
182 * The user-agent to use when making requests to the external URL.
183 */
184 'user_agent' => 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:57.0) Gecko/20100101 Firefox/57.0',
185
186 /**
187 * The number of seconds to spend attempting to download the image.
188 */
189 'timeout' => 10,
190 );
191
192 return $args;
193 }
194
195 /**
196 * Returns the image URL.
197 *
198 * @return string
199 * @since 1.0.71
200 */
201 public function url() {
202 return $this->url;
203 }
204
205 /**
206 * Returns the image's attachment ID.
207 *
208 * @return integer
209 * @since 1.0.71
210 */
211 public function attachment_id() {
212 return absint( $this->attachment_id );
213 }
214
215 /**
216 * Returns the response of wp_remote_get($this->url) or WP_Error on failure.
217 *
218 * @return array|WP_Error
219 * @since 1.0.71
220 */
221 public function response() {
222 return $this->response;
223 }
224
225 /**
226 * Returns a WP_Error encountered during the import() process.
227 *
228 * @return WP_Error|null
229 * @since 1.0.74
230 */
231 public function wp_error() {
232 return $this->wp_error;
233 }
234
235 /**
236 * Returns all args or a value of a specific arg.
237 *
238 * @param bool|string $key
239 * @param string $cast Possible values: boolean, integer, float, string, array, object or null. See settype().
240 *
241 * @return array|mixed Return all $args if $key doesn't exists or a single arg if $key isset.
242 * @since 1.0.74
243 */
244 public function args( $key = false, $cast = 'string' ) {
245
246 $args = $this->args;
247
248 if ( $key && isset( $args[ $key ] ) ) {
249 $value = $args[ $key ];
250 settype( $value, $cast );
251
252 return $value;
253 }
254
255 return $args;
256 }
257
258 /**
259 * Returns true if errors were encountered. Otherwise returns false.
260 *
261 * @return bool
262 * @since 1.0.74
263 */
264 public function has_error() {
265 return ( is_wp_error( $this->wp_error() ) ) ? true : false;
266 }
267
268 /**
269 * This handles the importing of an image into WordPress.
270 *
271 * This performs the following actions:
272 * - Requires necessary WordPress media.php, $file.php and image.php files
273 * - Performs a wp_remote_get() request to the image URL.
274 * - Extracts the image extension (.jpg, .png, etc...) for the image.
275 * - Downloads the image file and stores a temporary copy.
276 * - Adds the image to the WordPress uploads directory and saves a record to the posts table.
277 * - Sets the $this->attachment_id property
278 * - Maybe sets the image as the post's featured thumbnail.
279 * - Maybe sets the image's alt text.
280 *
281 * If the import encounters an error, a WP_Error will be added to the $this->errors array.
282 *
283 * @return Datafeedr_Image_Importer
284 * @since 1.0.71
285 */
286 public function import() {
287
288 $this->start_timer();
289
290 $this->require_files();
291
292 $tmp_name = $this->tmp_name();
293
294 if ( is_wp_error( $tmp_name ) ) {
295
296 $this->wp_error = $tmp_name;
297
298 /**
299 * Do something when image import fails.
300 *
301 * @param WP_Error $tmp_name
302 * @param Datafeedr_Image_Importer $this
303 *
304 * @since 1.0.72
305 *
306 */
307 do_action( 'datafeedr_image_importer/import/fail', $tmp_name, $this );
308
309 $this->stop_timer();
310
311 return $this;
312 }
313
314 $content_type = $this->response_header( $this->response(), 'content-type' );
315
316 $extension = $this->content_type_to_extension( $content_type );
317
318 $file_array = array();
319 $file_array['name'] = $this->args( 'file_name' ) . '.' . $extension;
320 $file_array['tmp_name'] = $tmp_name;
321
322 if ( is_wp_error( $file_array['tmp_name'] ) ) {
323
324 $this->wp_error = $file_array['tmp_name'];
325
326 /**
327 * Do something when image import fails.
328 *
329 * @param WP_Error $file_array ['tmp_name']
330 * @param Datafeedr_Image_Importer $this
331 *
332 * @since 1.0.72
333 *
334 */
335 do_action( 'datafeedr_image_importer/import/fail', $file_array['tmp_name'], $this );
336
337 $this->stop_timer();
338
339 return $this;
340 }
341
342 $result = $this->media_sideload_image(
343 $file_array,
344 $this->args( 'post_id', 'integer' ),
345 $this->args( 'title' ),
346 $this->post_data()
347 );
348
349 if ( is_wp_error( $result ) ) {
350
351 $this->wp_error = $result;
352
353 $this->unlink_tmp_file( $file_array['tmp_name'] );
354
355 /**
356 * Do something when image import fails.
357 *
358 * @param WP_Error $result
359 * @param Datafeedr_Image_Importer $this
360 *
361 * @since 1.0.72
362 *
363 */
364 do_action( 'datafeedr_image_importer/import/fail', $result, $this );
365
366 $this->stop_timer();
367
368 return $this;
369 }
370
371 $this->set_attachment_id( $result );
372 $this->set_post_thumbnail();
373 $this->set_alt_text();
374 $this->set_owner_source();
375
376 /**
377 * Do something when image import succeeds.
378 *
379 * @param int $result Attachment ID.
380 * @param Datafeedr_Image_Importer $this
381 *
382 * @since 1.0.72
383 */
384 do_action( 'datafeedr_image_importer/import/success', $result, $this );
385
386 $this->stop_timer();
387
388 return $this;
389 }
390
391 /**
392 * Returns an array of Post data to use in the call to media_sideload_image().
393 *
394 * This sets the image description, caption and user_id.
395 *
396 * @return array Post data.
397 * @since 1.0.71
398 */
399 protected function post_data() {
400
401 $post_data = array(
402 'post_content' => $this->args( 'description' ),
403 'post_excerpt' => $this->args( 'caption' ),
404 'post_author' => $this->args( 'user_id', 'integer' ),
405 );
406
407 /**
408 * Allow user to override post data.
409 *
410 * @param array $post_data
411 * @param Datafeedr_Image_Importer $this
412 *
413 * @since 1.0.72
414 *
415 */
416 return apply_filters( 'datafeedr_image_importer/post_data', $post_data, $this );
417 }
418
419 /**
420 * Sets the image's alt text if alt text was provided.
421 *
422 * @since 1.0.71
423 */
424 protected function set_alt_text() {
425 $text = $this->args( 'alt_text' );
426 if ( ! empty( $text ) ) {
427 update_post_meta( $this->attachment_id(), '_wp_attachment_image_alt', $text );
428 }
429 }
430
431 /**
432 * Sets the image's owner and source. _owner_datafeedr : dfrps
433 *
434 * @since 1.0.101
435 */
436 protected function set_owner_source() {
437 $source = isset( $this->args['_source_plugin'] ) ? trim( $this->args['_source_plugin'] ) : '';
438 update_post_meta( $this->attachment_id(), '_owner_datafeedr', $source );
439 }
440
441 /**
442 * Sets the image as the post thumbnail if the is_post_thumbnail argument is set to
443 * true and the post_id is greater than 0.
444 *
445 * @since 1.0.71
446 */
447 protected function set_post_thumbnail() {
448
449 if ( ! $this->args( 'is_post_thumbnail', 'boolean' ) ) {
450 return;
451 }
452
453 if ( $this->args( 'post_id', 'integer' ) < 1 ) {
454 return;
455 }
456
457 set_post_thumbnail( $this->args( 'post_id', 'integer' ), $this->attachment_id() );
458 }
459
460 /**
461 * Requires the necessary files for importing an image into WordPress.
462 *
463 * @since 1.0.71
464 */
465 protected function require_files() {
466 require_once( ABSPATH . 'wp-admin/includes/media.php' );
467 require_once( ABSPATH . 'wp-admin/includes/file.php' );
468 require_once( ABSPATH . 'wp-admin/includes/image.php' );
469 }
470
471 /**
472 * A wrapper for the media_handle_sideload() function.
473 *
474 * @param array $file_array
475 * @param int $post_id
476 * @param string $title
477 * @param array $post_data
478 *
479 * @return int|WP_Error The ID of the attachment or a WP_Error on failure.
480 * @see media_handle_sideload()
481 *
482 * @since 1.0.71
483 */
484 protected function media_sideload_image( $file_array, $post_id = 0, $title = '', $post_data = array() ) {
485
486 $attachment_id = media_handle_sideload( $file_array, $post_id, $title, $post_data );
487
488 // If there was an error sideloading the image, return the returned WP_Error.
489 if ( is_wp_error( $attachment_id ) ) {
490 return $attachment_id;
491 }
492
493 // If the image does not have a width or height, return WP_Error.
494 $meta = wp_get_attachment_metadata( $attachment_id );
495 if ( ! isset( $meta['width'], $meta['height'] ) ) {
496 return new WP_Error( 'http_no_image_width_or_height', __( 'Image data does not exist.', 'datafeedr' ) );
497 }
498
499 // If we made it this far, the image is valid so return the image's attachment ID.
500 return $attachment_id;
501 }
502
503 /**
504 * This is a modified version of download_url() from /wp-admin/includes/file.php
505 *
506 * We had to modify so we could change the user-agent.
507 *
508 * Downloads a URL to a local temporary file using the WordPress HTTP Class.
509 * Please note, That the calling function must unlink() the file.
510 *
511 * This also sets the $this->response property.
512 *
513 * @return WP_Error|string WP_Error on failure, string temporary filename on success.
514 * @since 1.0.72
515 *
516 * @see /wp-admin/includes/file.php:965 function download_url( $url, $timeout = 300 )
517 */
518 protected function tmp_name() {
519
520 $url = $this->url();
521
522 if ( ! $url ) {
523 return new WP_Error( 'http_no_url', __( 'Invalid URL Provided.' ) );
524 }
525
526 $url_filename = basename( parse_url( $url, PHP_URL_PATH ) );
527
528 $url_filename = substr( $url_filename, 0, 100 );
529
530 $tmp_name = wp_tempnam( $url_filename );
531
532 if ( ! $tmp_name ) {
533 return new WP_Error( 'http_no_file', __( 'Could not create Temporary file.' ) );
534 }
535
536 $this->set_response( $tmp_name );
537
538 if ( is_wp_error( $this->response() ) ) {
539 $this->unlink_tmp_file( $tmp_name );
540
541 return $this->response();
542 }
543
544 $response_code = $this->response_code( $this->response() );
545
546 if ( 200 != $response_code ) {
547 $this->unlink_tmp_file( $tmp_name );
548
549 return new WP_Error(
550 'http_' . $response_code,
551 __( 'Response code was something other than 200.', 'datafeedr' )
552 );
553 }
554
555 $content_md5 = $this->response_header( $this->response(), 'content-md5' );
556
557 if ( $content_md5 ) {
558 $md5_check = verify_file_md5( $tmp_name, $content_md5 );
559 if ( is_wp_error( $md5_check ) ) {
560 $this->unlink_tmp_file( $tmp_name );
561
562 return $md5_check;
563 }
564 }
565
566 return $tmp_name;
567 }
568
569 /**
570 * Sets the $this->attachment_id property.
571 *
572 * @param integer $id The ID to set the attachment_id to.
573 *
574 * @since 1.0.71
575 */
576 protected function set_attachment_id( $id ) {
577 $this->attachment_id = absint( $id );
578 }
579
580 /**
581 * Safely unlink a temporary file.
582 *
583 * @param string $file_name Name of temporary file to unlink.
584 *
585 * @since 1.0.72
586 */
587 protected function unlink_tmp_file( $file_name ) {
588 @unlink( $file_name );
589 }
590
591 /**
592 * Sets the $this->response property.
593 *
594 * Makes a call to wp_remote_get() and sets the $this->response property to the
595 * returned value.
596 *
597 * @param string $tmp_name Temporary file name generated by wp_tempnam().
598 *
599 * @since 1.0.71
600 */
601 protected function set_response( $tmp_name ) {
602
603 $args = array(
604 'user-agent' => $this->args( 'user_agent' ),
605 'timeout' => $this->args( 'timeout', 'integer' ),
606 'filename' => $tmp_name,
607 'stream' => true,
608 );
609
610 /**
611 * Allow user to override $args.
612 *
613 * @param array $args
614 * @param Datafeedr_Image_Importer $this
615 *
616 * @since 1.0.72
617 *
618 */
619 $args = apply_filters( 'datafeedr_image_importer/set_response/args', $args, $this );
620
621 $this->response = wp_safe_remote_get( $this->url, $args );
622 }
623
624 /**
625 * Retrieve a single header by name from the raw response.
626 *
627 * @param $response WP_Error|array The response or WP_Error on failure.
628 * @param string $header Header name to retrieve value from.
629 *
630 * @return string The header value. Empty string on if incorrect parameter given, or if the header doesn't exist.
631 * @since 1.0.72
632 */
633 protected function response_header( $response, $header ) {
634 return wp_remote_retrieve_header( $response, $header );
635 }
636
637 /**
638 * Retrieve only the response message from the raw response.
639 *
640 * Will return an empty array if incorrect parameter value is given.
641 *
642 * @param $response WP_Error|array The response or WP_Error on failure.
643 *
644 * @return string The response message. Empty string on incorrect parameter given.
645 * @since 1.0.72
646 */
647 protected function response_message( $response ) {
648 return wp_remote_retrieve_response_message( $response );
649 }
650
651 /**
652 * Retrieve only the response code from the raw response.
653 *
654 * Will return an empty array if incorrect parameter value is given.
655 *
656 * @param $response WP_Error|array The response or WP_Error on failure.
657 *
658 * @return int|string The response code as an integer. Empty string on incorrect parameter given.
659 * @since 1.0.72
660 */
661 protected function response_code( $response ) {
662 return wp_remote_retrieve_response_code( $response );
663 }
664
665 /**
666 * This returns the proper image extension given the $content_type requested.
667 *
668 * Handle content types of values like these:
669 * "image/png"
670 * "image/jpeg;charset=UTF-8"
671 * "JPG"
672 *
673 * @param string $content_type Content type received from HTTP request.
674 *
675 * @return string Extension (ex. jpg, png, gif, etc...).
676 * @since 1.0.71
677 */
678 protected function content_type_to_extension( $content_type ) {
679
680 $type = explode( ";", $content_type );
681 $type = trim( strtolower( $type[0] ) );
682
683 $extensions = $this->extensions();
684
685 foreach ( $extensions as $mime => $ext ) {
686 if ( $mime == $type ) {
687 return $ext;
688 }
689 }
690
691 return 'jpg';
692 }
693
694 /**
695 * Returns an associative array of valid content types (or mime types) and
696 * their respective extensions.
697 *
698 * @return array
699 * @since 1.0.71
700 */
701 protected function extensions() {
702
703 $extensions = array(
704 'image/jpeg' => 'jpg',
705 'image/jpg' => 'jpg',
706 'image/jpe' => 'jpg',
707 'image/png' => 'png',
708 'image/gif' => 'gif',
709 'image/bmp' => 'bmp',
710 'image/tiff' => 'tif',
711 'image/webp' => 'webp',
712 'jpg' => 'jpg',
713 'png' => 'png',
714 'gif' => 'gif',
715 );
716
717 /**
718 * Allow user to override $extensions.
719 *
720 * @param array $extensions
721 * @param Datafeedr_Image_Importer $this
722 *
723 * @since 1.0.72
724 *
725 */
726 return apply_filters( 'datafeedr_image_importer/extensions', $extensions, $this );
727 }
728
729 /**
730 * Returns the first found ID of an admin user.
731 *
732 * @return int
733 * @since 1.0.71
734 */
735 protected function get_admin_user_id() {
736
737 $args = array(
738 'blog_id' => $GLOBALS['blog_id'],
739 'role__in' => array( 'administrator' ),
740 'orderby' => 'ID',
741 'order' => 'ASC',
742 'number' => 1,
743 );
744
745 $users = get_users( $args );
746 $user = $users[0];
747
748 return absint( $user->ID );
749 }
750 }
751