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