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