PluginProbe ʕ •ᴥ•ʔ
Jetpack – WP Security, Backup, Speed, & Growth / 16.0.1
Jetpack – WP Security, Backup, Speed, & Growth v16.0.1
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 / modules / videopress / class.videopress-edit-attachment.php
jetpack / modules / videopress Last commit date
css 1 month ago js 1 month ago class-videopress-attachment-metadata.php 1 month ago class.jetpack-videopress.php 7 months ago class.videopress-cli.php 1 month ago class.videopress-edit-attachment.php 1 month ago class.videopress-gutenberg.php 7 months ago class.videopress-options.php 7 months ago class.videopress-player.php 1 week ago class.videopress-scheduler.php 7 months ago class.videopress-video.php 7 months ago class.videopress-xmlrpc.php 7 months ago editor-media-view.php 4 months ago shortcode.php 1 month ago videopress-admin-rtl.css 7 months ago videopress-admin-rtl.min.css 7 months ago videopress-admin.css 7 months ago videopress-admin.min.css 7 months ago
class.videopress-edit-attachment.php
443 lines
1 <?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2
3 use Automattic\Jetpack\Connection\Client;
4 use Automattic\Jetpack\Status;
5
6 if ( ! defined( 'ABSPATH' ) ) {
7 exit( 0 );
8 }
9
10 /**
11 * VideoPress edit attachment screen
12 *
13 * @since 4.1
14 */
15 class VideoPress_Edit_Attachment {
16
17 /**
18 * Singleton method to initialize the object only once.
19 *
20 * @return VideoPress_Edit_Attachment
21 */
22 public static function init() {
23 static $instance = null;
24
25 if ( ! $instance ) {
26 $instance = new VideoPress_Edit_Attachment();
27 }
28
29 return $instance;
30 }
31
32 /**
33 * VideoPress_Edit_Attachment constructor.
34 *
35 * Adds in appropriate actions for attachment fields editor, meta boxes and saving.
36 */
37 public function __construct() {
38 add_filter( 'attachment_fields_to_edit', array( $this, 'fields_to_edit' ), 10, 2 );
39 add_filter( 'attachment_fields_to_save', array( $this, 'save_fields' ), 10, 2 );
40 add_filter( 'wp_ajax_save-attachment', array( $this, 'save_fields' ), -1 );
41 add_filter( 'wp_ajax_save-attachment-compat', array( $this, 'save_fields' ), -1 );
42
43 add_action( 'add_meta_boxes', array( $this, 'configure_meta_boxes' ), 10, 2 );
44 }
45
46 /**
47 * Add VideoPress meta box.
48 *
49 * @param string $post_type Post type.
50 * @param object $post Post object.
51 */
52 public function configure_meta_boxes( $post_type = 'unknown', $post = null ) {
53 if ( null === $post ) {
54 $post = (object) array( 'ID' => 0 );
55 }
56
57 if ( 'attachment' !== $post_type ) {
58 return;
59 }
60
61 // If this has not been processed by videopress, we can skip the rest.
62 if ( ! is_videopress_attachment( $post->ID ) ) {
63 return;
64 }
65
66 add_meta_box( 'videopress-media-info', __( 'VideoPress Information', 'jetpack' ), array( $this, 'videopress_information_box' ), 'attachment', 'side', 'core' );
67 }
68
69 /**
70 * Filter attachment fields data to save.
71 *
72 * @param array $post Post data.
73 * @param array|null $attachment Attachment metadata.
74 *
75 * @return array
76 */
77 public function save_fields( $post, $attachment = null ) {
78 // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce verification already done by core.
79 if ( null === $attachment && isset( $_POST['attachment'] ) ) {
80 $attachment = filter_var( wp_unslash( $_POST['attachment'] ) );
81 }
82
83 if ( ! isset( $attachment['is_videopress_attachment'] ) || 'yes' !== $attachment['is_videopress_attachment'] ) {
84 return $post;
85 }
86
87 // If this has not been processed by videopress, we can skip the rest.
88 if ( ! is_videopress_attachment( $post['ID'] ) ) {
89 $post['errors']['videopress']['errors'][] = __( 'The media you are trying to update is not processed by VideoPress.', 'jetpack' );
90 return $post;
91 }
92
93 $post_title = isset( $_POST['post_title'] ) ? sanitize_text_field( wp_unslash( $_POST['post_title'] ) ) : null;
94 $post_excerpt = isset( $_POST['post_excerpt'] ) ? sanitize_textarea_field( wp_unslash( $_POST['post_excerpt'] ) ) : null;
95 $rating = $attachment['rating'] ?? null;
96 $display_embed = $attachment['display_embed'] ?? 0;
97 $allow_download = $attachment['allow_download'] ?? 0;
98 $privacy_setting = $attachment['privacy_setting'] ?? VIDEOPRESS_PRIVACY::SITE_DEFAULT;
99
100 $result = Videopress_Attachment_Metadata::persist_metadata(
101 $post['ID'],
102 get_post_meta( $post['ID'], 'videopress_guid', true ),
103 $post_title,
104 null, // @todo: Check why we haven't sent the caption in the first place.
105 $post_excerpt,
106 $rating,
107 $this->normalize_checkbox_value( $display_embed ),
108 $this->normalize_checkbox_value( $allow_download ),
109 $privacy_setting
110 );
111
112 if ( is_wp_error( $result ) ) {
113 $post['errors']['videopress']['errors'][] = $result->get_error_message();
114 return $post;
115 }
116
117 return $post;
118 // phpcs:enable WordPress.Security.NonceVerification.Missing
119 }
120
121 /**
122 * Convert the string values of a checkbox option to the format that they will be stored in db.
123 *
124 * @param string $value The denormalized version.
125 *
126 * @return int
127 */
128 private function normalize_checkbox_value( $value ) {
129 return 'on' === $value ? 1 : 0;
130 }
131
132 /**
133 * Get the upload api path.
134 *
135 * @param string $guid The guid of the video.
136 * @return string
137 */
138 public function make_video_api_path( $guid ) {
139 return sprintf(
140 '%s/rest/v%s/videos/%s',
141 JETPACK__WPCOM_JSON_API_BASE,
142 Client::WPCOM_JSON_API_VERSION,
143 $guid
144 );
145 }
146
147 /**
148 * Creates an array of video fields to edit based on transcoded videos.
149 *
150 * @param array $fields video fields of interest.
151 * @param stdClass $post Post object.
152 * @return array modified version of video fields for administrative interface display
153 */
154 public function fields_to_edit( $fields, $post ) {
155 $post_id = absint( $post->ID );
156
157 $meta = wp_get_attachment_metadata( $post_id );
158
159 // If this has not been processed by videopress, we can skip the rest.
160 if ( ! is_videopress_attachment( $post_id ) || ! isset( $meta['videopress'] ) ) {
161 return $fields;
162 }
163
164 $info = (object) $meta['videopress'];
165 $file_statuses = $meta['file_statuses'] ?? array();
166
167 $guid = get_post_meta( $post_id, 'videopress_guid', true );
168
169 unset( $fields['url'] );
170 unset( $fields['post_content'] );
171
172 // If a video isn't attached to any specific post, manually add a post ID.
173 if ( ! isset( $info->post_id ) ) {
174 $info->post_id = 0;
175 }
176
177 if ( isset( $file_statuses['ogg'] ) && 'done' === $file_statuses['ogg'] ) {
178 $v_name = preg_replace( '/\.\w+/', '', basename( $info->path ) );
179 $video_name = $v_name . '_fmt1.ogv';
180 $ogg_url = videopress_cdn_file_url( $guid, $video_name );
181
182 $fields['video-ogg'] = array(
183 'label' => __( 'Ogg File URL', 'jetpack' ),
184 'input' => 'html',
185 'html' => "<input type='text' class='urlfield' readonly='readonly' name='attachments[$post_id][oggurl]' value='" . esc_url( $ogg_url, array( 'http', 'https' ) ) . "' />",
186 'helps' => __( 'Location of the Ogg video file.', 'jetpack' ),
187 );
188 }
189
190 $fields['post_title']['helps'] = __( 'Title will appear on the first frame of your video', 'jetpack' );
191
192 $fields['post_excerpt']['label'] = _x( 'Description', 'A header for the short description display', 'jetpack' );
193 $fields['post_excerpt']['input'] = 'textarea';
194 $fields['post_excerpt']['value'] = ! empty( $info->description ) ? $info->description : '';
195
196 $fields['is_videopress_attachment'] = array(
197 'input' => 'hidden',
198 'value' => 'yes',
199 );
200
201 $fields['videopress_shortcode'] = array(
202 'label' => _x( 'Shortcode', 'A header for the shortcode display', 'jetpack' ),
203 'input' => 'html',
204 'html' => "<input type=\"text\" name=\"videopress_shortcode\" value=\"[videopress {$guid}]\" readonly=\"readonly\"/>",
205 'show_in_modal' => true,
206 'show_in_edit' => false,
207 );
208
209 $fields['display_embed'] = array(
210 'label' => _x( 'Share', 'A header for the video sharing options area', 'jetpack' ),
211 'input' => 'html',
212 'html' => $this->display_embed_choice( $info ),
213 );
214
215 $fields['allow_download'] = array(
216 'label' => _x( 'Download', 'A header for the video allow download option area', 'jetpack' ),
217 'input' => 'html',
218 'html' => $this->display_download_choice( $info ),
219 );
220
221 $fields['video-rating'] = array(
222 'label' => _x( 'Rating', 'A header for the video rating area', 'jetpack' ),
223 'input' => 'html',
224 'html' => $this->display_rating( $info ),
225 );
226
227 $fields['privacy_setting'] = array(
228 'label' => _x( 'Privacy Setting', 'A header for the video privacy setting area.', 'jetpack' ),
229 'input' => 'html',
230 'html' => $this->display_privacy_setting( $info ),
231 );
232
233 return $fields;
234 }
235
236 /**
237 * Meta box output.
238 *
239 * @param stdClass $post Post object.
240 */
241 public function videopress_information_box( $post ) {
242 $post_id = absint( $post->ID );
243 $guid = get_post_meta( $post_id, 'videopress_guid', true );
244
245 // If this has not been processed by videopress, we can skip the rest.
246 if ( ! is_videopress_attachment( $post_id ) || empty( $guid ) ) {
247 return;
248 }
249
250 $meta = wp_get_attachment_metadata( $post_id );
251 $info = (object) $meta['videopress'];
252
253 $is_public = VIDEOPRESS_PRIVACY::IS_PUBLIC === $info->privacy_setting || ( VIDEOPRESS_PRIVACY::SITE_DEFAULT === $info->privacy_setting && ! ( new Status() )->is_private_site() );
254 /* Translators: %s is the video title */
255 $alt_text = sprintf( __( 'Poster image for video: %s', 'jetpack' ), get_the_title( $post_id ) );
256 ?>
257
258 <p class="post-attributes-label-wrapper">
259 <label class="post-attributes-label" for="videopress-shortcode"><?php esc_html_e( 'Shortcode', 'jetpack' ); ?></label>
260 </p>
261 <input type="text" class="widefat" id="videopress-shortcode" readonly="readonly" value="<?php echo esc_attr( "[videopress $guid]" ); ?>" onclick="this.focus();this.select();" />
262
263 <p class="post-attributes-label-wrapper">
264 <label class="post-attributes-label"><?php esc_html_e( 'URL', 'jetpack' ); ?></label>
265 </p>
266 <?php printf( '<a href="%1$s">%1$s</a>', esc_url( videopress_build_url( $guid ) ) ); ?>
267
268 <p class="post-attributes-label-wrapper">
269 <label class="post-attributes-label"><?php esc_html_e( 'Poster', 'jetpack' ); ?></label>
270 </p>
271 <?php if ( ! empty( $info->poster ) ) : ?>
272 <?php if ( $is_public ) : ?>
273 <img src="<?php echo esc_url( $info->poster ); ?>" width="100%" alt="<?php echo esc_attr( $alt_text ); ?>" />
274 <?php else : ?>
275 <img
276 id="videopress-poster-<?php echo esc_attr( $guid ); ?>"
277 data-poster="<?php echo esc_url( $info->poster ); ?>"
278 data-guid="<?php echo esc_attr( $guid ); ?>"
279 width="100%"
280 alt="<?php echo esc_attr( $alt_text ); ?>"
281 style="display:none;"
282 src=""
283 />
284 <em class="videopress-poster-loading" data-error="<?php esc_attr_e( 'Poster unavailable.', 'jetpack' ); ?>"><?php esc_html_e( 'Loading…', 'jetpack' ); ?></em>
285 <script>
286 ( function() {
287 var img = document.getElementById( 'videopress-poster-<?php echo esc_attr( $guid ); ?>' );
288 var loading = img ? img.nextElementSibling : null;
289 if ( ! img || ! loading || ! window.videopressAjax ) {
290 return;
291 }
292
293 fetch( window.videopressAjax.ajaxUrl, {
294 method: 'POST',
295 credentials: 'same-origin',
296 body: new URLSearchParams( {
297 action: 'videopress-get-playback-jwt',
298 guid: img.dataset.guid,
299 post_id: window.videopressAjax.post_id || 0
300 } )
301 } )
302 .then( function( response ) { return response.json(); } )
303 .then( function( data ) {
304 if ( data.success && data.data.jwt ) {
305 img.src = img.dataset.poster + '?metadata_token=' + data.data.jwt;
306 img.style.display = '';
307 loading.style.display = 'none';
308 } else {
309 loading.textContent = loading.dataset.error;
310 }
311 } )
312 .catch( function() {
313 loading.textContent = loading.dataset.error;
314 } );
315 } )();
316 </script>
317 <?php endif; ?>
318 <?php else : ?>
319 <em><?php esc_html_e( 'Processing…', 'jetpack' ); ?></em>
320 <?php
321 endif;
322 }
323
324 /**
325 * Creates a checkbox and a label for a video option.
326 *
327 * @param string $id the checkbox id.
328 * @param string $name the checkbox name.
329 * @param string $label the label text.
330 * @param bool $is_checked if the checkbox should be checked.
331 *
332 * @return string the generated HTML
333 */
334 protected function create_checkbox_for_option( $id, $name, $label, $is_checked ) {
335 $html = "<label for='$id'><input type='checkbox' name='$name' id='$id'";
336 if ( $is_checked ) {
337 $html .= ' checked="checked"';
338 }
339 $html .= " />$label</label>";
340 return $html;
341 }
342
343 /**
344 * Build HTML to display a form checkbox for embedcode display preference
345 *
346 * @param object $info Database row from the videos table.
347 * @return string Input element of type checkbox set to checked state based on stored embed preference.
348 */
349 protected function display_embed_choice( $info ) {
350 return $this->create_checkbox_for_option(
351 "attachments-{$info->post_id}-displayembed",
352 "attachments[{$info->post_id}][display_embed]",
353 __( 'Display share menu and allow viewers to copy a link or embed this video', 'jetpack' ),
354 $info->display_embed ?? 0
355 );
356 }
357
358 /**
359 * Build HTML to display a form checkbox for the "allow download" video option
360 *
361 * @param object $info database row from the videos table.
362 * @return string input element of type checkbox with checked state matching the download preference
363 */
364 protected function display_download_choice( $info ) {
365 return $this->create_checkbox_for_option(
366 "attachments-{$info->post_id}-allowdownload",
367 "attachments[{$info->post_id}][allow_download]",
368 __( 'Display download option and allow viewers to download this video', 'jetpack' ),
369 isset( $info->allow_download ) && $info->allow_download
370 );
371 }
372
373 /**
374 * Build HTML to display a form input radio button for video ratings
375 *
376 * @param object $info Database row from the videos table.
377 *
378 * @return string Input Elements of type radio with existing stored value selected.
379 */
380 protected function display_privacy_setting( $info ) {
381 $privacy_settings = array(
382 VIDEOPRESS_PRIVACY::SITE_DEFAULT => __( 'Site Default', 'jetpack' ),
383 VIDEOPRESS_PRIVACY::IS_PUBLIC => __( 'Public', 'jetpack' ),
384 VIDEOPRESS_PRIVACY::IS_PRIVATE => __( 'Private', 'jetpack' ),
385 );
386
387 $displayed_privacy_setting = intval( $info->privacy_setting ?? VIDEOPRESS_PRIVACY::SITE_DEFAULT );
388
389 $out = "<select name='attachments[{$info->post_id}][privacy_setting]'>";
390 foreach ( $privacy_settings as $r => $label ) {
391 $out .= "<option value=\"$r\"";
392 if ( $r === $displayed_privacy_setting ) {
393 $out .= ' selected';
394 }
395
396 $out .= ">$label</option>";
397 }
398
399 $out .= '</select>';
400
401 return $out;
402 }
403
404 /**
405 * Build HTML to display a form input radio button for video ratings
406 *
407 * @param object $info Database row from the videos table.
408 * @return string Input elements of type radio with existing stored value selected.
409 */
410 protected function display_rating( $info ) {
411 $out = '';
412
413 $ratings = array(
414 'G' => 'G',
415 'PG-13' => 'PG-13',
416 'R-17' => 'R',
417 );
418
419 $displayed_rating = $info->rating ?? null;
420
421 // X-18 was previously supported but is now removed to better comply with our TOS.
422 if ( 'X-18' === $displayed_rating ) {
423 $displayed_rating = 'R-17';
424 }
425
426 foreach ( $ratings as $r => $label ) {
427 $id = "attachments-{$info->post_id}-rating-$r";
428 $out .= "<label for=\"$id\"><input type=\"radio\" name=\"attachments[{$info->post_id}][rating]\" id=\"$id\" value=\"$r\"";
429 if ( $displayed_rating === $r ) {
430 $out .= ' checked="checked"';
431 }
432
433 $out .= " />$label</label>";
434 unset( $id );
435 }
436
437 return $out;
438 }
439 }
440
441 // Let's start this thing up.
442 VideoPress_Edit_Attachment::init();
443