Posts_Import
Class Posts_Import
Source
File: includes/libs/posts-import.class.php
016 | class Posts_Import{ |
017 | /** |
018 | * @var Post_Type |
019 | */ |
020 | protected $post_type ; |
021 |
022 | /** |
023 | * Posts_Import constructor. |
024 | * |
025 | * @param Post_Type $post_type |
026 | */ |
027 | public function __construct( Post_Type $post_type ) { |
028 | $this ->post_type = $post_type ; |
029 | } |
030 |
031 | /** |
032 | * Imports videos from a given feed source. |
033 | * Used by automatic updates. |
034 | * |
035 | * @param $raw_feed |
036 | * @param Feed|array $import_options |
037 | * |
038 | * @return array|void |
039 | */ |
040 | public function run_import( $raw_feed , $import_options ){ |
041 | /** |
042 | * @var array $native_tax |
043 | * @var array $native_tag |
044 | * @var bool $import_description |
045 | * @var string $import_status |
046 | * @var bool $import_title |
047 | * @var bool $import_date |
048 | * @var int $import_user |
049 | */ |
050 | extract( $this ->get_import_options( $import_options ), EXTR_SKIP ); |
051 |
052 | // get import options |
053 | $options = Plugin::instance()->get_options(); |
054 |
055 | // overwrite plugin import settings with import settings |
056 | $options [ 'import_description' ] = $import_description ; |
057 | $options [ 'import_status' ] = $import_status ; |
058 | $options [ 'import_title' ] = $import_title ; |
059 |
060 | // store results |
061 | $result = [ |
062 | 'private' => 0, |
063 | 'imported' => 0, |
064 | 'skipped' => 0, |
065 | 'total' => count ( $raw_feed ), |
066 | 'ids' => [], |
067 | 'error' => [] |
068 | ]; |
069 |
070 | $duplicates = $this ->get_duplicate_posts( $raw_feed , $this ->post_type->get_post_type() ); |
071 |
072 | // parse feed |
073 | foreach ( $raw_feed as $video ){ |
074 |
075 | // video already exists, don't do anything |
076 | if ( array_key_exists ( $video [ 'video_id' ], $duplicates ) ){ |
077 |
078 | /** |
079 | * Generate an error and pass it for debugging |
080 | * @var WP_Error |
081 | */ |
082 | $error = new WP_Error( |
083 | 'cvm_import_skip_existing_video' , |
084 | sprintf( |
085 | '%s %s' , |
086 | sprintf( |
087 | __( 'Skipped video having ID %s because it already exists.' , 'codeflavors-vimeo-video-post-lite' ), |
088 | $video [ 'video_id' ] |
089 | ), |
090 | sprintf( |
091 | __( 'Existing post has ID %s.' , 'codeflavors-vimeo-video-post-lite' ), |
092 | $duplicates [ $video [ 'video_id' ] ][0] |
093 | ) |
094 | ), |
095 | [ |
096 | 'video_data' => $video , |
097 | 'existing_posts' => $duplicates [ $video [ 'video_id' ] ] |
098 | ] |
099 | ); |
100 | $result [ 'error' ][] = $error ; |
101 |
102 | /** |
103 | * Pass error to debug function |
104 | */ |
105 | Helper::debug_message( |
106 | 'Import error: ' . $error ->get_error_message(), |
107 | "\n" , |
108 | $error |
109 | ); |
110 |
111 | foreach ( $duplicates [ $video [ 'video_id' ] ] as $_post_id ){ |
112 | // retrieve the post object for backwards compatibility |
113 | $post = get_post( $_post_id ); |
114 |
115 | /** |
116 | * Action triggered when duplicate posts were detected. |
117 | * Can be used to set extra taxonomies for already existing posts. |
118 | * |
119 | * @param \WP_Post $post The WordPress post object that was found as duplicate. |
120 | * @param string $taxonomy The taxonomy that must be imported for the post. |
121 | * @param string $taxonomy_value The plugin taxonomy that must be set up. |
122 | * @param string $tag_taxonomy The tag taxonomy that must be set up. |
123 | * @param string $tag_taxonomy_value The tag taxonomy value that must be set for the post. |
124 | */ |
125 | do_action( 'vimeotheque\import_duplicate_taxonomies' , |
126 | $post , |
127 | $this ->post_type->get_post_tax(), |
128 | $native_tax , |
129 | $this ->post_type->get_tag_tax(), |
130 | $native_tag |
131 | ); |
132 | } |
133 |
134 | $result [ 'skipped' ] += 1; |
135 | continue ; |
136 | } |
137 |
138 | if ( 'private' == $video [ 'privacy' ] ){ |
139 | $result [ 'private' ] += 1; |
140 | if ( 'skip' == $options [ 'import_privacy' ] ){ |
141 | $result [ 'skipped' ] += 1; |
142 |
143 | /** |
144 | * Generate an error and pass it for debugging |
145 | * @var WP_Error |
146 | */ |
147 | $error = new WP_Error( |
148 | 'cvm_import_skip_private_video' , |
149 | sprintf( |
150 | __( 'Skipped private video having ID %s because of plugin settings.' , 'cvm-video' ), |
151 | $video [ 'video_id' ] |
152 | ), |
153 | [ |
154 | 'video_data' => $video |
155 | ] |
156 | ); |
157 | $result [ 'error' ][] = $error ; |
158 |
159 | /** |
160 | * Send error to debug function |
161 | */ |
162 | Helper::debug_message( |
163 | 'Import error: ' . $error ->get_error_message(), |
164 | "\n" , |
165 | $error |
166 | ); |
167 |
168 | continue ; |
169 | } |
170 | } |
171 |
172 | $post_id = $this ->import_video( [ |
173 | 'video' => $video , // video details retrieved from Vimeo |
174 | 'category' => $native_tax , // category name (if any) - will be created if category_id is false |
175 | 'tags' => $native_tag , |
176 | 'user' => ( isset( $import_user ) ? absint( $import_user ) : false ), // save as a given user if any |
177 | 'post_format' => 'video' , // post format will default to video |
178 | 'status' => $this ->post_type->get_post_settings()->post_status( $import_status ), // post status |
179 | 'options' => $options |
180 | ] ); |
181 |
182 | if ( $post_id ){ |
183 | $result [ 'imported' ] += 1; |
184 | $result [ 'ids' ][] = $post_id ; |
185 |
186 | $video = Helper::get_video_post( $post_id ); |
187 | $video ->set_embed_options( |
188 | $this ->get_import_options( $import_options ), |
189 | true |
190 | ); |
191 | } |
192 | } |
193 |
194 | Helper::debug_message( |
195 | sprintf( |
196 | 'Processed %d entries: created %d posts, skipped %d entries, %d entries were marked private.' , |
197 | $result [ 'total' ], |
198 | $result [ 'imported' ], |
199 | $result [ 'skipped' ], |
200 | $result [ 'private' ] |
201 | ) |
202 | ); |
203 |
204 | return $result ; |
205 | } |
206 |
207 | /** |
208 | * @param array $raw_feed |
209 | * @param $post_type |
210 | * |
211 | * @return array |
212 | */ |
213 | public function get_duplicate_posts( $raw_feed , $post_type ){ |
214 |
215 | if ( ! $raw_feed ){ |
216 | return []; |
217 | } |
218 |
219 | $video_ids = []; |
220 | foreach ( $raw_feed as $video ){ |
221 | $video_ids [] = $video [ 'video_id' ]; |
222 | } |
223 | /** |
224 | * @var \WP_Query |
225 | */ |
226 | global $wpdb ; |
227 | $query = $wpdb ->prepare( |
228 | " |
229 | SELECT { $wpdb ->postmeta}.post_id, { $wpdb ->postmeta}.meta_value |
230 | FROM { $wpdb ->postmeta} |
231 | LEFT JOIN { $wpdb ->posts} |
232 | ON { $wpdb ->postmeta}.post_id = { $wpdb ->posts}.ID |
233 | WHERE |
234 | { $wpdb ->posts}.post_type LIKE '%s' |
235 | AND meta_value IN( " . implode( ',', $video_ids ) . " ) |
236 | ", |
237 | $post_type |
238 | ); |
239 |
240 | $existing = $wpdb ->get_results( $query ); |
241 | $_result = []; |
242 |
243 | if ( $existing ){ |
244 | foreach ( $existing as $r ){ |
245 | $_result [ $r ->meta_value ][] = $r ->post_id; |
246 | } |
247 | } |
248 |
249 | /** |
250 | * Filter the duplicate posts found by the plugin. |
251 | * When perfoming imports, the filter runs when duplicate imports are detected. |
252 | * |
253 | * @param array $_result The post IDs found as duplicates. |
254 | */ |
255 | $result = apply_filters( 'vimeotheque\duplicate_posts_found' , $_result ); |
256 |
257 | if ( $_result !== $result ){ |
258 | Helper::debug_message( |
259 | sprintf( |
260 | 'Detected duplicate posts override by filter "%s".' , |
261 | 'vimeotheque\duplicate_posts_found' |
262 | ) |
263 | ); |
264 | } |
265 |
266 | return $result ; |
267 | } |
268 |
269 | /** |
270 | * Import a single video based on the passed data |
271 | * |
272 | * @param array $args |
273 | * |
274 | * @return bool|int |
275 | */ |
276 | public function import_video( $args = [] ){ |
277 |
278 | $defaults = [ |
279 | 'video' => [], // video details retrieved from Vimeo |
280 | 'post_id' => false, |
281 | 'category' => false, // category name (if any) - will be created if category_id is false |
282 | 'tags' => false, |
283 | 'user' => false, // save as a given user if any |
284 | 'post_format' => 'video' , // post format will default to video |
285 | 'status' => 'draft' , // post status |
286 | 'options' => false, |
287 | ]; |
288 | /** |
289 | * @var array $video |
290 | * @var int $post_id |
291 | * @var string $category |
292 | * @var array $tags |
293 | * @var int $user |
294 | * @var string $post_format |
295 | * @var string $status |
296 | * @var array $options |
297 | */ |
298 | extract( wp_parse_args( $args , $defaults ), EXTR_SKIP ); |
299 |
300 | if ( ! $options ){ |
301 | $options = Plugin::instance()->get_options(); |
302 | } |
303 |
304 | // if no video details, bail out |
305 | if ( ! $video ){ |
306 | return false; |
307 | } |
308 |
309 | /** |
310 | * Filter that allows changing of post format when importing videos. |
311 | * |
312 | * @param string $post_format The post format. |
313 | */ |
314 | $post_format = apply_filters( |
315 | 'vimeotheque\import_post_format' , |
316 | $post_format |
317 | ); |
318 |
319 | /** |
320 | * Filter that allows video imports. |
321 | * Can be used to prevent importing of videos. |
322 | * |
323 | * @param bool $allow Allow video improts to be made (true) or prevent them (false). |
324 | * @param array $video Video details array. |
325 | * @param string $post_type The post type that should be created from the video details. |
326 | * @param false $false An unset parameter. |
327 | */ |
328 | $allow_import = apply_filters( |
329 | 'vimeotheque\allow_import' , |
330 | true, |
331 | $video , |
332 | $this ->post_type->get_post_type(), |
333 | false |
334 | ); |
335 |
336 | if ( ! $allow_import ){ |
337 | /** |
338 | * Generate an error and pass it for debugging |
339 | * @var WP_Error |
340 | */ |
341 | $error = new WP_Error( |
342 | 'vimeotheque_video_import_prevented_by_filter' , |
343 | sprintf( |
344 | __( 'Video having ID %s could not be imported because of a filter blocking all imports.' , 'codeflavors-vimeo-video-post-lite' ), |
345 | $video [ 'video_id' ] |
346 | ), |
347 | [ 'video_data' => $video ] |
348 | ); |
349 |
350 | /** |
351 | * Send error to debug function |
352 | */ |
353 | Helper::debug_message( |
354 | 'Import error: ' . $error ->get_error_message(), |
355 | "\n" , |
356 | $error |
357 | ); |
358 |
359 | return false; |
360 | } |
361 |
362 | // plugin settings; caller can pass their own import options |
363 | if ( ! $options ){ |
364 | $options = Plugin::instance()->get_options(); |
365 | } |
366 |
367 | if ( 'private' == $video [ 'privacy' ] && 'pending' == $options [ 'import_privacy' ] ){ |
368 | $status = 'pending' ; |
369 | } |
370 |
371 | // post content |
372 | $post_content = '' ; |
373 | if ( 'content' == $options [ 'import_description' ] || 'content_excerpt' == $options [ 'import_description' ] ){ |
374 | $post_content = $video [ 'description' ]; |
375 | } |
376 | // post excerpt |
377 | $post_excerpt = '' ; |
378 | if ( 'excerpt' == $options [ 'import_description' ] || 'content_excerpt' == $options [ 'import_description' ] ){ |
379 | $post_excerpt = $video [ 'description' ]; |
380 | } |
381 |
382 | // post title |
383 | $post_title = $options [ 'import_title' ] ? $video [ 'title' ] : '' ; |
384 |
385 | /** |
386 | * Action that runs before the post is inserted into the database. |
387 | * |
388 | * @param array $video The video details array retrieved from Vimeo. |
389 | * @param false $false Always false in Vimeotheque Lite. |
390 | */ |
391 | do_action( |
392 | 'vimeotheque\import_before' , |
393 | $video , |
394 | false |
395 | ); |
396 |
397 | // set post data |
398 | $post_data = [ |
399 |
400 | /** |
401 | * Post title filter before the post is inserted into the database. |
402 | * |
403 | * @param string $title The post title. |
404 | * @param array $video The video details. |
405 | * @param bool $false Unused parameter. |
406 | */ |
407 | 'post_title' => apply_filters( |
408 | 'vimeotheque\import_post_title' , |
409 | $post_title , |
410 | $video , |
411 | false |
412 | ), |
413 |
414 | /** |
415 | * Post content filter before the post is inserted into the database. |
416 | * |
417 | * @param string $content The post content. |
418 | * @param array $video The video details. |
419 | * @param false $false Unused parameter. |
420 | */ |
421 | 'post_content' => apply_filters( |
422 | 'vimeotheque\import_post_content' , |
423 | $post_content , |
424 | $video , |
425 | false |
426 | ), |
427 |
428 | /** |
429 | * Post excerpt filter before the post is inserted into the database. |
430 | * |
431 | * @param string $excerpt The post excerpt. |
432 | * @param array $video The video details. |
433 | * @param false $false Unused parameter. |
434 | */ |
435 | 'post_excerpt' => apply_filters( |
436 | 'vimeotheque\import_post_excerpt' , |
437 | $post_excerpt , |
438 | $video , |
439 | false |
440 | ), |
441 | 'post_type' => $this ->post_type->get_post_type(), |
442 |
443 | /** |
444 | * Post status filter before the post is inserted into the database. |
445 | * |
446 | * @param string $status The post status. |
447 | * @param array $video The video details. |
448 | * @param false $false Unused parameter. |
449 | */ |
450 | 'post_status' => apply_filters( |
451 | 'vimeotheque\import_post_status' , |
452 | $status , |
453 | $video , |
454 | false |
455 | ) |
456 | ]; |
457 |
458 | $pd = $options [ 'import_date' ] ? date ( 'Y-m-d H:i:s' , strtotime ( $video [ 'published' ] )) : current_time( 'mysql' ); |
459 |
460 | /** |
461 | * Post date filter before the post is inserted into the database. |
462 | * |
463 | * @param string $date The post date. |
464 | * @param array $video The video details. |
465 | * @param false $false Unused parameter. |
466 | */ |
467 | $post_date = apply_filters( |
468 | 'vimeotheque\import_post_date' , |
469 | $pd , |
470 | $video , |
471 | false |
472 | ); |
473 |
474 | if ( isset( $options [ 'import_date' ] ) && $options [ 'import_date' ] ){ |
475 | $post_data [ 'post_date_gmt' ] = $post_date ; |
476 | $post_data [ 'edit_date' ] = $post_date ; |
477 | $post_data [ 'post_date' ] = $post_date ; |
478 | } |
479 |
480 | // set user |
481 | if ( $user ){ |
482 | $post_data [ 'post_author' ] = $user ; |
483 | } |
484 | /** |
485 | * @var int|\WP_Error $post_id |
486 | */ |
487 | // single video import will pass post ID |
488 | if ( isset( $post_id ) && $post_id ){ |
489 | $post_data [ 'ID' ] = $post_id ; |
490 | $post_id = wp_update_post( $post_data , true ); |
491 | } else { |
492 | // allow empty insert into post content |
493 | add_filter( |
494 | 'wp_insert_post_empty_content' , |
495 | '__return_false' |
496 | ); |
497 |
498 | $post_id = wp_insert_post( $post_data , true ); |
499 | } |
500 |
501 | if ( is_wp_error( $post_id ) ){ |
502 | Helper::debug_message( |
503 | sprintf( |
504 | 'Video with ID %s generated the following database error on insert: "%s"; video post could not be created.' , |
505 | $video [ 'video_id' ], |
506 | $post_id ->get_error_message() |
507 | ) |
508 | ); |
509 | } |
510 |
511 | // check if post was created |
512 | if ( !is_wp_error( $post_id ) ){ |
513 |
514 | // set post format |
515 | if ( $post_format ){ |
516 | set_post_format( $post_id , $post_format ); |
517 | } |
518 |
519 | // set post category |
520 | if ( $category ){ |
521 | $category = is_array ( $category ) ? $category : [ $category ]; |
522 | wp_set_post_terms( $post_id , $category , $this ->post_type->get_post_tax() ); |
523 | } |
524 |
525 | if ( $tags ){ |
526 | wp_set_post_terms( $post_id , $tags , $this ->post_type->get_tag_tax() ); |
527 | } |
528 |
529 | // insert tags |
530 | if ( ( isset( $options [ 'import_tags' ] ) && $options [ 'import_tags' ] ) && $this ->post_type->get_tag_tax() ){ |
531 | if ( isset( $video [ 'tags' ] ) && is_array ( $video [ 'tags' ] ) ){ |
532 | $count = absint( $options [ 'max_tags' ] ); |
533 | $tags = array_slice ( $video [ 'tags' ], 0, $count ); |
534 | if ( $tags ){ |
535 | wp_set_post_terms( $post_id , $tags , $this ->post_type->get_tag_tax(), true ); |
536 | } |
537 | } |
538 | } |
539 |
540 | // set post meta |
541 | $_post = Helper::get_video_post( $post_id ); |
542 | $_post ->set_video_data( $video ); |
543 | $_post ->set_video_id_meta(); |
544 | $_post ->set_video_url_meta(); |
545 |
546 | /** |
547 | * Action after a video post was successfully imported into the database. |
548 | * |
549 | * @param int $post_id ID of the post newly created from the Vimeo video. |
550 | * @param array $video Array of video details retrieved from Vimeo. |
551 | * @param false $unknown Unused parameter. |
552 | * @param string $post_type The post type that was created. |
553 | */ |
554 | do_action( |
555 | 'vimeotheque\import_success' , |
556 | $post_id , |
557 | $video , |
558 | false, |
559 | $this ->post_type->get_post_type() |
560 | ); |
561 |
562 | /** |
563 | * Send a debug message |
564 | */ |
565 | Helper::debug_message( |
566 | sprintf( |
567 | 'Imported video ID %s into post #%d having post type "%s".' , |
568 | $video [ 'video_id' ], |
569 | $post_id , |
570 | $this ->post_type->get_post_type() |
571 | ) |
572 | ); |
573 |
574 | // import image |
575 | if ( $options [ 'featured_image' ] ){ |
576 | $_post ->set_featured_image(); |
577 | } |
578 |
579 | return $post_id ; |
580 |
581 | } // end checking if not wp error on post insert |
582 |
583 | return false; |
584 | } |
585 |
586 | /** |
587 | * Process the import options |
588 | * |
589 | * @param array $source |
590 | * |
591 | * @return array |
592 | */ |
593 | private function get_import_options( $source = [] ){ |
594 | $taxonomy = $this ->post_type->get_post_tax(); |
595 | $tag_tax = $this ->post_type->get_tag_tax(); |
596 | $native_tax = isset( $source [ 'tax_input' ][ $taxonomy ] ) ? ( array ) $source [ 'tax_input' ][ $taxonomy ] : []; |
597 | $native_tag = isset( $source [ 'tax_input' ][ $tag_tax ] ) ? ( array ) $source [ 'tax_input' ][ $tag_tax ] : []; |
598 |
599 | $import_options = [ |
600 | 'native_tax' => $native_tax , |
601 | 'native_tag' => $native_tag , |
602 | 'import_description' => $source [ 'import_description' ], |
603 | 'import_status' => $source [ 'import_status' ], |
604 | 'import_title' => isset( $source [ 'import_title' ] ) |
605 | ]; |
606 |
607 | return $import_options ; |
608 | } |
609 | } |
Methods
- __construct — Posts_Import constructor.
- get_duplicate_posts
- get_import_options — Process the import options
- import_video — Import a single video based on the passed data
- run_import — Imports videos from a given feed source.