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

1,027 lines 25.2 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 $asset = require SHARING_IMAGE_DIR . 'assets/sidebar/index.asset.php';
276
277 wp_enqueue_script(
278 'sharing-image-sidebar',
279 plugins_url( 'assets/sidebar/index.js', SHARING_IMAGE_FILE ),
280 $asset['dependencies'],
281 $asset['version'],
282 true
283 );
284
285 wp_enqueue_style(
286 'sharing-image-sidebar',
287 plugins_url( 'assets/sidebar/index.css', SHARING_IMAGE_FILE ),
288 null,
289 $asset['version'],
290 'all'
291 );
292
293 // Translations availible only for WP 5.0+.
294 wp_set_script_translations( 'sharing-image-sidebar', 'sharing-image' );
295
296 $data = array(
297 'meta' => array(
298 'source' => self::META_SOURCE,
299 'fieldset' => self::META_FIELDSET,
300 ),
301 'templates' => Templates::get_templates(),
302 );
303
304 // Add widget script object.
305 wp_localize_script( 'sharing-image-sidebar', 'sharingImageSidebar', $data );
306 }
307
308 /**
309 * Save metabox data.
310 *
311 * @param int $post_id Post ID.
312 */
313 public static function save_post_widget( $post_id ) {
314 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
315 return;
316 }
317
318 if ( wp_is_post_revision( $post_id ) ) {
319 return;
320 }
321
322 if ( ! current_user_can( 'edit_post', $post_id ) ) {
323 return;
324 }
325
326 if ( ! isset( $_POST['sharing_image_nonce'] ) ) {
327 return;
328 }
329
330 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
331 if ( ! wp_verify_nonce( $_POST['sharing_image_nonce'], self::WIDGET_NONCE ) ) {
332 return;
333 }
334
335 if ( isset( $_POST[ self::META_SOURCE ] ) ) {
336 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
337 $meta = self::sanitize_source( wp_unslash( $_POST[ self::META_SOURCE ] ) );
338
339 update_post_meta( $post_id, self::META_SOURCE, $meta );
340 }
341
342 if ( isset( $_POST[ self::META_FIELDSET ] ) ) {
343 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
344 $meta = self::sanitize_fieldset( wp_unslash( $_POST[ self::META_FIELDSET ] ) );
345
346 update_post_meta( $post_id, self::META_FIELDSET, $meta );
347 }
348 }
349
350 /**
351 * Save taxonomy fields.
352 *
353 * @param int $term_id Term ID.
354 */
355 public static function save_taxonomy_widget( $term_id ) {
356 if ( ! current_user_can( 'edit_term', $term_id ) ) {
357 return;
358 }
359
360 if ( ! isset( $_POST['sharing_image_nonce'] ) ) {
361 return;
362 }
363
364 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
365 if ( ! wp_verify_nonce( $_POST['sharing_image_nonce'], self::WIDGET_NONCE ) ) {
366 return;
367 }
368
369 if ( isset( $_POST[ self::META_SOURCE ] ) ) {
370 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
371 $meta = self::sanitize_source( wp_unslash( $_POST[ self::META_SOURCE ] ) );
372
373 update_term_meta( $term_id, self::META_SOURCE, $meta );
374 }
375
376 if ( isset( $_POST[ self::META_FIELDSET ] ) ) {
377 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
378 $meta = self::sanitize_fieldset( wp_unslash( $_POST[ self::META_FIELDSET ] ) );
379
380 update_term_meta( $term_id, self::META_FIELDSET, $meta );
381 }
382 }
383
384 /**
385 * Display widget.
386 */
387 public static function display_widget() {
388 include_once SHARING_IMAGE_DIR . 'templates/widget.php';
389
390 /**
391 * Fires on widget template including.
392 */
393 do_action( 'sharing_image_widget' );
394 }
395
396 /**
397 * Create new endpoint for generate button in Gutenberg sidebar.
398 */
399 public static function add_generate_endpoint() {
400 register_rest_route(
401 'sharing-image/v1',
402 '/poster/(?P<id>\d+)',
403 array(
404 'methods' => 'POST',
405 'callback' => array( __CLASS__, 'handle_rest_generator' ),
406 'permission_callback' => function () {
407 return current_user_can( 'edit_posts' );
408 },
409 )
410 );
411 }
412
413 /**
414 * Sanitize widget source meta.
415 *
416 * @param array $source Source meta data.
417 *
418 * @return array Sanitized source meta fields.
419 */
420 public static function sanitize_source( $source ) {
421 $sanitized = array();
422
423 if ( isset( $source['poster'] ) ) {
424 $sanitized['poster'] = sanitize_text_field( $source['poster'] );
425 }
426
427 if ( ! empty( $source['width'] ) ) {
428 $sanitized['width'] = absint( $source['width'] );
429 }
430
431 if ( ! empty( $source['height'] ) ) {
432 $sanitized['height'] = absint( $source['height'] );
433 }
434
435 if ( ! empty( $source['template'] ) ) {
436 $sanitized['template'] = sanitize_key( $source['template'] );
437 }
438
439 if ( ! empty( $source['mode'] ) ) {
440 $sanitized['mode'] = sanitize_text_field( $source['mode'] );
441 }
442
443 /**
444 * Filters fieldset meta.
445 *
446 * @since 3.0
447 *
448 * @param array $sanitized List of sanitized fields.
449 * @param array $source List of fields before sanitization.
450 */
451 return apply_filters( 'sharing_image_sanitize_source', $sanitized, $source );
452 }
453
454 /**
455 * Sanitize fieldset.
456 *
457 * @param array $fieldset Fieldset list.
458 *
459 * @return array Sanitized fieldset data.
460 */
461 public static function sanitize_fieldset( $fieldset ) {
462 $sanitized = array();
463
464 // Get list of templates.
465 $templates = Templates::get_templates();
466
467 foreach ( $templates as $template ) {
468 if ( empty( $template['layers'] ) ) {
469 continue;
470 }
471
472 foreach ( $template['layers'] as $key => $layer ) {
473 if ( ! array_key_exists( $key, $fieldset ) ) {
474 continue;
475 }
476
477 if ( 'text' === $layer['type'] ) {
478 $sanitized[ $key ] = sanitize_textarea_field( $fieldset[ $key ] );
479 }
480
481 if ( 'image' === $layer['type'] ) {
482 $sanitized[ $key ] = absint( $fieldset[ $key ] );
483 }
484 }
485 }
486
487 /**
488 * Filters widget fieldset meta.
489 *
490 * @since 3.0
491 *
492 * @param array $sanitized Sanitized fieldset list.
493 * @param array $fieldset Fieldset list before sanitization.
494 */
495 return apply_filters( 'sharing_image_sanitize_fieldset', $sanitized, $fieldset );
496 }
497
498 /**
499 * Handle generate button in Gutenberg sidebar.
500 *
501 * @param WP_REST_Request $request Request params.
502 */
503 public static function handle_rest_generator( $request ) {
504 $post_id = $request->get_param( 'id' );
505
506 if ( empty( $post_id ) ) {
507 wp_send_json_error( __( 'Post data is empty.', 'sharing-image' ), 400 );
508 }
509
510 $params = $request->get_json_params();
511
512 if ( ! isset( $params['template'] ) ) {
513 wp_send_json_error( __( 'Incorrect request parameters.', 'sharing-image' ), 400 );
514 }
515
516 $index = sanitize_key( $params['template'] );
517
518 if ( empty( $params['fieldset'] ) ) {
519 $params['fieldset'] = array();
520 }
521
522 $fieldset = self::sanitize_fieldset( $params['fieldset'] );
523
524 // Invoke poster generation.
525 $result = self::generate_poster( $fieldset, $index, $post_id, 'post' );
526
527 if ( is_wp_error( $result ) ) {
528 wp_send_json_error( $result->get_error_messages(), $result->get_error_data() );
529 }
530
531 wp_send_json_success( $result );
532 }
533
534 /**
535 * Handle generate button on Classic Editor and taxonomy widget.
536 */
537 public static function handle_ajax_generator() {
538 $check = check_ajax_referer( self::WIDGET_NONCE, 'sharing_image_nonce', false );
539
540 if ( false === $check ) {
541 wp_send_json_error( __( 'Invalid security token. Please reload the page and try again.', 'sharing-image' ), 403 );
542 }
543
544 if ( empty( $_POST[ self::META_SOURCE ]['template'] ) ) {
545 wp_send_json_error( __( 'Template ID cannot be empty.', 'sharing-image' ), 400 );
546 }
547
548 $index = sanitize_key( $_POST[ self::META_SOURCE ]['template'] );
549
550 $screen_id = 0;
551
552 if ( ! empty( $_POST['sharing_image_screen'] ) ) {
553 $screen_id = absint( wp_unslash( $_POST['sharing_image_screen'] ) );
554 }
555
556 $context = 'post';
557
558 if ( ! empty( $_POST['sharing_image_context'] ) ) {
559 $context = sanitize_key( wp_unslash( $_POST['sharing_image_context'] ) );
560 }
561
562 $fieldset = array();
563
564 if ( ! empty( $_POST[ self::META_FIELDSET ] ) ) {
565 // phpcs:ignore WordPress.Security.ValidatedSanitizedInput
566 $fieldset = self::sanitize_fieldset( wp_unslash( $_POST[ self::META_FIELDSET ] ) );
567 }
568
569 // Invoke poster generation.
570 $result = self::generate_poster( $fieldset, $index, $screen_id, $context );
571
572 if ( is_wp_error( $result ) ) {
573 wp_send_json_error( $result->get_error_messages(), $result->get_error_data() );
574 }
575
576 wp_send_json_success( $result );
577 }
578
579 /**
580 * Try to autogenerate poster on post insert or update.
581 *
582 * @param int $post_id Updated post_id.
583 * @param object $post Updated post object.
584 * @param bool $update Whether this is an existing post being updated.
585 */
586 public static function autogenerate_poster( $post_id, $post, $update ) {
587 if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) {
588 return;
589 }
590
591 if ( ! $update ) {
592 return;
593 }
594
595 $meta = get_post_meta( $post_id, self::META_SOURCE, true );
596
597 if ( ! empty( $meta['mode'] ) && 'manual' === $meta['mode'] ) {
598 return;
599 }
600
601 $config = Config::get_config();
602
603 if ( empty( $config['autogenerate'] ) ) {
604 return;
605 }
606
607 $index = sanitize_key( $config['autogenerate'] );
608
609 // Compose fieldset by template index.
610 $fieldset = self::compose_fields( $index, $post_id );
611
612 /**
613 * Filters fieldset before autogeneration.
614 *
615 * @since 3.0
616 *
617 * @param array $fieldset Prepared fieldset data.
618 * @param string $index Template index.
619 * @param int $post_id Post ID.
620 */
621 $fieldset = apply_filters( 'sharing_image_autogenerated_fieldset', $fieldset, $index, $post_id );
622
623 if ( empty( $fieldset ) ) {
624 $fieldset = array();
625 }
626
627 // Invoke poster generation.
628 $result = self::generate_poster( $fieldset, $index, $post_id, 'post', true );
629
630 $result['template'] = $index;
631
632 /**
633 * Filters autogenerated poster data.
634 *
635 * @since 2.0.11
636 *
637 * @param array|WP_Erorr $result Poster image, width and height data or WP_Error if undefined.
638 * @param int $post_id Post ID.
639 */
640 $result = apply_filters( 'sharing_image_autogenerated_poster', $result, $post_id );
641
642 if ( is_wp_error( $result ) ) {
643 return;
644 }
645
646 update_post_meta( $post_id, self::META_SOURCE, $result );
647 update_post_meta( $post_id, self::META_FIELDSET, $fieldset );
648 }
649
650 /**
651 * Generate poster by AJAX request.
652 * Used in widget and sidebar.
653 *
654 * @param array $fieldset Fieldset data from widget.
655 * @param int $index Template index from editor.
656 * @param int $screen_id Post or term ID from admin screen.
657 * @param string $context Screen ID context field. Can be settings, post or term.
658 * @param boolean $auto Whether auto-generated poster.
659 */
660 private static function generate_poster( $fieldset, $index, $screen_id, $context, $auto = false ) {
661 $templates = Templates::get_templates();
662
663 if ( ! isset( $templates[ $index ] ) ) {
664 return new WP_Error( 'generate', esc_html__( 'Incorrect template ID.', 'sharing-image' ), 400 );
665 }
666
667 // Prepare template editor.
668 $editor = Generator::prepare_template( $templates[ $index ], $fieldset, $index, $screen_id, $context );
669
670 if ( ! Generator::check_required( $editor ) ) {
671 return new WP_Error( 'generate', esc_html__( 'Incorrect template settings.', 'sharing-image' ), 400 );
672 }
673
674 list( $path, $url ) = Generator::get_upload_file();
675
676 // Generate image and save it to given path.
677 $poster = Generator::create_poster( $editor, $path );
678
679 if ( is_wp_error( $poster ) ) {
680 return new WP_Error( 'generate', $poster->get_error_message(), 400 );
681 }
682
683 $attachment = self::save_attachment( $path, $screen_id, $context );
684
685 if ( is_wp_error( $attachment ) ) {
686 return new WP_Error( 'attachment', $attachment->get_error_message(), 400 );
687 }
688
689 $source = array(
690 'poster' => $url,
691 'width' => $editor['width'],
692 'height' => $editor['height'],
693 'mode' => 'manual',
694 );
695
696 if ( ! empty( $auto ) ) {
697 $source['mode'] = 'auto';
698 }
699
700 return $source;
701 }
702
703 /**
704 * Create script object to inject with widget.
705 *
706 * @param string $context Widget context. For example: metabox or taxonomy.
707 * @param int $screen_id Post or taxonomy screen ID.
708 *
709 * @return array Filtered widget script object.
710 */
711 private static function create_script_object( $context, $screen_id ) {
712 $object = array(
713 'nonce' => wp_create_nonce( self::WIDGET_NONCE ),
714 'context' => $context,
715 'screen' => $screen_id,
716
717 'name' => array(
718 'source' => self::META_SOURCE,
719 'fieldset' => self::META_FIELDSET,
720 ),
721 'links' => array(
722 'uploads' => esc_url( admin_url( 'upload.php' ) ),
723 ),
724 'templates' => Templates::get_templates(),
725 );
726
727 /**
728 * Filter widget script object.
729 *
730 * @param array $object Array of widget script object.
731 */
732 return apply_filters( 'sharing_image_widget_object', $object );
733 }
734
735 /**
736 * Enqueue widget scripts.
737 *
738 * @param array $data Widget data object.
739 */
740 private static function enqueue_widget_scripts( $data ) {
741 $asset = require SHARING_IMAGE_DIR . 'assets/widget/index.asset.php';
742
743 wp_enqueue_media();
744
745 wp_enqueue_script(
746 'sharing-image-widget',
747 plugins_url( 'assets/widget/index.js', SHARING_IMAGE_FILE ),
748 $asset['dependencies'],
749 $asset['version'],
750 true
751 );
752
753 wp_enqueue_style(
754 'sharing-image-widget',
755 plugins_url( 'assets/widget/index.css', SHARING_IMAGE_FILE ),
756 $asset['dependencies'],
757 $asset['version'],
758 'all'
759 );
760
761 // Translations availible only for WP 5.0+.
762 wp_set_script_translations( 'sharing-image-widget', 'sharing-image' );
763
764 // Add widget script object.
765 wp_localize_script( 'sharing-image-widget', 'sharingImageWidget', $data );
766 }
767
768 /**
769 * Get an array of post types where the metabox is displayed.
770 *
771 * @return array Array of post types.
772 */
773 private static function get_metabox_post_types() {
774 $post_types = get_post_types(
775 array(
776 'public' => true,
777 )
778 );
779
780 unset( $post_types['attachment'] );
781
782 /**
783 * Filter widget post types.
784 *
785 * @param array $post_types Array of post types for which the metabox is displayed.
786 */
787 return apply_filters( 'sharing_image_metabox_post_types', array_values( $post_types ) );
788 }
789
790 /**
791 * Update template with post data for preset fields.
792 *
793 * @param string $index Template index.
794 * @param int $post_id Post id.
795 * @param array $fieldset Optional. Prepared fieldset to modify.
796 *
797 * @return array List of prepared fields.
798 */
799 private static function compose_fields( $index, $post_id, $fieldset = array() ) {
800 $templates = Templates::get_templates();
801
802 if ( ! isset( $templates[ $index ] ) ) {
803 return $fieldset;
804 }
805
806 $template = $templates[ $index ];
807
808 if ( ! isset( $template['layers'] ) ) {
809 return $fieldset;
810 }
811
812 $layers = $template['layers'];
813
814 foreach ( $layers as $key => $layer ) {
815 $field = null;
816
817 if ( empty( $layer['type'] ) ) {
818 continue;
819 }
820
821 if ( 'text' === $layer['type'] ) {
822 $field = self::compose_text_presets( $layer, $post_id );
823 }
824
825 if ( 'image' === $layer['type'] ) {
826 $field = self::compose_image_presets( $layer, $post_id );
827 }
828
829 if ( ! empty( $field ) ) {
830 $fieldset[ $key ] = $field;
831 }
832 }
833
834 return $fieldset;
835 }
836
837 /**
838 * Compose preset fields for text layers.
839 *
840 * @param string $layer List of layer options.
841 * @param int $post_id Post id.
842 *
843 * @return array|null List of prepared fieldset for text layer.
844 */
845 private static function compose_text_presets( $layer, $post_id ) {
846 if ( empty( $layer['preset'] ) || empty( $layer['dynamic'] ) ) {
847 return null;
848 }
849
850 $separator = ', ';
851
852 if ( ! empty( $layer['separator'] ) ) {
853 $separator = $layer['separator'];
854 }
855
856 if ( 'title' === $layer['preset'] ) {
857 return get_the_title( $post_id );
858 }
859
860 if ( 'excerpt' === $layer['preset'] ) {
861 $post = get_post( $post_id );
862
863 return $post->post_excerpt;
864 }
865
866 if ( 'categories' === $layer['preset'] ) {
867 $categories = get_the_category( $post_id );
868
869 if ( $categories ) {
870 return implode( $separator, wp_list_pluck( $categories, 'name' ) );
871 }
872 }
873
874 if ( 'tags' === $layer['preset'] ) {
875 $tags = get_the_tags( $post_id );
876
877 if ( $tags ) {
878 return implode( $separator, wp_list_pluck( $tags, 'name' ) );
879 }
880 }
881
882 return null;
883 }
884
885 /**
886 * Compose preset fields for image layers.
887 *
888 * @param string $layer List of layer options.
889 * @param int $post_id Post id.
890 *
891 * @return array|null List of prepared fieldset for image layer.
892 */
893 private static function compose_image_presets( $layer, $post_id ) {
894 if ( empty( $layer['preset'] ) || empty( $layer['dynamic'] ) ) {
895 return null;
896 }
897
898 if ( 'featured' === $layer['preset'] ) {
899 return absint( get_post_thumbnail_id( $post_id ) );
900 }
901
902 return null;
903 }
904
905 /**
906 * Get an array of taxonomeis where the widget is displayed.
907 *
908 * @return array Array of taxonomies.
909 */
910 private static function get_widget_taxonomies() {
911 $taxonomies = get_taxonomies(
912 array(
913 'public' => true,
914 'show_ui' => true,
915 )
916 );
917
918 /**
919 * Filter the taxonomies where we need to show the poster settings.
920 *
921 * @param array $taxonomies List of taxonomies to show settings.
922 */
923 return apply_filters( 'sharing_image_widget_taxonomies', array_values( $taxonomies ) );
924 }
925
926 /**
927 * Save attachment to media library.
928 *
929 * @param string $path Path to poster image.
930 * @param int $screen_id Post or taxonomy screen ID.
931 * @param string $context Widget context. For example: metabox or autogenerate.
932 *
933 * @return int|null Attachment ID or null;
934 */
935 private static function save_attachment( $path, $screen_id, $context ) {
936 $config = Config::get_config();
937
938 if ( ! isset( $config['attachment'] ) ) {
939 return null;
940 }
941
942 $id = null;
943
944 try {
945 $name = implode( '-', array( 'sharing-image', $context, $screen_id ) );
946
947 // Try to delete current attachment if exists.
948 self::delete_attachment( $name );
949
950 $uploads = wp_upload_dir();
951
952 // Get poster filetype.
953 $filetype = wp_check_filetype( basename( $path ), null );
954
955 $attachment = array(
956 'post' => array(
957 'guid' => $uploads['url'] . '/' . basename( $path ),
958 'post_mime_type' => $filetype['type'],
959 'post_name' => $name,
960 'post_title' => $name,
961 'post_content' => '',
962 'post_status' => 'inherit',
963 ),
964 'parent_id' => 0,
965 );
966
967 if ( 'post' === $context ) {
968 $attachment['parent_id'] = $screen_id;
969 }
970
971 /**
972 * Filter attachment data before insertion.
973 *
974 * @param string $attachment Attachment data.
975 * @param string $path Path to poster image.
976 * @param string $screen_id Post or taxonomy screen ID.
977 * @param string $context Widget context. For example: metabox or autogenerate.
978 */
979 $attachment = apply_filters( 'sharing_image_attachment_data', $attachment, $path, $screen_id, $context );
980
981 if ( empty( $attachment['post'] ) ) {
982 return null;
983 }
984
985 if ( ! function_exists( 'wp_crop_image' ) ) {
986 include ABSPATH . 'wp-admin/includes/image.php';
987 }
988
989 $id = wp_insert_attachment( $attachment['post'], $path, $attachment['parent_id'] );
990
991 // Get attachment metadata.
992 $metadata = wp_generate_attachment_metadata( $id, $path );
993
994 wp_update_attachment_metadata( $id, $metadata );
995 } catch ( Exception $e ) {
996 return new WP_Error( 'attachment', $e->getMessage() );
997 }
998
999 return $id;
1000 }
1001
1002 /**
1003 * Delete attachment by name if exists.
1004 *
1005 * @param string $name Name of attachment.
1006 *
1007 * @return bool Whether attachment deleted
1008 */
1009 private static function delete_attachment( $name ) {
1010 $posts = get_posts(
1011 array(
1012 'post_type' => 'attachment',
1013 'post_name__in' => array( $name ),
1014 'update_post_term_cache' => false,
1015 'update_post_meta_cache' => false,
1016 'posts_per_page' => 1,
1017 )
1018 );
1019
1020 if ( empty( $posts[0]->ID ) ) {
1021 return false;
1022 }
1023
1024 return wp_delete_attachment( $posts[0]->ID, true );
1025 }
1026 }
1027