PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 9.2
Jetpack – WP Security, Backup, Speed, & Growth v9.2
16.2-beta 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 All 501 releases
jetpack / modules / shortcodes / gist.php

gist.php in Jetpack – WP Security, Backup, Speed, & Growth 9.2, at modules/shortcodes/gist.php

257 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * GitHub's Gist site supports oEmbed but their oembed provider only
4 * returns raw HTML (no styling) and the first little bit of the code.
5 *
6 * Their JavaScript-based embed method is a lot better, so that's what we're using.
7 *
8 * Supported formats:
9 * Full URL: https://gist.github.com/57cc50246aab776e110060926a2face2
10 * Full URL with username: https://gist.github.com/jeherve/57cc50246aab776e110060926a2face2
11 * Full URL linking to specific file: https://gist.github.com/jeherve/57cc50246aab776e110060926a2face2#file-wp-config-php
12 * Full URL, no username, linking to specific file: https://gist.github.com/57cc50246aab776e110060926a2face2#file-wp-config-php
13 * Gist ID: [gist]57cc50246aab776e110060926a2face2[/gist]
14 * Gist ID within tag: [gist 57cc50246aab776e110060926a2face2]
15 * Gist ID with username: [gist jeherve/57cc50246aab776e110060926a2face2]
16 * Gist private ID with username: [gist xknown/fc5891af153e2cf365c9]
17 *
18 * @package Jetpack
19 */
20
21 wp_embed_register_handler( 'github-gist', '#https?://gist\.github\.com/([a-zA-Z0-9/]+)(\#file\-[a-zA-Z0-9\_\-]+)?#', 'github_gist_embed_handler' );
22 add_shortcode( 'gist', 'github_gist_shortcode' );
23
24 /**
25 * Handle gist embeds.
26 *
27 * @since 2.8.0
28 *
29 * @global WP_Embed $wp_embed
30 *
31 * @param array $matches Results after parsing the URL using the regex in wp_embed_register_handler().
32 * @param array $attr Embed attributes.
33 * @param string $url The original URL that was matched by the regex.
34 * @param array $rawattr The original unmodified attributes.
35 * @return string The embed HTML.
36 */
37 function github_gist_embed_handler( $matches, $attr, $url, $rawattr ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable
38 // Let the shortcode callback do all the work.
39 return github_gist_shortcode( $matches, $url );
40 }
41
42 /**
43 * Extract an ID from a Gist shortcode or a full Gist URL.
44 *
45 * @since 7.3.0
46 *
47 * @param string $gist Gist shortcode or full Gist URL.
48 *
49 * @return array $gist_info {
50 * Array of information about our gist.
51 * @type string $id Unique identifier for the gist.
52 * @type string $file File name if the gist links to a specific file.
53 * }
54 */
55 function jetpack_gist_get_shortcode_id( $gist = '' ) {
56 $gist_info = array(
57 'id' => '',
58 'file' => '',
59 'ts' => 8,
60 );
61 // Simple shortcode, with just an ID.
62 if ( ctype_alnum( $gist ) ) {
63 $gist_info['id'] = $gist;
64 }
65
66 // Full URL? Only keep the relevant parts.
67 $parsed_url = wp_parse_url( $gist );
68 if (
69 ! empty( $parsed_url )
70 && is_array( $parsed_url )
71 && isset( $parsed_url['scheme'], $parsed_url['host'], $parsed_url['path'] )
72 ) {
73 // Not a Gist URL? Bail.
74 if ( 'gist.github.com' !== $parsed_url['host'] ) {
75 return array(
76 'id' => '',
77 'file' => '',
78 'ts' => 8,
79 );
80 }
81
82 // Keep the file name if there was one.
83 if ( ! empty( $parsed_url['fragment'] ) ) {
84 $gist_info['file'] = preg_replace( '/(?:file-)(.+)/', '$1', $parsed_url['fragment'] );
85 }
86
87 // Keep the unique identifier without any leading or trailing slashes.
88 if ( ! empty( $parsed_url['path'] ) ) {
89 $gist_info['id'] = trim( $parsed_url['path'], '/' );
90 // Overwrite $gist with our identifier to clean it up below.
91 $gist = $gist_info['id'];
92 }
93
94 // Parse the query args to obtain the tab spacing.
95 if ( ! empty( $parsed_url['query'] ) ) {
96 $query_args = array();
97 wp_parse_str( $parsed_url['query'], $query_args );
98 if ( ! empty( $query_args['ts'] ) ) {
99 $gist_info['ts'] = absint( $query_args['ts'] );
100 }
101 }
102 }
103
104 // Not a URL nor an ID? Look for "username/id", "/username/id", or "id", and only keep the ID.
105 if ( preg_match( '#^/?(([a-z0-9_-]+/)?([a-z0-9]+))$#i', $gist, $matches ) ) {
106 $gist_info['id'] = $matches[3];
107 }
108
109 return $gist_info;
110 }
111
112 /**
113 * Callback for gist shortcode.
114 *
115 * @since 2.8.0
116 *
117 * @param array $atts Attributes found in the shortcode.
118 * @param string $content Content enclosed by the shortcode.
119 *
120 * @return string The gist HTML.
121 */
122 function github_gist_shortcode( $atts, $content = '' ) {
123
124 if ( empty( $atts[0] ) && empty( $content ) ) {
125 if ( current_user_can( 'edit_posts' ) ) {
126 return esc_html__( 'Please specify a Gist URL or ID.', 'jetpack' );
127 } else {
128 return '<!-- Missing Gist ID -->';
129 }
130 }
131
132 $id = ( ! empty( $content ) ) ? $content : $atts[0];
133
134 // Parse a URL to get an ID we can use.
135 $gist_info = jetpack_gist_get_shortcode_id( $id );
136 if ( empty( $gist_info['id'] ) ) {
137 if ( current_user_can( 'edit_posts' ) ) {
138 return esc_html__( 'The Gist ID you provided is not valid. Please try a different one.', 'jetpack' );
139 } else {
140 return '<!-- Invalid Gist ID -->';
141 }
142 } else {
143 // Add trailing .json to all unique gist identifiers.
144 $id = $gist_info['id'] . '.json';
145 }
146
147 // The file name can come from the URL passed, or from a shortcode attribute.
148 if ( ! empty( $gist_info['file'] ) ) {
149 $file = $gist_info['file'];
150 } elseif ( ! empty( $atts['file'] ) ) {
151 $file = $atts['file'];
152 } else {
153 $file = '';
154 }
155
156 // Replace - by . to get a real file name from slug.
157 if ( ! empty( $file ) ) {
158 // Find the last -.
159 $dash_position = strrpos( $file, '-' );
160 if ( false !== $dash_position ) {
161 // Replace the - by a period.
162 $file = substr_replace( $file, '.', $dash_position, 1 );
163 }
164
165 $file = rawurlencode( $file );
166 }
167
168 // Set the tab size, allowing attributes to override the query string.
169 $tab_size = $gist_info['ts'];
170 if ( ! empty( $atts['ts'] ) ) {
171 $tab_size = absint( $atts['ts'] );
172 }
173
174 if (
175 class_exists( 'Jetpack_AMP_Support' )
176 && Jetpack_AMP_Support::is_amp_request()
177 ) {
178 /*
179 * According to <https://www.ampproject.org/docs/reference/components/amp-gist#height-(required)>:
180 *
181 * > Note: You must find the height of the gist by inspecting it with your browser (e.g., Chrome Developer Tools).
182 *
183 * However, this does not seem to be the case any longer. The actual height of the content does get set in the
184 * page after loading. So this is just the initial height.
185 * See <https://github.com/ampproject/amphtml/pull/17738>.
186 */
187 $height = 240;
188
189 $amp_tag = sprintf(
190 '<amp-gist layout="fixed-height" data-gistid="%s" height="%s"',
191 esc_attr( basename( $id, '.json' ) ),
192 esc_attr( $height )
193 );
194 if ( ! empty( $file ) ) {
195 $amp_tag .= sprintf( ' data-file="%s"', esc_attr( $file ) );
196 }
197 $amp_tag .= '></amp-gist>';
198 return $amp_tag;
199 }
200
201 // URL points to the entire gist, including the file name if there was one.
202 $id = ( ! empty( $file ) ? $id . '?file=' . $file : $id );
203 $return = false;
204
205 $request = wp_remote_get( esc_url_raw( 'https://gist.github.com/' . esc_attr( $id ) ) );
206 $request_code = wp_remote_retrieve_response_code( $request );
207
208 if ( 200 === $request_code ) {
209 $request_body = wp_remote_retrieve_body( $request );
210 $request_data = json_decode( $request_body );
211
212 wp_enqueue_style( 'jetpack-gist-styling', esc_url( $request_data->stylesheet ), array(), JETPACK__VERSION );
213
214 $gist = substr_replace( $request_data->div, sprintf( 'style="tab-size: %1$s" ', absint( $tab_size ) ), 5, 0 );
215
216 // Add inline styles for the tab style in the opening div of the gist.
217 $gist = preg_replace(
218 '#(\<div\s)+(id=\"gist[0-9]+\")+(\sclass=\"gist\"\>)?#',
219 sprintf( '$1style="tab-size: %1$s" $2$3', absint( $tab_size ) ),
220 $request_data->div,
221 1
222 );
223
224 // Add inline style to prevent the bottom margin to the embed that themes like TwentyTen, et al., add to tables.
225 $return = sprintf( '<style>.gist table { margin-bottom: 0; }</style>%1$s', $gist );
226 }
227
228 if (
229 // No need to check for a nonce here, that's already handled by Core further up.
230 // phpcs:disable WordPress.Security.NonceVerification.Missing
231 isset( $_POST['type'] )
232 && 'embed' === $_POST['type']
233 && isset( $_POST['action'] )
234 && 'parse-embed' === $_POST['action']
235 // phpcs:enable WordPress.Security.NonceVerification.Missing
236 ) {
237 return github_gist_simple_embed( $id, $tab_size );
238 }
239
240 return $return;
241 }
242
243 /**
244 * Use script tag to load shortcode in editor.
245 * Can't use wp_enqueue_script here.
246 *
247 * @since 3.9.0
248 *
249 * @param string $id The ID of the gist.
250 * @param int $tab_size The tab size of the gist.
251 * @return string The script tag of the gist.
252 */
253 function github_gist_simple_embed( $id, $tab_size = 8 ) {
254 $id = str_replace( 'json', 'js', $id );
255 return '<script src="' . esc_url( "https://gist.github.com/$id?ts=$tab_size" ) . '"></script>'; // phpcs:ignore WordPress.WP.EnqueuedResources.NonEnqueuedScript
256 }
257