PluginProbe
Sharing Image / 3.1
Sharing Image v3.1
3.10 trunk 2.0 2.0.0 2.0.1 2.0.10 2.0.11 2.0.12 2.0.13 2.0.14 2.0.15 2.0.16 2.0.17 2.0.2 2.0.3 2.0.4 2.0.5 2.0.6 2.0.7 2.0.8 2.0.9 3.0 3.1 3.2 3.3 All 29 releases
sharing-image / classes / class-widget.php

class-widget.php in Sharing Image 3.1, at classes/class-widget.php

1,049 lines 25.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Display widgets and sidebars on post and term editors screens.
4 *
5 * @package sharing-image
6 * @author Anton Lukin
7 */
8
9 namespace Sharing_Image;
10
11 use WP_Error;
12 use Exception;
13
14 if ( ! defined( 'ABSPATH' ) ) {
15 die;
16 }
17
18 /**
19 * Widget class.
20 *
21 * @class Widget
22 */
23 class Widget {
24 /**
25 * Post meta key to store poster image.
26 *
27 * @var string
28 */
29 const META_SOURCE = '_sharing_image';
30
31 /**
32 * Post meta key to store poster image fielset.
33 *
34 * @var string
35 */
36 const META_FIELDSET = '_sharing_image_fieldset';
37
38 /**
39 * Common nonce for settings tabs.
40 *
41 * @var string
42 */
43 const WIDGET_NONCE = 'sharing-image-widget';
44
45 /**
46 * Init class actions and filters.
47 */
48 public static function init() {
49 add_action( 'init', array( __CLASS__, 'init_post_widget' ) );
50 add_action( 'init', array( __CLASS__, 'init_post_sidebar' ) );
51 add_action( 'init', array( __CLASS__, 'init_taxonomy_widget' ) );
52
53 // Generate poster handlers.
54 add_action( 'wp_ajax_sharing_image_generate', array( __CLASS__, 'handle_ajax_generator' ) );
55 add_action( 'rest_api_init', array( __CLASS__, 'add_generate_endpoint' ) );
56
57 // Try to autogenerate poster if it is needed.
58 add_action( 'wp_after_insert_post', array( __CLASS__, 'autogenerate_poster' ), 20, 3 );
59 }
60
61 /**
62 * Init post sidebar for gutenberg enabled dashboard.
63 */
64 public static function init_post_sidebar() {
65 if ( Config::is_hidden_post_widget() ) {
66 return;
67 }
68
69 $schema = array(
70 'type' => 'object',
71 'properties' => array(
72 'poster' => array(
73 'type' => 'string',
74 ),
75 'width' => array(
76 'type' => 'integer',
77 ),
78 'height' => array(
79 'type' => 'integer',
80 ),
81 'template' => array(
82 'type' => 'string',
83 ),
84 'mode' => array(
85 'type' => 'string',
86 ),
87 ),
88 );
89
90 register_meta(
91 'post',
92 self::META_SOURCE,
93 array(
94 'type' => 'object',
95 'show_in_rest' => array(
96 'schema' => $schema,
97 ),
98 'single' => true,
99 'auth_callback' => function () {
100 return current_user_can( 'edit_posts' );
101 },
102 'sanitize_callback' => array( __CLASS__, 'sanitize_source' ),
103 )
104 );
105
106 register_meta(
107 'post',
108 self::META_FIELDSET,
109 array(
110 'type' => 'object',
111 'show_in_rest' => array(
112 'schema' => array(
113 'type' => 'object',
114 'properties' => array(),
115 'additionalProperties' => array(
116 'type' => array( 'string', 'integer' ),
117 ),
118 ),
119 ),
120 'single' => true,
121 'auth_callback' => function () {
122 return current_user_can( 'edit_posts' );
123 },
124 'sanitize_callback' => array( __CLASS__, 'sanitize_fieldset' ),
125 )
126 );
127
128 // Add required assets and objects.
129 add_action( 'enqueue_block_editor_assets', array( __CLASS__, 'enqueue_sidebar_assets' ) );
130 }
131
132 /**
133 * Init widget on post editing page.
134 */
135 public static function init_post_widget() {
136 if ( Config::is_hidden_post_widget() ) {
137 return;
138 }
139
140 add_action( 'add_meta_boxes', array( __CLASS__, 'add_metabox' ) );
141
142 // Handle actions on post save.
143 add_action( 'save_post', array( __CLASS__, 'save_post_widget' ), 10 );
144
145 // Add required assets and objects.
146 add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_metabox_assets' ) );
147 }
148
149 /**
150 * Init widget on taxonomy term editing page.
151 */
152 public static function init_taxonomy_widget() {
153 // Skip if the Premium is not active.
154 if ( ! Premium::is_premium_features() ) {
155 return;
156 }
157
158 $taxonomies = self::get_widget_taxonomies();
159
160 foreach ( $taxonomies as $taxonomy ) {
161 // Display taxonomy term widget.
162 add_action( $taxonomy . '_edit_form', array( __CLASS__, 'display_widget' ), 10, 2 );
163
164 // Save taxonomy term meta.
165 add_action( 'edited_' . $taxonomy, array( __CLASS__, 'save_taxonomy_widget' ) );
166
167 // Enqueue term assets.
168 add_action( 'admin_enqueue_scripts', array( __CLASS__, 'enqueue_taxonomy_assets' ) );
169 }
170 }
171
172 /**
173 * Add metabox to post editing page.
174 */
175 public static function add_metabox() {
176 add_meta_box(
177 'sharing-image-metabox',
178 esc_html__( 'Sharing Image', 'sharing-image' ),
179 array( __CLASS__, 'display_widget' ),
180 self::get_metabox_post_types(),
181 'side',
182 'high',
183 array(
184 '__back_compat_meta_box' => true,
185 )
186 );
187 }
188
189 /**
190 * Add metabox scripts and styles to admin page.
191 *
192 * @param string $hook_suffix The current admin page.
193 */
194 public static function enqueue_metabox_assets( $hook_suffix ) {
195 if ( ! in_array( $hook_suffix, array( 'post.php', 'post-new.php' ), true ) ) {
196 return;
197 }
198
199 $screen = get_current_screen();
200
201 if ( ! in_array( $screen->post_type, self::get_metabox_post_types(), true ) ) {
202 return;
203 }
204
205 $post = get_post();
206
207 if ( use_block_editor_for_post( $post ) ) {
208 return;
209 }
210
211 // Get post meta for current post ID.
212 $data = self::create_script_object( 'post', $post->ID );
213
214 $data['meta'] = array(
215 'source' => get_post_meta( $post->ID, self::META_SOURCE, true ),
216 'fieldset' => get_post_meta( $post->ID, self::META_FIELDSET, true ),
217 );
218
219 /**
220 * Filter widget script object.
221 *
222 * @param array $object Array of widget script object.
223 */
224 self::enqueue_widget_scripts( $data );
225 }
226
227 /**
228 * Add widget scripts and styles to admin page.
229 *
230 * @param string $hook_suffix The current admin page.
231 */
232 public static function enqueue_taxonomy_assets( $hook_suffix ) {
233 if ( 'term.php' !== $hook_suffix ) {
234 return;
235 }
236
237 $screen = get_current_screen();
238
239 if ( ! in_array( $screen->taxonomy, self::get_widget_taxonomies(), true ) ) {
240 return;
241 }
242
243 // phpcs:disable WordPress.Security.NonceVerification
244 if ( ! isset( $_REQUEST['tag_ID'] ) ) {
245 return;
246 }
247
248 $term_id = absint( $_REQUEST['tag_ID'] );
249 // phpcs:enable WordPress.Security.NonceVerification
250
251 $data = self::create_script_object( 'term', $term_id );
252
253 $data['meta'] = array(
254 'source' => get_term_meta( $term_id, self::META_SOURCE, true ),
255 'fieldset' => get_term_meta( $term_id, self::META_FIELDSET, true ),
256 );
257
258 /**
259 * Filter widget script object.
260 *
261 * @param array $object Array of widget script object.
262 */
263 self::enqueue_widget_scripts( $data );
264 }
265
266 /**
267 * Add Gutenberg block scripts and sryles.
268 *
269 * @since 3.0
270 */
271 public static function enqueue_sidebar_assets() {
272 if ( ! is_admin() ) {
273 return;
274 }
275
276 $screen = get_current_screen();
277
278 if ( ! in_array( $screen->post_type, self::get_metabox_post_types(), true ) ) {
279 return;
280 }
281
282 $asset = require SHARING_IMAGE_DIR . 'assets/sidebar/index.asset.php';
283
284 wp_enqueue_script(
285 'sharing-image-sidebar',
286 plugins_url( 'assets/sidebar/index.js', SHARING_IMAGE_FILE ),
287 $asset['dependencies'],
288 $asset['version'],
289 true
290 );
291
292 wp_enqueue_style(
293 'sharing-image-sidebar',
294 plugins_url( 'assets/sidebar/index.css', SHARING_IMAGE_FILE ),
295 null,
296 $asset['version'],
297 'all'
298 );
299
300 // Translations availible only for WP 5.0+.
301 wp_set_script_translations( 'sharing-image-sidebar', 'sharing-image' );
302
303 $data = array(
304 'meta' => array(
305 'source' => self::META_SOURCE,
306 'fieldset' => self::META_FIELDSET,
307 ),
308 'autogenerate' => Config::get_autogenerate_index(),
309 'templates' => Templates::get_templates(),
310 );
311
312 // Add widget script object.
313 wp_localize_script( 'sharing-image-sidebar', 'sharingImageSidebar', $data );
314 }
315
316 /**
317 * Save metabox data.
318 *
319 * @param int $post_id Post ID.
320 */
321 public static function save_post_widget( $post_id ) {
322 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
323 return;
324 }
325
326 if ( wp_is_post_revision( $post_id ) ) {
327 return;
328 }
329
330 if ( ! current_user_can( 'edit_post', $post_id ) ) {
331 return;
332 }
333
334 if ( ! isset( $_POST['sharing_image_nonce'] ) ) {
335 return;
336 }
337
338 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
339 if ( ! wp_verify_nonce( $_POST['sharing_image_nonce'], self::WIDGET_NONCE ) ) {
340 return;
341 }
342
343 if ( isset( $_POST[ self::META_SOURCE ] ) ) {
344 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
345 $meta = self::sanitize_source( wp_unslash( $_POST[ self::META_SOURCE ] ) );
346
347 update_post_meta( $post_id, self::META_SOURCE, $meta );
348 }
349
350 if ( isset( $_POST[ self::META_FIELDSET ] ) ) {
351 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
352 $meta = self::sanitize_fieldset( wp_unslash( $_POST[ self::META_FIELDSET ] ) );
353
354 update_post_meta( $post_id, self::META_FIELDSET, $meta );
355 }
356 }
357
358 /**
359 * Save taxonomy fields.
360 *
361 * @param int $term_id Term ID.
362 */
363 public static function save_taxonomy_widget( $term_id ) {
364 if ( ! current_user_can( 'edit_term', $term_id ) ) {
365 return;
366 }
367
368 if ( ! isset( $_POST['sharing_image_nonce'] ) ) {
369 return;
370 }
371
372 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
373 if ( ! wp_verify_nonce( $_POST['sharing_image_nonce'], self::WIDGET_NONCE ) ) {
374 return;
375 }
376
377 if ( isset( $_POST[ self::META_SOURCE ] ) ) {
378 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
379 $meta = self::sanitize_source( wp_unslash( $_POST[ self::META_SOURCE ] ) );
380
381 update_term_meta( $term_id, self::META_SOURCE, $meta );
382 }
383
384 if ( isset( $_POST[ self::META_FIELDSET ] ) ) {
385 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
386 $meta = self::sanitize_fieldset( wp_unslash( $_POST[ self::META_FIELDSET ] ) );
387
388 update_term_meta( $term_id, self::META_FIELDSET, $meta );
389 }
390 }
391
392 /**
393 * Display widget.
394 */
395 public static function display_widget() {
396 include_once SHARING_IMAGE_DIR . 'templates/widget.php';
397
398 /**
399 * Fires on widget template including.
400 */
401 do_action( 'sharing_image_widget' );
402 }
403
404 /**
405 * Create new endpoint for generate button in Gutenberg sidebar.
406 */
407 public static function add_generate_endpoint() {
408 register_rest_route(
409 'sharing-image/v1',
410 '/poster/(?P<id>\d+)',
411 array(
412 'methods' => 'POST',
413 'callback' => array( __CLASS__, 'handle_rest_generator' ),
414 'permission_callback' => function () {
415 return current_user_can( 'edit_posts' );
416 },
417 )
418 );
419 }
420
421 /**
422 * Sanitize widget source meta.
423 *
424 * @param array $source Source meta data.
425 *
426 * @return array Sanitized source meta fields.
427 */
428 public static function sanitize_source( $source ) {
429 $sanitized = array();
430
431 if ( isset( $source['poster'] ) ) {
432 $sanitized['poster'] = sanitize_text_field( $source['poster'] );
433 }
434
435 if ( ! empty( $source['width'] ) ) {
436 $sanitized['width'] = absint( $source['width'] );
437 }
438
439 if ( ! empty( $source['height'] ) ) {
440 $sanitized['height'] = absint( $source['height'] );
441 }
442
443 if ( ! empty( $source['template'] ) ) {
444 $sanitized['template'] = sanitize_key( $source['template'] );
445 }
446
447 if ( ! empty( $source['mode'] ) ) {
448 $sanitized['mode'] = sanitize_text_field( $source['mode'] );
449 }
450
451 /**
452 * Filters fieldset meta.
453 *
454 * @since 3.0
455 *
456 * @param array $sanitized List of sanitized fields.
457 * @param array $source List of fields before sanitization.
458 */
459 return apply_filters( 'sharing_image_sanitize_source', $sanitized, $source );
460 }
461
462 /**
463 * Sanitize fieldset.
464 *
465 * @param array $fieldset Fieldset list.
466 *
467 * @return array Sanitized fieldset data.
468 */
469 public static function sanitize_fieldset( $fieldset ) {
470 $sanitized = array();
471
472 // Get list of templates.
473 $templates = Templates::get_templates();
474
475 foreach ( $templates as $template ) {
476 if ( empty( $template['layers'] ) ) {
477 continue;
478 }
479
480 foreach ( $template['layers'] as $key => $layer ) {
481 if ( ! array_key_exists( $key, $fieldset ) ) {
482 continue;
483 }
484
485 if ( 'text' === $layer['type'] ) {
486 $sanitized[ $key ] = sanitize_textarea_field( $fieldset[ $key ] );
487 }
488
489 if ( 'image' === $layer['type'] ) {
490 $sanitized[ $key ] = absint( $fieldset[ $key ] );
491 }
492 }
493 }
494
495 /**
496 * Filters widget fieldset meta.
497 *
498 * @since 3.0
499 *
500 * @param array $sanitized Sanitized fieldset list.
501 * @param array $fieldset Fieldset list before sanitization.
502 */
503 return apply_filters( 'sharing_image_sanitize_fieldset', $sanitized, $fieldset );
504 }
505
506 /**
507 * Handle generate button in Gutenberg sidebar.
508 *
509 * @param WP_REST_Request $request Request params.
510 */
511 public static function handle_rest_generator( $request ) {
512 $post_id = $request->get_param( 'id' );
513
514 if ( empty( $post_id ) ) {
515 wp_send_json_error( __( 'Post data is empty.', 'sharing-image' ), 400 );
516 }
517
518 $params = $request->get_json_params();
519
520 if ( ! isset( $params['template'] ) ) {
521 wp_send_json_error( __( 'Incorrect request parameters.', 'sharing-image' ), 400 );
522 }
523
524 $index = sanitize_key( $params['template'] );
525
526 if ( empty( $params['fieldset'] ) ) {
527 $params['fieldset'] = array();
528 }
529
530 $fieldset = self::sanitize_fieldset( $params['fieldset'] );
531
532 // Invoke poster generation.
533 $result = self::generate_poster( $fieldset, $index, $post_id, 'post' );
534
535 if ( is_wp_error( $result ) ) {
536 wp_send_json_error( $result->get_error_messages(), $result->get_error_data() );
537 }
538
539 wp_send_json_success( $result );
540 }
541
542 /**
543 * Handle generate button on Classic Editor and taxonomy widget.
544 */
545 public static function handle_ajax_generator() {
546 $check = check_ajax_referer( self::WIDGET_NONCE, 'sharing_image_nonce', false );
547
548 if ( false === $check ) {
549 wp_send_json_error( __( 'Invalid security token. Please reload the page and try again.', 'sharing-image' ), 403 );
550 }
551
552 if ( empty( $_POST[ self::META_SOURCE ]['template'] ) ) {
553 wp_send_json_error( __( 'Template ID cannot be empty.', 'sharing-image' ), 400 );
554 }
555
556 $index = sanitize_key( $_POST[ self::META_SOURCE ]['template'] );
557
558 $screen_id = 0;
559
560 if ( ! empty( $_POST['sharing_image_screen'] ) ) {
561 $screen_id = absint( wp_unslash( $_POST['sharing_image_screen'] ) );
562 }
563
564 $context = 'post';
565
566 if ( ! empty( $_POST['sharing_image_context'] ) ) {
567 $context = sanitize_key( wp_unslash( $_POST['sharing_image_context'] ) );
568 }
569
570 $fieldset = array();
571
572 if ( ! empty( $_POST[ self::META_FIELDSET ] ) ) {
573 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
574 $fieldset = self::sanitize_fieldset( wp_unslash( $_POST[ self::META_FIELDSET ] ) );
575 }
576
577 // Invoke poster generation.
578 $result = self::generate_poster( $fieldset, $index, $screen_id, $context );
579
580 if ( is_wp_error( $result ) ) {
581 wp_send_json_error( $result->get_error_messages(), $result->get_error_data() );
582 }
583
584 wp_send_json_success( $result );
585 }
586
587 /**
588 * Try to autogenerate poster on post insert or update.
589 *
590 * @param int $post_id Updated post_id.
591 * @param object $post Updated post object.
592 * @param bool $update Whether this is an existing post being updated.
593 */
594 public static function autogenerate_poster( $post_id, $post, $update ) {
595 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
596 return;
597 }
598
599 if ( ! $update ) {
600 return;
601 }
602
603 if ( wp_is_post_revision( $post_id ) ) {
604 return;
605 }
606
607 if ( ! current_user_can( 'edit_post', $post_id ) ) {
608 return;
609 }
610
611 if ( 'trash' === $post->post_status ) {
612 return;
613 }
614
615 if ( ! in_array( $post->post_type, self::get_metabox_post_types(), true ) ) {
616 return;
617 }
618
619 $meta = get_post_meta( $post_id, self::META_SOURCE, true );
620
621 if ( ! empty( $meta['mode'] ) && 'manual' === $meta['mode'] ) {
622 return;
623 }
624
625 $index = Config::get_autogenerate_index();
626
627 if ( empty( $index ) ) {
628 return;
629 }
630
631 // Compose fieldset by template index.
632 $fieldset = self::compose_fields( $index, $post_id );
633
634 /**
635 * Filters fieldset before autogeneration.
636 *
637 * @since 3.0
638 *
639 * @param array $fieldset Prepared fieldset data.
640 * @param string $index Template index.
641 * @param int $post_id Post ID.
642 */
643 $fieldset = apply_filters( 'sharing_image_autogenerated_fieldset', $fieldset, $index, $post_id );
644
645 if ( empty( $fieldset ) ) {
646 $fieldset = array();
647 }
648
649 // Invoke poster generation.
650 $result = self::generate_poster( $fieldset, $index, $post_id, 'post', true );
651
652 $result['template'] = $index;
653
654 /**
655 * Filters autogenerated poster data.
656 *
657 * @since 2.0.11
658 *
659 * @param array|WP_Erorr $result Poster image, width and height data or WP_Error if undefined.
660 * @param int $post_id Post ID.
661 */
662 $result = apply_filters( 'sharing_image_autogenerated_poster', $result, $post_id );
663
664 if ( is_wp_error( $result ) ) {
665 return;
666 }
667
668 update_post_meta( $post_id, self::META_SOURCE, $result );
669 update_post_meta( $post_id, self::META_FIELDSET, $fieldset );
670 }
671
672 /**
673 * Generate poster by AJAX request.
674 * Used in widget and sidebar.
675 *
676 * @param array $fieldset Fieldset data from widget.
677 * @param int $index Template index from editor.
678 * @param int $screen_id Post or term ID from admin screen.
679 * @param string $context Screen ID context field. Can be settings, post or term.
680 * @param boolean $auto Whether auto-generated poster.
681 */
682 private static function generate_poster( $fieldset, $index, $screen_id, $context, $auto = false ) {
683 $templates = Templates::get_templates();
684
685 if ( ! isset( $templates[ $index ] ) ) {
686 return new WP_Error( 'generate', esc_html__( 'Incorrect template ID.', 'sharing-image' ), 400 );
687 }
688
689 // Prepare template editor.
690 $editor = Generator::prepare_template( $templates[ $index ], $fieldset, $index, $screen_id, $context );
691
692 if ( ! Generator::check_required( $editor ) ) {
693 return new WP_Error( 'generate', esc_html__( 'Incorrect template settings.', 'sharing-image' ), 400 );
694 }
695
696 list( $path, $url ) = Generator::get_upload_file();
697
698 // Generate image and save it to given path.
699 $poster = Generator::create_poster( $editor, $path );
700
701 if ( is_wp_error( $poster ) ) {
702 return new WP_Error( 'generate', $poster->get_error_message(), 400 );
703 }
704
705 $attachment = self::save_attachment( $path, $screen_id, $context );
706
707 if ( is_wp_error( $attachment ) ) {
708 return new WP_Error( 'attachment', $attachment->get_error_message(), 400 );
709 }
710
711 $source = array(
712 'poster' => $url,
713 'width' => $editor['width'],
714 'height' => $editor['height'],
715 'mode' => 'manual',
716 );
717
718 if ( ! empty( $auto ) ) {
719 $source['mode'] = 'auto';
720 }
721
722 return $source;
723 }
724
725 /**
726 * Create script object to inject with widget.
727 *
728 * @param string $context Widget context. For example: metabox or taxonomy.
729 * @param int $screen_id Post or taxonomy screen ID.
730 *
731 * @return array Filtered widget script object.
732 */
733 private static function create_script_object( $context, $screen_id ) {
734 $object = array(
735 'nonce' => wp_create_nonce( self::WIDGET_NONCE ),
736 'context' => $context,
737 'screen' => $screen_id,
738
739 'name' => array(
740 'source' => self::META_SOURCE,
741 'fieldset' => self::META_FIELDSET,
742 ),
743 'links' => array(
744 'uploads' => esc_url( admin_url( 'upload.php' ) ),
745 ),
746 'templates' => Templates::get_templates(),
747 );
748
749 /**
750 * Filter widget script object.
751 *
752 * @param array $object Array of widget script object.
753 */
754 return apply_filters( 'sharing_image_widget_object', $object );
755 }
756
757 /**
758 * Enqueue widget scripts.
759 *
760 * @param array $data Widget data object.
761 */
762 private static function enqueue_widget_scripts( $data ) {
763 $asset = require SHARING_IMAGE_DIR . 'assets/widget/index.asset.php';
764
765 wp_enqueue_media();
766
767 wp_enqueue_script(
768 'sharing-image-widget',
769 plugins_url( 'assets/widget/index.js', SHARING_IMAGE_FILE ),
770 $asset['dependencies'],
771 $asset['version'],
772 true
773 );
774
775 wp_enqueue_style(
776 'sharing-image-widget',
777 plugins_url( 'assets/widget/index.css', SHARING_IMAGE_FILE ),
778 $asset['dependencies'],
779 $asset['version'],
780 'all'
781 );
782
783 // Translations availible only for WP 5.0+.
784 wp_set_script_translations( 'sharing-image-widget', 'sharing-image' );
785
786 // Add widget script object.
787 wp_localize_script( 'sharing-image-widget', 'sharingImageWidget', $data );
788 }
789
790 /**
791 * Get an array of post types where the metabox is displayed.
792 *
793 * @return array Array of post types.
794 */
795 private static function get_metabox_post_types() {
796 $post_types = get_post_types(
797 array(
798 'public' => true,
799 )
800 );
801
802 unset( $post_types['attachment'] );
803
804 /**
805 * Filter widget post types.
806 *
807 * @param array $post_types Array of post types for which the metabox is displayed.
808 */
809 return apply_filters( 'sharing_image_metabox_post_types', array_values( $post_types ) );
810 }
811
812 /**
813 * Update template with post data for preset fields.
814 *
815 * @param string $index Template index.
816 * @param int $post_id Post id.
817 * @param array $fieldset Optional. Prepared fieldset to modify.
818 *
819 * @return array List of prepared fields.
820 */
821 private static function compose_fields( $index, $post_id, $fieldset = array() ) {
822 $templates = Templates::get_templates();
823
824 if ( ! isset( $templates[ $index ] ) ) {
825 return $fieldset;
826 }
827
828 $template = $templates[ $index ];
829
830 if ( ! isset( $template['layers'] ) ) {
831 return $fieldset;
832 }
833
834 $layers = $template['layers'];
835
836 foreach ( $layers as $key => $layer ) {
837 $field = null;
838
839 if ( empty( $layer['type'] ) ) {
840 continue;
841 }
842
843 if ( 'text' === $layer['type'] ) {
844 $field = self::compose_text_presets( $layer, $post_id );
845 }
846
847 if ( 'image' === $layer['type'] ) {
848 $field = self::compose_image_presets( $layer, $post_id );
849 }
850
851 if ( ! empty( $field ) ) {
852 $fieldset[ $key ] = $field;
853 }
854 }
855
856 return $fieldset;
857 }
858
859 /**
860 * Compose preset fields for text layers.
861 *
862 * @param string $layer List of layer options.
863 * @param int $post_id Post id.
864 *
865 * @return array|null List of prepared fieldset for text layer.
866 */
867 private static function compose_text_presets( $layer, $post_id ) {
868 if ( empty( $layer['preset'] ) || empty( $layer['dynamic'] ) ) {
869 return null;
870 }
871
872 $separator = ', ';
873
874 if ( ! empty( $layer['separator'] ) ) {
875 $separator = $layer['separator'];
876 }
877
878 if ( 'title' === $layer['preset'] ) {
879 return get_the_title( $post_id );
880 }
881
882 if ( 'excerpt' === $layer['preset'] ) {
883 $post = get_post( $post_id );
884
885 return $post->post_excerpt;
886 }
887
888 if ( 'categories' === $layer['preset'] ) {
889 $categories = get_the_category( $post_id );
890
891 if ( $categories ) {
892 return implode( $separator, wp_list_pluck( $categories, 'name' ) );
893 }
894 }
895
896 if ( 'tags' === $layer['preset'] ) {
897 $tags = get_the_tags( $post_id );
898
899 if ( $tags ) {
900 return implode( $separator, wp_list_pluck( $tags, 'name' ) );
901 }
902 }
903
904 return null;
905 }
906
907 /**
908 * Compose preset fields for image layers.
909 *
910 * @param string $layer List of layer options.
911 * @param int $post_id Post id.
912 *
913 * @return array|null List of prepared fieldset for image layer.
914 */
915 private static function compose_image_presets( $layer, $post_id ) {
916 if ( empty( $layer['preset'] ) || empty( $layer['dynamic'] ) ) {
917 return null;
918 }
919
920 if ( 'featured' === $layer['preset'] ) {
921 return absint( get_post_thumbnail_id( $post_id ) );
922 }
923
924 return null;
925 }
926
927 /**
928 * Get an array of taxonomeis where the widget is displayed.
929 *
930 * @return array Array of taxonomies.
931 */
932 private static function get_widget_taxonomies() {
933 $taxonomies = get_taxonomies(
934 array(
935 'public' => true,
936 'show_ui' => true,
937 )
938 );
939
940 /**
941 * Filter the taxonomies where we need to show the poster settings.
942 *
943 * @param array $taxonomies List of taxonomies to show settings.
944 */
945 return apply_filters( 'sharing_image_widget_taxonomies', array_values( $taxonomies ) );
946 }
947
948 /**
949 * Save attachment to media library.
950 *
951 * @param string $path Path to poster image.
952 * @param int $screen_id Post or taxonomy screen ID.
953 * @param string $context Widget context. For example: metabox or autogenerate.
954 *
955 * @return int|null Attachment ID or null;
956 */
957 private static function save_attachment( $path, $screen_id, $context ) {
958 $config = Config::get_config();
959
960 if ( ! isset( $config['attachment'] ) ) {
961 return null;
962 }
963
964 $id = null;
965
966 try {
967 $name = implode( '-', array( 'sharing-image', $context, $screen_id ) );
968
969 // Try to delete current attachment if exists.
970 self::delete_attachment( $name );
971
972 $uploads = wp_upload_dir();
973
974 // Get poster filetype.
975 $filetype = wp_check_filetype( basename( $path ), null );
976
977 $attachment = array(
978 'post' => array(
979 'guid' => $uploads['url'] . '/' . basename( $path ),
980 'post_mime_type' => $filetype['type'],
981 'post_name' => $name,
982 'post_title' => $name,
983 'post_content' => '',
984 'post_status' => 'inherit',
985 ),
986 'parent_id' => 0,
987 );
988
989 if ( 'post' === $context ) {
990 $attachment['parent_id'] = $screen_id;
991 }
992
993 /**
994 * Filter attachment data before insertion.
995 *
996 * @param string $attachment Attachment data.
997 * @param string $path Path to poster image.
998 * @param string $screen_id Post or taxonomy screen ID.
999 * @param string $context Widget context. For example: metabox or autogenerate.
1000 */
1001 $attachment = apply_filters( 'sharing_image_attachment_data', $attachment, $path, $screen_id, $context );
1002
1003 if ( empty( $attachment['post'] ) ) {
1004 return null;
1005 }
1006
1007 if ( ! function_exists( 'wp_crop_image' ) ) {
1008 include ABSPATH . 'wp-admin/includes/image.php';
1009 }
1010
1011 $id = wp_insert_attachment( $attachment['post'], $path, $attachment['parent_id'] );
1012
1013 // Get attachment metadata.
1014 $metadata = wp_generate_attachment_metadata( $id, $path );
1015
1016 wp_update_attachment_metadata( $id, $metadata );
1017 } catch ( Exception $e ) {
1018 return new WP_Error( 'attachment', $e->getMessage() );
1019 }
1020
1021 return $id;
1022 }
1023
1024 /**
1025 * Delete attachment by name if exists.
1026 *
1027 * @param string $name Name of attachment.
1028 *
1029 * @return bool Whether attachment deleted
1030 */
1031 private static function delete_attachment( $name ) {
1032 $posts = get_posts(
1033 array(
1034 'post_type' => 'attachment',
1035 'post_name__in' => array( $name ),
1036 'update_post_term_cache' => false,
1037 'update_post_meta_cache' => false,
1038 'posts_per_page' => 1,
1039 )
1040 );
1041
1042 if ( empty( $posts[0]->ID ) ) {
1043 return false;
1044 }
1045
1046 return wp_delete_attachment( $posts[0]->ID, true );
1047 }
1048 }
1049