Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
77.33% covered (warning)
77.33%
58 / 75
55.56% covered (warning)
55.56%
5 / 9
CRAP
0.00% covered (danger)
0.00%
0 / 1
Script_Data
77.33% covered (warning)
77.33%
58 / 75
55.56% covered (warning)
55.56%
5 / 9
21.77
0.00% covered (danger)
0.00%
0 / 1
 configure
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
12
 register_assets
0.00% covered (danger)
0.00%
0 / 9
0.00% covered (danger)
0.00%
0 / 1
2
 render_script_data
94.12% covered (success)
94.12%
16 / 17
0.00% covered (danger)
0.00%
0 / 1
5.01
 is_authenticated_rest_request
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
2
 get_admin_script_data
100.00% covered (success)
100.00%
25 / 25
100.00% covered (success)
100.00%
1 / 1
1
 get_public_script_data
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 get_site_title
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
2
 get_site_icon
66.67% covered (warning)
66.67%
2 / 3
0.00% covered (danger)
0.00%
0 / 1
2.15
 get_current_user_data
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2/**
3 * Jetpack script data.
4 *
5 * @package  automattic/jetpack-assets
6 */
7
8namespace Automattic\Jetpack\Assets;
9
10use Automattic\Jetpack\Assets;
11use Automattic\Jetpack\Status;
12use Automattic\Jetpack\Status\Host;
13
14/**
15 * Class script data
16 */
17class Script_Data {
18
19    const SCRIPT_HANDLE = 'jetpack-script-data';
20
21    /**
22     * Whether the script data has been rendered.
23     *
24     * @var bool
25     */
26    private static $did_render_script_data = false;
27
28    /**
29     * Configure.
30     */
31    public static function configure() {
32        // Hooked by actions.php since 2.3.0, so this is the package's recovery point.
33        // Assets can still resolve to an older copy than this class, hence the guard.
34        if ( method_exists( Assets::class, 'ensure_package_bootstrap' ) ) {
35            Assets::ensure_package_bootstrap();
36        }
37
38        /**
39         * Ensure that assets are registered on wp_loaded,
40         * which is fired before *_enqueue_scripts actions.
41         * It means that when the dependent scripts are registered,
42         * the scripts here are already registered.
43         */
44        add_action( 'wp_loaded', array( self::class, 'register_assets' ) );
45
46        /**
47         * Notes:
48         * 1. wp_print_scripts action is fired on both admin and public pages.
49         *    On admin pages, it's fired before admin_enqueue_scripts action,
50         *    which can be a problem if the consumer package uses admin_enqueue_scripts
51         *    to hook into the script data. Thus, we prefer to use admin_print_scripts on admin pages.
52         * 2. We want to render the script data on print, instead of init or enqueue actions,
53         *    so that the hook callbacks have enough time and information
54         *    to decide whether to update the script data or not.
55         */
56        $hook = is_admin() ? 'admin_print_scripts' : 'wp_print_scripts';
57        add_action( $hook, array( self::class, 'render_script_data' ), 1 );
58        add_action( 'enqueue_block_editor_assets', array( self::class, 'render_script_data' ), 1 );
59    }
60
61    /**
62     * Register assets.
63     *
64     * @access private
65     */
66    public static function register_assets() {
67
68        Assets::register_script(
69            self::SCRIPT_HANDLE,
70            '../build/jetpack-script-data.js',
71            __FILE__,
72            array(
73                'in_footer'  => true,
74                'textdomain' => 'jetpack-assets',
75            )
76        );
77    }
78
79    /**
80     * Render the script data using an inline script.
81     *
82     * @access private
83     *
84     * @return void
85     */
86    public static function render_script_data() {
87        // In case of 'enqueue_block_editor_assets' action, this can be called multiple times.
88        if ( self::$did_render_script_data ) {
89            return;
90        }
91
92        self::$did_render_script_data = true;
93
94        $script_data = is_admin() || self::is_authenticated_rest_request()
95            ? self::get_admin_script_data()
96            : self::get_public_script_data();
97
98        if ( ! empty( $script_data ) ) {
99            $script_data = wp_json_encode(
100                $script_data,
101                JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE
102            );
103
104            wp_add_inline_script(
105                self::SCRIPT_HANDLE,
106                sprintf( 'window.JetpackScriptData = %s;', $script_data ),
107                'before'
108            );
109            Assets::enqueue_script( self::SCRIPT_HANDLE );
110        }
111    }
112
113    /**
114     * Whether the current request is an authenticated REST API request.
115     *
116     * @return bool
117     */
118    protected static function is_authenticated_rest_request() {
119        return wp_is_serving_rest_request() && current_user_can( 'read' );
120    }
121
122    /**
123     * Get the admin script data.
124     *
125     * @return array
126     */
127    protected static function get_admin_script_data() {
128
129        global $wp_version;
130
131        $data = array(
132            'site' => array(
133                'admin_url'         => esc_url_raw( admin_url() ),
134                'date_format'       => get_option( 'date_format' ),
135                'icon'              => self::get_site_icon(),
136                'is_multisite'      => is_multisite(),
137                'host'              => ( new Host() )->get_known_host_guess(),
138                'is_wpcom_platform' => ( new Host() )->is_wpcom_platform(),
139                'plan'              => array(
140                    // The properties here should be updated by the consumer package/plugin.
141                    // It includes properties like 'product_slug', 'features', etc.
142                    'product_slug' => '',
143                ),
144                'rest_nonce'        => wp_create_nonce( 'wp_rest' ),
145                'rest_root'         => esc_url_raw( rest_url() ),
146                'suffix'            => ( new Status() )->get_site_suffix(),
147                'title'             => self::get_site_title(),
148                'wp_version'        => $wp_version,
149                'wpcom'             => array(
150                    // This should contain the connected site details like blog_id, is_atomic etc.
151                    'blog_id' => 0,
152                ),
153            ),
154            'user' => array(
155                'current_user' => self::get_current_user_data(),
156            ),
157        );
158
159        /**
160         * Filter the admin script data.
161         *
162         * When using this filter, ensure that the data is added only if it is used by some script.
163         * This filter may be called on almost every admin page load. So, one should check if the data is needed/used on that page.
164         * For example, the social (publicize) data is used only on Social admin page, Jetpack settings page and the post editor.
165         * So, the social data should be added only on those pages.
166         *
167         * @since 2.3.0
168         *
169         * @param array $data The script data.
170         */
171        return apply_filters( 'jetpack_admin_js_script_data', $data );
172    }
173
174    /**
175     * Get the public script data.
176     *
177     * @return array
178     */
179    protected static function get_public_script_data() {
180
181        $data = array();
182
183        /**
184         * Filter the public script data.
185         *
186         * See the docs for `jetpack_admin_js_script_data` filter for more information.
187         *
188         * @since 2.3.0
189         *
190         * @param array $data The script data.
191         */
192        return apply_filters( 'jetpack_public_js_script_data', $data );
193    }
194
195    /**
196     * Get the site title.
197     *
198     * @return string
199     */
200    protected static function get_site_title() {
201        $title = wp_specialchars_decode( get_bloginfo( 'name' ), ENT_QUOTES );
202
203        return $title ? $title : esc_url_raw( ( get_site_url() ) );
204    }
205
206    /**
207     * Get the site icon.
208     *
209     * @return string
210     */
211    protected static function get_site_icon() {
212        if ( ! has_site_icon() ) {
213            return '';
214        }
215
216        /**
217         * Filters the site icon using Photon.
218         *
219         * @see https://developer.wordpress.com/docs/photon/
220         *
221         * @param string       $url The URL of the site icon.
222         * @param array|string $args An array of arguments, e.g. array( 'w' => '300', 'resize' => array( 123, 456 ) ), or in string form (w=123&h=456).
223         */
224        return apply_filters( 'jetpack_photon_url', get_site_icon_url(), array( 'w' => 64 ) );
225    }
226
227    /**
228     * Get the current user data.
229     *
230     * @return array
231     */
232    protected static function get_current_user_data() {
233        $current_user = wp_get_current_user();
234
235        return array(
236            'display_name' => $current_user->display_name,
237            'id'           => $current_user->ID,
238            'capabilities' => array(
239                'edit_others_posts' => current_user_can( 'edit_others_posts' ),
240                'manage_options'    => current_user_can( 'manage_options' ),
241                'manage_modules'    => current_user_can( 'jetpack_manage_modules' ),
242            ),
243        );
244    }
245}