Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 123
0.00% covered (danger)
0.00%
0 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
Jetpack_Newsletter_Dashboard_Widget
0.00% covered (danger)
0.00%
0 / 117
0.00% covered (danger)
0.00%
0 / 5
812
0.00% covered (danger)
0.00%
0 / 1
 init
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
6
 get_config_data
0.00% covered (danger)
0.00%
0 / 39
0.00% covered (danger)
0.00%
0 / 1
210
 wp_dashboard_setup
0.00% covered (danger)
0.00%
0 / 24
0.00% covered (danger)
0.00%
0 / 1
20
 render
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 load_admin_scripts
0.00% covered (danger)
0.00%
0 / 47
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2/**
3 * Jetpack Newsletter Dashboard Widget.
4 *
5 * @package jetpack
6 */
7
8use Automattic\Jetpack\Connection\Client;
9use Automattic\Jetpack\Modules;
10
11if ( ! defined( 'ABSPATH' ) ) {
12    exit( 0 );
13}
14
15/**
16 * Class that adds the Jetpack Newsletter Dashboard Widget to the WordPress admin dashboard.
17 */
18class Jetpack_Newsletter_Dashboard_Widget {
19    /**
20     * Indicates whether the class initialized or not.
21     *
22     * @var bool
23     */
24    private static $initialized = false;
25
26    /**
27     * The Widget ID.
28     *
29     * @var string
30     */
31    private static $widget_id = 'jetpack_newsletter_dashboard_widget';
32
33    /**
34     * Initialize the class by calling the setup static function.
35     *
36     * @return void
37     */
38    public static function init() {
39        if ( ! self::$initialized ) {
40            self::$initialized = true;
41            self::wp_dashboard_setup();
42        }
43    }
44
45    /**
46     * Get the config data for the Jetpack Newsletter widget.
47     *
48     * @return array
49     */
50    public static function get_config_data() {
51        $config_data = array(
52            'emailSubscribers'       => 0,
53            'paidSubscribers'        => 0,
54            'allSubscribers'         => 0,
55            'subscriberTotalsByDate' => array(),
56            'isStatsModuleActive'    => false,
57            'showHeader'             => false,
58            'showChart'              => false,
59            'isWidgetVisible'        => false,
60            'newsletterSettingsUrl'  => \Automattic\Jetpack\Newsletter\Urls::get_newsletter_settings_url(),
61        );
62
63        if ( Jetpack::is_connection_ready() ) {
64            $site_id  = Jetpack_Options::get_option( 'id' );
65            $api_path = sprintf( '/sites/%d/subscribers/stats', $site_id );
66            $response = Client::wpcom_json_api_request_as_blog(
67                $api_path,
68                '2',
69                array(),
70                null,
71                'wpcom'
72            );
73
74            if ( 200 === wp_remote_retrieve_response_code( $response ) ) {
75                $subscriber_counts = json_decode( wp_remote_retrieve_body( $response ), true );
76                if ( isset( $subscriber_counts['counts']['email_subscribers'] ) ) {
77                    $config_data['emailSubscribers'] = (int) $subscriber_counts['counts']['email_subscribers'];
78                }
79
80                if ( isset( $subscriber_counts['counts']['paid_subscribers'] ) ) {
81                    $config_data['paidSubscribers'] = (int) $subscriber_counts['counts']['paid_subscribers'];
82                }
83
84                if ( isset( $subscriber_counts['counts']['all_subscribers'] ) ) {
85                    $config_data['allSubscribers'] = (int) $subscriber_counts['counts']['all_subscribers'];
86                }
87
88                if ( isset( $subscriber_counts['aggregate'] ) ) {
89                    $config_data['subscriberTotalsByDate'] = $subscriber_counts['aggregate'];
90                }
91            }
92
93            $config_data['isStatsModuleActive'] = ( new Modules() )->is_active( 'stats' );
94
95            $config_data['showHeader'] = $config_data['isStatsModuleActive'] && ( $config_data['allSubscribers'] > 0 || $config_data['paidSubscribers'] > 0 );
96            foreach ( $config_data['subscriberTotalsByDate'] as $day ) {
97                if ( $day && ( $day['all'] >= 5 || $day['paid'] > 0 ) ) {
98                    $config_data['showChart'] = true;
99                    break;
100                }
101            }
102
103            $config_data['isWidgetVisible'] = $config_data['showHeader'] || $config_data['showChart'];
104        }
105
106        return $config_data;
107    }
108
109    /**
110     * Sets up the Jetpack Newsletter widget in the WordPress admin dashboard.
111     */
112    public static function wp_dashboard_setup() {
113        // Do not show the widget to non-admins.
114        if ( ! current_user_can( 'manage_options' ) ) {
115            return;
116        }
117
118        if ( Jetpack::is_connection_ready() ) {
119            $config_data = static::get_config_data();
120
121            if ( ! $config_data['isWidgetVisible'] ) {
122                return;
123            }
124
125            static::load_admin_scripts(
126                'jp-newsletter-widget',
127                'newsletter-widget',
128                array(
129                    'config_variable_name' => 'jetpackNewsletterWidgetConfigData',
130                    'config_data'          => $config_data,
131                    'load_minified_js'     => false,
132                )
133            );
134
135            wp_add_dashboard_widget(
136                self::$widget_id,
137                /** "Newsletter" is a product name, do not translate. */
138                'Jetpack Newsletter',
139                array( static::class, 'render' ),
140                // @phan-suppress-next-line PhanTypeMismatchArgumentProbablyReal -- Core should ideally document null for no-callback arg. https://core.trac.wordpress.org/ticket/52539.
141                null,
142                array(),
143                'side',
144                'high'
145            );
146        }
147    }
148
149    /**
150     * Render the Jetpack Newsletter widget.
151     *
152     * @return void
153     */
154    public static function render() {
155        ?>
156        <div id="wpcom">
157            <div id="newsletter-widget-app"></div>
158        </div>
159        <?php
160    }
161
162    /**
163     * Load the admin scripts for the Jetpack Newsletter widget.
164     *
165     * @param string $asset_handle The handle of the asset.
166     * @param string $asset_name The name of the asset.
167     * @param array  $options The options for the asset.
168     * @return void
169     */
170    public static function load_admin_scripts( $asset_handle, $asset_name, $options = array() ) {
171        $default_options = array(
172            'config_data'          => array(),
173            'config_variable_name' => 'configData',
174            'enqueue_css'          => true,
175            'load_minified_js'     => true,
176        );
177        $options         = wp_parse_args( $options, $default_options );
178
179        /*
180         * Get the asset file path. The widget is built unminified (see tools/webpack.config.js),
181         * so read the matching non-minified asset metadata.
182         */
183        $asset_path = JETPACK__PLUGIN_DIR . '_inc/build/' . $asset_name . '.asset.php';
184
185        // Get dependencies and version from asset file
186        $dependencies = array();
187        $version      = JETPACK__VERSION;
188
189        if ( file_exists( $asset_path ) ) {
190            $asset        = require $asset_path;
191            $dependencies = $asset['dependencies'];
192            $version      = $asset['version'];
193        }
194
195        $file_extension = '.min.js';
196        if ( ! $options['load_minified_js'] ) {
197            $file_extension = '.js';
198        }
199        // Register and enqueue the script
200        wp_register_script(
201            $asset_handle,
202            plugins_url( '_inc/build/' . $asset_name . $file_extension, JETPACK__PLUGIN_FILE ),
203            $dependencies,
204            $version,
205            true
206        );
207        wp_enqueue_script( $asset_handle );
208        if ( in_array( 'wp-i18n', $dependencies, true ) ) {
209            wp_set_script_translations( $asset_handle, 'jetpack' );
210        }
211
212        // Enqueue the CSS if enabled
213        if ( $options['enqueue_css'] ) {
214            wp_enqueue_style(
215                $asset_handle,
216                plugins_url( '_inc/build/' . $asset_name . '.css', JETPACK__PLUGIN_FILE ),
217                array(),
218                $version
219            );
220
221            // Enqueue RTL stylesheet if needed
222            if ( is_rtl() ) {
223                wp_enqueue_style(
224                    $asset_handle . '-rtl',
225                    plugins_url( '_inc/build/' . $asset_name . '.rtl.css', JETPACK__PLUGIN_FILE ),
226                    array( $asset_handle ),
227                    $version
228                );
229            }
230        }
231
232        // Add any configuration data if needed
233        if ( ! empty( $options['config_data'] ) ) {
234            wp_add_inline_script(
235                $asset_handle,
236                "window.{$options['config_variable_name']} = " . wp_json_encode( $options['config_data'], JSON_UNESCAPED_SLASHES | JSON_HEX_TAG | JSON_HEX_AMP ) . ';',
237                'before'
238            );
239        }
240    }
241}
242
243add_action(
244    'wp_dashboard_setup',
245    array( 'Jetpack_Newsletter_Dashboard_Widget', 'init' )
246);