PluginProbe
Sharing Image / 3.2
Sharing Image v3.2
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.2, at classes/class-widget.php

1,045 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, 2 );
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 */
593 public static function autogenerate_poster( $post_id, $post ) {
594 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
595 return;
596 }
597
598 if ( wp_is_post_revision( $post_id ) ) {
599 return;
600 }
601
602 if ( ! current_user_can( 'edit_post', $post_id ) ) {
603 return;
604 }
605
606 if ( 'trash' === $post->post_status || 'auto-draft' === $post->post_status ) {
607 return;
608 }
609
610 if ( ! in_array( $post->post_type, self::get_metabox_post_types(), true ) ) {
611 return;
612 }
613
614 $meta = get_post_meta( $post_id, self::META_SOURCE, true );
615
616 if ( ! empty( $meta['mode'] ) && 'manual' === $meta['mode'] ) {
617 return;
618 }
619
620 $index = Config::get_autogenerate_index();
621
622 if ( ! Templates::has_template( $index ) ) {
623 return;
624 }
625
626 // Compose fieldset by template index.
627 $fieldset = self::compose_fields( $index, $post_id );
628
629 /**
630 * Filters fieldset before autogeneration.
631 *
632 * @since 3.0
633 *
634 * @param array $fieldset Prepared fieldset data.
635 * @param string $index Template index.
636 * @param int $post_id Post ID.
637 */
638 $fieldset = apply_filters( 'sharing_image_autogenerated_fieldset', $fieldset, $index, $post_id );
639
640 if ( empty( $fieldset ) ) {
641 $fieldset = array();
642 }
643
644 // Invoke poster generation.
645 $result = self::generate_poster( $fieldset, $index, $post_id, 'post', true );
646
647 if ( is_wp_error( $result ) ) {
648 return;
649 }
650
651 $result['template'] = $index;
652
653 /**
654 * Filters autogenerated poster data.
655 *
656 * @since 2.0.11
657 *
658 * @param array|WP_Erorr $result Poster image, width and height data or WP_Error if undefined.
659 * @param int $post_id Post ID.
660 */
661 $result = apply_filters( 'sharing_image_autogenerated_poster', $result, $post_id );
662
663 update_post_meta( $post_id, self::META_SOURCE, $result );
664 update_post_meta( $post_id, self::META_FIELDSET, $fieldset );
665 }
666
667 /**
668 * Generate poster by AJAX request.
669 * Used in widget and sidebar.
670 *
671 * @param array $fieldset Fieldset data from widget.
672 * @param int $index Template index from editor.
673 * @param int $screen_id Post or term ID from admin screen.
674 * @param string $context Screen ID context field. Can be settings, post or term.
675 * @param boolean $auto Whether auto-generated poster.
676 */
677 private static function generate_poster( $fieldset, $index, $screen_id, $context, $auto = false ) {
678 $templates = Templates::get_templates();
679
680 if ( ! isset( $templates[ $index ] ) ) {
681 return new WP_Error( 'generate', esc_html__( 'Incorrect template ID.', 'sharing-image' ), 400 );
682 }
683
684 // Prepare template editor.
685 $editor = Generator::prepare_template( $templates[ $index ], $fieldset, $index, $screen_id, $context );
686
687 if ( ! Generator::check_required( $editor ) ) {
688 return new WP_Error( 'generate', esc_html__( 'Incorrect template settings.', 'sharing-image' ), 400 );
689 }
690
691 list( $path, $url ) = Generator::get_upload_file();
692
693 // Generate image and save it to given path.
694 $poster = Generator::create_poster( $editor, $path );
695
696 if ( is_wp_error( $poster ) ) {
697 return new WP_Error( 'generate', $poster->get_error_message(), 400 );
698 }
699
700 $attachment = self::save_attachment( $path, $screen_id, $context );
701
702 if ( is_wp_error( $attachment ) ) {
703 return new WP_Error( 'attachment', $attachment->get_error_message(), 400 );
704 }
705
706 $source = array(
707 'poster' => $url,
708 'width' => $editor['width'],
709 'height' => $editor['height'],
710 'mode' => 'manual',
711 );
712
713 if ( ! empty( $auto ) ) {
714 $source['mode'] = 'auto';
715 }
716
717 return $source;
718 }
719
720 /**
721 * Create script object to inject with widget.
722 *
723 * @param string $context Widget context. For example: metabox or taxonomy.
724 * @param int $screen_id Post or taxonomy screen ID.
725 *
726 * @return array Filtered widget script object.
727 */
728 private static function create_script_object( $context, $screen_id ) {
729 $object = array(
730 'nonce' => wp_create_nonce( self::WIDGET_NONCE ),
731 'context' => $context,
732 'screen' => $screen_id,
733
734 'name' => array(
735 'source' => self::META_SOURCE,
736 'fieldset' => self::META_FIELDSET,
737 ),
738 'links' => array(
739 'uploads' => esc_url( admin_url( 'upload.php' ) ),
740 ),
741 'templates' => Templates::get_templates(),
742 'autogenerate' => Config::get_autogenerate_index(),
743 );
744
745 /**
746 * Filter widget script object.
747 *
748 * @param array $object Array of widget script object.
749 */
750 return apply_filters( 'sharing_image_widget_object', $object );
751 }
752
753 /**
754 * Enqueue widget scripts.
755 *
756 * @param array $data Widget data object.
757 */
758 private static function enqueue_widget_scripts( $data ) {
759 $asset = require SHARING_IMAGE_DIR . 'assets/widget/index.asset.php';
760
761 wp_enqueue_media();
762
763 wp_enqueue_script(
764 'sharing-image-widget',
765 plugins_url( 'assets/widget/index.js', SHARING_IMAGE_FILE ),
766 $asset['dependencies'],
767 $asset['version'],
768 true
769 );
770
771 wp_enqueue_style(
772 'sharing-image-widget',
773 plugins_url( 'assets/widget/index.css', SHARING_IMAGE_FILE ),
774 $asset['dependencies'],
775 $asset['version'],
776 'all'
777 );
778
779 // Translations availible only for WP 5.0+.
780 wp_set_script_translations( 'sharing-image-widget', 'sharing-image' );
781
782 // Add widget script object.
783 wp_localize_script( 'sharing-image-widget', 'sharingImageWidget', $data );
784 }
785
786 /**
787 * Get an array of post types where the metabox is displayed.
788 *
789 * @return array Array of post types.
790 */
791 private static function get_metabox_post_types() {
792 $post_types = get_post_types(
793 array(
794 'public' => true,
795 )
796 );
797
798 unset( $post_types['attachment'] );
799
800 /**
801 * Filter widget post types.
802 *
803 * @param array $post_types Array of post types for which the metabox is displayed.
804 */
805 return apply_filters( 'sharing_image_metabox_post_types', array_values( $post_types ) );
806 }
807
808 /**
809 * Update template with post data for preset fields.
810 *
811 * @param string $index Template index.
812 * @param int $post_id Post id.
813 * @param array $fieldset Optional. Prepared fieldset to modify.
814 *
815 * @return array List of prepared fields.
816 */
817 private static function compose_fields( $index, $post_id, $fieldset = array() ) {
818 $templates = Templates::get_templates();
819
820 if ( ! isset( $templates[ $index ] ) ) {
821 return $fieldset;
822 }
823
824 $template = $templates[ $index ];
825
826 if ( ! isset( $template['layers'] ) ) {
827 return $fieldset;
828 }
829
830 $layers = $template['layers'];
831
832 foreach ( $layers as $key => $layer ) {
833 $field = null;
834
835 if ( empty( $layer['type'] ) ) {
836 continue;
837 }
838
839 if ( 'text' === $layer['type'] ) {
840 $field = self::compose_text_presets( $layer, $post_id );
841 }
842
843 if ( 'image' === $layer['type'] ) {
844 $field = self::compose_image_presets( $layer, $post_id );
845 }
846
847 if ( ! empty( $field ) ) {
848 $fieldset[ $key ] = $field;
849 }
850 }
851
852 return $fieldset;
853 }
854
855 /**
856 * Compose preset fields for text layers.
857 *
858 * @param string $layer List of layer options.
859 * @param int $post_id Post id.
860 *
861 * @return array|null List of prepared fieldset for text layer.
862 */
863 private static function compose_text_presets( $layer, $post_id ) {
864 if ( empty( $layer['preset'] ) || empty( $layer['dynamic'] ) ) {
865 return null;
866 }
867
868 $separator = ', ';
869
870 if ( ! empty( $layer['separator'] ) ) {
871 $separator = $layer['separator'];
872 }
873
874 if ( 'title' === $layer['preset'] ) {
875 return get_the_title( $post_id );
876 }
877
878 if ( 'excerpt' === $layer['preset'] ) {
879 $post = get_post( $post_id );
880
881 return $post->post_excerpt;
882 }
883
884 if ( 'categories' === $layer['preset'] ) {
885 $categories = get_the_category( $post_id );
886
887 if ( $categories ) {
888 return implode( $separator, wp_list_pluck( $categories, 'name' ) );
889 }
890 }
891
892 if ( 'tags' === $layer['preset'] ) {
893 $tags = get_the_tags( $post_id );
894
895 if ( $tags ) {
896 return implode( $separator, wp_list_pluck( $tags, 'name' ) );
897 }
898 }
899
900 return null;
901 }
902
903 /**
904 * Compose preset fields for image layers.
905 *
906 * @param string $layer List of layer options.
907 * @param int $post_id Post id.
908 *
909 * @return array|null List of prepared fieldset for image layer.
910 */
911 private static function compose_image_presets( $layer, $post_id ) {
912 if ( empty( $layer['preset'] ) || empty( $layer['dynamic'] ) ) {
913 return null;
914 }
915
916 if ( 'featured' === $layer['preset'] ) {
917 return absint( get_post_thumbnail_id( $post_id ) );
918 }
919
920 return null;
921 }
922
923 /**
924 * Get an array of taxonomeis where the widget is displayed.
925 *
926 * @return array Array of taxonomies.
927 */
928 private static function get_widget_taxonomies() {
929 $taxonomies = get_taxonomies(
930 array(
931 'public' => true,
932 'show_ui' => true,
933 )
934 );
935
936 /**
937 * Filter the taxonomies where we need to show the poster settings.
938 *
939 * @param array $taxonomies List of taxonomies to show settings.
940 */
941 return apply_filters( 'sharing_image_widget_taxonomies', array_values( $taxonomies ) );
942 }
943
944 /**
945 * Save attachment to media library.
946 *
947 * @param string $path Path to poster image.
948 * @param int $screen_id Post or taxonomy screen ID.
949 * @param string $context Widget context. For example: metabox or autogenerate.
950 *
951 * @return int|null Attachment ID or null;
952 */
953 private static function save_attachment( $path, $screen_id, $context ) {
954 $config = Config::get_config();
955
956 if ( ! isset( $config['attachment'] ) ) {
957 return null;
958 }
959
960 $id = null;
961
962 try {
963 $name = implode( '-', array( 'sharing-image', $context, $screen_id ) );
964
965 // Try to delete current attachment if exists.
966 self::delete_attachment( $name );
967
968 $uploads = wp_upload_dir();
969
970 // Get poster filetype.
971 $filetype = wp_check_filetype( basename( $path ), null );
972
973 $attachment = array(
974 'post' => array(
975 'guid' => $uploads['url'] . '/' . basename( $path ),
976 'post_mime_type' => $filetype['type'],
977 'post_name' => $name,
978 'post_title' => $name,
979 'post_content' => '',
980 'post_status' => 'inherit',
981 ),
982 'parent_id' => 0,
983 );
984
985 if ( 'post' === $context ) {
986 $attachment['parent_id'] = $screen_id;
987 }
988
989 /**
990 * Filter attachment data before insertion.
991 *
992 * @param string $attachment Attachment data.
993 * @param string $path Path to poster image.
994 * @param string $screen_id Post or taxonomy screen ID.
995 * @param string $context Widget context. For example: metabox or autogenerate.
996 */
997 $attachment = apply_filters( 'sharing_image_attachment_data', $attachment, $path, $screen_id, $context );
998
999 if ( empty( $attachment['post'] ) ) {
1000 return null;
1001 }
1002
1003 if ( ! function_exists( 'wp_crop_image' ) ) {
1004 include ABSPATH . 'wp-admin/includes/image.php';
1005 }
1006
1007 $id = wp_insert_attachment( $attachment['post'], $path, $attachment['parent_id'] );
1008
1009 // Get attachment metadata.
1010 $metadata = wp_generate_attachment_metadata( $id, $path );
1011
1012 wp_update_attachment_metadata( $id, $metadata );
1013 } catch ( Exception $e ) {
1014 return new WP_Error( 'attachment', $e->getMessage() );
1015 }
1016
1017 return $id;
1018 }
1019
1020 /**
1021 * Delete attachment by name if exists.
1022 *
1023 * @param string $name Name of attachment.
1024 *
1025 * @return bool Whether attachment deleted
1026 */
1027 private static function delete_attachment( $name ) {
1028 $posts = get_posts(
1029 array(
1030 'post_type' => 'attachment',
1031 'post_name__in' => array( $name ),
1032 'update_post_term_cache' => false,
1033 'update_post_meta_cache' => false,
1034 'posts_per_page' => 1,
1035 )
1036 );
1037
1038 if ( empty( $posts[0]->ID ) ) {
1039 return false;
1040 }
1041
1042 return wp_delete_attachment( $posts[0]->ID, true );
1043 }
1044 }
1045