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