PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.3-beta
16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 All 507 releases
← All changes | jetpack_vendor/automattic/jetpack-comments/src/class-avatars.php +94 -30 16.2 → 16.3-beta View file →
@@ -14,26 +14,16 @@
14 14 */
15 15 class Avatars {
16 16
17 17 /**
18 - * Comment meta holding a stored avatar URL.
19 - */
20 - const AVATAR_META = 'hc_avatar';
21 -
22 - /**
23 - * Hosts whose avatars are served.
18 + * Register the avatar filters.
24 19 *
25 - * @var string[]
26 - */
27 - private static $avatar_hosts = array( 'graph.facebook.com', 'twimg.com' );
28 -
29 - /**
30 - * Register the avatar filter.
31 - *
32 20 * @return void
33 21 */
34 22 public static function init() {
35 23 add_filter( 'pre_get_avatar_data', array( __CLASS__, 'avatar_data' ), 10, 2 );
24 + // WordPress.com replaces get_avatar() with its own, which never reaches pre_get_avatar_data.
25 + add_filter( 'wpcom_get_avatar_url', array( __CLASS__, 'wpcom_avatar_url' ), 10, 6 );
36 26 }
37 27
38 28 /**
39 29 * Serve a stored avatar for comments that carry one.
@@ -46,41 +36,115 @@
46 36 if ( ! $id_or_email instanceof \WP_Comment || isset( $args['url'] ) ) {
47 37 return $args;
48 38 }
49 39
50 - $stored = get_comment_meta( (int) $id_or_email->comment_ID, self::AVATAR_META, true );
40 + $url = self::stored_url( (int) $id_or_email->comment_ID, isset( $args['size'] ) ? (int) $args['size'] : 96 );
51 41
52 - if ( ! is_string( $stored ) || $stored === '' || ! self::is_servable_avatar( $stored ) ) {
53 - return $args;
42 + if ( null !== $url ) {
43 + $args['url'] = $url;
44 + $args['found_avatar'] = true;
45 + } elseif ( self::is_signed_in( (int) $id_or_email->comment_ID ) ) {
46 + // The provider had no photo: the site default, not a Gravatar the commenter never chose.
47 + $args['force_default'] = true;
54 48 }
55 49
56 - $size = isset( $args['size'] ) ? (int) $args['size'] : 96;
50 + return $args;
51 + }
57 52
58 - $args['url'] = Image_CDN_Core::cdn_url( $stored, array( 'resize' => "$size,$size" ) );
53 + /**
54 + * Serve a stored avatar on WordPress.com.
55 + *
56 + * @param array|false $url_class Avatar URL and CSS class, or false.
57 + * @param mixed $id_or_email What the avatar was requested for.
58 + * @param int|string $size Avatar size.
59 + * @param string $default_value Default avatar. Unused.
60 + * @param bool $force_display Whether to show avatars when disabled. Unused.
61 + * @param bool $force_default Whether to force the default avatar.
62 + * @return array|false
63 + */
64 + public static function wpcom_avatar_url( $url_class, $id_or_email, $size = 96, $default_value = '', $force_display = false, $force_default = false ) {
65 + if ( $force_default || ! is_array( $url_class ) || ! is_object( $id_or_email ) || empty( $id_or_email->comment_ID ) ) {
66 + return $url_class;
67 + }
59 68
60 - $args['found_avatar'] = true;
69 + $url = self::stored_url( (int) $id_or_email->comment_ID, (int) $size );
61 70
62 - return $args;
71 + if ( null !== $url ) {
72 + $url_class[0] = $url;
73 + } elseif ( self::is_signed_in( (int) $id_or_email->comment_ID ) ) {
74 + $url_class[0] = self::default_url( (int) $size );
75 + $url_class[1] = ( isset( $url_class[1] ) ? $url_class[1] . ' ' : '' ) . 'avatar-default';
76 + }
77 +
78 + return $url_class;
63 79 }
64 80
65 81 /**
66 - * Whether a stored avatar URL is one we are willing to serve.
82 + * The site's default avatar, resolved the way the host resolves it.
67 83 *
68 - * @param string $url The stored avatar URL.
84 + * @param int $size Avatar size.
85 + * @return string
86 + */
87 + public static function default_url( $size ) {
88 + if ( function_exists( 'wpcom_get_avatar_url' ) ) {
89 + // Re-enters wpcom_avatar_url() with no comment, so it returns early there.
90 + $url_class = wpcom_get_avatar_url( '', $size, '', false, true );
91 +
92 + // Built for HTML, so its query string is joined with &, which Gravatar reads as a parameter named amp;d.
93 + return is_array( $url_class ) ? html_entity_decode( (string) $url_class[0], ENT_QUOTES ) : '';
94 + }
95 +
96 + return (string) get_avatar_url(
97 + '',
98 + array(
99 + 'size' => $size,
100 + 'force_default' => true,
101 + )
102 + );
103 + }
104 +
105 + /**
106 + * Whether the comment was left through a popup sign-in.
107 + *
108 + * @param int $comment_id The comment ID.
69 109 * @return bool
70 110 */
71 - private static function is_servable_avatar( $url ) {
72 - $host = wp_parse_url( $url, PHP_URL_HOST );
111 + private static function is_signed_in( $comment_id ) {
112 + return '' !== (string) get_comment_meta( $comment_id, Checkpoint::META_PROVIDER, true );
113 + }
73 114
74 - if ( ! is_string( $host ) ) {
75 - return false;
115 + /**
116 + * The stored avatar for a comment, sized through the image CDN.
117 + *
118 + * @param int $comment_id The comment ID.
119 + * @param int $size Avatar size.
120 + * @return string|null Null when the comment carries no servable avatar.
121 + */
122 + private static function stored_url( $comment_id, $size ) {
123 + // WordPress.com asks twice per comment, through get_avatar_data() and again through wpcom_get_avatar_url.
124 + static $resolved = array();
125 +
126 + $key = get_current_blog_id() . ":$comment_id:$size";
127 +
128 + if ( array_key_exists( $key, $resolved ) ) {
129 + return $resolved[ $key ];
76 130 }
77 131
78 - foreach ( self::$avatar_hosts as $allowed ) {
79 - if ( $host === $allowed || str_ends_with( $host, ".$allowed" ) ) {
80 - return true;
132 + // Written only from an authenticated exchange with WordPress.com, so any https URL is served.
133 + $stored = get_comment_meta( $comment_id, Checkpoint::META_AVATAR, true );
134 +
135 + if ( ! is_string( $stored ) || $stored === '' || 'https' !== wp_parse_url( $stored, PHP_URL_SCHEME ) ) {
136 + // Highlander and Verbum stored a Facebook or X avatar here, which the email cannot
137 + // bring back. Written by the browser, so only those two hosts are served.
138 + $stored = get_comment_meta( $comment_id, 'hc_avatar', true );
139 + $host = is_string( $stored ) ? wp_parse_url( $stored, PHP_URL_HOST ) : null;
140 +
141 + if ( ! is_string( $host ) || ! preg_match( '/(^|\.)(graph\.facebook\.com|twimg\.com)$/', $host ) ) {
142 + $stored = null;
81 143 }
82 144 }
83 145
84 - return false;
146 + $resolved[ $key ] = null === $stored ? null : Image_CDN_Core::cdn_url( $stored, array( 'resize' => "$size,$size" ) );
147 +
148 + return $resolved[ $key ];
85 149 }
86 150 }