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

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