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