Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 80
0.00% covered (danger)
0.00%
0 / 6
CRAP
0.00% covered (danger)
0.00%
0 / 1
WPCOM_Smart_Dictation
0.00% covered (danger)
0.00%
0 / 79
0.00% covered (danger)
0.00%
0 / 6
756
0.00% covered (danger)
0.00%
0 / 1
 init
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
 is_proxied
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
12
 is_block_editor
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 determine_iso_639_locale
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
12
 enqueue_scripts
0.00% covered (danger)
0.00%
0 / 48
0.00% covered (danger)
0.00%
0 / 1
182
 get_assets_json
0.00% covered (danger)
0.00%
0 / 15
0.00% covered (danger)
0.00%
0 / 1
20
1<?php
2/**
3 * WordPress.com Smart Dictation
4 *
5 * @package automattic/jetpack-mu-wpcom
6 */
7
8namespace A8C\FSE;
9
10use Automattic\Jetpack\Status\Host;
11
12/**
13 * Class WPCOM_Smart_Dictation
14 */
15class WPCOM_Smart_Dictation {
16    /**
17     * WPCOM_Smart_Dictation constructor.
18     */
19    public static function init() {
20        add_action( 'admin_enqueue_scripts', array( self::class, 'enqueue_scripts' ), 100 );
21    }
22
23    /**
24     * Returns whether the current request is coming from the a8c proxy.
25     */
26    private static function is_proxied() {
27        return isset( $_SERVER['A8C_PROXIED_REQUEST'] )
28            ? sanitize_text_field( wp_unslash( $_SERVER['A8C_PROXIED_REQUEST'] ) )
29            : defined( 'A8C_PROXIED_REQUEST' ) && A8C_PROXIED_REQUEST;
30    }
31
32    /**
33     * Returns true if the current screen if the block editor.
34     */
35    public static function is_block_editor() {
36        global $current_screen;
37
38        if ( ! $current_screen ) {
39            return false;
40        }
41
42        return $current_screen->is_block_editor() && $current_screen->id !== 'widgets';
43    }
44
45    /**
46     * Returns ISO 639 conforming locale string of the current user.
47     *
48     * @return string ISO 639 locale string e.g. "en"
49     */
50    private static function determine_iso_639_locale() {
51        $language = get_user_locale();
52        $language = strtolower( $language );
53
54        if ( in_array( $language, array( 'pt_br', 'pt-br', 'zh_tw', 'zh-tw', 'zh_cn', 'zh-cn' ), true ) ) {
55            $language = str_replace( '_', '-', $language );
56        } else {
57            $language = preg_replace( '/([-_].*)$/i', '', $language );
58        }
59
60        if ( empty( $language ) ) {
61            return 'en';
62        }
63
64        return $language;
65    }
66
67    /**
68     * Enqueue the Smart Dictation assets.
69     */
70    public static function enqueue_scripts() {
71        $is_simple_site = ( new Host() )->is_wpcom_simple();
72        if ( ! $is_simple_site ) {
73            return;
74        }
75
76        $asset_file = self::get_assets_json( 'widgets.wp.com/wpcom-smart-dictation/wpcom-smart-dictation.asset.json' );
77        $is_proxied = self::is_proxied();
78        $is_a11n    = function_exists( '\is_automattician' ) && \is_automattician();
79
80        if ( self::is_block_editor() && $is_proxied && $is_a11n ) {
81            $version = ( is_array( $asset_file ) && isset( $asset_file['version'] ) )
82                ? $asset_file['version']
83                : false;
84            wp_enqueue_script(
85                'wpcom-smart-dictation',
86                'https://widgets.wp.com/wpcom-smart-dictation/wpcom-smart-dictation.min.js',
87                array(),
88                $version,
89                true
90            );
91
92            $user_id      = get_current_user_id();
93            $user_data    = get_userdata( $user_id );
94            $username     = $user_data ? $user_data->user_login : null;
95            $user_email   = $user_data ? $user_data->user_email : null;
96            $display_name = $user_data ? $user_data->display_name : null;
97            $avatar_url   = $user_data ? ( function_exists( 'wpcom_get_avatar_url' ) ? wpcom_get_avatar_url( $user_email, 64, '', true )[0] : get_avatar_url( $user_id ) ) : null;
98
99            wp_add_inline_script(
100                'wpcom-smart-dictation',
101                'if ( typeof wpcomSmartDictationData === "undefined" ) { var wpcomSmartDictationData = ' . wp_json_encode(
102                    array(
103                        'isProxied'   => boolval( self::is_proxied() ),
104                        'currentUser' => array(
105                            'ID'           => $user_id,
106                            'username'     => $username,
107                            'display_name' => $display_name,
108                            'avatar_URL'   => $avatar_url,
109                            'email'        => $user_email,
110                            'is_a11n'      => $is_a11n,
111                        ),
112                        'locale'      => self::determine_iso_639_locale(),
113                    ),
114                    JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP
115                ) . '; }',
116                'before'
117            );
118
119            wp_enqueue_style(
120                'wpcom-smart-dictation-style',
121                'https://widgets.wp.com/wpcom-smart-dictation/wpcom-smart-dictation.css',
122                array(),
123                $version
124            );
125        }
126    }
127
128    /**
129     * Get the asset via file-system on wpcom and via network on Atomic sites.
130     *
131     * Checks a transient cache of successful remote fetches before the filesystem or a new HTTP request.
132     * Failures are not cached.
133     *
134     * @param string $filepath The path to the asset file.
135     * @return array|null Decoded manifest, or null when JSON is invalid or empty.
136     */
137    private static function get_assets_json( $filepath ) {
138        $local_path = ABSPATH . $filepath;
139
140        if ( file_exists( $local_path ) ) {
141            // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents -- Reading a local file, not a remote URL.
142            return json_decode( file_get_contents( $local_path ), true );
143        }
144
145        $cache_key = 'jetpack_mu_wpcom_sd_asset_' . md5( $filepath );
146        $cached    = get_transient( $cache_key );
147
148        if ( is_array( $cached ) ) {
149            return $cached;
150        }
151
152        $request = wp_remote_get(
153            'https://' . $filepath,
154            array( 'timeout' => 10 )
155        );
156
157        $decoded = json_decode( wp_remote_retrieve_body( $request ), true );
158        if ( is_array( $decoded ) ) {
159            set_transient( $cache_key, $decoded, 12 * HOUR_IN_SECONDS );
160        }
161        return $decoded;
162    }
163}
164
165add_action( 'init', array( __NAMESPACE__ . '\WPCOM_Smart_Dictation', 'init' ) );