Completed
Push — fix/flickr-shortcode ( 3e5712...02d728 )
by
unknown
24:33 queued 14:33
created

class.jetpack-twitter-cards.php (4 issues)

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
/*
4
 * Twitter Cards
5
 *
6
 * Hooks onto the Open Graph protocol and extends it by adding only the tags
7
 * we need for twitter cards.
8
 *
9
 * @see /wp-content/blog-plugins/open-graph.php
10
 * @see https://dev.twitter.com/cards/overview
11
 */
12
class Jetpack_Twitter_Cards {
13
14
	static function twitter_cards_tags( $og_tags ) {
15
		global $post;
16
17
		/**
18
		 * Maximum alt text length.
19
		 *
20
		 * @see https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/summary-card-with-large-image.html
21
		 */
22
		$alt_length = 420;
23
24
		if ( post_password_required() ) {
25
			return $og_tags;
26
		}
27
28
		/** This action is documented in class.jetpack.php */
29
		if ( apply_filters( 'jetpack_disable_twitter_cards', false ) ) {
30
			return $og_tags;
31
		}
32
33
		/*
34
		 * These tags apply to any page (home, archives, etc)
35
		 */
36
37
		// If we have information on the author/creator, then include that as well
38
		if ( ! empty( $post ) && ! empty( $post->post_author ) ) {
39
			/** This action is documented in modules/sharedaddy/sharing-sources.php */
40
			$handle = apply_filters( 'jetpack_sharing_twitter_via', '', $post->ID );
0 ignored issues
show
The call to apply_filters() has too many arguments starting with $post->ID.

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...
41
			if ( ! empty( $handle ) && ! self::is_default_site_tag( $handle ) ) {
42
				$og_tags['twitter:creator'] = self::sanitize_twitter_user( $handle );
43
			}
44
		}
45
46
		$site_tag = self::site_tag();
47
		/** This action is documented in modules/sharedaddy/sharing-sources.php */
48
		$site_tag = apply_filters( 'jetpack_sharing_twitter_via', $site_tag, ( is_singular() ? $post->ID : null ) );
0 ignored issues
show
The call to apply_filters() has too many arguments starting with is_singular() ? $post->ID : null.

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...
49
		/** This action is documented in modules/sharedaddy/sharing-sources.php */
50
		$site_tag = apply_filters( 'jetpack_twitter_cards_site_tag', $site_tag, $og_tags );
0 ignored issues
show
The call to apply_filters() has too many arguments starting with $og_tags.

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...
51
		if ( ! empty( $site_tag ) ) {
52
			$og_tags['twitter:site'] = self::sanitize_twitter_user( $site_tag );
53
		}
54
55
		if ( ! is_singular() || ! empty( $og_tags['twitter:card'] ) ) {
56
			/**
57
			 * Filter the default Twitter card image, used when no image can be found in a post.
58
			 *
59
			 * @module sharedaddy, publicize
60
			 *
61
			 * @since 5.9.0
62
			 *
63
			 * @param string $str Default image URL.
64
			 */
65
			$image = apply_filters( 'jetpack_twitter_cards_image_default', '' );
66
			if ( ! empty( $image ) ) {
67
				$og_tags['twitter:image'] = $image;
68
			}
69
70
			return $og_tags;
71
		}
72
73
		$the_title = get_the_title();
74
		if ( ! $the_title ) {
75
			$the_title = get_bloginfo( 'name' );
76
		}
77
		$og_tags['twitter:text:title'] = $the_title;
78
79
		/*
80
		 * The following tags only apply to single pages.
81
		 */
82
83
		$card_type = 'summary';
84
85
		// Try to give priority to featured images
86
		if ( class_exists( 'Jetpack_PostImages' ) ) {
87
			$post_image = Jetpack_PostImages::get_image(
88
				$post->ID,
89
				array(
90
					'width'  => 144,
91
					'height' => 144,
92
				)
93
			);
94
			if ( ! empty( $post_image ) && is_array( $post_image ) ) {
95
				// 4096 is the maximum size for an image per https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/summary .
96
				if ( (int) $post_image['src_width'] <= 4096 && (int) $post_image['src_height'] <= 4096 ) {
97
					// 300x157 is the minimum size for a summary_large_image per https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/summary-card-with-large-image .
98
					if ( (int) $post_image['src_width'] >= 300 && (int) $post_image['src_height'] >= 157 ) {
99
						$card_type                = 'summary_large_image';
100
						$og_tags['twitter:image'] = esc_url( add_query_arg( 'w', 640, $post_image['src'] ) );
101
					} else {
102
						$og_tags['twitter:image'] = esc_url( add_query_arg( 'w', 144, $post_image['src'] ) );
103
					}
104
105
					// Add the alt tag if we have one.
106
					if ( ! empty( $post_image['alt_text'] ) ) {
107
						// Shorten it if it is too long.
108
						if ( strlen( $post_image['alt_text'] ) > $alt_length ) {
109
							$og_tags['twitter:image:alt'] = esc_attr( mb_substr( $post_image['alt_text'], 0, $alt_length ) . '…' );
110
						} else {
111
							$og_tags['twitter:image:alt'] = esc_attr( $post_image['alt_text'] );
112
						}
113
					}
114
				}
115
			}
116
		}
117
118
		// Only proceed with media analysis if a featured image has not superseded it already.
119
		if ( empty( $og_tags['twitter:image'] ) && empty( $og_tags['twitter:image:src'] ) ) {
120 View Code Duplication
			if ( ! class_exists( 'Jetpack_Media_Summary' ) && defined( 'IS_WPCOM' ) && IS_WPCOM ) {
121
				include WP_CONTENT_DIR . '/lib/class.wpcom-media-summary.php';
122
			}
123
124
			if ( ! class_exists( 'Jetpack_Media_Summary' ) ) {
125
				jetpack_require_lib( 'class.media-summary' );
126
			}
127
128
			// Test again, class should already be auto-loaded in Jetpack.
129
			// If not, skip extra media analysis and stick with a summary card
130
			if ( class_exists( 'Jetpack_Media_Summary' ) ) {
131
				$extract = Jetpack_Media_Summary::get( $post->ID );
132
133
				if ( 'gallery' == $extract['type'] ) {
134
					list( $og_tags, $card_type ) = self::twitter_cards_define_type_based_on_image_count( $og_tags, $extract );
135
				} elseif ( 'video' == $extract['type'] ) {
136
					// Leave as summary, but with large pict of poster frame (we know those comply to Twitter's size requirements)
137
					$card_type                = 'summary_large_image';
138
					$og_tags['twitter:image'] = esc_url( add_query_arg( 'w', 640, $extract['image'] ) );
139
				} else {
140
					list( $og_tags, $card_type ) = self::twitter_cards_define_type_based_on_image_count( $og_tags, $extract );
141
				}
142
			}
143
		}
144
145
		$og_tags['twitter:card'] = $card_type;
146
147
		// Make sure we have a description for Twitter, their validator isn't happy without some content (single space not valid).
148
		if ( ! isset( $og_tags['og:description'] ) || '' == trim( $og_tags['og:description'] ) || __( 'Visit the post for more.', 'jetpack' ) == $og_tags['og:description'] ) { // empty( trim( $og_tags['og:description'] ) ) isn't valid php
149
			$has_creator = ( ! empty( $og_tags['twitter:creator'] ) && '@wordpressdotcom' != $og_tags['twitter:creator'] ) ? true : false;
150
			if ( ! empty( $extract ) && 'video' == $extract['type'] ) { // use $extract['type'] since $card_type is 'summary' for video posts
151
				/* translators: %s is the post author */
152
				$og_tags['twitter:description'] = ( $has_creator ) ? sprintf( __( 'Video post by %s.', 'jetpack' ), $og_tags['twitter:creator'] ) : __( 'Video post.', 'jetpack' );
153
			} else {
154
				/* translators: %s is the post author */
155
				$og_tags['twitter:description'] = ( $has_creator ) ? sprintf( __( 'Post by %s.', 'jetpack' ), $og_tags['twitter:creator'] ) : __( 'Visit the post for more.', 'jetpack' );
156
			}
157
		}
158
159
		if ( empty( $og_tags['twitter:image'] ) && empty( $og_tags['twitter:image:src'] ) ) {
160
			/** This action is documented in class.jetpack-twitter-cards.php */
161
			$image = apply_filters( 'jetpack_twitter_cards_image_default', '' );
162
			if ( ! empty( $image ) ) {
163
				$og_tags['twitter:image'] = $image;
164
			}
165
		}
166
167
		return $og_tags;
168
	}
169
170
	static function sanitize_twitter_user( $str ) {
171
		return '@' . preg_replace( '/^@/', '', $str );
172
	}
173
174
	static function is_default_site_tag( $site_tag ) {
175
		return in_array( $site_tag, array( '@wordpressdotcom', '@jetpack', 'wordpressdotcom', 'jetpack' ) );
176
	}
177
178
	static function prioritize_creator_over_default_site( $site_tag, $og_tags = array() ) {
179
		if ( ! empty( $og_tags['twitter:creator'] ) && self::is_default_site_tag( $site_tag ) ) {
180
			return $og_tags['twitter:creator'];
181
		}
182
		return $site_tag;
183
	}
184
185
	static function twitter_cards_define_type_based_on_image_count( $og_tags, $extract ) {
186
		$card_type = 'summary';
187
		$img_count = $extract['count']['image'];
188
189
		if ( empty( $img_count ) ) {
190
191
			// No images, use Blavatar as a thumbnail for the summary type.
192
			if ( function_exists( 'blavatar_domain' ) ) {
193
				$blavatar_domain = blavatar_domain( site_url() );
194
				if ( blavatar_exists( $blavatar_domain ) ) {
195
					$og_tags['twitter:image'] = blavatar_url( $blavatar_domain, 'img', 240 );
196
				}
197
			}
198
199
			// Second fall back, Site Logo
200
			if ( empty( $og_tags['twitter:image'] ) && ( function_exists( 'jetpack_has_site_logo' ) && jetpack_has_site_logo() ) ) {
201
				$og_tags['twitter:image'] = jetpack_get_site_logo( 'url' );
202
			}
203
204
			// Third fall back, Site Icon
205
			if ( empty( $og_tags['twitter:image'] ) && has_site_icon() ) {
206
				$og_tags['twitter:image'] = get_site_icon_url( '240' );
207
			}
208
209
			// Not falling back on Gravatar, because there's no way to know if we end up with an auto-generated one.
210
211
		} elseif ( $img_count && ( 'image' == $extract['type'] || 'gallery' == $extract['type'] ) ) {
212
			// Test for $extract['type'] to limit to image and gallery, so we don't send a potential fallback image like a Gravatar as a photo post.
213
			$card_type                = 'summary_large_image';
214
			$og_tags['twitter:image'] = esc_url( add_query_arg( 'w', 1400, ( empty( $extract['images'] ) ) ? $extract['image'] : $extract['images'][0]['url'] ) );
215
		}
216
217
		return array( $og_tags, $card_type );
218
	}
219
220
	static function twitter_cards_output( $og_tag ) {
221
		return ( false !== strpos( $og_tag, 'twitter:' ) ) ? preg_replace( '/property="([^"]+)"/', 'name="\1"', $og_tag ) : $og_tag;
222
	}
223
224
	static function settings_init() {
225
		add_settings_section( 'jetpack-twitter-cards-settings', 'Twitter Cards', '__return_false', 'sharing' );
226
		add_settings_field(
227
			'jetpack-twitter-cards-site-tag',
228
			__( 'Twitter Site Tag', 'jetpack' ),
229
			array( __CLASS__, 'settings_field' ),
230
			'sharing',
231
			'jetpack-twitter-cards-settings',
232
			array(
233
				'label_for' => 'jetpack-twitter-cards-site-tag',
234
			)
235
		);
236
	}
237
238
	static function sharing_global_options() {
239
		do_settings_fields( 'sharing', 'jetpack-twitter-cards-settings' );
240
	}
241
242
	static function site_tag() {
243
		$site_tag = ( defined( 'IS_WPCOM' ) && IS_WPCOM ) ?
244
			trim( get_option( 'twitter_via' ) ) :
245
			Jetpack_Options::get_option_and_ensure_autoload( 'jetpack-twitter-cards-site-tag', '' );
246
		if ( empty( $site_tag ) ) {
247
			/** This action is documented in modules/sharedaddy/sharing-sources.php */
248
			return apply_filters( 'jetpack_sharing_twitter_via', '', null );
0 ignored issues
show
The call to apply_filters() has too many arguments starting with null.

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...
249
		}
250
		return $site_tag;
251
	}
252
253
	static function settings_field() {
254
		wp_nonce_field( 'jetpack-twitter-cards-settings', 'jetpack_twitter_cards_nonce', false );
255
		?>
256
		<input type="text" id="jetpack-twitter-cards-site-tag" class="regular-text" name="jetpack-twitter-cards-site-tag" value="<?php echo esc_attr( get_option( 'jetpack-twitter-cards-site-tag' ) ); ?>" />
257
		<p class="description" style="width: auto;"><?php esc_html_e( 'The Twitter username of the owner of this site\'s domain.', 'jetpack' ); ?></p>
258
		<?php
259
	}
260
261
	static function settings_validate() {
262
		if ( wp_verify_nonce( $_POST['jetpack_twitter_cards_nonce'], 'jetpack-twitter-cards-settings' ) ) {
263
			update_option( 'jetpack-twitter-cards-site-tag', trim( ltrim( strip_tags( $_POST['jetpack-twitter-cards-site-tag'] ), '@' ) ) );
264
		}
265
	}
266
267
	static function init() {
268
		add_filter( 'jetpack_open_graph_tags', array( __CLASS__, 'twitter_cards_tags' ) );
269
		add_filter( 'jetpack_open_graph_output', array( __CLASS__, 'twitter_cards_output' ) );
270
		add_filter( 'jetpack_twitter_cards_site_tag', array( __CLASS__, 'site_tag' ), -99 );
271
		add_filter( 'jetpack_twitter_cards_site_tag', array( __CLASS__, 'prioritize_creator_over_default_site' ), 99, 2 );
272
		add_action( 'admin_init', array( __CLASS__, 'settings_init' ) );
273
		add_action( 'sharing_global_options', array( __CLASS__, 'sharing_global_options' ) );
274
		add_action( 'sharing_admin_update', array( __CLASS__, 'settings_validate' ) );
275
	}
276
}
277
278
Jetpack_Twitter_Cards::init();
279