PluginProbe
Open Graph / 2.0.1
Open Graph v2.0.1
trunk 1.1 1.10.0 1.11.0 1.11.1 1.11.2 1.11.3 1.12.0 1.12.1 1.12.2 1.2 1.3 1.4 1.5 1.5.1 1.6 1.7.0 1.8.0 1.8.1 1.8.2 1.8.3 1.9.0 2.0.0 2.0.1 2.0.2
opengraph / opengraph.php

opengraph.php in Open Graph 2.0.1, at opengraph.php

985 lines 25.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Plugin Name: Open Graph
4 * Plugin URI: https://wordpress.org/plugins/opengraph
5 * Description: Adds Open Graph metadata to your pages
6 * Author: Will Norris & Matthias Pfefferle
7 * Author URI: https://github.com/pfefferle/wordpress-opengraph
8 * Version: 2.0.1
9 * License: Apache License, Version 2.0
10 * License URI: http://www.apache.org/licenses/LICENSE-2.0.html
11 * Text Domain: opengraph
12 *
13 * @package opengraph
14 */
15
16 // If you have the opengraph plugin running alongside jetpack, we assume you'd
17 // rather use our opengraph support, so disable jetpack's opengraph functionality.
18 add_filter( 'jetpack_enable_opengraph', '__return_false' );
19 add_filter( 'jetpack_enable_open_graph', '__return_false' );
20
21
22 // Disable strict mode by default.
23 defined( 'OPENGRAPH_STRICT_MODE' ) || define( 'OPENGRAPH_STRICT_MODE', false );
24
25
26 /**
27 * Add Open Graph XML prefix to <html> element.
28 *
29 * @uses apply_filters calls 'opengraph_prefixes' filter on RDFa prefix array
30 *
31 * @param string $output The current list of prefixes.
32 *
33 * @return string The updated list of prefixes.
34 */
35 function opengraph_add_prefix( $output ) {
36 $prefixes = array(
37 'og' => 'http://ogp.me/ns#',
38 );
39 $prefixes = apply_filters( 'opengraph_prefixes', $prefixes );
40
41 $prefix_str = '';
42 foreach ( $prefixes as $k => $v ) {
43 $prefix_str .= $k . ': ' . $v . ' ';
44 }
45 $prefix_str = trim( $prefix_str );
46
47 if ( preg_match( '/(prefix\s*=\s*[\"|\'])/i', $output ) ) {
48 $output = preg_replace( '/(prefix\s*=\s*[\"|\'])/i', '${1}' . $prefix_str, $output );
49 } else {
50 $output .= ' prefix="' . esc_attr( $prefix_str ) . '"';
51 }
52
53 return $output;
54 }
55 add_filter( 'language_attributes', 'opengraph_add_prefix' );
56
57
58 /**
59 * Add additional prefix namespaces that are supported by the opengraph plugin.
60 *
61 * @param array $prefixes The current list of prefixes.
62 *
63 * @return array The updated list of prefixes.
64 */
65 function opengraph_additional_prefixes( $prefixes ) {
66 if ( is_author() ) {
67 $prefixes['profile'] = 'http://ogp.me/ns/profile#';
68 }
69 if ( is_singular() ) {
70 $prefixes['article'] = 'http://ogp.me/ns/article#';
71 }
72
73 return $prefixes;
74 }
75
76
77 /**
78 * Get the Open Graph metadata for the current page.
79 *
80 * @uses apply_filters() Calls 'opengraph_{$name}' for each property name
81 * @uses apply_filters() Calls 'twitter_{$name}' for each property name
82 * @uses apply_filters() Calls 'opengraph_metadata' before returning metadata array
83 */
84 function opengraph_metadata() {
85 $metadata = array();
86
87 // Default properties defined at http://ogp.me/.
88 $properties = array(
89 // Required properties.
90 'title' => '',
91 'type' => '',
92 'image' => array(),
93 'url' => '',
94
95 // Optional properties.
96 'audio' => array(),
97 'description' => '',
98 'determiner' => '',
99 'locale' => '',
100 'site_name' => '',
101 'video' => array(),
102 );
103
104 foreach ( $properties as $property => $default ) {
105 $filter = 'opengraph_' . $property;
106
107 /**
108 * Filter the Open Graph metadata.
109 *
110 * @param array $default The default value.
111 */
112 $metadata[ "og:$property" ] = apply_filters( $filter, $default );
113 }
114
115 $twitter_properties = array(
116 'card' => '',
117 'creator' => '',
118 );
119
120 foreach ( $twitter_properties as $property => $default ) {
121 $filter = 'twitter_' . $property;
122
123 /**
124 * Filter the Twitter Card metadata.
125 *
126 * @param array $default The default value.
127 */
128 $metadata[ "twitter:$property" ] = apply_filters( $filter, $default );
129 }
130
131 $fediverse_properties = array(
132 'creator' => array(),
133 );
134
135 foreach ( $fediverse_properties as $property => $default ) {
136 $filter = 'fediverse_' . $property;
137
138 /**
139 * Filter the Fediverse metadata.
140 *
141 * @param array $default The default value.
142 */
143 $metadata[ "fediverse:$property" ] = apply_filters( $filter, $default );
144 }
145
146 /**
147 * Filter the Open Graph metadata.
148 *
149 * @param array $metadata The metadata array.
150 */
151 return apply_filters( 'opengraph_metadata', $metadata );
152 }
153
154
155 /**
156 * Register filters for default Open Graph metadata.
157 */
158 function opengraph_default_metadata() {
159 // Core metadata attributes.
160 add_filter( 'opengraph_title', 'opengraph_default_title', 5 );
161 add_filter( 'opengraph_type', 'opengraph_default_type', 5 );
162 add_filter( 'opengraph_url', 'opengraph_default_url', 5 );
163
164 // Image metadata attributes with fallbacks.
165 add_filter( 'opengraph_image', 'opengraph_default_image', 5 );
166 add_filter( 'opengraph_image', 'opengraph_block_image', 15 );
167 add_filter( 'opengraph_image', 'opengraph_parsed_image', 25 );
168 add_filter( 'opengraph_image', 'opengraph_attached_image', 25 );
169 add_filter( 'opengraph_image', 'opengraph_fallback_image', 35 );
170 add_filter( 'opengraph_image', 'opengraph_ensure_max_image', 999 );
171
172 add_filter( 'opengraph_description', 'opengraph_default_description', 5 );
173 add_filter( 'opengraph_locale', 'opengraph_default_locale', 5 );
174 add_filter( 'opengraph_site_name', 'opengraph_default_sitename', 5 );
175 add_filter( 'opengraph_audio', 'opengraph_default_audio', 5 );
176 add_filter( 'opengraph_video', 'opengraph_default_video', 5 );
177
178 // Additional prefixes.
179 add_filter( 'opengraph_prefixes', 'opengraph_additional_prefixes' );
180
181 // Additional profile metadata.
182 add_filter( 'opengraph_metadata', 'opengraph_profile_metadata' );
183
184 // Additional article metadata.
185 add_filter( 'opengraph_metadata', 'opengraph_article_metadata' );
186
187 // twitter card metadata.
188 add_filter( 'twitter_card', 'twitter_default_card', 5 );
189 add_filter( 'twitter_creator', 'twitter_default_creator', 5 );
190
191 // fediverse creator metadata.
192 add_filter( 'fediverse_creator', 'fediverse_default_creator', 5 );
193 }
194 add_action( 'wp', 'opengraph_default_metadata' );
195
196
197 /**
198 * Default title property, using the page title.
199 *
200 * @param string $title The current title.
201 *
202 * @return string The title.
203 */
204 function opengraph_default_title( $title ) {
205 if ( $title ) {
206 return $title;
207 }
208
209 // Set default title, because twitter is requiring one.
210 $title = __( 'Untitled', 'opengraph' );
211
212 if ( is_home() || is_front_page() ) {
213 $title = get_bloginfo( 'name' );
214 } elseif ( is_singular() ) {
215 $title = get_the_title( get_queried_object_id() );
216 // Fall back to description.
217 if ( empty( $title ) ) {
218 $title = opengraph_default_description( null, 5 );
219 }
220 } elseif ( is_author() ) {
221 $author = get_queried_object();
222 $title = $author->display_name;
223 } elseif ( is_category() && single_cat_title( '', false ) ) {
224 $title = single_cat_title( '', false );
225 } elseif ( is_tag() && single_tag_title( '', false ) ) {
226 $title = single_tag_title( '', false );
227 } elseif ( is_archive() && get_post_format() ) {
228 $title = get_post_format_string( get_post_format() );
229 } elseif (
230 is_archive() &&
231 function_exists( 'get_the_archive_title' ) &&
232 get_the_archive_title()
233 ) { // New in version 4.1 to get all other archive titles.
234 $title = get_the_archive_title();
235 }
236
237 return wp_strip_all_tags( $title );
238 }
239
240
241 /**
242 * Default type property.
243 *
244 * @param string $type The current type.
245 *
246 * @return string The type.
247 */
248 function opengraph_default_type( $type = '' ) {
249 if ( empty( $type ) ) {
250 if ( is_singular( array( 'post', 'page' ) ) ) {
251 $type = 'article';
252 } elseif ( is_author() ) {
253 $type = 'profile';
254 } else {
255 $type = 'website';
256 }
257 }
258
259 return $type;
260 }
261
262
263 /**
264 * Default image property, using the post-thumbnail and any attached images.
265 *
266 * @param array $image The current list of images.
267 *
268 * @return array The list of images.
269 */
270 function opengraph_default_image( $image = array() ) {
271 // Show avatar on profile pages.
272 if ( is_author() ) {
273 return array( get_avatar_url( get_the_author_meta( 'ID' ), array( 'size' => 512 ) ) );
274 }
275
276 if ( count( $image ) >= opengraph_max_images() ) {
277 return $image;
278 }
279
280 if ( is_attachment() && wp_attachment_is_image() ) {
281 $id = get_queried_object_id();
282 $image[] = current( wp_get_attachment_image_src( $id, 'large' ) ?: array() ); // phpcs:ignore
283 } elseif ( is_singular() && ! is_attachment() ) {
284 $id = get_queried_object_id();
285
286 // List post thumbnail first if this post has one.
287 if ( function_exists( 'has_post_thumbnail' ) && has_post_thumbnail( $id ) ) {
288 $thumbnail_id = get_post_thumbnail_id( $id );
289 $image[] = current( wp_get_attachment_image_src( $thumbnail_id, 'large' ) ?: array() ); // phpcs:ignore
290 }
291 }
292
293 return array_unique( $image );
294 }
295
296
297 /**
298 * Block image property, using the first image in the post content.
299 *
300 * @param array $image The current list of images.
301 *
302 * @return array The list of images.
303 */
304 function opengraph_block_image( $image = array() ) {
305 if (
306 ! opengraph_site_supports_blocks() ||
307 count( $image ) >= opengraph_max_images() ||
308 ! is_singular() ||
309 is_attachment()
310 ) {
311 return $image;
312 }
313
314 // Get the first image in the post content.
315 $blocks = parse_blocks( get_the_content( null, false ) );
316 foreach ( $blocks as $block ) {
317 if ( count( $image ) >= opengraph_max_images() ) {
318 break;
319 }
320
321 if (
322 'core/image' === $block['blockName'] ||
323 'core/cover' === $block['blockName']
324 ) {
325 if ( ! isset( $block['attrs']['id'] ) ) {
326 continue;
327 }
328
329 $id = $block['attrs']['id'];
330 $image[] = current( wp_get_attachment_image_src( $id, 'large' ) ?: array() ); // phpcs:ignore
331 }
332 }
333
334 return array_unique( $image );
335 }
336
337
338 /**
339 * Parse images in the HTML content.
340 *
341 * @param array $image The current list of images.
342 *
343 * @return array The list of images.
344 */
345 function opengraph_parsed_image( $image = array() ) {
346 // If someone calls that function directly, bail.
347 if (
348 ! \class_exists( 'WP_HTML_Tag_Processor' ) ||
349 ! opengraph_site_supports_blocks() ||
350 count( $image ) >= opengraph_max_images() ||
351 ! is_singular() ||
352 is_attachment()
353 ) {
354 return $image;
355 }
356
357 $post_id = get_queried_object_id();
358 $base = wp_upload_dir()['baseurl'];
359 $content = get_post_field( 'post_content', $post_id );
360 $tags = new WP_HTML_Tag_Processor( $content );
361
362 // This linter warning is a false positive - we have to re-count each time here as we modify $images.
363 // phpcs:ignore Squiz.PHP.DisallowSizeFunctionsInLoops.Found
364 while ( $tags->next_tag( 'img' ) && ( count( $image ) <= opengraph_max_images() ) ) {
365 $src = $tags->get_attribute( 'src' );
366
367 /*
368 * If the img source is in our uploads dir, get the
369 * associated ID. Note: if there's a -500x500
370 * type suffix, we remove it, but we try the original
371 * first in case the original image is actually called
372 * that. Likewise, we try adding the -scaled suffix for
373 * the case that this is a small version of an image
374 * that was big enough to get scaled down on upload:
375 * https://make.wordpress.org/core/2019/10/09/introducing-handling-of-big-images-in-wordpress-5-3/
376 */
377 if ( null === $src || ! str_starts_with( $src, $base ) ) {
378 continue;
379 }
380
381 $img_id = attachment_url_to_postid( $src );
382
383 if ( 0 === $img_id ) {
384 $count = 0;
385 $src = strtok( $src, '?' );
386 $img_id = attachment_url_to_postid( $src );
387 }
388
389 if ( 0 === $img_id ) {
390 $count = 0;
391 $src = preg_replace( '/-(?:\d+x\d+)(\.[a-zA-Z]+)$/', '$1', $src, 1, $count );
392 if ( $count > 0 ) {
393 $img_id = attachment_url_to_postid( $src );
394 }
395 }
396
397 if ( 0 === $img_id ) {
398 $src = preg_replace( '/(\.[a-zA-Z]+)$/', '-scaled$1', $src );
399 $img_id = attachment_url_to_postid( $src );
400 }
401
402 if ( 0 !== $img_id ) {
403 $image[] = current( wp_get_attachment_image_src( $img_id, 'large' ) ?: array() ); // phpcs:ignore
404 }
405 }
406
407 return array_unique( $image );
408 }
409
410
411 /**
412 * Attached images.
413 *
414 * @param array $image The current list of images.
415 *
416 * @return array The list of images.
417 */
418 function opengraph_attached_image( $image = array() ) {
419 $max_images = opengraph_max_images();
420
421 if (
422 count( $image ) >= $max_images ||
423 ! is_singular() || is_attachment()
424 ) {
425 return $image;
426 }
427
428 $id = get_queried_object_id();
429
430 $query = new WP_Query(
431 array(
432 'post_parent' => $id,
433 'post_status' => 'inherit',
434 'post_type' => 'attachment',
435 'post_mime_type' => 'image',
436 'order' => 'ASC',
437 'orderby' => 'menu_order ID',
438 'fields' => 'ids',
439 'posts_per_page' => $max_images,
440 )
441 );
442
443 $image_ids = $query->get_posts();
444
445 // Get URLs for each image.
446 foreach ( $image_ids as $id ) {
447 if ( count( $image ) >= opengraph_max_images() ) {
448 break;
449 }
450
451 $thumbnail = wp_get_attachment_image_src( $id, 'large' );
452 if ( $thumbnail ) {
453 $image[] = $thumbnail[0];
454 }
455 }
456
457 return array_unique( $image );
458 }
459
460 /**
461 * Fallback image property, using the site icon, custom logo, or header images.
462 *
463 * @param array $image The current list of images.
464 *
465 * @return array The list of images.
466 */
467 function opengraph_fallback_image( $image = array() ) {
468 if ( $image ) {
469 return $image;
470 }
471
472 $max_images = opengraph_max_images();
473
474 // Try site icon.
475 if ( function_exists( 'get_site_icon_url' ) && has_site_icon() ) {
476 $image[] = get_site_icon_url( 512 );
477 }
478
479 // Try custom logo second.
480 if ( empty( $image ) ) {
481 $custom_logo = get_theme_mod( 'custom_logo' );
482 $image[] = wp_get_attachment_image_src( $custom_logo, 'large' );
483 }
484
485 // Try header images.
486 if ( empty( $image ) && function_exists( 'get_uploaded_header_images' ) ) {
487 if ( is_random_header_image() ) {
488 foreach ( get_uploaded_header_images() as $header_image ) {
489 $image[] = $header_image['url'];
490 if ( count( $image ) >= $max_images ) {
491 break;
492 }
493 }
494 } elseif ( get_header_image() ) {
495 $image[] = get_header_image();
496 }
497 }
498
499 return array_unique( $image );
500 }
501
502 /**
503 * Ensure the image count does not exceed the maximum.
504 *
505 * @param array $image The current list of images.
506 *
507 * @return array The list of images.
508 */
509 function opengraph_ensure_max_image( $image = array() ) {
510 return array_slice( $image, 0, opengraph_max_images() );
511 }
512
513 /**
514 * Default audio property, using get_attached_media.
515 *
516 * @param array $audio The current list of audio files.
517 *
518 * @return array The list of audio files.
519 */
520 function opengraph_default_audio( $audio = array() ) {
521 $id = get_queried_object_id();
522 $attachments = get_attached_media( 'audio', $id );
523
524 if ( empty( $attachments ) ) {
525 return $audio;
526 }
527
528 foreach ( $attachments as $attachment ) {
529 $audio[] = wp_get_attachment_url( $attachment->ID );
530 }
531
532 return $audio;
533 }
534
535
536 /**
537 * Default video property, using get_attached_media.
538 *
539 * @param array $video The current list of video files.
540 *
541 * @return array The list of video files.
542 */
543 function opengraph_default_video( $video = array() ) {
544 $id = get_queried_object_id();
545 $attachments = get_attached_media( 'video', $id );
546
547 if ( empty( $attachments ) ) {
548 return $video;
549 }
550
551 foreach ( $attachments as $attachment ) {
552 $video[] = wp_get_attachment_url( $attachment->ID );
553 }
554
555 return $video;
556 }
557
558
559 /**
560 * Default url property, using the permalink for the page.
561 *
562 * @param string $url The current URL.
563 *
564 * @return string The URL.
565 */
566 function opengraph_default_url( $url = '' ) {
567 if ( empty( $url ) ) {
568 if ( is_singular() ) {
569 $url = get_permalink();
570 } elseif ( is_author() ) {
571 $url = get_author_posts_url( get_queried_object_id() );
572 }
573 }
574
575 return esc_url( $url );
576 }
577
578
579 /**
580 * Default site_name property, using the bloginfo name.
581 *
582 * @param string $name The current site name.
583 *
584 * @return string The site name.
585 */
586 function opengraph_default_sitename( $name = '' ) {
587 if ( empty( $name ) ) {
588 $name = get_bloginfo( 'name' );
589 }
590
591 return wp_strip_all_tags( $name );
592 }
593
594
595 /**
596 * Default description property, using the excerpt or content for posts, or the
597 * bloginfo description.
598 *
599 * @param string $description The current description.
600 * @param int $length The maximum length of the description.
601 *
602 * @return string The description.
603 */
604 function opengraph_default_description( $description = '', $length = 55 ) {
605 if ( $description ) {
606 return $description;
607 }
608
609 if ( is_singular() ) {
610 $post = get_queried_object();
611 if ( post_password_required( $post ) ) {
612 $description = __( 'This content is password protected.', 'opengraph' );
613 } elseif ( ! empty( $post->post_excerpt ) ) {
614 $description = $post->post_excerpt;
615 } else {
616 $description = $post->post_content;
617 }
618 } elseif ( is_author() ) {
619 $id = get_queried_object_id();
620 $description = get_user_meta( $id, 'description', true );
621 } elseif ( is_category() && category_description() ) {
622 $description = category_description();
623 } elseif ( is_tag() && tag_description() ) {
624 $description = tag_description();
625 } elseif (
626 is_archive() &&
627 function_exists( 'get_the_archive_description' ) &&
628 get_the_archive_description()
629 ) { // New in version 4.1 to get all other archive descriptions.
630 $description = get_the_archive_description();
631 } else {
632 $description = get_bloginfo( 'description' );
633 }
634
635 // strip description to first 55 words.
636 $description = wp_strip_all_tags( strip_shortcodes( $description ) );
637 $description = opengraph_trim_text( $description, $length );
638
639 return wp_strip_all_tags( $description );
640 }
641
642
643 /**
644 * Default locale property, using the WordPress locale.
645 *
646 * @param string $locale The current locale.
647 *
648 * @return string The locale.
649 */
650 function opengraph_default_locale( $locale = '' ) {
651 if ( empty( $locale ) ) {
652 $locale = get_locale();
653 }
654
655 return $locale;
656 }
657
658
659 /**
660 * Default twitter-card type.
661 *
662 * @param string $card The current card type.
663 *
664 * @return string The card type.
665 */
666 function twitter_default_card( $card = '' ) {
667 if ( $card ) {
668 return $card;
669 }
670
671 $card = 'summary';
672 $images = apply_filters( 'opengraph_image', array() );
673
674 // Show large image on...
675 if ( is_singular() ) {
676 if (
677 // Gallery and image posts.
678 in_array( get_post_format(), array( 'image', 'gallery' ), true ) ||
679 // Posts with more than one image.
680 ( is_array( $images ) && count( $images ) > 1 ) ||
681 // Posts with a post-thumbnail.
682 ( function_exists( 'has_post_thumbnail' ) && has_post_thumbnail() )
683 ) {
684 $card = 'summary_large_image';
685 }
686 }
687
688 return $card;
689 }
690
691
692 /**
693 * Default twitter-card creator.
694 *
695 * @see https://developer.twitter.com/en/docs/twitter-for-websites/cards/guides/getting-started
696 *
697 * @param string $creator The current creator.
698 *
699 * @return string The creator.
700 */
701 function twitter_default_creator( $creator = '' ) {
702 if ( $creator || ! is_singular() ) {
703 return $creator;
704 }
705
706 $post = get_queried_object();
707 $author = $post->post_author;
708 $twitter = get_the_author_meta( 'twitter', $author );
709
710 if ( ! $twitter ) {
711 return $creator;
712 }
713
714 // Check if twitter-account matches "http://twitter.com/username".
715 if ( preg_match( '/^http:\/\/twitter\.com\/(#!\/)?(\w+)/i', $twitter, $matches ) ) {
716 $creator = '@' . $matches[2];
717 } elseif ( preg_match( '/^@?(\w+)$/i', $twitter, $matches ) ) { // Check if twitter-account matches "(@)username".
718 $creator = '@' . $matches[1];
719 }
720
721 return $creator;
722 }
723
724
725 /**
726 * Default fediverse creator.
727 *
728 * @see https://github.com/mastodon/mastodon/pull/30398
729 *
730 * @param string $creator The current creator.
731 *
732 * @return string The creator.
733 */
734 function fediverse_default_creator( $creator = '' ) {
735 if ( ! is_singular() ) {
736 return $creator;
737 }
738
739 $post = get_queried_object();
740 $author = $post->post_author;
741 $webfinger = get_the_author_meta( 'fediverse', $author );
742
743 if ( ! $webfinger ) {
744 return $creator;
745 }
746
747 $webfinger = ltrim( $webfinger, '@' );
748 $webfinger = str_replace( 'acct:', '', $webfinger );
749
750 return $webfinger;
751 }
752
753
754 /**
755 * Output Open Graph <meta> tags in the page header.
756 */
757 function opengraph_meta_tags() {
758 $metadata = opengraph_metadata();
759 foreach ( $metadata as $key => $value ) {
760 if ( empty( $key ) || empty( $value ) ) {
761 continue;
762 }
763 $value = (array) $value;
764
765 foreach ( $value as $v ) {
766 // Skip empty values.
767 if ( empty( $v ) ) {
768 continue;
769 }
770
771 // Check if "strict mode" is enabled.
772 if ( OPENGRAPH_STRICT_MODE === true ) {
773 if ( // Use "name" attribute for Twitter Cards.
774 str_starts_with( $key, 'twitter:' ) ||
775 str_starts_with( $key, 'fediverse:' )
776 ) {
777 printf(
778 '<meta name="%1$s" content="%2$s" />' . PHP_EOL,
779 esc_attr( $key ),
780 esc_attr( $v )
781 );
782 } else { // Use "property" attribute for Open Graph.
783 printf(
784 '<meta property="%1$s" content="%2$s" />' . PHP_EOL,
785 esc_attr( $key ),
786 esc_attr( $v )
787 );
788 }
789 } else {
790 // Use the "property" and "name" attributes.
791 printf(
792 '<meta property="%1$s" name="%1$s" content="%2$s" />' . PHP_EOL,
793 esc_attr( $key ),
794 esc_attr( $v )
795 );
796 }
797 }
798 }
799 }
800 add_action( 'wp_head', 'opengraph_meta_tags' );
801
802
803 /**
804 * Include profile metadata for author pages.
805 *
806 * @link http://ogp.me/#type_profile
807 *
808 * @param array $metadata The current metadata.
809 *
810 * @return array The updated metadata.
811 */
812 function opengraph_profile_metadata( $metadata ) {
813 if ( is_author() ) {
814 $id = get_queried_object_id();
815
816 $metadata['profile:first_name'] = get_the_author_meta( 'first_name', $id );
817 $metadata['profile:last_name'] = get_the_author_meta( 'last_name', $id );
818 $metadata['profile:username'] = get_the_author_meta( 'nicename', $id );
819 }
820
821 return $metadata;
822 }
823
824
825 /**
826 * Include article metadata for posts and pages.
827 *
828 * @link http://ogp.me/#type_article
829 *
830 * @param array $metadata The current metadata.
831 *
832 * @return array The updated metadata.
833 */
834 function opengraph_article_metadata( $metadata ) {
835 if ( ! is_singular() ) {
836 return $metadata;
837 }
838
839 $post = get_queried_object();
840 $author = $post->post_author;
841
842 // Check if page/post has tags.
843 $tags = wp_get_object_terms( $post->ID, 'post_tag' );
844 if ( $tags && is_array( $tags ) ) {
845 foreach ( $tags as $tag ) {
846 $metadata['article:tag'][] = $tag->name;
847 }
848 }
849
850 // Check if page/post has categories.
851 $categories = wp_get_object_terms( $post->ID, 'category' );
852 if ( $categories && is_array( $categories ) ) {
853 $metadata['article:section'][] = current( $categories )->name;
854 }
855
856 $metadata['article:published_time'] = get_the_time( 'c', $post->ID );
857 $metadata['article:modified_time'] = get_the_modified_time( 'c', $post->ID );
858 $metadata['article:author'][] = get_author_posts_url( $author );
859
860 $facebook = get_the_author_meta( 'facebook', $author );
861
862 if ( ! empty( $facebook ) ) {
863 $metadata['article:author'][] = $facebook;
864 }
865
866 return $metadata;
867 }
868
869
870 /**
871 * Add "twitter" as a contact method
872 *
873 * @param array $user_contactmethods The current list of contact methods.
874 *
875 * @return array The updated list of contact methods.
876 */
877 function opengraph_user_contactmethods( $user_contactmethods = array() ) {
878 $user_contactmethods['twitter'] = __( 'Twitter', 'opengraph' );
879 $user_contactmethods['facebook'] = __( 'Facebook (Profile URL)', 'opengraph' );
880 $user_contactmethods['fediverse'] = __( 'Fediverse (username@host.tld)', 'opengraph' );
881
882 return $user_contactmethods;
883 }
884 add_filter( 'user_contactmethods', 'opengraph_user_contactmethods', 1 );
885
886
887 /**
888 * Add 512x512 icon size
889 *
890 * @param array $sizes Sizes available for the site icon.
891 *
892 * @return array updated list of icons.
893 */
894 function opengraph_site_icon_image_sizes( $sizes ) {
895 $sizes[] = 512;
896
897 return array_unique( $sizes );
898 }
899 add_filter( 'site_icon_image_sizes', 'opengraph_site_icon_image_sizes' );
900
901
902 /**
903 * Helper function to trim text using the same default values for length and
904 * 'more' text as wp_trim_excerpt.
905 *
906 * @param string $text The text to trim.
907 * @param int $length The maximum number of words to include.
908 *
909 * @return string The trimmed text.
910 */
911 function opengraph_trim_text( $text, $length = 55 ) {
912 $excerpt_length = apply_filters( 'excerpt_length', $length );
913 $excerpt_more = apply_filters( 'excerpt_more', ' [...]' );
914
915 return wp_trim_words( $text, $excerpt_length, $excerpt_more );
916 }
917
918 /**
919 * Get the maximum number of images to include in Open Graph metadata.
920 *
921 * @return int The maximum number of images to include.
922 */
923 function opengraph_max_images() {
924 /**
925 * Filter the maximum number of images to include in Open Graph metadata.
926 *
927 * As of July 2014, Facebook seems to only let you select from the first 3 images.
928 *
929 * @param int $max_images The maximum number of images to include.
930 */
931 $max_images = apply_filters( 'opengraph_max_images', 3 );
932
933 // Max images can't be negative or zero.
934 if ( $max_images <= 0 ) {
935 $max_images = 1;
936 }
937
938 return $max_images;
939 }
940
941 /**
942 * Check if a site supports the block editor.
943 *
944 * @return boolean True if the site supports the block editor, false otherwise.
945 */
946 function opengraph_site_supports_blocks() {
947 $return = true;
948
949 if ( version_compare( get_bloginfo( 'version' ), '5.9', '<' ) ) {
950 $return = false;
951 } elseif ( function_exists( 'classicpress_version' ) ) {
952 $return = false;
953 } elseif (
954 ! function_exists( 'register_block_type_from_metadata' ) ||
955 ! function_exists( 'do_blocks' )
956 ) {
957 $return = false;
958 }
959
960 /**
961 * Allow plugins to disable block editor support,
962 * thus disabling blocks registered by the OpenGraph plugin.
963 *
964 * @param boolean $supports_blocks True if the site supports the block editor, false otherwise.
965 */
966 return apply_filters( 'opengraph_site_supports_blocks', $return );
967 }
968
969
970 if ( ! function_exists( 'str_starts_with' ) ) {
971 /**
972 * `str_starts_with` function for PHP < 8.0.
973 *
974 * @see https://www.php.net/manual/en/function.str-starts-with
975 *
976 * @param string $haystack The string to search in.
977 * @param string $needle The string to search for.
978 *
979 * @return bool True if the string starts with the needle, false otherwise.
980 */
981 function str_starts_with( $haystack, $needle ) {
982 return 0 === strncmp( $haystack, $needle, \strlen( $needle ) );
983 }
984 }
985