Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
17.86% covered (danger)
17.86%
15 / 84
0.00% covered (danger)
0.00%
0 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
VideoPress_Gutenberg
18.52% covered (danger)
18.52%
15 / 81
0.00% covered (danger)
0.00%
0 / 9
421.37
0.00% covered (danger)
0.00%
0 / 1
 init
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
 __construct
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
2
 get_blog_id
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
30
 check_videopress_availability
70.59% covered (warning)
70.59%
12 / 17
0.00% covered (danger)
0.00%
0 / 1
6.92
 set_extension_availability
75.00% covered (warning)
75.00%
3 / 4
0.00% covered (danger)
0.00%
0 / 1
2.06
 register_video_block_with_videopress
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
2
 render_video_block_with_videopress
0.00% covered (danger)
0.00%
0 / 21
0.00% covered (danger)
0.00%
0 / 1
42
 add_resumable_upload_support
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 override_video_upload
0.00% covered (danger)
0.00%
0 / 12
0.00% covered (danger)
0.00%
0 / 1
12
1<?php // phpcs:ignore WordPress.Files.FileName.InvalidClassFileName
2/**
3 * Block Editor functionality for VideoPress users.
4 *
5 * @package automattic/jetpack
6 */
7
8use Automattic\Jetpack\Assets;
9use Automattic\Jetpack\Blocks;
10use Automattic\Jetpack\Current_Plan as Jetpack_Plan;
11use Automattic\Jetpack\VideoPress\Block_Replacement;
12
13if ( ! defined( 'ABSPATH' ) ) {
14    exit( 0 );
15}
16
17/**
18 * Register a VideoPress extension to replace the default Core Video block.
19 */
20class VideoPress_Gutenberg {
21
22    /**
23     * Singleton
24     */
25    public static function init() {
26        static $instance = false;
27
28        if ( ! $instance ) {
29            $instance = new VideoPress_Gutenberg();
30        }
31
32        return $instance;
33    }
34
35    /**
36     * VideoPress_Gutenberg constructor.
37     *
38     * Initialize the VideoPress Gutenberg extension
39     */
40    private function __construct() {
41        // Run late to avoid race condition with other plugins that register the video block
42        // Jetpack's jetpack_register_block function bails if the block is already registered
43        add_action( 'init', array( $this, 'register_video_block_with_videopress' ), 99 );
44        add_action( 'jetpack_register_gutenberg_extensions', array( $this, 'set_extension_availability' ) );
45        add_action( 'enqueue_block_editor_assets', array( $this, 'override_video_upload' ) );
46        add_action( 'enqueue_block_editor_assets', array( $this, 'add_resumable_upload_support' ) );
47        Block_Replacement::init();
48    }
49
50    /**
51     * Get site's ID.
52     *
53     * @return int $blog_id Site ID.
54     */
55    private static function get_blog_id() {
56        if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) {
57            return get_current_blog_id();
58        } elseif ( method_exists( 'Jetpack', 'is_active' ) && Jetpack::is_active() ) {
59            /**
60             * We're intentionally not using `get_current_blog_id` because it was returning unexpected values.
61             *
62             * @see https://github.com/Automattic/jetpack/pull/11193#issuecomment-457883886
63             * @see https://github.com/Automattic/jetpack/pull/11193/commits/215cf789f3d8bd03ff9eb1bbdb693acb8831d273
64             */
65            return Jetpack_Options::get_option( 'id' );
66        }
67
68        return null;
69    }
70
71    /**
72     * Used to check whether VideoPress is enabled for given site.
73     *
74     * @todo Create a global `jetpack_check_module_availability( $module )` helper so we can re-use it on other modules.
75     *       This global helper should be created in a file synced with WordPress.com so we can use it there too.
76     * @see https://github.com/Automattic/jetpack/pull/11321#discussion_r255477815
77     *
78     * @return array Associative array indicating if the module is available (key `available`) and the reason why it is
79     * unavailable (key `unavailable_reason`)
80     */
81    public function check_videopress_availability() {
82        if ( ! Jetpack_Plan::supports( 'videopress' ) ) {
83            return array(
84                'available'          => false,
85                'unavailable_reason' => 'missing_plan',
86            );
87        }
88
89        if ( ! defined( 'IS_WPCOM' ) || ! IS_WPCOM ) {
90            if ( Jetpack::is_connection_ready() ) {
91                if ( ! Jetpack::is_module_active( 'videopress' ) ) {
92                    return array(
93                        'available'          => false,
94                        'unavailable_reason' => 'missing_module',
95                    );
96                }
97            } else {
98                return array(
99                    'available'          => false,
100                    'unavailable_reason' => 'unknown',
101                );
102            }
103        }
104
105        return array( 'available' => true );
106    }
107
108    /**
109     * Set the Jetpack Gutenberg extension availability.
110     */
111    public function set_extension_availability() {
112        $availability = $this->check_videopress_availability();
113        if ( $availability['available'] ) {
114            Jetpack_Gutenberg::set_extension_available( 'videopress' );
115        } else {
116            Jetpack_Gutenberg::set_extension_unavailable( 'videopress', $availability['unavailable_reason'] );
117        }
118    }
119
120    /**
121     * Register the core video block as a dynamic block.
122     *
123     * It defines a server-side rendering that adds VideoPress support to the core video block.
124     */
125    public function register_video_block_with_videopress() {
126        Blocks::jetpack_register_block(
127            'core/video',
128            array(
129                'render_callback' => array( $this, 'render_video_block_with_videopress' ),
130            )
131        );
132    }
133
134    /**
135     * Render the core video block replacing the src attribute with the VideoPress URL
136     *
137     * @param array  $attributes Array containing the video block attributes.
138     * @param string $content    String containing the video block content.
139     *
140     * @return string
141     */
142    public function render_video_block_with_videopress( $attributes, $content ) {
143        if ( ! isset( $attributes['id'] ) || isset( $attributes['guid'] ) ) {
144            return $content;
145        }
146
147        $blog_id = self::get_blog_id();
148
149        if ( ! isset( $blog_id ) ) {
150            return $content;
151        }
152
153        $post_id         = absint( $attributes['id'] );
154        $videopress_id   = video_get_info_by_blogpostid( $blog_id, $post_id )->guid;
155        $videopress_data = videopress_get_video_details( $videopress_id );
156
157        if ( empty( $videopress_data->file_url_base->https ) || empty( $videopress_data->files->hd->mp4 ) ) {
158            return $content;
159        }
160
161        $videopress_url = $videopress_data->file_url_base->https . $videopress_data->files->hd->mp4;
162
163        $pattern = '/(\s)src=([\'"])(?:(?!\2).)+?\2/';
164
165        return preg_replace(
166            $pattern,
167            sprintf(
168                '\1src="%1$s"',
169                esc_url_raw( $videopress_url )
170            ),
171            $content,
172            1
173        );
174    }
175
176    /**
177     * Temporary method to enable resumable uploads for testing by Automatticians
178     */
179    public function add_resumable_upload_support() {
180        wp_enqueue_script(
181            'videopress-add-resumable-upload-support',
182            plugins_url( 'js/videopress-add-resumable-upload-support.js', __FILE__ ),
183            null,
184            '1',
185            false
186        );
187    }
188
189    /**
190     * Replaces the video uploaded in the block editor.
191     *
192     * Enqueues a script that registers an API fetch middleware replacing the video uploads in Gutenberg so they are
193     * uploaded against the WP.com API media endpoint and thus transcoded by VideoPress.
194     */
195    public function override_video_upload() {
196        // Bail if Jetpack is not connected or VideoPress module is not active.
197        if ( ! Jetpack::is_connection_ready() || ! Jetpack::is_module_active( 'videopress' ) ) {
198            return;
199        }
200
201        wp_enqueue_script(
202            'jetpack-videopress-gutenberg-override-video-upload',
203            Assets::get_file_url_for_environment(
204                '_inc/build/videopress/js/gutenberg-video-upload.min.js',
205                'modules/videopress/js/gutenberg-video-upload.js'
206            ),
207            array( 'wp-api-fetch', 'wp-polyfill' ),
208            JETPACK__VERSION,
209            false
210        );
211    }
212}
213
214VideoPress_Gutenberg::init();