PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.5
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.5
16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.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 All 506 releases
jetpack / jetpack_vendor / automattic / jetpack-videopress / src / class-attachment-handler.php

class-attachment-handler.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.5, at jetpack_vendor/automattic/jetpack-videopress/src/class-attachment-handler.php

390 lines 10.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * VideoPress Attachment_Handler
4 *
5 * @package automattic/jetpack-videopress
6 */
7
8 namespace Automattic\Jetpack\VideoPress;
9
10 use Automattic\Jetpack\Connection\Client;
11 use Automattic\Jetpack\Current_Plan;
12 use WP_Error;
13 use WP_Post;
14
15 /**
16 * VideoPress Attachment_Handler class.
17 */
18 class Attachment_Handler {
19
20 /**
21 * Initializer
22 *
23 * This method should be called only once by the Initializer class. Do not call this method again.
24 */
25 public static function init() {
26
27 if ( ! Status::is_active() ) {
28 return;
29 }
30
31 add_filter( 'wp_get_attachment_url', array( __CLASS__, 'maybe_get_attached_url_for_videopress' ), 10, 2 );
32 add_filter( 'get_attached_file', array( __CLASS__, 'maybe_get_attached_url_for_videopress' ), 10, 2 );
33
34 if ( Current_Plan::supports( 'videopress' ) ) {
35 add_filter( 'upload_mimes', array( __CLASS__, 'add_video_upload_mimes' ), 999 );
36 }
37
38 add_filter( 'pre_delete_attachment', array( __CLASS__, 'delete_video_wpcom' ), 10, 2 );
39 add_filter( 'wp_mime_type_icon', array( __CLASS__, 'wp_mime_type_icon' ), 10, 3 );
40 add_filter( 'wp_video_extensions', array( __CLASS__, 'add_videopress_extenstion' ) );
41
42 add_filter( 'wp_prepare_attachment_for_js', array( __CLASS__, 'prepare_attachment_for_js' ) );
43 add_filter( 'ajax_query_attachments_args', array( __CLASS__, 'ajax_query_attachments_args' ) );
44 add_action( 'pre_get_posts', array( __CLASS__, 'media_list_table_query' ) );
45
46 add_filter( 'user_has_cap', array( __CLASS__, 'disable_delete_if_disconnected' ), 10, 3 );
47
48 add_action( 'admin_print_scripts-upload.php', array( __CLASS__, 'enqueue_media_library_poll' ) );
49 add_action( 'admin_print_styles-upload.php', array( __CLASS__, 'enqueue_media_library_styles' ) );
50 add_filter( 'heartbeat_received', array( __CLASS__, 'heartbeat_received' ), 10, 2 );
51 }
52
53 /**
54 * Returns the VideoPress URL for the give post id, otherwise returns the provided default.
55 *
56 * This is an attachment-based filter handler.
57 *
58 * @param string $default The default return value if post id is not a VideoPress video.
59 * @param int $post_id The post id for the current attachment.
60 */
61 public static function maybe_get_attached_url_for_videopress( $default, $post_id ) {
62 $videopress_url = videopress_get_attachment_url( $post_id );
63
64 if ( null !== $videopress_url ) {
65 return $videopress_url;
66 }
67
68 return $default;
69 }
70
71 /**
72 * Makes sure that all video mimes are added in, as multi site installs can remove them.
73 *
74 * @param array $existing_mimes Mime types to extend/filter.
75 * @return array
76 */
77 public static function add_video_upload_mimes( $existing_mimes = array() ) {
78 $mime_types = wp_get_mime_types();
79 $video_types = array_filter( $mime_types, array( __CLASS__, 'filter_video_mimes' ) );
80
81 foreach ( $video_types as $key => $value ) {
82 $existing_mimes[ $key ] = $value;
83 }
84
85 // Make sure that videopress mimes are considered videos.
86 $existing_mimes['videopress'] = 'video/videopress';
87
88 return $existing_mimes;
89 }
90
91 /**
92 * Filter designed to get rid of non video mime types.
93 *
94 * @param string $value Mime type to filter.
95 * @return int
96 */
97 public static function filter_video_mimes( $value ) {
98 return preg_match( '@^video/@', $value );
99 }
100
101 /**
102 * Attempts to delete a VideoPress video from wp.com.
103 * Will block the deletion from continuing if certain errors return from the wp.com API.
104 *
105 * @param Boolean $delete if the deletion should occur or not (unused).
106 * @param WP_Post $post the post object.
107 *
108 * @return null|WP_Error|Boolean null if deletion should continue.
109 */
110 public static function delete_video_wpcom( $delete, $post ) {
111 if ( ! is_videopress_attachment( $post->ID ) ) {
112 return null;
113 }
114
115 $guid = get_post_meta( $post->ID, 'videopress_guid', true );
116 if ( empty( $guid ) ) {
117 self::delete_video_poster_attachment( $post->ID );
118 return null;
119 }
120
121 // Phone home and have wp.com delete the VideoPress entry and files.
122 $wpcom_response = Client::wpcom_json_api_request_as_blog(
123 sprintf( '/videos/%s/delete', $guid ),
124 '1.1',
125 array( 'method' => 'POST' ),
126 array( 'user_id' => get_current_user_id() )
127 );
128
129 if ( is_wp_error( $wpcom_response ) ) {
130 return $wpcom_response;
131 }
132
133 // Upon success or a 404 (video already deleted on wp.com), return null to allow the deletion to continue.
134 if ( 200 === $wpcom_response['response']['code'] || 404 === $wpcom_response['response']['code'] ) {
135 self::delete_video_poster_attachment( $post->ID );
136 return null;
137 }
138
139 // Otherwise we stop the deletion from proceeding.
140 return false;
141 }
142
143 /**
144 * Deletes a video poster attachment if it exists.
145 *
146 * @param int $attachment_id the WP attachment id.
147 */
148 private static function delete_video_poster_attachment( $attachment_id ) {
149 $thumbnail_id = get_post_meta( $attachment_id, '_thumbnail_id', true );
150 if ( ! empty( $thumbnail_id ) ) {
151 // Let's ensure this is a VP poster image before we delete it.
152 if ( '1' === get_post_meta( $thumbnail_id, 'videopress_poster_image', true ) ) {
153 // This call triggers the `delete_video_wpcom` filter again but it bails early at the is_videopress_attachment() check.
154 wp_delete_attachment( $thumbnail_id );
155 }
156 }
157 }
158
159 /**
160 * Filter the mime type icon.
161 *
162 * @param string $icon Icon path.
163 * @param string $mime Mime type.
164 * @param int $post_id Post ID.
165 *
166 * @return string
167 */
168 public static function wp_mime_type_icon( $icon, $mime, $post_id ) {
169
170 if ( $mime !== 'video/videopress' ) {
171 return $icon;
172 }
173
174 $status = get_post_meta( $post_id, 'videopress_status', true );
175
176 if ( $status === 'complete' ) {
177 return $icon;
178 }
179
180 return 'https://wordpress.com/wp-content/mu-plugins/videopress/images/media-video-processing-icon.png';
181 }
182
183 /**
184 * Filter the list of supported video formats.
185 *
186 * @param array $extensions Supported video formats.
187 *
188 * @return array
189 */
190 public static function add_videopress_extenstion( $extensions ) {
191 $extensions[] = 'videopress';
192 return $extensions;
193 }
194
195 /**
196 * Make sure that any Video that has a VideoPress GUID passes that data back.
197 *
198 * @param array $post Attachment data array.
199 * @return array
200 */
201 public static function prepare_attachment_for_js( $post ) {
202 if ( 'video' === $post['type'] ) {
203 $guid = get_post_meta( $post['id'], 'videopress_guid', true );
204 if ( $guid ) {
205 $post['videopress_guid'] = $guid;
206 }
207 $status = get_post_meta( $post['id'], 'videopress_status', true );
208 if ( $status ) {
209 $post['videopress_status'] = $status;
210 }
211 }
212 return $post;
213 }
214
215 /**
216 * Media Grid:
217 * Filter out any videopress video posters that we've downloaded,
218 * so that they don't seem to display twice.
219 *
220 * @param array $args Query variables.
221 */
222 public static function ajax_query_attachments_args( $args ) {
223 $meta_query = array(
224 array(
225 'key' => 'videopress_poster_image',
226 'compare' => 'NOT EXISTS',
227 ),
228 );
229
230 // If there was already a meta query, let's AND it via
231 // nesting it with our new one. No need to specify the
232 // relation, as it defaults to AND.
233 if ( ! empty( $args['meta_query'] ) ) {
234 $meta_query[] = $args['meta_query'];
235 }
236 $args['meta_query'] = $meta_query;
237
238 return $args;
239 }
240
241 /**
242 * Media List:
243 * Do the same as `videopress_ajax_query_attachments_args()` but for the list view.
244 *
245 * @param array $query WP_Query instance.
246 */
247 public static function media_list_table_query( $query ) {
248
249 if (
250 ! function_exists( 'get_current_screen' )
251 || get_current_screen() === null
252 ) {
253 return;
254 }
255
256 if ( is_admin() && $query->is_main_query() && ( 'upload' === get_current_screen()->id ) ) {
257 $meta_query = array(
258 array(
259 'key' => 'videopress_poster_image',
260 'compare' => 'NOT EXISTS',
261 ),
262 );
263
264 $old_meta_query = $query->get( 'meta_query' );
265 if ( $old_meta_query ) {
266 $meta_query[] = $old_meta_query;
267 }
268
269 $query->set( 'meta_query', $meta_query );
270 }
271 }
272
273 /**
274 * Filter to disable the `delete_post` capability
275 * for VideoPress attachments if the current user is
276 * not connected.
277 *
278 * @param array $allcaps All the capabilities of the user.
279 * @param array $cap [0] Required capability.
280 * @param array $args [0] Requested capability.
281 * [1] User ID.
282 * [2] Associated object ID.
283 * @return array the filtered array of capabilities.
284 */
285 public static function disable_delete_if_disconnected( $allcaps, $cap, $args ) {
286
287 // Only apply this filter to `delete_post` checks
288 if ( ! isset( $args[0] ) || 'delete_post' !== $args[0] ) {
289 return $allcaps;
290 }
291
292 // Only apply this filter to VideoPress attachments
293 if ( ! isset( $args[2] ) || ! is_videopress_attachment( $args[2] ) ) {
294 return $allcaps;
295 }
296
297 // Set the capability to false if the user can't perform the actions
298 if ( ! Data::can_perform_action() ) {
299 $allcaps[ $cap[0] ] = false;
300 }
301
302 return $allcaps;
303 }
304
305 /**
306 * Enqueues the script that hooks into WP Heartbeat to refresh
307 * processing VideoPress video data in the media library grid.
308 *
309 * @since 0.35.4
310 *
311 * @return void
312 */
313 public static function enqueue_media_library_poll() {
314 wp_enqueue_script(
315 'videopress-media-library-poll',
316 plugins_url( 'js/media-library-poll.js', __FILE__ ),
317 array( 'jquery', 'heartbeat', 'media-grid' ),
318 Package_Version::PACKAGE_VERSION,
319 true
320 );
321
322 add_filter( 'heartbeat_settings', array( __CLASS__, 'heartbeat_settings' ) );
323 }
324
325 /**
326 * Adds inline styles for the media library grid on the upload page.
327 *
328 * @since 0.35.4
329 *
330 * @return void
331 */
332 public static function enqueue_media_library_styles() {
333 // Constrain poster images so they fit within the media library grid cell.
334 wp_add_inline_style(
335 'media-views',
336 '.attachment-preview.type-video .thumbnail .centered img.thumbnail { max-width: 100%; max-height: 100%; }'
337 );
338 }
339
340 /**
341 * Lowers the Heartbeat minimum interval on the media library page
342 * so the polling script can request a faster tick rate.
343 *
344 * @since 0.35.4
345 *
346 * @param array $settings Heartbeat settings.
347 * @return array
348 */
349 public static function heartbeat_settings( $settings ) {
350 $settings['minimalInterval'] = 5;
351
352 return $settings;
353 }
354
355 /**
356 * Heartbeat API handler that checks the processing status of VideoPress videos.
357 *
358 * @since 0.35.4
359 *
360 * @param array $response The Heartbeat response.
361 * @param array $data The data sent with the Heartbeat request.
362 * @return array
363 */
364 public static function heartbeat_received( $response, $data ) {
365 if ( empty( $data['videopress_processing_ids'] ) || ! is_array( $data['videopress_processing_ids'] ) ) {
366 return $response;
367 }
368
369 $statuses = array();
370 foreach ( $data['videopress_processing_ids'] as $id ) {
371 $id = (int) $id;
372
373 if ( ! current_user_can( 'edit_post', $id ) ) {
374 continue;
375 }
376
377 $status = get_post_meta( $id, 'videopress_status', true );
378 if ( $status ) {
379 $statuses[ $id ] = $status;
380 }
381 }
382
383 if ( ! empty( $statuses ) ) {
384 $response['videopress_processing_status'] = $statuses;
385 }
386
387 return $response;
388 }
389 }
390