PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.0-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.0-beta
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 1 month ago
pinterest.php
255 lines
1 <?php
2 /**
3 * Pinterest Block.
4 *
5 * Note: this block is no longer available to be added to new posts.
6 * It is only kept available for existing posts with Pinterest blocks.
7 * You can still embed Pinterest content using the embed method provided by WordPress itself.
8 *
9 * @since 8.0.0
10 *
11 * @package automattic/jetpack
12 */
13
14 namespace Automattic\Jetpack\Extensions\Pinterest;
15
16 use Automattic\Jetpack\Blocks;
17 use Automattic\Jetpack\Status\Request;
18 use WP_Error;
19
20 if ( ! defined( 'ABSPATH' ) ) {
21 exit( 0 );
22 }
23
24 const URL_PATTERN = '#^https?://(?:www\.)?(?:[a-z]{2}\.)?pinterest\.[a-z.]+/pin/(?P<pin_id>[^/]+)/?#i'; // Taken from AMP plugin, originally from Jetpack.
25 // This is the validate Pinterest URLs, converted from URL_REGEX in extensions/blocks/pinterest/index.js.
26 const PINTEREST_URL_REGEX = '/^https?:\/\/(?:www\.)?(?:[a-z]{2}\.)?(?:pinterest\.[a-z.]+|pin\.it)\/([^\/]+)(\/[^\/]+)?/i';
27 // This looks for matches in /foo/ of https://www.pinterest.ca/foo/.
28 const REMAINING_URL_PATH_REGEX = '/^\/([^\/]+)\/?$/';
29 // This looks for matches with /foo/bar/ of https://www.pinterest.ca/foo/bar/.
30 const REMAINING_URL_PATH_WITH_SUBPATH_REGEX = '/^\/([^\/]+)\/([^\/]+)\/?$/';
31
32 /**
33 * Determines the Pinterest embed type from the URL.
34 *
35 * @param string $url the URL to check.
36 * @return string The pin type. Empty string if it isn't a valid Pinterest URL.
37 */
38 function pin_type( $url ) {
39 if ( null === $url || ! preg_match( PINTEREST_URL_REGEX, $url ) ) {
40 return '';
41 }
42
43 $path = wp_parse_url( $url, PHP_URL_PATH );
44
45 if ( ! $path ) {
46 return '';
47 }
48
49 if ( str_starts_with( $path, '/pin/' ) ) {
50 return 'embedPin';
51 }
52
53 if ( preg_match( REMAINING_URL_PATH_REGEX, $path ) ) {
54 return 'embedUser';
55 }
56
57 if ( preg_match( REMAINING_URL_PATH_WITH_SUBPATH_REGEX, $path ) ) {
58 return 'embedBoard';
59 }
60
61 return '';
62 }
63
64 /**
65 * Registers the block for use in Gutenberg
66 * This is done via an action so that we can disable
67 * registration if we need to.
68 */
69 function register_block() {
70 Blocks::jetpack_register_block(
71 __DIR__,
72 array( 'render_callback' => __NAMESPACE__ . '\load_assets' )
73 );
74 }
75 add_action( 'init', __NAMESPACE__ . '\register_block' );
76
77 /**
78 * Fetch info for a Pin.
79 *
80 * This is using the same pin info API as AMP is using client-side in the amp-pinterest component.
81 * Successful API responses are cached in a transient for 1 month. Unsuccessful responses are cached for 1 hour.
82 *
83 * @link https://github.com/ampproject/amphtml/blob/b5dea36e0b8bd012585d50839766a084f99a3685/extensions/amp-pinterest/0.1/pin-widget.js#L83-L97
84 * @param string $pin_id Pin ID.
85 * @return array|WP_Error Pin info or error on failure.
86 */
87 function fetch_pin_info( $pin_id ) {
88 $transient_id = substr( "jetpack_pin_info_{$pin_id}", 0, 172 );
89
90 $info = get_transient( $transient_id );
91 if ( is_array( $info ) || is_wp_error( $info ) ) {
92 return $info;
93 }
94
95 $pin_info_api_url = add_query_arg(
96 array(
97 'pin_ids' => rawurlencode( $pin_id ),
98 'sub' => 'wwww',
99 'base_scheme' => 'https',
100 ),
101 'https://widgets.pinterest.com/v3/pidgets/pins/info/'
102 );
103
104 $response = wp_remote_get( esc_url_raw( $pin_info_api_url ) );
105 if ( is_wp_error( $response ) ) {
106 set_transient( $transient_id, $response, HOUR_IN_SECONDS );
107 return $response;
108 }
109
110 $error = null;
111 $body = json_decode( wp_remote_retrieve_body( $response ), true );
112 if ( ! is_array( $body ) || ! isset( $body['status'] ) ) {
113 $error = new WP_Error( 'bad_json_response', '', compact( 'pin_id' ) );
114 } elseif ( 'success' !== $body['status'] || ! isset( $body['data'][0] ) ) {
115 $error = new WP_Error( 'unsuccessful_request', '', compact( 'pin_id' ) );
116 } elseif ( ! isset( $body['data'][0]['images']['237x'] ) ) {
117 // See <https://github.com/ampproject/amphtml/blob/b5dea36e0b8bd012585d50839766a084f99a3685/extensions/amp-pinterest/0.1/pin-widget.js#L106>.
118 $error = new WP_Error( 'missing_required_image', '', compact( 'pin_id' ) );
119 }
120
121 if ( $error ) {
122 set_transient( $transient_id, $error, HOUR_IN_SECONDS );
123 return $error;
124 } else {
125 $data = $body['data'][0];
126 set_transient( $transient_id, $data, MONTH_IN_SECONDS );
127 return $data;
128 }
129 }
130
131 /**
132 * Render a Pin using the amp-pinterest component.
133 *
134 * This does not render boards or user profiles.
135 *
136 * Since AMP components need to be statically sized to be valid (so as to avoid layout shifting), there are quite a few
137 * hard-coded numbers as taken from the CSS for the AMP component.
138 *
139 * @param array $attr Block attributes.
140 * @return string Markup for <amp-pinterest>.
141 */
142 function render_amp_pin( $attr ) {
143 $info = null;
144 if ( preg_match( URL_PATTERN, $attr['url'], $matches ) ) {
145 $info = fetch_pin_info( $matches['pin_id'] );
146 }
147
148 if ( is_array( $info ) ) {
149 $image = $info['images']['237x'];
150 $title = $info['rich_metadata']['title'] ?? null;
151 $description = $info['rich_metadata']['description'] ?? null;
152
153 // This placeholder will appear while waiting for the amp-pinterest component to initialize (or if it fails to initialize due to JS being disabled).
154 $placeholder = sprintf(
155 // The AMP_Img_Sanitizer will convert his to <amp-img> while also supplying `noscript > img` as fallback when JS is disabled.
156 '<a href="%s" placeholder><img src="%s" alt="%s" layout="fill" object-fit="contain" object-position="top left"></a>',
157 esc_url( $attr['url'] ),
158 esc_url( $image['url'] ),
159 esc_attr( $title )
160 );
161
162 $amp_padding = 5; // See <https://github.com/ampproject/amphtml/blob/b5dea36e0b8bd012585d50839766a084f99a3685/extensions/amp-pinterest/0.1/amp-pinterest.css#L269>.
163 $amp_fixed_width = 237; // See <https://github.com/ampproject/amphtml/blob/b5dea36e0b8bd012585d50839766a084f99a3685/extensions/amp-pinterest/0.1/amp-pinterest.css#L270>.
164 $pin_info_height = 60; // Minimum Obtained by measuring the height of the .-amp-pinterest-embed-pin-text element.
165
166 // Add height based on how much description there is. There are roughly 30 characters on a line of description text.
167 $has_description = false;
168 if ( ! empty( $info['description'] ) ) {
169 $desc_padding_top = 5; // See <https://github.com/ampproject/amphtml/blob/b5dea36e0b8bd012585d50839766a084f99a3685/extensions/amp-pinterest/0.1/amp-pinterest.css#L342>.
170 $pin_info_height += $desc_padding_top;
171
172 // Trim whitespace on description if there is any left, use to calculate the likely rows of text.
173 $description = trim( $info['description'] );
174 if ( strlen( $description ) > 0 ) {
175 $has_description = true;
176 $desc_line_height = 17; // See <https://github.com/ampproject/amphtml/blob/b5dea36e0b8bd012585d50839766a084f99a3685/extensions/amp-pinterest/0.1/amp-pinterest.css#L341>.
177 $pin_info_height += ceil( strlen( $description ) / 30 ) * $desc_line_height;
178 }
179 }
180
181 if ( ! empty( $info['repin_count'] ) ) {
182 $pin_stats_height = 16; // See <https://github.com/ampproject/amphtml/blob/b5dea36e0b8bd012585d50839766a084f99a3685/extensions/amp-pinterest/0.1/amp-pinterest.css#L322>.
183 $pin_info_height += $pin_stats_height;
184 }
185
186 // When Pin description is empty, make sure title and description from rich metadata are supplied for accessibility and discoverability.
187 $title = $has_description ? '' : implode( "\n", array_filter( array( $title, $description ) ) );
188
189 $amp_pinterest = sprintf(
190 '<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>',
191 esc_attr( 'line-height:1.5; font-size:21px' ), // Override styles from theme due to precise height calculations above.
192 esc_url( $attr['url'] ),
193 $amp_fixed_width + ( $amp_padding * 2 ),
194 $image['height'] + $pin_info_height + ( $amp_padding * 2 ),
195 esc_attr( $title ),
196 $placeholder
197 );
198 } else {
199 // Fallback embed when info is not available.
200 $amp_pinterest = sprintf(
201 '<amp-pinterest data-do="embedPin" data-url="%1$s" width="%2$d" height="%3$d">%4$s</amp-pinterest>',
202 esc_url( $attr['url'] ),
203 450, // Fallback width.
204 750, // Fallback height.
205 sprintf(
206 '<a placeholder href="%s">%s</a>',
207 esc_url( $attr['url'] ),
208 esc_html( $attr['url'] )
209 )
210 );
211 }
212
213 return sprintf(
214 '<div class="wp-block-jetpack-pinterest">%s</div>',
215 $amp_pinterest
216 );
217 }
218
219 /**
220 * Pinterest block registration/dependency declaration.
221 *
222 * @param array $attr Array containing the Pinterest block attributes.
223 * @param string $content String containing the Pinterest block content.
224 *
225 * @return string
226 */
227 function load_assets( $attr, $content ) {
228 if ( ! Request::is_frontend() ) {
229 return $content;
230 }
231 $attr['url'] = $attr['url'] ?? '';
232 if ( Blocks::is_amp_request() ) {
233 return render_amp_pin( $attr );
234 } else {
235 $url = $attr['url'];
236 $type = pin_type( $url );
237
238 if ( ! $type ) {
239 return '';
240 }
241
242 wp_enqueue_script( 'pinterest-pinit', 'https://assets.pinterest.com/js/pinit.js', array(), JETPACK__VERSION, true );
243 return sprintf(
244 '
245 <div class="%1$s">
246 <a data-pin-do="%2$s" href="%3$s"></a>
247 </div>
248 ',
249 esc_attr( Blocks::classes( Blocks::get_block_feature( __DIR__ ), $attr ) ),
250 esc_attr( $type ),
251 esc_url( $url )
252 );
253 }
254 }
255