Completed
Push — update/meta-descriptions-clean... ( 6da2f0 )
by Jeremy
09:26
created

functions.opengraph.php (1 issue)

Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * Open Graph Tags
4
 *
5
 * Add Open Graph tags so that Facebook (and any other service that supports them)
6
 * can crawl the site better and we provide a better sharing experience.
7
 *
8
 * @link https://ogp.me/
9
 * @link https://developers.facebook.com/docs/opengraph/
10
 *
11
 * @package Jetpack
12
 */
13
14
add_action( 'wp_head', 'jetpack_og_tags' );
15
add_action( 'amp_story_head', 'jetpack_og_tags' );
16
17
/**
18
 * Outputs Open Graph tags generated by Jetpack.
19
 */
20
function jetpack_og_tags() {
21
	/**
22
	 * Allow Jetpack to output Open Graph Meta Tags.
23
	 *
24
	 * @module sharedaddy, publicize
25
	 *
26
	 * @since 2.0.0
27
	 * @deprecated 2.0.3 Duplicative filter. Use `jetpack_enable_open_graph`.
28
	 *
29
	 * @param bool true Should Jetpack's Open Graph Meta Tags be enabled. Default to true.
30
	 */
31
	if ( false === apply_filters( 'jetpack_enable_opengraph', true ) ) {
32
		_deprecated_function( 'jetpack_enable_opengraph', '2.0.3', 'jetpack_enable_open_graph' );
33
		return;
34
	}
35
36
	$is_amp_response = ( class_exists( 'Jetpack_AMP_Support' ) && Jetpack_AMP_Support::is_amp_request() );
37
38
	// Disable the widont filter on WP.com to avoid stray &nbsps.
39
	$disable_widont = remove_filter( 'the_title', 'widont' );
40
41
	$og_output = "\n";
42
	if ( ! $is_amp_response ) { // Because AMP optimizes the order or the nodes in the head.
43
		$og_output .= "<!-- Jetpack Open Graph Tags -->\n";
44
	}
45
	$tags = array();
46
47
	/**
48
	 * Filter the minimum width of the images used in Jetpack Open Graph Meta Tags.
49
	 *
50
	 * @module sharedaddy, publicize
51
	 *
52
	 * @since 2.0.0
53
	 *
54
	 * @param int 200 Minimum image width used in Jetpack Open Graph Meta Tags.
55
	 */
56
	$image_width = absint( apply_filters( 'jetpack_open_graph_image_width', 200 ) );
57
	/**
58
	 * Filter the minimum height of the images used in Jetpack Open Graph Meta Tags.
59
	 *
60
	 * @module sharedaddy, publicize
61
	 *
62
	 * @since 2.0.0
63
	 *
64
	 * @param int 200 Minimum image height used in Jetpack Open Graph Meta Tags.
65
	 */
66
	$image_height       = absint( apply_filters( 'jetpack_open_graph_image_height', 200 ) );
67
	$description_length = 197;
68
69
	if ( is_home() || is_front_page() ) {
70
		$site_type              = Jetpack_Options::get_option_and_ensure_autoload( 'open_graph_protocol_site_type', '' );
71
		$tags['og:type']        = ! empty( $site_type ) ? $site_type : 'website';
72
		$tags['og:title']       = get_bloginfo( 'name' );
73
		$tags['og:description'] = get_bloginfo( 'description' );
74
75
		$front_page_id = get_option( 'page_for_posts' );
76
		if ( 'page' === get_option( 'show_on_front' ) && $front_page_id && is_home() ) {
77
			$tags['og:url'] = get_permalink( $front_page_id );
78
		} else {
79
			$tags['og:url'] = home_url( '/' );
80
		}
81
82
		// Associate a blog's root path with one or more Facebook accounts.
83
		$facebook_admins = Jetpack_Options::get_option_and_ensure_autoload( 'facebook_admins', array() );
84
		if ( ! empty( $facebook_admins ) ) {
85
			$tags['fb:admins'] = $facebook_admins;
86
		}
87
	} elseif ( is_author() ) {
88
		$tags['og:type'] = 'profile';
89
90
		$author = get_queried_object();
91
92
		if ( is_a( $author, 'WP_User' ) ) {
93
			$tags['og:title'] = $author->display_name;
94
			if ( ! empty( $author->user_url ) ) {
95
				$tags['og:url'] = $author->user_url;
96
			} else {
97
				$tags['og:url'] = get_author_posts_url( $author->ID );
98
			}
99
			$tags['og:description']     = $author->description;
100
			$tags['profile:first_name'] = get_the_author_meta( 'first_name', $author->ID );
101
			$tags['profile:last_name']  = get_the_author_meta( 'last_name', $author->ID );
102
		}
103
	} elseif ( is_archive() ) {
104
		$tags['og:type']  = 'website';
105
		$tags['og:title'] = wp_get_document_title();
106
107
		$archive = get_queried_object();
108
		if ( ! empty( $archive ) ) {
109
			if ( is_category() || is_tag() || is_tax() ) {
110
				$tags['og:url']         = get_term_link( $archive->term_id, $archive->taxonomy );
111
				$tags['og:description'] = $archive->description;
112
			} elseif ( is_post_type_archive() ) {
113
				$tags['og:url']         = get_post_type_archive_link( $archive->name );
114
				$tags['og:description'] = $archive->description;
115
			}
116
		}
117
	} elseif ( is_singular() ) {
118
		global $post;
119
		$data = $post; // so that we don't accidentally explode the global.
120
121
		$tags['og:type'] = 'article';
122
		if ( empty( $data->post_title ) ) {
123
			$tags['og:title'] = ' ';
124
		} else {
125
			/** This filter is documented in core/src/wp-includes/post-template.php */
126
			$tags['og:title'] = wp_kses( apply_filters( 'the_title', $data->post_title, $data->ID ), array() );
127
		}
128
129
		$tags['og:url'] = get_permalink( $data->ID );
130
		if ( ! post_password_required() ) {
131
			if ( ! empty( $data->post_excerpt ) ) {
132
				$tags['og:description'] = preg_replace( '@https?://[\S]+@', '', strip_shortcodes( wp_kses( $data->post_excerpt, array() ) ) );
133
			} else {
134
				$exploded_content_on_more_tag = explode( '<!--more-->', $data->post_content );
135
				$tags['og:description']       = wp_trim_words( preg_replace( '@https?://[\S]+@', '', strip_shortcodes( wp_kses( $exploded_content_on_more_tag[0], array() ) ) ) );
136
			}
137
		}
138
		if ( empty( $tags['og:description'] ) ) {
139
				/**
140
				 * Filter the fallback `og:description` used when no excerpt information is provided.
141
				 *
142
				 * @module sharedaddy, publicize
143
				 *
144
				 * @since 3.9.0
145
				 *
146
				 * @param string $var  Fallback og:description. Default is translated `Visit the post for more'.
147
				 * @param object $data Post object for the current post.
148
				 */
149
			$tags['og:description'] = apply_filters( 'jetpack_open_graph_fallback_description', __( 'Visit the post for more.', 'jetpack' ), $data );
0 ignored issues
show
The call to apply_filters() has too many arguments starting with $data.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
150
		} else {
151
			// Intentionally not using a filter to prevent pollution. @see https://github.com/Automattic/jetpack/pull/2899#issuecomment-151957382 .
152
			$tags['og:description'] = wp_kses( trim( convert_chars( wptexturize( $tags['og:description'] ) ) ), array() );
153
		}
154
155
		$tags['article:published_time'] = gmdate( 'c', strtotime( $data->post_date_gmt ) );
156
		$tags['article:modified_time']  = gmdate( 'c', strtotime( $data->post_modified_gmt ) );
157
		if ( post_type_supports( get_post_type( $data ), 'author' ) && isset( $data->post_author ) ) {
158
			$publicize_facebook_user = get_post_meta( $data->ID, '_publicize_facebook_user', true );
159
			if ( ! empty( $publicize_facebook_user ) ) {
160
				$tags['article:author'] = esc_url( $publicize_facebook_user );
161
			}
162
		}
163
	} elseif ( is_search() ) {
164
		if ( '' !== get_query_var( 's', '' ) ) {
165
			$tags['og:title'] = wp_get_document_title();
166
		}
167
	}
168
	/**
169
	 * Allow plugins to inject additional template-specific Open Graph tags.
170
	 *
171
	 * @module sharedaddy, publicize
172
	 *
173
	 * @since 3.0.0
174
	 *
175
	 * @param array $tags Array of Open Graph Meta tags.
176
	 * @param array $args Array of image size parameters.
177
	 */
178
	$tags = apply_filters( 'jetpack_open_graph_base_tags', $tags, compact( 'image_width', 'image_height' ) );
179
180
	// Re-enable widont if we had disabled it.
181
	if ( $disable_widont ) {
182
		add_filter( 'the_title', 'widont' );
183
	}
184
185
	/**
186
	 * Do not return any Open Graph Meta tags if we don't have any info about a post.
187
	 *
188
	 * @module sharedaddy, publicize
189
	 *
190
	 * @since 3.0.0
191
	 *
192
	 * @param bool true Do not return any Open Graph Meta tags if we don't have any info about a post.
193
	 */
194
	if ( empty( $tags ) && apply_filters( 'jetpack_open_graph_return_if_empty', true ) ) {
195
		return;
196
	}
197
198
	$tags['og:site_name'] = get_bloginfo( 'name' );
199
200
	// Get image info and build tags.
201
	if ( ! post_password_required() ) {
202
		$image_info       = jetpack_og_get_image( $image_width, $image_height );
203
		$tags['og:image'] = $image_info['src'];
204
205
		if ( ! empty( $image_info['width'] ) ) {
206
			$tags['og:image:width'] = (int) $image_info['width'];
207
		}
208
		if ( ! empty( $image_info['height'] ) ) {
209
			$tags['og:image:height'] = (int) $image_info['height'];
210
		}
211
		if ( ! empty( $image_info['alt_text'] ) ) {
212
			$tags['og:image:alt'] = esc_attr( $image_info['alt_text'] );
213
		}
214
	}
215
216
	// Facebook whines if you give it an empty title.
217
	if ( empty( $tags['og:title'] ) ) {
218
		$tags['og:title'] = __( '(no title)', 'jetpack' );
219
	}
220
221
	// Shorten the description if it's too long.
222
	if ( isset( $tags['og:description'] ) ) {
223
		$tags['og:description'] = strlen( $tags['og:description'] ) > $description_length ? mb_substr( $tags['og:description'], 0, $description_length ) . '…' : $tags['og:description'];
224
	}
225
226
	// Try to add OG locale tag if the WP->FB data mapping exists.
227
	if ( defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) && file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
228
		require_once JETPACK__GLOTPRESS_LOCALES_PATH;
229
		$_locale = get_locale();
230
231
		// We have to account for w.org vs WP.com locale divergence.
232
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
233
			$gp_locale = GP_Locales::by_field( 'slug', $_locale );
234
		} else {
235
			$gp_locale = GP_Locales::by_field( 'wp_locale', $_locale );
236
		}
237
	}
238
239
	if ( isset( $gp_locale->facebook_locale ) && ! empty( $gp_locale->facebook_locale ) ) {
240
		$tags['og:locale'] = $gp_locale->facebook_locale;
241
	}
242
243
	/**
244
	 * Allow the addition of additional Open Graph Meta tags, or modify the existing tags.
245
	 *
246
	 * @module sharedaddy, publicize
247
	 *
248
	 * @since 2.0.0
249
	 *
250
	 * @param array $tags Array of Open Graph Meta tags.
251
	 * @param array $args Array of image size parameters.
252
	 */
253
	$tags = apply_filters( 'jetpack_open_graph_tags', $tags, compact( 'image_width', 'image_height' ) );
254
255
	// secure_urls need to go right after each og:image to work properly so we will abstract them here.
256
	$tags['og:image:secure_url'] = ( empty( $tags['og:image:secure_url'] ) ) ? '' : $tags['og:image:secure_url'];
257
	$secure                      = $tags['og:image:secure_url'];
258
	unset( $tags['og:image:secure_url'] );
259
	$secure_image_num = 0;
260
261
	foreach ( (array) $tags as $tag_property => $tag_content ) {
262
		// to accommodate multiple images.
263
		$tag_content = (array) $tag_content;
264
		$tag_content = array_unique( $tag_content );
265
266
		foreach ( $tag_content as $tag_content_single ) {
267
			if ( empty( $tag_content_single ) ) {
268
				continue; // Don't ever output empty tags.
269
			}
270
			$og_tag = sprintf( '<meta property="%s" content="%s" />', esc_attr( $tag_property ), esc_attr( $tag_content_single ) );
271
			/**
272
			 * Filter the HTML Output of each Open Graph Meta tag.
273
			 *
274
			 * @module sharedaddy, publicize
275
			 *
276
			 * @since 2.0.0
277
			 *
278
			 * @param string $og_tag HTML HTML Output of each Open Graph Meta tag.
279
			 */
280
			$og_output .= apply_filters( 'jetpack_open_graph_output', $og_tag );
281
			$og_output .= "\n";
282
283
			if ( 'og:image' === $tag_property ) {
284
				if ( is_array( $secure ) && ! empty( $secure[ $secure_image_num ] ) ) {
285
					$og_tag = sprintf( '<meta property="og:image:secure_url" content="%s" />', esc_url( $secure[ $secure_image_num ] ) );
286
					/** This filter is documented in functions.opengraph.php */
287
					$og_output .= apply_filters( 'jetpack_open_graph_output', $og_tag );
288
					$og_output .= "\n";
289
				} elseif ( ! is_array( $secure ) && ! empty( $secure ) ) {
290
					$og_tag = sprintf( '<meta property="og:image:secure_url" content="%s" />', esc_url( $secure ) );
291
					/** This filter is documented in functions.opengraph.php */
292
					$og_output .= apply_filters( 'jetpack_open_graph_output', $og_tag );
293
					$og_output .= "\n";
294
				}
295
				$secure_image_num++;
296
			}
297
		}
298
	}
299
300
	if ( ! $is_amp_response ) { // Because AMP optimizes the order or the nodes in the head.
301
		$og_output .= "\n<!-- End Jetpack Open Graph Tags -->";
302
	}
303
	$og_output .= "\n";
304
	// This is trusted output or added by a filter.
305
	echo $og_output; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
306
}
307
308
/**
309
 * Returns an image used in social shares.
310
 *
311
 * @since 2.0.0
312
 *
313
 * @param int  $width Minimum width for the image. Default is 200 based on Facebook's requirement.
314
 * @param int  $height Minimum height for the image. Default is 200 based on Facebook's requirement.
315
 * @param null $deprecated Deprecated.
316
 *
317
 * @return array The source ('src'), 'width', and 'height' of the image.
318
 */
319
function jetpack_og_get_image( $width = 200, $height = 200, $deprecated = null ) {
320
	if ( ! empty( $deprecated ) ) {
321
		_deprecated_argument( __FUNCTION__, '6.6.0' );
322
	}
323
	$image = array();
324
325
	if ( is_singular() && ! is_home() ) {
326
		// Grab obvious image if post is an attachment page for an image.
327
		if ( is_attachment( get_the_ID() ) && 'image' === substr( get_post_mime_type(), 0, 5 ) ) {
328
			$image['src'] = wp_get_attachment_url( get_the_ID() );
329
		}
330
331
		// Attempt to find something good for this post using our generalized PostImages code.
332
		if ( empty( $image ) && class_exists( 'Jetpack_PostImages' ) ) {
333
			$post_images = Jetpack_PostImages::get_images(
334
				get_the_ID(),
335
				array(
336
					'width'  => $width,
337
					'height' => $height,
338
				)
339
			);
340
			if ( $post_images && ! is_wp_error( $post_images ) ) {
341
				foreach ( (array) $post_images as $post_image ) {
342
					$image['src'] = $post_image['src'];
343 View Code Duplication
					if ( isset( $post_image['src_width'], $post_image['src_height'] ) ) {
344
						$image['width']  = $post_image['src_width'];
345
						$image['height'] = $post_image['src_height'];
346
					}
347
					if ( ! empty( $post_image['alt_text'] ) ) {
348
						$image['alt_text'] = $post_image['alt_text'];
349
					}
350
				}
351
			}
352
		}
353
	} elseif ( is_author() ) {
354
		$author = get_queried_object();
355
		if ( is_a( $author, 'WP_User' ) ) {
356
			$image['src'] = get_avatar_url(
357
				$author->user_email,
358
				array(
359
					'size' => $width,
360
				)
361
			);
362
		}
363
	}
364
365
	// First fall back, blavatar.
366
	if ( empty( $image ) && function_exists( 'blavatar_domain' ) ) {
367
		$blavatar_domain = blavatar_domain( site_url() );
368
		if ( blavatar_exists( $blavatar_domain ) ) {
369
			$image['src']    = blavatar_url( $blavatar_domain, 'img', $width, false, true );
370
			$image['width']  = $width;
371
			$image['height'] = $height;
372
		}
373
	}
374
375
	// Second fall back, Site Logo.
376 View Code Duplication
	if ( empty( $image ) && ( function_exists( 'jetpack_has_site_logo' ) && jetpack_has_site_logo() ) ) {
377
		$image_id = jetpack_get_site_logo( 'id' );
378
		$logo     = wp_get_attachment_image_src( $image_id, 'full' );
379
		if (
380
			isset( $logo[0], $logo[1], $logo[2] )
381
			&& ( _jetpack_og_get_image_validate_size( $logo[1], $logo[2], $width, $height ) )
382
		) {
383
			$image['src']    = $logo[0];
384
			$image['width']  = $logo[1];
385
			$image['height'] = $logo[2];
386
		}
387
	}
388
389
	// Third fall back, Core Site Icon, if valid in size.
390 View Code Duplication
	if ( empty( $image ) && has_site_icon() ) {
391
		$image_id = get_option( 'site_icon' );
392
		$icon     = wp_get_attachment_image_src( $image_id, 'full' );
393
		if (
394
			isset( $icon[0], $icon[1], $icon[2] )
395
			&& ( _jetpack_og_get_image_validate_size( $icon[1], $icon[2], $width, $height ) )
396
		) {
397
			$image['src']    = $icon[0];
398
			$image['width']  = $icon[1];
399
			$image['height'] = $icon[2];
400
		}
401
	}
402
403
	// Final fall back, blank image.
404
	if ( empty( $image ) ) {
405
		/**
406
		 * Filter the default Open Graph Image tag, used when no Image can be found in a post.
407
		 *
408
		 * @since 3.0.0
409
		 *
410
		 * @param string $str Default Image URL.
411
		 */
412
		$image['src'] = apply_filters( 'jetpack_open_graph_image_default', 'https://s0.wp.com/i/blank.jpg' );
413
	}
414
415
	return $image;
416
}
417
418
419
/**
420
 * Validate the width and height against required width and height
421
 *
422
 * @param int $width      Width of the image.
423
 * @param int $height     Height of the image.
424
 * @param int $req_width  Required width to pass validation.
425
 * @param int $req_height Required height to pass validation.
426
 *
427
 * @return bool - True if the image passed the required size validation
428
 */
429
function _jetpack_og_get_image_validate_size( $width, $height, $req_width, $req_height ) {
430
	if ( ! $width || ! $height ) {
431
		return false;
432
	}
433
434
	$valid_width         = ( $width >= $req_width );
435
	$valid_height        = ( $height >= $req_height );
436
	$is_image_acceptable = $valid_width && $valid_height;
437
438
	return $is_image_acceptable;
439
}
440
441
/**
442
 * Gets a gravatar URL of the specified size.
443
 *
444
 * @param string $email E-mail address to get gravatar for.
445
 * @param int    $width Size of returned gravatar.
446
 * @return array|bool|mixed|string
447
 */
448
function jetpack_og_get_image_gravatar( $email, $width ) {
449
	return get_avatar_url(
450
		$email,
451
		array(
452
			'size' => $width,
453
		)
454
	);
455
}
456