Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
99.19% covered (success)
99.19%
244 / 246
100.00% covered (success)
100.00%
5 / 5
CRAP
n/a
0 / 0
Automattic\Jetpack\PremiumAnalytics\is_subscribers_dashboard_section_available
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
Automattic\Jetpack\PremiumAnalytics\get_traffic_section_default_layout
100.00% covered (success)
100.00%
89 / 89
100.00% covered (success)
100.00%
1 / 1
1
Automattic\Jetpack\PremiumAnalytics\get_insights_section_default_layout
100.00% covered (success)
100.00%
79 / 79
100.00% covered (success)
100.00%
1 / 1
1
Automattic\Jetpack\PremiumAnalytics\get_subscribers_section_default_layout
100.00% covered (success)
100.00%
33 / 33
100.00% covered (success)
100.00%
1 / 1
1
Automattic\Jetpack\PremiumAnalytics\register_default_dashboard_sections
100.00% covered (success)
100.00%
39 / 39
100.00% covered (success)
100.00%
1 / 1
4
1<?php
2/**
3 * The package's own dashboard sections: the built-in tabs, their availability gates and default
4 * layouts, and the filters over those gates. They register through the section API in
5 * dashboard-sections.php, the same way a plugin extending the dashboard does; the Ads tab and the
6 * WooCommerce tab are such sections, registered by their own packages.
7 *
8 * @package automattic/jetpack-premium-analytics
9 */
10
11namespace Automattic\Jetpack\PremiumAnalytics;
12
13use Automattic\Jetpack\Modules;
14
15require_once __DIR__ . '/dashboard-sections.php';
16
17/**
18 * Filter through which Subscribers section availability is resolved.
19 */
20const SUBSCRIBERS_DASHBOARD_SECTION_AVAILABLE_FILTER = 'jetpack_premium_analytics_subscribers_dashboard_section_available';
21
22/**
23 * Whether the Subscribers dashboard section should be exposed.
24 *
25 * Sites without Jetpack have no module state to check, so the section remains
26 * available. Modules::is_active() also returns true on WPCOM Simple.
27 *
28 * @since 0.3.0
29 *
30 * @return bool True when the subscriptions module is active and the reader may see Stats.
31 */
32function is_subscribers_dashboard_section_available() {
33    // Outside the filter, which answers only whether the module is there.
34    if ( ! Capabilities::current_user_can_view_stats() ) {
35        return false;
36    }
37
38    $is_available = ! class_exists( 'Jetpack' ) || ( new Modules() )->is_active( 'subscriptions' );
39
40    /**
41     * Filters whether the Subscribers dashboard section is available.
42     *
43     * @since 0.3.0
44     *
45     * @param bool $is_available Whether the subscriptions module was detected in the current request.
46     */
47    return (bool) apply_filters( SUBSCRIBERS_DASHBOARD_SECTION_AVAILABLE_FILTER, $is_available );
48}
49
50/**
51 * The Traffic tab's default widget layout.
52 *
53 * @return array Widget instances.
54 */
55function get_traffic_section_default_layout() {
56    return array(
57        // Rows fill the three-column grid in the prototype's order.
58        // Row 1: traffic chart.
59        get_dashboard_default_widget_instance(
60            'default-traffic-chart-widget-instance',
61            'jpa/traffic-chart',
62            0,
63            3,
64            2
65        ),
66        // Row 2: most-viewed posts + referrers + devices.
67        get_dashboard_default_widget_instance(
68            'default-stats-top-posts-widget-instance',
69            'jpa/stats-top-posts',
70            1,
71            1,
72            2
73        ),
74        get_dashboard_default_widget_instance(
75            'default-referrers-widget-instance',
76            'jpa/referrers',
77            2,
78            1,
79            2
80        ),
81        get_dashboard_default_widget_instance(
82            'default-devices-widget-instance',
83            'jpa/devices',
84            3,
85            1,
86            2
87        ),
88        // Row 3: locations map + top platforms.
89        get_dashboard_default_widget_instance(
90            'default-locations-widget-instance',
91            'jpa/locations',
92            4,
93            2,
94            2
95        ),
96        get_dashboard_default_widget_instance(
97            'default-top-platforms-widget-instance',
98            'jpa/top-platforms',
99            5,
100            1,
101            2
102        ),
103        // Row 4: UTM insights + clicks; the VideoPress package seeds Top videos at order 8.
104        get_dashboard_default_widget_instance(
105            'default-utm-insights-widget-instance',
106            'jpa/utm-insights',
107            6,
108            1,
109            2,
110            array(
111                'utmDimension' => 'utm_source,utm_medium',
112            )
113        ),
114        get_dashboard_default_widget_instance(
115            'default-clicks-widget-instance',
116            'jpa/clicks',
117            7,
118            1,
119            2
120        ),
121        // Row 5: authors + search terms + file downloads (Simple only).
122        get_dashboard_default_widget_instance(
123            'default-authors-widget-instance',
124            'jpa/authors',
125            9,
126            1,
127            2
128        ),
129        get_dashboard_default_widget_instance(
130            'default-search-terms-widget-instance',
131            'jpa/search-terms',
132            10,
133            1,
134            2
135        ),
136        get_dashboard_default_widget_instance(
137            'default-file-downloads-widget-instance',
138            'jpa/file-downloads',
139            11,
140            1,
141            2
142        ),
143        // Row 6: tags & categories, ranked over the tab's period.
144        get_dashboard_default_widget_instance(
145            'default-tags-widget-instance',
146            'jpa/tags',
147            12,
148            2,
149            2
150        ),
151    );
152}
153
154/**
155 * The Insights tab's default widget layout.
156 *
157 * @return array Widget instances.
158 */
159function get_insights_section_default_layout() {
160    return array(
161        // Rows follow the design (WOOA7S-2009); Emails lives on the Subscribers tab.
162        // Row 1: highlights banner.
163        get_dashboard_default_widget_instance(
164            'default-annual-highlights-widget-instance',
165            'jpa/annual-highlights',
166            0,
167            3,
168            1
169        ),
170        // Row 2: at-a-glance cards. Two rows tall: their display-sized figures overflow a 200px tile.
171        get_dashboard_default_widget_instance(
172            'default-all-time-stats-widget-instance',
173            'jpa/all-time-stats',
174            1,
175            1,
176            2
177        ),
178        get_dashboard_default_widget_instance(
179            'default-most-popular-time-widget-instance',
180            'jpa/most-popular-time',
181            2,
182            1,
183            2
184        ),
185        get_dashboard_default_widget_instance(
186            'default-most-popular-day-widget-instance',
187            'jpa/most-popular-day',
188            3,
189            1,
190            2
191        ),
192        // Row 3: the two post spotlights.
193        get_dashboard_default_widget_instance(
194            'default-popular-post-widget-instance',
195            'jpa/popular-post',
196            4,
197            2,
198            2
199        ),
200        get_dashboard_default_widget_instance(
201            'default-latest-post-widget-instance',
202            'jpa/latest-post',
203            5,
204            1,
205            2
206        ),
207        // Row 4: posting-activity heatmap. Two rows tall so the months wrap into larger days.
208        get_dashboard_default_widget_instance(
209            'default-posting-activity-widget-instance',
210            'jpa/posting-activity',
211            6,
212            3,
213            2
214        ),
215        // Row 5: the all-time views table, one row per year. Two rows tall so a
216        // few years fit before the grid scrolls.
217        get_dashboard_default_widget_instance(
218            'default-views-over-years-widget-instance',
219            'jpa/views-over-years',
220            7,
221            3,
222            2
223        ),
224        // Row 6: most commented posts + most commented authors fill the row, so shares
225        // (Simple only) opens row 7 instead of pushing authors down with a gap on both rows.
226        get_dashboard_default_widget_instance(
227            'default-most-commented-posts-widget-instance',
228            'jpa/most-commented-posts',
229            8,
230            1,
231            2
232        ),
233        get_dashboard_default_widget_instance(
234            'default-most-commented-authors-widget-instance',
235            'jpa/most-commented-authors',
236            9,
237            2,
238            2
239        ),
240        get_dashboard_default_widget_instance(
241            'default-shares-widget-instance',
242            'jpa/shares',
243            10,
244            1,
245            2
246        ),
247    );
248}
249
250/**
251 * The Subscribers tab's default widget layout.
252 *
253 * @return array Widget instances.
254 */
255function get_subscribers_section_default_layout() {
256    return array(
257        // Row 1: subscribers chart.
258        get_dashboard_default_widget_instance(
259            'default-subscribers-chart-widget-instance',
260            'jpa/subscribers-chart',
261            0,
262            3,
263            2
264        ),
265        // Row 2: subscriber highlights.
266        get_dashboard_default_widget_instance(
267            'default-subscriber-highlights-widget-instance',
268            'jpa/subscriber-highlights',
269            1,
270            3,
271            1
272        ),
273        // Row 3: latest subscribers + the wider latest emails sent table.
274        get_dashboard_default_widget_instance(
275            'default-subscribers-list-widget-instance',
276            'jpa/subscribers-list',
277            2,
278            1,
279            2
280        ),
281        get_dashboard_default_widget_instance(
282            'default-subscribers-emails-widget-instance',
283            'jpa/stats-emails',
284            3,
285            2,
286            2,
287            array(
288                'metric' => 'opens',
289            )
290        ),
291    );
292}
293
294/**
295 * Registers the default Premium Analytics dashboard sections.
296 *
297 * Hooked on the registration action and safe to call directly: a section already registered
298 * is skipped.
299 *
300 * @param Dashboard_Section_Registry|null $registry Optional. The registry being hydrated. Defaults to the main instance.
301 * @return void
302 */
303function register_default_dashboard_sections( $registry = null ) {
304    if ( ! $registry instanceof Dashboard_Section_Registry ) {
305        $registry = Dashboard_Section_Registry::get_instance();
306    }
307
308    $sections = array(
309        'analytics/traffic'     => array(
310            'label'                    => __( 'Traffic', 'jetpack-premium-analytics-pkg' ),
311            'title'                    => __( 'Site traffic', 'jetpack-premium-analytics-pkg' ),
312            'order'                    => 10,
313            // Only the Traffic summary groups by interval, and it saves its own.
314            'date_filter_options'      => array(
315                'with_header_interval_control' => false,
316            ),
317            'follows_plan_date_limits' => true,
318            'default_layout'           => __NAMESPACE__ . '\\get_traffic_section_default_layout',
319        ),
320        'analytics/insights'    => array(
321            'label'               => __( 'Insights', 'jetpack-premium-analytics-pkg' ),
322            'title'               => __( 'Site insights', 'jetpack-premium-analytics-pkg' ),
323            'order'               => 20,
324            // Insights reads whole history: all time and single years, with nothing
325            // to compare them against. Most widgets have fixed periods of their own,
326            // so no header control; Highlights hosts the only year control.
327            'date_filter'         => Dashboard_Section::DATE_FILTER_YEAR,
328            'date_filter_options' => array(
329                'with_date_comparison'     => false,
330                'with_header_date_control' => false,
331            ),
332            'default_layout'      => __NAMESPACE__ . '\\get_insights_section_default_layout',
333        ),
334        'analytics/subscribers' => array(
335            'label'               => __( 'Subscribers', 'jetpack-premium-analytics-pkg' ),
336            'title'               => __( 'Subscribers stats', 'jetpack-premium-analytics-pkg' ),
337            'order'               => 30,
338            'is_available'        => __NAMESPACE__ . '\\is_subscribers_dashboard_section_available',
339            // Only the summary chart supports dates, so it owns the control. No
340            // Subscribers widget supports comparison.
341            'date_filter_options' => array(
342                'with_date_comparison'     => false,
343                'with_header_date_control' => false,
344            ),
345            'default_layout'      => __NAMESPACE__ . '\\get_subscribers_section_default_layout',
346        ),
347    );
348
349    foreach ( $sections as $id => $args ) {
350        if ( ! $registry->is_registered( DASHBOARD_NAME, $id ) ) {
351            $registry->register( DASHBOARD_NAME, $id, $args );
352        }
353    }
354}
355
356// Registered when the registry hydrates, through the same action a plugin extending the dashboard uses.
357add_action( Dashboard_Section_Registry::REGISTER_ACTION, __NAMESPACE__ . '\\register_default_dashboard_sections' );