Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 71
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Render_Callback_Trait
0.00% covered (danger)
0.00%
0 / 69
0.00% covered (danger)
0.00%
0 / 5
182
0.00% covered (danger)
0.00%
0 / 1
 render_callback
0.00% covered (danger)
0.00%
0 / 30
0.00% covered (danger)
0.00%
0 / 1
12
 render_video_player
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
2
 build_embed_url
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
42
 get_player_options
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 is_toggle_on
0.00% covered (danger)
0.00%
0 / 2
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * Render callback for the Divi 5 VideoPress module.
4 *
5 * @package automattic/jetpack-videopress
6 */
7
8declare( strict_types = 1 );
9
10namespace Automattic\Jetpack\VideoPress\Divi5\Traits;
11
12use Automattic\Jetpack\VideoPress\Divi5\VideoPress_Module;
13use Automattic\Jetpack\VideoPress\Jwt_Token_Bridge;
14use Automattic\Jetpack\VideoPress\Package_Version;
15use ET\Builder\FrontEnd\BlockParser\BlockParserStore;
16use ET\Builder\Packages\Module\Module;
17
18if ( ! defined( 'ABSPATH' ) ) {
19    exit( 0 );
20}
21
22/**
23 * Builds the front-end markup for the VideoPress module.
24 */
25trait Render_Callback_Trait {
26
27    /**
28     * Renders the module on the front end.
29     *
30     * @param array  $attrs    The module attributes.
31     * @param string $content  The module content.
32     * @param object $block    The parsed block object.
33     * @param object $elements The module element helpers.
34     *
35     * @return string The rendered HTML, or an empty string when no valid GUID is set.
36     */
37    public static function render_callback( $attrs, $content, $block, $elements ) {
38        $guid_value = $attrs['guid']['innerContent']['desktop']['value'] ?? '';
39
40        $matches = array();
41        if ( ! preg_match( VideoPress_Module::VIDEOPRESS_REGEX, (string) $guid_value, $matches ) || ! isset( $matches[1] ) ) {
42            return '';
43        }
44
45        Jwt_Token_Bridge::enqueue_jwt_token_bridge();
46
47        $video_player = self::render_video_player( $matches[1], $attrs );
48        $parent       = BlockParserStore::get_parent( $block->parsed_block['id'], $block->parsed_block['storeInstance'] );
49
50        return Module::render(
51            array(
52                'orderIndex'          => $block->parsed_block['orderIndex'],
53                'storeInstance'       => $block->parsed_block['storeInstance'],
54                'attrs'               => $attrs,
55                'elements'            => $elements,
56                'id'                  => $block->parsed_block['id'],
57                'moduleClassName'     => '',
58                'name'                => $block->block_type->name,
59                'classnamesFunction'  => array( VideoPress_Module::class, 'module_classnames' ),
60                'moduleCategory'      => $block->block_type->category,
61                'stylesComponent'     => array( VideoPress_Module::class, 'module_styles' ),
62                'scriptDataComponent' => array( VideoPress_Module::class, 'module_script_data' ),
63                'parentAttrs'         => $parent->attrs ?? array(),
64                'parentId'            => $parent->id ?? '',
65                // phpcs:ignore WordPress.NamingConventions.ValidVariableName.UsedPropertyNotSnakeCase -- Property defined by the Divi 5 framework.
66                'parentName'          => $parent->blockName ?? '',
67                'children'            => $elements->style_components(
68                    array(
69                        'attrName' => 'module',
70                    )
71                ) . $video_player,
72            )
73        );
74    }
75
76    /**
77     * Builds the VideoPress iframe wrapper markup for a GUID.
78     *
79     * @param string $guid  The VideoPress GUID.
80     * @param array  $attrs The module attributes, used to derive player options.
81     *
82     * @return string The iframe wrapper markup.
83     */
84    private static function render_video_player( $guid, $attrs ) {
85        /*
86         * Enqueue the shared VideoPress iframe API bootstrap rather than printing a
87         * <script> per render. This reuses the `videopress-iframe` handle the
88         * VideoPress block registers, so the script loads once per page no matter
89         * how many videos (or blocks) are present.
90         */
91        wp_enqueue_script(
92            'videopress-iframe',
93            'https://videopress.com/videopress-iframe.js',
94            array(),
95            Package_Version::PACKAGE_VERSION,
96            true
97        );
98
99        $iframe_title = sprintf(
100            /* translators: %s: Video GUID. */
101            esc_html__( 'Video player for %s', 'jetpack-videopress-pkg' ),
102            esc_html( $guid )
103        );
104
105        $iframe_src = self::build_embed_url( $guid, $attrs );
106
107        return '<div class="vidi-videopress-wrapper" style="position:relative;width:100%;height:0;padding-bottom:56.25%;">' .
108            '<iframe title="' . esc_attr( $iframe_title ) . '" src="' . esc_url( $iframe_src ) . '" style="position:absolute;top:0;left:0;width:100%;height:100%;border:0;" frameborder="0" allowfullscreen></iframe>' .
109            '</div>';
110    }
111
112    /**
113     * Builds the VideoPress embed URL, applying only the player options that differ
114     * from the VideoPress player defaults (for a clean URL, mirroring the block).
115     * Kept in sync with `getEmbedUrl()` in the Visual Builder's `utils.js`.
116     *
117     * @param string $guid  The VideoPress GUID.
118     * @param array  $attrs The module attributes.
119     *
120     * @return string The embed URL.
121     */
122    private static function build_embed_url( $guid, $attrs ) {
123        $options = self::get_player_options( $attrs );
124
125        $params = array();
126        if ( $options['autoplay'] ) {
127            $params['autoPlay'] = '1';
128        }
129        if ( $options['loop'] ) {
130            $params['loop'] = '1';
131        }
132        if ( $options['muted'] ) {
133            $params['muted']         = '1';
134            $params['persistVolume'] = '0';
135        }
136        if ( ! $options['controls'] ) {
137            $params['controls'] = '0';
138        }
139        if ( $options['playsinline'] ) {
140            $params['playsinline'] = '1';
141        }
142        // Always identify the embedder so VideoPress can attribute Divi traffic.
143        $params['embedder'] = 'divi-builder';
144
145        return add_query_arg( $params, 'https://videopress.com/embed/' . rawurlencode( $guid ) );
146    }
147
148    /**
149     * Resolves the module's player options from its attributes, falling back to the
150     * VideoPress player defaults when a toggle is unset.
151     *
152     * @param array $attrs The module attributes.
153     *
154     * @return array<string, bool> The resolved player options.
155     */
156    private static function get_player_options( $attrs ) {
157        return array(
158            'autoplay'    => self::is_toggle_on( $attrs, 'autoplay', false ),
159            'loop'        => self::is_toggle_on( $attrs, 'loop', false ),
160            'muted'       => self::is_toggle_on( $attrs, 'muted', false ),
161            'controls'    => self::is_toggle_on( $attrs, 'controls', true ),
162            'playsinline' => self::is_toggle_on( $attrs, 'playsinline', false ),
163        );
164    }
165
166    /**
167     * Reads a Divi 5 toggle attribute, which stores `'on'`/`'off'` strings.
168     *
169     * @param array  $attrs        The module attributes.
170     * @param string $name         The attribute name.
171     * @param bool   $default_value The value to use when the toggle is unset.
172     *
173     * @return bool Whether the toggle is on.
174     */
175    private static function is_toggle_on( $attrs, $name, $default_value ) {
176        $value = $attrs[ $name ]['innerContent']['desktop']['value'] ?? ( $default_value ? 'on' : 'off' );
177
178        return 'on' === $value;
179    }
180}