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