Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
78.65% covered (warning)
78.65%
70 / 89
71.43% covered (warning)
71.43%
5 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
XMLRPC
78.65% covered (warning)
78.65%
70 / 89
71.43% covered (warning)
71.43%
5 / 7
40.35
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 init
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 xmlrpc_methods
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 create_media_item
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
1 / 1
11
 update_videopress_media_item
87.10% covered (warning)
87.10%
27 / 31
0.00% covered (danger)
0.00%
0 / 1
9.17
 update_poster_image
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
12
 authenticate_user
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2/**
3 * The VIdeoPress XMLRPC class
4 *
5 * @package automattic/jetpack-videopress
6 */
7
8namespace Automattic\Jetpack\VideoPress;
9
10use WP_User;
11
12/**
13 * VideoPress playback module markup generator.
14 *
15 * @since 0.1.1
16 */
17class XMLRPC {
18
19    /**
20     * Singleton XMLRPC instance.
21     *
22     * @var XMLRPC
23     **/
24    private static $instance = null;
25
26    /**
27     * The current user object.
28     *
29     * @var WP_User
30     */
31    private $current_user;
32
33    /**
34     * Private VideoPress_XMLRPC constructor.
35     *
36     * Use the VideoPress_XMLRPC::init() method to get an instance.
37     */
38    private function __construct() {
39        add_filter( 'jetpack_xmlrpc_methods', array( $this, 'xmlrpc_methods' ), 10, 3 );
40    }
41
42    /**
43     * Initialize the VideoPress_XMLRPC and get back a singleton instance.
44     *
45     * @return XMLRPC
46     */
47    public static function init() {
48        if ( self::$instance === null ) {
49            self::$instance = new XMLRPC();
50        }
51
52        return self::$instance;
53    }
54
55    /**
56     * Adds additional methods the WordPress xmlrpc API for handling VideoPress specific features
57     *
58     * @param array   $methods The Jetpack API methods.
59     * @param array   $core_methods The WordPress Core API methods (ignored).
60     * @param WP_User $user The user object the API request is signed by.
61     *
62     * @return array
63     */
64    public function xmlrpc_methods( $methods, $core_methods, $user ) {
65        if ( $user && $user instanceof WP_User ) {
66            $this->current_user = $user;
67        }
68
69        $methods['jetpack.createMediaItem']                = array( $this, 'create_media_item' );
70        $methods['jetpack.updateVideoPressMediaItem']      = array( $this, 'update_videopress_media_item' );
71        $methods['jetpack.updateVideoPressMediaItemError'] = array( $this, 'update_videopress_media_item' );
72        $methods['jetpack.updateVideoPressPosterImage']    = array( $this, 'update_poster_image' );
73
74        return $methods;
75    }
76
77    /**
78     * This is used by the WPCOM VideoPress uploader in order to create a media item with
79     * specific meta data about an uploaded file. After this, the transcoding session will
80     * update the meta information via the update_videopress_media_item() method.
81     *
82     * Note: This method technically handles the creation of multiple media objects, though
83     * in practice this is never done.
84     *
85     * @param array $media Media items being uploaded.
86     * @return array
87     */
88    public function create_media_item( $media ) {
89        $this->authenticate_user();
90
91        foreach ( $media as & $media_item ) {
92            $url   = is_string( $media_item['url'] ?? null ) ? $media_item['url'] : '';
93            $title = isset( $media_item['title'] ) && '' !== $media_item['title']
94                ? sanitize_text_field( $media_item['title'] )
95                : sanitize_title( basename( $url ) );
96            $guid  = $media['guid'] ?? null;
97
98            $media_id = videopress_create_new_media_item( $title, $guid, $media_item['minimum_attachment_id'] ?? 0 );
99            if ( is_wp_error( $media_id ) ) {
100                return array( 'errors' => array( $media_id->get_error_code() => $media_id->get_error_message() ) );
101            }
102
103            $post_update = array();
104            if ( isset( $media_item['description'] ) && '' !== $media_item['description'] ) {
105                $post_update['post_content'] = sanitize_textarea_field( $media_item['description'] );
106            }
107            if ( isset( $media_item['caption'] ) && '' !== $media_item['caption'] ) {
108                $post_update['post_excerpt'] = sanitize_textarea_field( $media_item['caption'] );
109            }
110            if ( $post_update ) {
111                $post_update['ID'] = $media_id;
112                wp_update_post( $post_update );
113            }
114
115            wp_update_attachment_metadata(
116                $media_id,
117                array(
118                    'original' => array(
119                        'url' => $media_item['url'],
120                    ),
121                )
122            );
123
124            $media_item['post'] = get_post( $media_id );
125        }
126
127        return array( 'media' => $media );
128    }
129
130    /**
131     * Update VideoPress metadata for a media item.
132     *
133     * @param array $request Media item to update.
134     *
135     * @return bool
136     */
137    public function update_videopress_media_item( $request ) {
138        $this->authenticate_user();
139
140        $id     = $request['post_id'];
141        $status = $request['status'];
142        $format = $request['format'];
143        $info   = $request['info'];
144
145        $attachment = get_post( $id );
146        if ( ! $attachment ) {
147            return false;
148        }
149
150        $attachment->guid           = $info['original'];
151        $attachment->post_mime_type = 'video/videopress';
152
153        wp_update_post( $attachment );
154
155        // Update the vp guid and set it to a direct meta property.
156        update_post_meta( $id, 'videopress_guid', $info['guid'] );
157
158        $meta = wp_get_attachment_metadata( $id );
159        if ( ! is_array( $meta ) ) {
160            $meta = array();
161        }
162
163        $meta['width']             = $info['width'];
164        $meta['height']            = $info['height'];
165        $meta['original']['url']   = $info['original'];
166        $meta['videopress']        = $info;
167        $meta['videopress']['url'] = 'https://videopress.com/v/' . $info['guid'];
168
169        // Update file statuses
170        if ( ! empty( $format ) ) {
171            $meta['file_statuses'][ $format ] = $status;
172        }
173
174        if ( ! empty( $info['poster'] ) && ! get_post_meta( $id, '_thumbnail_id', true ) ) {
175            // Update the poster in the VideoPress info.
176            $thumbnail_id = videopress_download_poster_image( $info['poster'], $id );
177
178            if ( is_int( $thumbnail_id ) ) {
179                update_post_meta( $id, '_thumbnail_id', $thumbnail_id );
180            }
181        }
182
183        wp_update_attachment_metadata( $id, $meta );
184
185        videopress_update_meta_data( $id );
186
187        // A later auxiliary-format callback must not erase an MP4 failure.
188        $failed = 'ERROR' === ( $meta['file_statuses']['mp4'] ?? '' );
189        update_post_meta( $id, 'videopress_status', $failed ? 'failed' : ( videopress_is_finished_processing( $id ) ? 'complete' : 'processing' ) );
190
191        return true;
192    }
193
194    /**
195     * Update poster image for a VideoPress media item.
196     *
197     * @param array $request The media item to update.
198     * @return bool
199     */
200    public function update_poster_image( $request ) {
201        $this->authenticate_user();
202
203        $post_id = $request['post_id'];
204        $poster  = $request['poster'];
205
206        $attachment = get_post( $post_id );
207        if ( ! $attachment ) {
208            return false;
209        }
210
211        $poster = apply_filters( 'jetpack_photon_url', $poster );
212
213        $meta                         = wp_get_attachment_metadata( $post_id );
214        $meta['videopress']['poster'] = $poster;
215        wp_update_attachment_metadata( $post_id, $meta );
216
217        // Update the poster in the VideoPress info.
218        $thumbnail_id = videopress_download_poster_image( $poster, $post_id );
219
220        if ( ! is_int( $thumbnail_id ) ) {
221            return false;
222        }
223
224        update_post_meta( $post_id, '_thumbnail_id', $thumbnail_id );
225
226        return true;
227    }
228
229    /**
230     * Check if the XML-RPC request is signed by a user token, and authenticate the user in WordPress.
231     *
232     * @return bool
233     */
234    private function authenticate_user() {
235        if ( $this->current_user ) {
236            wp_set_current_user( $this->current_user->ID );
237
238            return true;
239        }
240
241        return false;
242    }
243}