PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 13.0.2
Jetpack – WP Security, Backup, Speed, & Growth v13.0.2
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 13.8.3 13.9.2 14.0.1 14.1.1 14.2.2 14.3.1 14.4.2 14.5.1 14.6.1 14.7.1 14.8.1 14.9.2 15.0.3 15.1.2 15.2.1 15.3.2 15.4.1 15.5.1 15.6.1 15.7.2 15.8.1 15.9.2 16.0.2 16.1.3 16.2-a.5 16.2-a.3 16.1.2 16.2-a.1 16.1.1 16.1 16.1-beta 16.1-beta.2 16.1-beta.3 16.1-a.5 16.1-a.3 16.0.1 16.1-a.1 16.0 16.0-beta 16.0-a.7 16.0-a.5 15.9.1 16.0-a.3 16.0-a.1 15.9 15.9-beta 15.9-a.7 15.9-a.5 15.9-a.3 15.9-a.1 15.8 15.8-beta 15.8-a.7 15.8-a.5 5.2.5 5.3.4 5.4.4 5.5.5 5.6.5 5.7.5 5.8.4 5.9.4 6.0.4 6.1 6.1.1 6.1.2 6.1.3 6.1.4 6.1.5 6.2 6.2.1 6.2.2 6.2.3 6.2.4 6.2.5 6.3 6.3.1 6.3.2 6.3.3 6.3.4 6.3.5 6.3.6 6.3.7 6.4 6.4.1 6.4.2 6.4.3 6.4.4 6.4.5 6.4.6 6.5 6.5.1 6.5.2 6.5.3 6.5.4 6.6 6.6.1 6.6.2 6.6.3 6.6.4 6.6.5 6.7 6.7.1 6.7.2 6.7.3 6.7.4 6.8 6.8.1 6.8.2 6.8.3 6.8.4 6.8.5 6.9 6.9.1 6.9.2 6.9.3 6.9.4 7.0 7.0.1 7.0.2 7.0.3 7.0.4 7.0.5 7.1 7.1.1 7.1.2 7.1.3 7.1.4 7.1.5 7.2 7.2.1 7.2.1.1 7.2.2 7.2.3 7.2.4 7.2.5 7.3 7.3.0.1 7.3.1 7.3.1.1 7.3.2 7.3.3 7.3.4 7.3.5 7.4 7.4.1 7.4.2 7.4.3 7.4.4 7.4.5 7.5 7.5.0.1 7.5.1 7.5.2 7.5.3 7.5.4 7.5.5 7.5.6 7.5.7 7.6 7.6.1 7.6.2 7.6.3 7.6.4 7.7 7.7.1 7.7.2 7.7.3 7.7.4 7.7.5 7.7.6 7.8 7.8.1 7.8.2 7.8.3 7.8.4 7.9 7.9.1 7.9.2 7.9.3 7.9.4 8.0 8.0.1 8.0.2 8.0.3 8.1 8.1.1 8.1.2 8.1.3 8.1.4 8.2 8.2.0.1 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.2.6 8.3 8.3.1 8.3.2 8.3.3 8.4 8.4.1 8.4.2 8.4.3 8.4.4 8.4.5 8.5 8.5.1 8.5.2 8.5.3 8.6 8.6.1 8.6.2 8.6.3 8.6.4 8.7 8.7.0.1 8.7.1 8.7.2 8.7.3 8.7.4 8.8 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.9 8.9.1 8.9.2 8.9.3 8.9.4 9.0 9.0.1 9.0.2 9.0.3 9.0.4 9.0.5 9.1 9.1.1 9.1.2 9.1.3 9.2 9.2.1 9.2.2 9.2.3 9.2.4 9.3 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.4 9.4.1 9.4.2 9.4.3 9.4.4 9.5 9.5.1 9.5.2 9.5.3 9.5.4 9.5.5 9.6 9.6.1 9.6.2 9.6.3 9.6.4 9.7 9.7.1 9.7.2 15.7-beta.2 9.7.3 15.7.1 9.8 15.8-a.1 9.8.1 15.8-a.3 9.8.2 2.0.9 9.8.3 2.1.7 9.9 2.2.10 9.9.1 2.3.10 9.9.2 2.4.7 9.9.3 2.5.5 2.6.6 2.7.5 2.8.5 2.9.6 3.0.6 3.1.5 3.2.5 3.3.6 3.4.6 3.5.6 3.6.4 3.7.5 3.8.5 3.9.10 4.0.7 4.1.4 4.2.5 4.3.5 4.4.5 4.5.3 4.6.3 4.7.4 4.8.5 4.9.3 5.0.3 5.1.4 trunk 10.0 10.0.1 10.0.2 10.1 10.1.1 10.1.2 10.2 10.2.1 10.2.2 10.2.3 10.3 10.3.1 10.3.2 10.4 10.4.1 10.4.2 10.5 10.5.1 10.5.2 10.5.3 10.6 10.6.1 10.6.2 10.7 10.7.1 10.7.2 10.8 10.8.1 10.8.2 10.9 10.9.1 10.9.2 10.9.3 11.0 11.0.1 11.0.2 11.1 11.1.1 11.1.2 11.1.3 11.1.4 11.2 11.2.1 11.2.2 11.3 11.3.1 11.3.2 11.3.3 11.3.4 11.4 11.4.1 11.4.2 11.5 11.5.1 11.5.2 11.5.3 11.6 11.6.1 11.6.2 11.7 11.7.1 11.7.2 11.7.3 11.8 11.8.3 11.8.4 11.8.5 11.8.6 11.9 11.9.1 11.9.2 11.9.3 12.0 12.0.1 12.0.2 12.1 12.1.1 12.1.2 12.2 12.2.1 12.2.2 12.3 12.3.1 12.4 12.4.1 12.5 12.5.1 12.6 12.6.1 12.6.2 12.6.3 12.7 12.7.1 12.7.2 12.8 12.8.1 12.8.2 12.9 12.9.1 12.9.2 12.9.3 12.9.4 13.0 13.0.1 13.1 13.1.1 13.1.2 13.1.3 13.1.4 13.2 13.2.1 13.2.2 13.2.3 13.3 13.3.1 13.3.2 13.4 13.4.1 13.4.2 13.4.3 13.4.4 13.5 13.5.1 13.6 13.6.1 13.7 13.7.1 13.8 13.8.1 13.8.2 13.9 13.9.1 14.0 14.1 14.2 14.2.1 14.3 14.4 14.4.1 14.5 14.6 14.7 14.8 14.9 14.9.1 15.0 15.0.1 15.0.2 15.1 15.1.1 15.2 15.3 15.3.1 15.4 15.5 15.6 15.7 15.7-a.1 15.7-a.3 15.7-a.5 15.7-a.7 15.7-beta
jetpack / extensions / blocks / pinterest / pinterest.php
jetpack / extensions / blocks / pinterest Last commit date
pinterest.php 2 years ago
pinterest.php
245 lines
1 <?php
2 /**
3 * Pinterest Block.
4 *
5 * @since 8.0.0
6 *
7 * @package automattic/jetpack
8 */
9
10 namespace Automattic\Jetpack\Extensions\Pinterest;
11
12 use Automattic\Jetpack\Blocks;
13 use WP_Error;
14
15 const URL_PATTERN = '#^https?://(?:www\.)?(?:[a-z]{2}\.)?pinterest\.[a-z.]+/pin/(?P<pin_id>[^/]+)/?#i'; // Taken from AMP plugin, originally from Jetpack.
16 // This is the validate Pinterest URLs, converted from URL_REGEX in extensions/blocks/pinterest/index.js.
17 const PINTEREST_URL_REGEX = '/^https?:\/\/(?:www\.)?(?:[a-z]{2}\.)?(?:pinterest\.[a-z.]+|pin\.it)\/([^\/]+)(\/[^\/]+)?/i';
18 // This looks for matches in /foo/ of https://www.pinterest.ca/foo/.
19 const REMAINING_URL_PATH_REGEX = '/^\/([^\/]+)\/?$/';
20 // This looks for matches with /foo/bar/ of https://www.pinterest.ca/foo/bar/.
21 const REMAINING_URL_PATH_WITH_SUBPATH_REGEX = '/^\/([^\/]+)\/([^\/]+)\/?$/';
22
23 /**
24 * Determines the Pinterest embed type from the URL.
25 *
26 * @param string $url the URL to check.
27 * @returns {string} The pin type. Empty string if it isn't a valid Pinterest URL.
28 */
29 function pin_type( $url ) {
30 if ( null === $url || ! preg_match( PINTEREST_URL_REGEX, $url ) ) {
31 return '';
32 }
33
34 $path = wp_parse_url( $url, PHP_URL_PATH );
35
36 if ( ! $path ) {
37 return '';
38 }
39
40 if ( str_starts_with( $path, '/pin/' ) ) {
41 return 'embedPin';
42 }
43
44 if ( preg_match( REMAINING_URL_PATH_REGEX, $path ) ) {
45 return 'embedUser';
46 }
47
48 if ( preg_match( REMAINING_URL_PATH_WITH_SUBPATH_REGEX, $path ) ) {
49 return 'embedBoard';
50 }
51
52 return '';
53 }
54
55 /**
56 * Registers the block for use in Gutenberg
57 * This is done via an action so that we can disable
58 * registration if we need to.
59 */
60 function register_block() {
61 Blocks::jetpack_register_block(
62 __DIR__,
63 array( 'render_callback' => __NAMESPACE__ . '\load_assets' )
64 );
65 }
66 add_action( 'init', __NAMESPACE__ . '\register_block' );
67
68 /**
69 * Fetch info for a Pin.
70 *
71 * This is using the same pin info API as AMP is using client-side in the amp-pinterest component.
72 * Successful API responses are cached in a transient for 1 month. Unsuccessful responses are cached for 1 hour.
73 *
74 * @link https://github.com/ampproject/amphtml/blob/b5dea36e0b8bd012585d50839766a084f99a3685/extensions/amp-pinterest/0.1/pin-widget.js#L83-L97
75 * @param string $pin_id Pin ID.
76 * @return array|WP_Error Pin info or error on failure.
77 */
78 function fetch_pin_info( $pin_id ) {
79 $transient_id = substr( "jetpack_pin_info_{$pin_id}", 0, 172 );
80
81 $info = get_transient( $transient_id );
82 if ( is_array( $info ) || is_wp_error( $info ) ) {
83 return $info;
84 }
85
86 $pin_info_api_url = add_query_arg(
87 array(
88 'pin_ids' => rawurlencode( $pin_id ),
89 'sub' => 'wwww',
90 'base_scheme' => 'https',
91 ),
92 'https://widgets.pinterest.com/v3/pidgets/pins/info/'
93 );
94
95 $response = wp_remote_get( esc_url_raw( $pin_info_api_url ) );
96 if ( is_wp_error( $response ) ) {
97 set_transient( $transient_id, $response, HOUR_IN_SECONDS );
98 return $response;
99 }
100
101 $error = null;
102 $body = json_decode( wp_remote_retrieve_body( $response ), true );
103 if ( ! is_array( $body ) || ! isset( $body['status'] ) ) {
104 $error = new WP_Error( 'bad_json_response', '', compact( 'pin_id' ) );
105 } elseif ( 'success' !== $body['status'] || ! isset( $body['data'][0] ) ) {
106 $error = new WP_Error( 'unsuccessful_request', '', compact( 'pin_id' ) );
107 } elseif ( ! isset( $body['data'][0]['images']['237x'] ) ) {
108 // See <https://github.com/ampproject/amphtml/blob/b5dea36e0b8bd012585d50839766a084f99a3685/extensions/amp-pinterest/0.1/pin-widget.js#L106>.
109 $error = new WP_Error( 'missing_required_image', '', compact( 'pin_id' ) );
110 }
111
112 if ( $error ) {
113 set_transient( $transient_id, $error, HOUR_IN_SECONDS );
114 return $error;
115 } else {
116 $data = $body['data'][0];
117 set_transient( $transient_id, $data, MONTH_IN_SECONDS );
118 return $data;
119 }
120 }
121
122 /**
123 * Render a Pin using the amp-pinterest component.
124 *
125 * This does not render boards or user profiles.
126 *
127 * Since AMP components need to be statically sized to be valid (so as to avoid layout shifting), there are quite a few
128 * hard-coded numbers as taken from the CSS for the AMP component.
129 *
130 * @param array $attr Block attributes.
131 * @return string Markup for <amp-pinterest>.
132 */
133 function render_amp_pin( $attr ) {
134 $info = null;
135 if ( preg_match( URL_PATTERN, $attr['url'], $matches ) ) {
136 $info = fetch_pin_info( $matches['pin_id'] );
137 }
138
139 if ( is_array( $info ) ) {
140 $image = $info['images']['237x'];
141 $title = isset( $info['rich_metadata']['title'] ) ? $info['rich_metadata']['title'] : null;
142 $description = isset( $info['rich_metadata']['description'] ) ? $info['rich_metadata']['description'] : null;
143
144 // This placeholder will appear while waiting for the amp-pinterest component to initialize (or if it fails to initialize due to JS being disabled).
145 $placeholder = sprintf(
146 // The AMP_Img_Sanitizer will convert his to <amp-img> while also supplying `noscript > img` as fallback when JS is disabled.
147 '<a href="%s" placeholder><img src="%s" alt="%s" layout="fill" object-fit="contain" object-position="top left"></a>',
148 esc_url( $attr['url'] ),
149 esc_url( $image['url'] ),
150 esc_attr( $title )
151 );
152
153 $amp_padding = 5; // See <https://github.com/ampproject/amphtml/blob/b5dea36e0b8bd012585d50839766a084f99a3685/extensions/amp-pinterest/0.1/amp-pinterest.css#L269>.
154 $amp_fixed_width = 237; // See <https://github.com/ampproject/amphtml/blob/b5dea36e0b8bd012585d50839766a084f99a3685/extensions/amp-pinterest/0.1/amp-pinterest.css#L270>.
155 $pin_info_height = 60; // Minimum Obtained by measuring the height of the .-amp-pinterest-embed-pin-text element.
156
157 // Add height based on how much description there is. There are roughly 30 characters on a line of description text.
158 $has_description = false;
159 if ( ! empty( $info['description'] ) ) {
160 $desc_padding_top = 5; // See <https://github.com/ampproject/amphtml/blob/b5dea36e0b8bd012585d50839766a084f99a3685/extensions/amp-pinterest/0.1/amp-pinterest.css#L342>.
161 $pin_info_height += $desc_padding_top;
162
163 // Trim whitespace on description if there is any left, use to calculate the likely rows of text.
164 $description = trim( $info['description'] );
165 if ( strlen( $description ) > 0 ) {
166 $has_description = true;
167 $desc_line_height = 17; // See <https://github.com/ampproject/amphtml/blob/b5dea36e0b8bd012585d50839766a084f99a3685/extensions/amp-pinterest/0.1/amp-pinterest.css#L341>.
168 $pin_info_height += ceil( strlen( $description ) / 30 ) * $desc_line_height;
169 }
170 }
171
172 if ( ! empty( $info['repin_count'] ) ) {
173 $pin_stats_height = 16; // See <https://github.com/ampproject/amphtml/blob/b5dea36e0b8bd012585d50839766a084f99a3685/extensions/amp-pinterest/0.1/amp-pinterest.css#L322>.
174 $pin_info_height += $pin_stats_height;
175 }
176
177 // When Pin description is empty, make sure title and description from rich metadata are supplied for accessibility and discoverability.
178 $title = $has_description ? '' : implode( "\n", array_filter( array( $title, $description ) ) );
179
180 $amp_pinterest = sprintf(
181 '<amp-pinterest style="%1$s" data-do="embedPin" data-url="%2$s" width="%3$d" height="%4$d" title="%5$s">%6$s</amp-pinterest>',
182 esc_attr( 'line-height:1.5; font-size:21px' ), // Override styles from theme due to precise height calculations above.
183 esc_url( $attr['url'] ),
184 $amp_fixed_width + ( $amp_padding * 2 ),
185 $image['height'] + $pin_info_height + ( $amp_padding * 2 ),
186 esc_attr( $title ),
187 $placeholder
188 );
189 } else {
190 // Fallback embed when info is not available.
191 $amp_pinterest = sprintf(
192 '<amp-pinterest data-do="embedPin" data-url="%1$s" width="%2$d" height="%3$d">%4$s</amp-pinterest>',
193 esc_url( $attr['url'] ),
194 450, // Fallback width.
195 750, // Fallback height.
196 sprintf(
197 '<a placeholder href="%s">%s</a>',
198 esc_url( $attr['url'] ),
199 esc_html( $attr['url'] )
200 )
201 );
202 }
203
204 return sprintf(
205 '<div class="wp-block-jetpack-pinterest">%s</div>',
206 $amp_pinterest
207 );
208 }
209
210 /**
211 * Pinterest block registration/dependency declaration.
212 *
213 * @param array $attr Array containing the Pinterest block attributes.
214 * @param string $content String containing the Pinterest block content.
215 *
216 * @return string
217 */
218 function load_assets( $attr, $content ) {
219 if ( ! jetpack_is_frontend() ) {
220 return $content;
221 }
222 if ( Blocks::is_amp_request() ) {
223 return render_amp_pin( $attr );
224 } else {
225 $url = $attr['url'];
226 $type = pin_type( $url );
227
228 if ( ! $type ) {
229 return '';
230 }
231
232 wp_enqueue_script( 'pinterest-pinit', 'https://assets.pinterest.com/js/pinit.js', array(), JETPACK__VERSION, true );
233 return sprintf(
234 '
235 <div class="%1$s">
236 <a data-pin-do="%2$s" href="%3$s"></a>
237 </div>
238 ',
239 esc_attr( Blocks::classes( Blocks::get_block_feature( __DIR__ ), $attr ) ),
240 esc_attr( $type ),
241 esc_url( $url )
242 );
243 }
244 }
245