Completed
Push — update/unit-testing ( 17ecef )
by Jeremy
08:04
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 http://ogp.me/
9
 * @link http://developers.facebook.com/docs/opengraph/
10
 */
11
add_action( 'wp_head', 'jetpack_og_tags' );
12
13
function jetpack_og_tags() {
14
	/**
15
	 * Allow Jetpack to output Open Graph Meta Tags.
16
	 *
17
	 * @module sharedaddy, publicize
18
	 *
19
	 * @since 2.0.0
20
	 * @deprecated 2.0.3 Duplicative filter. Use `jetpack_enable_open_graph`.
21
	 *
22
	 * @param bool true Should Jetpack's Open Graph Meta Tags be enabled. Default to true.
23
	 */
24
	if ( false === apply_filters( 'jetpack_enable_opengraph', true ) ) {
25
		_deprecated_function( 'jetpack_enable_opengraph', '2.0.3', 'jetpack_enable_open_graph' );
26
		return;
27
	}
28
29
	// Disable the widont filter on WP.com to avoid stray &nbsps
30
	$disable_widont = remove_filter( 'the_title', 'widont' );
31
32
	$og_output = "\n<!-- Jetpack Open Graph Tags -->\n";
33
	$tags = array();
34
35
	/**
36
	 * Filter the minimum width of the images used in Jetpack Open Graph Meta Tags.
37
	 *
38
	 * @module sharedaddy, publicize
39
	 *
40
	 * @since 2.0.0
41
	 *
42
	 * @param int 200 Minimum image width used in Jetpack Open Graph Meta Tags.
43
	 */
44
	$image_width        = absint( apply_filters( 'jetpack_open_graph_image_width', 200 ) );
45
	/**
46
	 * Filter the minimum height of the images used in Jetpack Open Graph Meta Tags.
47
	 *
48
	 * @module sharedaddy, publicize
49
	 *
50
	 * @since 2.0.0
51
	 *
52
	 * @param int 200 Minimum image height used in Jetpack Open Graph Meta Tags.
53
	 */
54
	$image_height       = absint( apply_filters( 'jetpack_open_graph_image_height', 200 ) );
55
	$description_length = 197;
56
57
	if ( is_home() || is_front_page() ) {
58
		$site_type              = Jetpack_Options::get_option_and_ensure_autoload( 'open_graph_protocol_site_type', '' );
59
		$tags['og:type']        = ! empty( $site_type ) ? $site_type : 'website';
60
		$tags['og:title']       = get_bloginfo( 'name' );
61
		$tags['og:description'] = get_bloginfo( 'description' );
62
63
		$front_page_id = get_option( 'page_for_posts' );
64
		if ( 'page' == get_option( 'show_on_front' ) && $front_page_id && is_home() )
65
			$tags['og:url'] = get_permalink( $front_page_id );
66
		else
67
			$tags['og:url'] = home_url( '/' );
68
69
		// Associate a blog's root path with one or more Facebook accounts
70
		$facebook_admins = Jetpack_Options::get_option_and_ensure_autoload( 'facebook_admins', array() );
71
		if ( ! empty( $facebook_admins ) )
72
			$tags['fb:admins'] = $facebook_admins;
73
74
	} else if ( is_author() ) {
75
		$tags['og:type'] = 'profile';
76
77
		$author = get_queried_object();
78
79
		$tags['og:title']           = $author->display_name;
80
		if ( ! empty( $author->user_url ) ) {
81
			$tags['og:url']     = $author->user_url;
82
		} else {
83
			$tags['og:url']     = get_author_posts_url( $author->ID );
84
		}
85
		$tags['og:description']     = $author->description;
86
		$tags['profile:first_name'] = get_the_author_meta( 'first_name', $author->ID );
87
		$tags['profile:last_name']  = get_the_author_meta( 'last_name', $author->ID );
88
89
	} else if ( is_singular() ) {
90
		global $post;
91
		$data = $post; // so that we don't accidentally explode the global
92
93
		$tags['og:type'] = 'article';
94
		if ( empty( $data->post_title ) ) {
95
			$tags['og:title'] = ' ';
96
		} else {
97
			/** This filter is documented in core/src/wp-includes/post-template.php */
98
			$tags['og:title'] = wp_kses( apply_filters( 'the_title', $data->post_title, $data->ID ), array() );
99
		}
100
101
		$tags['og:url']         = get_permalink( $data->ID );
102
		if ( ! post_password_required() ) {
103
			if ( ! empty( $data->post_excerpt ) ) {
104
				$tags['og:description'] = preg_replace( '@https?://[\S]+@', '', strip_shortcodes( wp_kses( $data->post_excerpt, array() ) ) );
105
			} else {
106
				$exploded_content_on_more_tag = explode( '<!--more-->', $data->post_content );
107
				$tags['og:description'] = wp_trim_words( preg_replace( '@https?://[\S]+@', '', strip_shortcodes( wp_kses( $exploded_content_on_more_tag[0], array() ) ) ) );
108
			}
109
		}
110
		if ( empty( $tags['og:description'] ) ) {
111
				/**
112
				 * Filter the fallback `og:description` used when no excerpt information is provided.
113
				 *
114
				 * @module sharedaddy, publicize
115
				 *
116
				 * @since 3.9.0
117
				 *
118
				 * @param string $var  Fallback og:description. Default is translated `Visit the post for more'.
119
				 * @param object $data Post object for the current post.
120
				 */
121
			$tags['og:description'] = apply_filters( 'jetpack_open_graph_fallback_description', __( 'Visit the post for more.', 'jetpack' ), $data );
122
		} else {
123
			// Intentionally not using a filter to prevent pollution. @see https://github.com/Automattic/jetpack/pull/2899#issuecomment-151957382
124
			$tags['og:description'] = wp_kses( trim( convert_chars( wptexturize( $tags['og:description'] ) ) ), array() );
125
		}
126
127
		$tags['article:published_time'] = date( 'c', strtotime( $data->post_date_gmt ) );
128
		$tags['article:modified_time'] = date( 'c', strtotime( $data->post_modified_gmt ) );
129
		if ( post_type_supports( get_post_type( $data ), 'author' ) && isset( $data->post_author ) ) {
130
			$publicize_facebook_user = get_post_meta( $data->ID, '_publicize_facebook_user', true );
131
			if ( ! empty( $publicize_facebook_user ) ) {
132
				$tags['article:author'] = esc_url( $publicize_facebook_user );
133
			}
134
		}
135
	}
136
137
	/**
138
	 * Allow plugins to inject additional template-specific Open Graph tags.
139
	 *
140
	 * @module sharedaddy, publicize
141
	 *
142
	 * @since 3.0.0
143
	 *
144
	 * @param array $tags Array of Open Graph Meta tags.
145
	 * @param array $args Array of image size parameters.
146
	 */
147
	$tags = apply_filters( 'jetpack_open_graph_base_tags', $tags, compact( 'image_width', 'image_height' ) );
148
149
	// Re-enable widont if we had disabled it
150
	if ( $disable_widont )
151
		add_filter( 'the_title', 'widont' );
152
153
	/**
154
	 * Do not return any Open Graph Meta tags if we don't have any info about a post.
155
	 *
156
	 * @module sharedaddy, publicize
157
	 *
158
	 * @since 3.0.0
159
	 *
160
	 * @param bool true Do not return any Open Graph Meta tags if we don't have any info about a post.
161
	 */
162
	if ( empty( $tags ) && apply_filters( 'jetpack_open_graph_return_if_empty', true ) )
163
		return;
164
165
	$tags['og:site_name'] = get_bloginfo( 'name' );
166
167
	// Get image info and build tags
168
	if ( ! post_password_required() ) {
169
		$image_info       = jetpack_og_get_image( $image_width, $image_height );
170
		$tags['og:image'] = $image_info['src'];
171
172
		if ( ! empty( $image_info['width'] ) ) {
173
			$tags['og:image:width'] = $image_info['width'];
174
		}
175
		if ( ! empty( $image_info['height'] ) ) {
176
			$tags['og:image:height'] = $image_info['height'];
177
		}
178
	}
179
180
	// Facebook whines if you give it an empty title
181
	if ( empty( $tags['og:title'] ) )
182
		$tags['og:title'] = __( '(no title)', 'jetpack' );
183
184
	// Shorten the description if it's too long
185
	if ( isset( $tags['og:description'] ) ) {
186
		$tags['og:description'] = strlen( $tags['og:description'] ) > $description_length ? mb_substr( $tags['og:description'], 0, $description_length ) . '…' : $tags['og:description'];
187
	}
188
189
	// Try to add OG locale tag if the WP->FB data mapping exists
190
	if ( defined( 'JETPACK__GLOTPRESS_LOCALES_PATH' ) && file_exists( JETPACK__GLOTPRESS_LOCALES_PATH ) ) {
191
		require_once JETPACK__GLOTPRESS_LOCALES_PATH;
192
		$_locale = get_locale();
193
194
		// We have to account for w.org vs WP.com locale divergence
195
		if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
196
			$gp_locale = GP_Locales::by_field( 'slug', $_locale );
197
		} else {
198
			$gp_locale = GP_Locales::by_field( 'wp_locale', $_locale );
199
		}
200
	}
201
202
	if ( isset( $gp_locale->facebook_locale ) && ! empty( $gp_locale->facebook_locale ) ) {
203
		$tags['og:locale'] = $gp_locale->facebook_locale;
204
	}
205
206
	/**
207
	 * Allow the addition of additional Open Graph Meta tags, or modify the existing tags.
208
	 *
209
	 * @module sharedaddy, publicize
210
	 *
211
	 * @since 2.0.0
212
	 *
213
	 * @param array $tags Array of Open Graph Meta tags.
214
	 * @param array $args Array of image size parameters.
215
	 */
216
	$tags = apply_filters( 'jetpack_open_graph_tags', $tags, compact( 'image_width', 'image_height' ) );
217
218
	// secure_urls need to go right after each og:image to work properly so we will abstract them here
219
	$secure = $tags['og:image:secure_url'] = ( empty( $tags['og:image:secure_url'] ) ) ? '' : $tags['og:image:secure_url'];
220
	unset( $tags['og:image:secure_url'] );
221
	$secure_image_num = 0;
222
223
	foreach ( (array) $tags as $tag_property => $tag_content ) {
224
		// to accommodate multiple images
225
		$tag_content = (array) $tag_content;
226
		$tag_content = array_unique( $tag_content );
227
228
		foreach ( $tag_content as $tag_content_single ) {
229
			if ( empty( $tag_content_single ) )
230
				continue; // Don't ever output empty tags
231
			$og_tag = sprintf( '<meta property="%s" content="%s" />', esc_attr( $tag_property ), esc_attr( $tag_content_single ) );
232
			/**
233
			 * Filter the HTML Output of each Open Graph Meta tag.
234
			 *
235
			 * @module sharedaddy, publicize
236
			 *
237
			 * @since 2.0.0
238
			 *
239
			 * @param string $og_tag HTML HTML Output of each Open Graph Meta tag.
240
			 */
241
			$og_output .= apply_filters( 'jetpack_open_graph_output', $og_tag );
242
			$og_output .= "\n";
243
244
			if ( 'og:image' == $tag_property ) {
245
				if ( is_array( $secure ) && !empty( $secure[$secure_image_num] ) ) {
246
					$og_tag = sprintf( '<meta property="og:image:secure_url" content="%s" />', esc_url( $secure[ $secure_image_num ] ) );
247
					/** This filter is documented in functions.opengraph.php */
248
					$og_output .= apply_filters( 'jetpack_open_graph_output', $og_tag );
249
					$og_output .= "\n";
250
				} else if ( !is_array( $secure ) && !empty( $secure ) ) {
251
					$og_tag = sprintf( '<meta property="og:image:secure_url" content="%s" />', esc_url( $secure ) );
252
					/** This filter is documented in functions.opengraph.php */
253
					$og_output .= apply_filters( 'jetpack_open_graph_output', $og_tag );
254
					$og_output .= "\n";
255
				}
256
				$secure_image_num++;
257
			}
258
		}
259
	}
260
	echo $og_output;
261
}
262
263
function jetpack_og_get_image( $width = 200, $height = 200, $max_images = 4 ) { // Facebook requires thumbnails to be a minimum of 200x200
0 ignored issues
show
The parameter $max_images is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
264
	$image = array();
265
266
	if ( is_singular() && ! is_home() ) {
267
		// Grab obvious image if post is an attachment page for an image
268
		if ( is_attachment( get_the_ID() ) && 'image' == substr( get_post_mime_type(), 0, 5 ) ) {
269
			$image['src'] = wp_get_attachment_url( get_the_ID() );
270
		}
271
272
		// Attempt to find something good for this post using our generalized PostImages code
273
		if ( empty( $image ) && class_exists( 'Jetpack_PostImages' ) ) {
274
			$post_images = Jetpack_PostImages::get_images( get_the_ID(), array( 'width' => $width, 'height' => $height ) );
275
			if ( $post_images && ! is_wp_error( $post_images ) ) {
276
				foreach ( (array) $post_images as $post_image ) {
277
					$image['src'] = $post_image['src'];
278
					if ( isset( $post_image['src_width'], $post_image['src_height'] ) ) {
279
						$image['width']  = $post_image['src_width'];
280
						$image['height'] = $post_image['src_height'];
281
					}
282
				}
283
			}
284
		}
285
	} elseif ( is_author() ) {
286
		$author = get_queried_object();
287
		$image['src'] = get_avatar_url( $author->user_email, array(
288
			'size' => $width,
289
		) );
290
	}
291
292
	// First fall back, blavatar
293
	if ( empty( $image ) && function_exists( 'blavatar_domain' ) ) {
294
		$blavatar_domain = blavatar_domain( site_url() );
295
		if ( blavatar_exists( $blavatar_domain ) ) {
296
			$img_width  = '';
297
			$img_height = '';
298
299
			$image_url = blavatar_url( $blavatar_domain, 'img', $width, false, true );
300
301
			/**
302
			 * Build a hash of the Image URL. We'll use it later when building the transient.
303
			 *
304
			 * Transient names are 45 chars max.
305
			 * Let's generate a hash that's never more than 40 chars long.
306
			 */
307
			$image_hash = sha1( $image_url );
308
309
			// Look for data in our transient. If nothing, let's get an attachment ID.
310
			$cached_image_id = get_transient( 'jp_' . $image_hash );
311
			if ( ! is_int( $cached_image_id ) ) {
312
				$image_id = attachment_url_to_postid( $image_url );
313
				set_transient( 'jp_' . $image_hash, $image_id );
314
			} else {
315
				$image_id = $cached_image_id;
316
			}
317
318
			$image_size = wp_get_attachment_image_src( $image_id, $width >= 512
319
				? 'full'
320
				: array( $width, $width ) );
321 View Code Duplication
			if ( isset( $image_size[1], $image_size[2] ) ) {
322
				$img_width  = $image_size[1];
323
				$img_height = $image_size[2];
324
			}
325
326 View Code Duplication
			if (_jetpack_og_get_image_validate_size($img_width, $img_height, $width, $height)) {
327
				$image['src']    = $image_url;
328
				$image['width']  = $width;
329
				$image['height'] = $height;
330
			}
331
		}
332
	}
333
334
	// Second fall back, Site Logo
335
	if ( empty( $image ) && ( function_exists( 'jetpack_has_site_logo' ) && jetpack_has_site_logo() ) ) {
336
		$image_dimensions    = jetpack_get_site_logo_dimensions();
337
		if ( ! empty( $image_dimensions ) ) {
338
			$img_width = $image_dimensions['width'];
339
			$img_height = $image_dimensions['height'];
340 View Code Duplication
			if (_jetpack_og_get_image_validate_size($img_width, $img_height, $width, $height)) {
341
				$image['src']    = jetpack_get_site_logo( 'url' );
342
				$image['width']  = $width;
343
				$image['height'] = $height;
344
			}
345
		}
346
	}
347
348
	// Third fall back, Core Site Icon, if valid in size. Added in WP 4.3.
349
	if ( empty( $image ) && ( function_exists( 'has_site_icon') && has_site_icon() ) ) {
350
		$img_width  = '';
351
		$img_height = '';
352
353
		$max_side = max( $width, $height );
354
		$image_url = get_site_icon_url( $max_side );
355
		$image_id = get_option( 'site_icon' );
356
		$image_size = wp_get_attachment_image_src( $image_id, $max_side >= 512
357
			? 'full'
358
			: array( $max_side, $max_side ) );
359 View Code Duplication
		if ( isset( $image_size[1], $image_size[2] ) ) {
360
			$img_width  = $image_size[1];
361
			$img_height = $image_size[2];
362
		}
363
364 View Code Duplication
		if (_jetpack_og_get_image_validate_size($img_width, $img_height, $width, $height)) {
365
			$image['src']     = $image_url;
366
			$image['width']   = $width;
367
			$image['height']  = $height;
368
		}
369
	}
370
371
	// Finally fall back, blank image
372
	if ( empty( $image ) ) {
373
		/**
374
		 * Filter the default Open Graph Image tag, used when no Image can be found in a post.
375
		 *
376
		 * @since 3.0.0
377
		 *
378
		 * @param string $str Default Image URL.
379
		 */
380
		$image['src'] = apply_filters( 'jetpack_open_graph_image_default', 'https://s0.wp.com/i/blank.jpg' );
381
	}
382
383
	return $image;
384
}
385
386
387
/**
388
* Validate the width and height against required width and height
389
*
390
* @param $width      int  Width of the image
391
* @param $height     int  Height of the image
392
* @param $req_width  int  Required width to pass validation
393
* @param $req_height int  Required height to pass validation
394
* @return bool - True if the image passed the required size validation
395
*/
396
function _jetpack_og_get_image_validate_size($width, $height, $req_width, $req_height) {
397
	if (!$width || !$height) {
398
		return false;
399
	}
400
401
	$valid_width = ( $width >= $req_width );
402
	$valid_height = ( $height >= $req_height );
403
	$is_image_acceptable = $valid_width && $valid_height;
404
	return $is_image_acceptable;
405
}
406
407
/**
408
 * Gets a gravatar URL of the specified size.
409
 *
410
 * @param string $email E-mail address to get gravatar for.
411
 * @param int    $width Size of returned gravatar.
412
 * @return array|bool|mixed|string
413
 */
414
function jetpack_og_get_image_gravatar( $email, $width ) {
415
	return get_avatar_url( $email, array(
416
		'size' => $width,
417
	) );
418
}
419