PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 16.3-beta 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 All 508 releases
← All changes | jetpack_vendor/automattic/jetpack-videopress/src/class-attachment-handler.php +389 -0 16.2-beta → 16.3 View file →
@@ -1,0 +1,389 @@
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 +}