Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 117
0.00% covered (danger)
0.00%
0 / 4
CRAP
n/a
0 / 0
Automattic\Jetpack\Extensions\Eventbrite\get_current_url
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
20
Automattic\Jetpack\Extensions\Eventbrite\render
0.00% covered (danger)
0.00%
0 / 11
0.00% covered (danger)
0.00%
0 / 1
56
Automattic\Jetpack\Extensions\Eventbrite\render_embed_block
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
12
Automattic\Jetpack\Extensions\Eventbrite\render_modal_block
0.00% covered (danger)
0.00%
0 / 58
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2/**
3 * Eventbrite block render implementation.
4 *
5 * Loaded lazily from eventbrite.php only when the block is rendered, to keep the
6 * render body out of the eager front-end PHP/opcache footprint.
7 *
8 * @package automattic/jetpack
9 */
10
11namespace Automattic\Jetpack\Extensions\Eventbrite;
12
13use Automattic\Jetpack\Blocks;
14use Jetpack_Gutenberg;
15
16if ( ! defined( 'ABSPATH' ) ) {
17    exit( 0 );
18}
19
20/**
21 * Get current URL.
22 *
23 * @return string Current URL.
24 */
25function get_current_url() {
26    if ( isset( $_SERVER['HTTP_HOST'] ) ) {
27        $host = wp_unslash( $_SERVER['HTTP_HOST'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
28    } else {
29        $host = wp_parse_url( home_url(), PHP_URL_HOST );
30    }
31    if ( isset( $_SERVER['REQUEST_URI'] ) ) {
32        $path = wp_unslash( $_SERVER['REQUEST_URI'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
33    } else {
34        $path = '/';
35    }
36    return esc_url_raw( ( is_ssl() ? 'https' : 'http' ) . '://' . $host . $path );
37}
38
39/**
40 * Eventbrite block registration/dependency delclaration.
41 *
42 * @param array  $attr    Eventbrite block attributes.
43 * @param string $content Rendered embed element (without scripts) from the block editor.
44 *
45 * @return string Rendered block.
46 */
47function render( $attr, $content ) {
48    if ( is_admin() || empty( $attr['eventId'] ) || empty( $attr['url'] ) ) {
49        return '';
50    }
51
52    $attr['url'] = Jetpack_Gutenberg::validate_block_embed_url(
53        $attr['url'],
54        array( '#^https?:\/\/(?:[0-9a-z]+\.)?eventbrite\.(?:com|co\.uk|com\.ar|com\.au|be|com\.br|ca|cl|co|dk|de|es|fi|fr|hk|ie|it|com\.mx|nl|co\.nz|at|com\.pe|pt|ch|sg|se)\/e\/[^\/]*?(?:\d+)\/?(?:\?[^\/]*)?$#' ),
55        true
56    );
57
58    $widget_id = wp_unique_id( 'eventbrite-widget-' );
59
60    // Show the embedded version.
61    if ( empty( $attr['useModal'] ) && ( empty( $attr['style'] ) || 'modal' !== $attr['style'] ) ) {
62        return render_embed_block( $widget_id, Blocks::is_amp_request(), $attr );
63    } else {
64        return render_modal_block( $widget_id, Blocks::is_amp_request(), $attr, $content );
65    }
66}
67
68/**
69 * Render block with embed style.
70 *
71 * @param string $widget_id Widget ID to use.
72 * @param bool   $is_amp    Whether AMP page.
73 * @param array  $attr      Eventbrite block attributes.
74 * @return string Rendered block.
75 */
76function render_embed_block( $widget_id, $is_amp, $attr ) {
77    /*
78     * $content contains a fallback link to the event that's saved in the post_content.
79     * Append a div that will hold the iframe embed created by the Eventbrite widget.js.
80     */
81    $classes = Blocks::classes( Blocks::get_block_feature( __DIR__ ), $attr );
82
83    $classes .= ' wp-block-jetpack-eventbrite--embed';
84
85    $direct_link = sprintf(
86        '<a href="%s" rel="noopener noreferrer" target="_blank" class="eventbrite__direct-link" %s>%s</a>',
87        esc_url( $attr['url'] ),
88        $is_amp ? 'placeholder fallback' : '',
89        esc_html__( 'Register on Eventbrite', 'jetpack' )
90    );
91
92    if ( $is_amp ) {
93        $embed = sprintf(
94            '<amp-iframe src="%s" layout="responsive" resizable width="1" height="1" sandbox="allow-scripts allow-same-origin allow-forms"><button overflow>%s</button>%s</amp-iframe>',
95            esc_url(
96                add_query_arg(
97                    array(
98                        'eid'    => $attr['eventId'],
99                        'parent' => rawurlencode( get_current_url() ),
100                    ),
101                    'https://www.eventbrite.com/checkout-external'
102                )
103            ),
104            esc_html__( 'Expand', 'jetpack' ),
105            $direct_link
106        );
107    } else {
108        $embed = $direct_link;
109
110        wp_enqueue_script( 'eventbrite-widget', 'https://www.eventbrite.com/static/widgets/eb_widgets.js', array(), JETPACK__VERSION, true );
111
112        // Add CSS to hide direct link.
113        Jetpack_Gutenberg::load_assets_as_required( __DIR__ );
114
115        wp_add_inline_script(
116            'eventbrite-widget',
117            "window.EBWidgets.createWidget( {
118                widgetType: 'checkout',
119                eventId: " . absint( $attr['eventId'] ) . ',
120                iframeContainerId: ' . wp_json_encode( $widget_id, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) . ',
121            } );'
122        );
123    }
124
125    return sprintf(
126        '<div id="%1$s" class="%2$s">%3$s</div>',
127        esc_attr( $widget_id ),
128        esc_attr( $classes ),
129        $embed
130    );
131}
132
133/**
134 * Render block with modal style.
135 *
136 * @param string $widget_id Widget ID to use.
137 * @param bool   $is_amp    Whether AMP page.
138 * @param array  $attr      Eventbrite block attributes.
139 * @param string $content   Rendered embed element (without scripts) from the block editor.
140 * @return string Rendered block.
141 */
142function render_modal_block( $widget_id, $is_amp, $attr, $content ) {
143
144    if ( $is_amp ) {
145        $lightbox_id = "{$widget_id}-lightbox";
146
147        // Add CSS to for lightbox.
148        Jetpack_Gutenberg::load_assets_as_required( __DIR__ );
149
150        $content = preg_replace(
151            '/\shref="#" target="_blank/',
152            sprintf( ' on="%s" ', esc_attr( "tap:{$lightbox_id}.open" ) ),
153            $content
154        );
155
156        $iframe_src = add_query_arg(
157            array(
158                // Note that modal=1 is intentionally omitted here since we need to put the close button inside the amp-lightbox.
159                'eid'    => $attr['eventId'],
160                'parent' => rawurlencode( get_current_url() ),
161            ),
162            'https://www.eventbrite.com/checkout-external'
163        );
164
165        $lightbox = sprintf(
166            '<amp-lightbox id="%1$s" on="%2$s" class="eventbrite__lightbox" layout="nodisplay">%3$s</amp-lightbox>',
167            esc_attr( $lightbox_id ),
168            esc_attr( "tap:{$lightbox_id}.close" ),
169            sprintf(
170                '
171                    <div class="eventbrite__lighbox-inside">
172                        <div class="eventbrite__lighbox-iframe-wrapper">
173                            <amp-iframe class="eventbrite__lighbox-iframe" src="%s" layout="fill" sandbox="allow-scripts allow-same-origin allow-forms">
174                                <span placeholder=""></span>
175                            </amp-iframe>
176                            <span class="eventbrite__lighbox-close" on="%s" role="button" tabindex="0" aria-label="%s">
177                                <svg viewBox="0 0 24 24">
178                                    <path d="M13.4 12l3.5-3.5-1.4-1.4-3.5 3.5-3.5-3.5-1.4 1.4 3.5 3.5-3.5 3.5 1.4 1.4 3.5-3.5 3.5 3.5 1.4-1.4z"></path>
179                                </svg>
180                            </span>
181                        </div>
182                    </div>
183                ',
184                esc_url( $iframe_src ),
185                esc_attr( "tap:{$lightbox_id}.close" ),
186                esc_attr__( 'Close', 'jetpack' )
187            )
188        );
189
190        $content = preg_replace(
191            ':(?=</div>\s*$):',
192            $lightbox,
193            $content
194        );
195
196        return $content;
197    }
198
199    wp_enqueue_script( 'eventbrite-widget', 'https://www.eventbrite.com/static/widgets/eb_widgets.js', array(), JETPACK__VERSION, true );
200
201    // Show the modal version.
202    wp_add_inline_script(
203        'eventbrite-widget',
204        "window.EBWidgets.createWidget( {
205            widgetType: 'checkout',
206            eventId: " . absint( $attr['eventId'] ) . ',
207            modal: true,
208            modalTriggerElementId: ' . wp_json_encode( $widget_id, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) . ',
209        } );'
210    );
211
212    /*
213     * Modal button is saved as an `<a>` element with `role="button"` because `<button>` is not allowed
214     * by WordPress.com wp_kses. This javascript adds the necessary event handling for button-like behavior.
215     * @link https://www.w3.org/TR/wai-aria-practices/examples/button/button.html.
216     */
217    wp_add_inline_script(
218        'eventbrite-widget',
219        '( function() {
220            var widget = document.getElementById( ' . wp_json_encode( $widget_id, JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) . " );
221            if ( widget ) {
222                widget.addEventListener( 'click', function( event ) {
223                    event.preventDefault();
224                } );
225
226                widget.addEventListener( 'keydown', function( event ) {
227                    // Enter and space keys.
228                    if ( event.keyCode === 13 || event.keyCode === 32 ) {
229                        event.preventDefault();
230                        event.target && event.target.click();
231                    }
232                } );
233            }
234        } )();"
235    );
236
237    // Replace the placeholder id saved in the post_content with a unique id used by widget.js.
238    $content = str_replace( 'eventbrite-widget-id', esc_attr( $widget_id ), $content );
239
240    // Fallback for block version deprecated/v2.
241    $content = preg_replace( '/eventbrite-widget-\d+/', esc_attr( $widget_id ), $content );
242
243    // Inject URL to event in case the JS for the lightbox fails to load.
244    $content = preg_replace(
245        '/\shref="#"/',
246        sprintf(
247            ' href="%s" rel="noopener noreferrer" target="_blank"',
248            esc_url( $attr['url'] )
249        ),
250        $content
251    );
252
253    return $content;
254}