Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
44.36% covered (danger)
44.36%
114 / 257
35.71% covered (danger)
35.71%
5 / 14
CRAP
0.00% covered (danger)
0.00%
0 / 1
Data
44.36% covered (danger)
44.36%
114 / 257
35.71% covered (danger)
35.71%
5 / 14
427.54
0.00% covered (danger)
0.00%
0 / 1
 get_blog_id
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_videopress_videos_private_for_site
50.00% covered (danger)
50.00%
3 / 6
0.00% covered (danger)
0.00%
0 / 1
8.12
 get_videopress_auto_subtitles_disabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_videopress_player_preload_disabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_videopress_inline_player_enabled
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 get_videopress_settings
75.00% covered (warning)
75.00%
12 / 16
0.00% covered (danger)
0.00%
0 / 1
4.25
 get_video_data
96.97% covered (success)
96.97%
32 / 33
0.00% covered (danger)
0.00%
0 / 1
5
 get_user_data
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
20
 get_storage_used
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 get_connected_initial_state
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 has_connected_owner
60.00% covered (warning)
60.00%
3 / 5
0.00% covered (danger)
0.00%
0 / 1
5.02
 can_perform_action
87.50% covered (warning)
87.50%
7 / 8
0.00% covered (danger)
0.00%
0 / 1
5.05
 get_initial_state
0.00% covered (danger)
0.00%
0 / 87
0.00% covered (danger)
0.00%
0 / 1
30
 prepare_videopress_video_data
100.00% covered (success)
100.00%
53 / 53
100.00% covered (success)
100.00%
1 / 1
6
1<?php
2/**
3 * The Data class.
4 * This class provides methods for data VideoPress access and manipulation.
5 *
6 * @package automattic/jetpack-videopress
7 */
8
9namespace Automattic\Jetpack\VideoPress;
10
11use Automattic\Jetpack\Connection\Manager as Connection_Manager;
12use Automattic\Jetpack\Status\Host;
13use WP_REST_Request;
14/**
15 * The Data class.
16 */
17class Data {
18
19    /**
20     * Gets the Jetpack blog ID
21     *
22     * @return int The blog ID
23     */
24    public static function get_blog_id() {
25        return VideoPressToken::blog_id();
26    }
27
28    /**
29     * Gets the VideoPress site privacy configuration.
30     *
31     * @return boolean If all the videos are private on the site
32     */
33    public static function get_videopress_videos_private_for_site() {
34        /**
35         * If it's a Simple site, returns the site privacy setting.
36         */
37        if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
38            return video_is_private_wpcom_blog( get_current_blog_id() );
39        }
40        /**
41         * If it's a private Atomic site, the default setting is private as well.
42         */
43        if ( ( new Host() )->is_woa_site() ) {
44            if ( ( intval( get_option( 'blog_public', '' ) ) === -1 ) ) {
45                return true;
46            }
47        }
48
49        /* If it's a Jetpack site or a public Atomic site, check the settings */
50        return boolval( get_option( 'videopress_private_enabled_for_site', false ) );
51    }
52
53    /**
54     * Gets whether auto-generated subtitles are disabled for the site.
55     *
56     * Subtitles are generated by default, so this opt-out option defaults to false.
57     *
58     * @return boolean If auto-generated subtitles should be skipped for new videos.
59     */
60    public static function get_videopress_auto_subtitles_disabled() {
61        return boolval( get_option( 'videopress_auto_subtitles_disabled', false ) );
62    }
63
64    /**
65     * Gets whether player preloading is turned off for every embed on the site.
66     *
67     * Preloading is on by default, so this opt-out option defaults to false.
68     *
69     * @return boolean If embeds should wait for playback before fetching video data and player assets.
70     */
71    public static function get_videopress_player_preload_disabled() {
72        return boolval( get_option( 'videopress_player_preload_disabled', false ) );
73    }
74
75    /**
76     * Gets whether embeds render an inline player instead of one iframe per video.
77     *
78     * Iframes are the default, so this opt-in option defaults to false.
79     *
80     * @return boolean If embeds should mount players in the page from one shared player script.
81     */
82    public static function get_videopress_inline_player_enabled() {
83        return boolval( get_option( 'videopress_inline_player_enabled', false ) );
84    }
85
86    /**
87     * Gets the VideoPress Settings.
88     *
89     * @return array The settings as an associative array.
90     */
91    public static function get_videopress_settings() {
92        $site_type       = 'jetpack';
93        $site_is_private = false;
94
95        if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
96            $site_type       = 'simple';
97            $site_is_private = video_is_private_wpcom_blog( get_current_blog_id() );
98        } elseif ( ( new Host() )->is_woa_site() ) {
99            $site_type       = 'atomic';
100            $site_is_private = intval( get_option( 'blog_public', '' ) ) === -1;
101        }
102
103        return array(
104            'videopress_videos_private_for_site' => self::get_videopress_videos_private_for_site(),
105            'videopress_auto_subtitles_disabled' => self::get_videopress_auto_subtitles_disabled(),
106            'videopress_player_preload_disabled' => self::get_videopress_player_preload_disabled(),
107            'videopress_inline_player_enabled'   => self::get_videopress_inline_player_enabled(),
108            'site_is_private'                    => $site_is_private,
109            'site_type'                          => $site_type,
110        );
111    }
112
113    /**
114     * Gets the video data
115     *
116     * @param boolean $is_videopress - True when getting VideoPress data.
117     * @return array
118     */
119    public static function get_video_data( $is_videopress = true ) {
120        $video_data = array(
121            'videos'     => array(),
122            'total'      => 0,
123            'totalPages' => 0,
124            'query'      => array(
125                'order'        => 'desc',
126                'orderBy'      => 'date',
127                'itemsPerPage' => 6,
128                'page'         => 1,
129            ),
130        );
131
132        $args = array(
133            'order'    => $video_data['query']['order'],
134            'orderby'  => $video_data['query']['orderBy'],
135            'per_page' => $video_data['query']['itemsPerPage'],
136            'page'     => $video_data['query']['page'],
137        );
138
139        if ( $is_videopress ) {
140            $args['mime_type'] = 'video/videopress';
141        } else {
142            $args['media_type']    = 'video';
143            $args['no_videopress'] = true;
144        }
145
146        // Do an internal request for the media list
147        $request = new WP_REST_Request( 'GET', '/wp/v2/media' );
148        $request->set_query_params( $args );
149        $response = rest_do_request( $request );
150
151        if ( $response->is_error() ) {
152            // @todo: error handling
153            return $video_data;
154        }
155
156        // load the real values
157        $video_data['videos'] = $response->get_data();
158        $headers              = $response->get_headers();
159
160        if ( isset( $headers['X-WP-Total'] ) ) {
161            $video_data['total'] = $headers['X-WP-Total'];
162        }
163
164        if ( isset( $headers['X-WP-TotalPages'] ) ) {
165            $video_data['totalPages'] = $headers['X-WP-TotalPages'];
166        }
167
168        return $video_data;
169    }
170
171    /**
172     * Gets the user data
173     *
174     * @return array
175     */
176    public static function get_user_data() {
177        $user_data = array(
178            'items'      => array(),
179            'pagination' => array(
180                'total'      => 0,
181                'totalPages' => 1,
182            ),
183            'query'      => array(
184                'order'   => 'asc',
185                'orderBy' => 'name',
186            ),
187            '_meta'      => array(
188                'relyOnInitialState' => true,
189            ),
190        );
191
192        $args = array(
193            'order'   => $user_data['query']['order'],
194            'orderby' => $user_data['query']['orderBy'],
195        );
196
197        // Do an internal request for the user list
198        $request = new WP_REST_Request( 'GET', '/wp/v2/users' );
199        $request->set_query_params( $args );
200        $response = rest_do_request( $request );
201
202        if ( $response->is_error() ) {
203            // @todo: error handling
204            return $user_data;
205        }
206
207        // load the real values
208        $user_data['items'] = $response->get_data();
209        $headers            = $response->get_headers();
210
211        if ( isset( $headers['X-WP-Total'] ) ) {
212            $user_data['pagination']['total'] = $headers['X-WP-Total'];
213        }
214
215        if ( isset( $headers['X-WP-TotalPages'] ) ) {
216            $user_data['pagination']['totalPages'] = $headers['X-WP-TotalPages'];
217        }
218
219        return $user_data;
220    }
221
222    /**
223     * Gets the VideoPress used storage space in bytes
224     *
225     * @return int the used storage space
226     */
227    public static function get_storage_used() {
228        $site_data = Site::get_site_info();
229        if ( is_wp_error( $site_data ) ) {
230            return 0;
231        }
232
233        if ( isset( $site_data['options'] ) && isset( $site_data['options']['videopress_storage_used'] ) ) {
234            return intval( round( $site_data['options']['videopress_storage_used'] * 1024 * 1024 ) );
235        } else {
236            return 0;
237        }
238    }
239
240    /**
241     * Return all the initial state that depends on a valid site connection
242     *
243     * @return array
244     */
245    public static function get_connected_initial_state() {
246        return array(
247            'videos'    => array(
248                'storageUsed' => self::get_storage_used(),
249            ),
250            'purchases' => array(
251                'items'      => Site::get_purchases(),
252                'isFetching' => false,
253            ),
254        );
255    }
256
257    /**
258     * Checks if the site has as connected owner
259     */
260    public static function has_connected_owner() {
261        if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
262            return true;
263        }
264
265        if ( ( new Connection_Manager() )->has_connected_owner() ) {
266            return true;
267        }
268
269        return false;
270    }
271
272    /**
273     * Checks if the user is able to perform actions that modify data
274     */
275    public static function can_perform_action() {
276        if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
277            return true;
278        }
279
280        $connection = new Connection_Manager();
281
282        return (
283            $connection->is_connected() &&
284            self::has_connected_owner() &&
285            $connection->is_user_connected()
286        );
287    }
288
289    /**
290     * Return the initial state of the VideoPress app,
291     * used to render initially the app in the frontend.
292     *
293     * @return array
294     */
295    public static function get_initial_state() {
296        $videopress_data   = self::get_video_data();
297        $local_videos_data = self::get_video_data( false );
298
299        // Tweak local videos data.
300        $local_videos = array_map(
301            function ( $video ) {
302                $video              = (array) $video;
303                $id                 = $video['id'];
304                $media_details      = $video['media_details'];
305                $jetpack_videopress = $video['jetpack_videopress'];
306                $read_error         = null;
307
308                // In malformed files, the media_details or jetpack_videopress properties are not arrays.
309                if ( ! is_array( $media_details ) || ! is_array( $jetpack_videopress ) ) {
310                    $media_details      = (array) $media_details;
311                    $jetpack_videopress = (array) $jetpack_videopress;
312                    $read_error         = Upload_Exception::ERROR_MALFORMED_FILE;
313                }
314
315                // Check if video is already uploaded to VideoPress or has some error.
316                try {
317                    $uploader                  = new Uploader( $id );
318                    $is_uploaded_to_videopress = $uploader->is_uploaded();
319                } catch ( Upload_Exception $e ) {
320                    $is_uploaded_to_videopress = false;
321                    $read_error                = $e->getCode();
322                }
323
324                $upload_date = $video['date'];
325                $url         = $video['source_url'];
326
327                $title       = $jetpack_videopress['title'];
328                $description = $jetpack_videopress['description'];
329                $caption     = $jetpack_videopress['caption'];
330
331                $width    = $media_details['width'] ?? null;
332                $height   = $media_details['height'] ?? null;
333                $duration = $media_details['length'] ?? null;
334
335                return array(
336                    'id'                     => $id,
337                    'title'                  => $title,
338                    'description'            => $description,
339                    'caption'                => $caption,
340                    'width'                  => $width,
341                    'height'                 => $height,
342                    'url'                    => $url,
343                    'uploadDate'             => $upload_date,
344                    'duration'               => $duration,
345                    'isUploadedToVideoPress' => $is_uploaded_to_videopress,
346                    'readError'              => $read_error,
347                );
348            },
349            $local_videos_data['videos']
350        );
351
352        // Tweak VideoPress videos data.
353        $videos = array_map(
354            array( __CLASS__, 'prepare_videopress_video_data' ),
355            $videopress_data['videos']
356        );
357
358        $site_settings = self::get_videopress_settings();
359
360        $initial_state = array(
361            'users'        => self::get_user_data(),
362            'siteSettings' => array(
363                'videoPressVideosPrivateForSite' => $site_settings['videopress_videos_private_for_site'],
364                'siteIsPrivate'                  => $site_settings['site_is_private'],
365                'siteType'                       => $site_settings['site_type'],
366            ),
367            'videos'       => array(
368                'uploadedVideoCount'           => $videopress_data['total'],
369                'items'                        => $videos,
370                'isFetching'                   => false,
371                'isFetchingUploadedVideoCount' => false,
372                'pagination'                   => array(
373                    'totalPages' => $videopress_data['totalPages'],
374                    'total'      => $videopress_data['total'],
375                ),
376                'query'                        => $videopress_data['query'],
377                '_meta'                        => array(
378                    'processedAllVideosBeingRemoved' => true,
379                    'relyOnInitialState'             => true,
380                ),
381            ),
382            'localVideos'  => array(
383                'uploadedVideoCount'           => $local_videos_data['total'],
384                'items'                        => $local_videos,
385                'isFetching'                   => false,
386                'isFetchingUploadedVideoCount' => false,
387                'pagination'                   => array(
388                    'totalPages' => $local_videos_data['totalPages'],
389                    'total'      => $local_videos_data['total'],
390                ),
391                'query'                        => $local_videos_data['query'],
392                '_meta'                        => array(
393                    'relyOnInitialState' => true,
394                ),
395            ),
396        );
397
398        if ( self::has_connected_owner() ) {
399            return array_merge_recursive( $initial_state, self::get_connected_initial_state() );
400        }
401
402        return $initial_state;
403    }
404
405    /**
406     * Maps a single VideoPress video, as returned by the media REST endpoint, to
407     * the shape consumed by the VideoPress dashboard app.
408     *
409     * Videos that are still processing (or otherwise have partial metadata) come
410     * back without `videopress`, `width` or `height` in their media_details, so
411     * every nested access is coalesced to avoid undefined-key and offset-on-null
412     * warnings.
413     *
414     * @param array|object $video A single video entry from the media REST endpoint.
415     * @return array The video data formatted for the dashboard app.
416     */
417    private static function prepare_videopress_video_data( $video ) {
418        $video              = (array) $video;
419        $id                 = $video['id'];
420        $guid               = $video['jetpack_videopress_guid'];
421        $media_details      = (array) $video['media_details'];
422        $jetpack_videopress = (array) $video['jetpack_videopress'];
423
424        $videopress_media_details = $media_details['videopress'] ?? array();
425        $width                    = $media_details['width'] ?? null;
426        $height                   = $media_details['height'] ?? null;
427
428        $title                = $jetpack_videopress['title'];
429        $description          = $jetpack_videopress['description'];
430        $caption              = $jetpack_videopress['caption'];
431        $rating               = $jetpack_videopress['rating'];
432        $allow_download       = $jetpack_videopress['allow_download'];
433        $display_embed        = $jetpack_videopress['display_embed'];
434        $privacy_setting      = $jetpack_videopress['privacy_setting'];
435        $needs_playback_token = $jetpack_videopress['needs_playback_token'];
436        $is_private           = $jetpack_videopress['is_private'];
437
438        $original      = $videopress_media_details['original'] ?? null;
439        $poster        = ( ! $needs_playback_token ) ? ( $videopress_media_details['poster'] ?? null ) : null;
440        $upload_date   = $videopress_media_details['upload_date'] ?? null;
441        $duration      = $videopress_media_details['duration'] ?? null;
442        $file_url_base = $videopress_media_details['file_url_base'] ?? null;
443        $finished      = $videopress_media_details['finished'] ?? null;
444        $files         = $videopress_media_details['files'] ?? null;
445        $filename      = $original !== null ? basename( $original ) : null;
446
447        if ( isset( $files['dvd']['original_img'] ) && isset( $file_url_base['https'] ) && $privacy_setting !== 1 ) {
448            $thumbnail = $file_url_base['https'] . $files['dvd']['original_img'];
449        } else {
450            $thumbnail = null;
451        }
452
453        return array(
454            'id'                 => $id,
455            'guid'               => $guid,
456            'title'              => $title,
457            'description'        => $description,
458            'caption'            => $caption,
459            'url'                => $original,
460            'uploadDate'         => $upload_date,
461            'duration'           => $duration,
462            'isPrivate'          => $is_private,
463            'posterImage'        => $poster,
464            'allowDownload'      => $allow_download,
465            'displayEmbed'       => $display_embed,
466            'rating'             => $rating,
467            'privacySetting'     => $privacy_setting,
468            'needsPlaybackToken' => $needs_playback_token,
469            'width'              => $width,
470            'height'             => $height,
471            'poster'             => array(
472                'src' => $poster,
473            ),
474            'thumbnail'          => $thumbnail,
475            'finished'           => $finished,
476            'filename'           => $filename,
477        );
478    }
479}