PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-beta
Jetpack – WP Security, Backup, Speed, & Growth v16.3-beta
16.3-beta 16.3-a.5 16.3-a.7 16.3-a.3 16.3-a.1 16.2 16.2-beta 12.0.3 12.1.3 12.2.3 12.3.2 12.4.2 12.5.2 12.6.4 12.7.3 12.8.3 12.9.5 13.0.2 13.1.5 13.2.4 13.3.3 13.4.5 13.5.2 13.6.2 13.7.2 All 507 releases
← All changes | jetpack_vendor/automattic/jetpack-premium-analytics/src/dashboard-sections.php +45 -269 16.2 → 16.3-beta View file →
@@ -1,49 +1,28 @@
1 1 <?php
2 2 /**
3 - * Dashboard Sections: registry bootstrap and REST routes.
3 + * Dashboard Sections API: the registry helpers, the section script data, and the REST routes.
4 4 *
5 + * The package's own sections register through this API from default-dashboard-sections.php,
6 + * the same way a plugin extending the dashboard does.
7 + *
5 8 * @package automattic/jetpack-premium-analytics
6 9 */
7 10
8 11 namespace Automattic\Jetpack\PremiumAnalytics;
9 12
10 -use Automattic\Jetpack\Modules;
11 -use Automattic\Jetpack\Status\Host;
12 -
13 13 require_once __DIR__ . '/dashboard-layout.php';
14 14 require_once __DIR__ . '/dashboard-grammar.php';
15 +require_once __DIR__ . '/rest-namespace.php';
15 16 require_once __DIR__ . '/class-dashboard-section.php';
16 17 require_once __DIR__ . '/class-dashboard-section-registry.php';
17 18
18 -/**
19 - * Filter through which WooCommerce section availability is resolved.
20 - */
21 -const WOOCOMMERCE_DASHBOARD_SECTION_AVAILABLE_FILTER = 'jetpack_premium_analytics_woocommerce_dashboard_section_available';
19 +// Guarded on a symbol the file declares, so a second copy of the package can't redeclare it.
20 +if ( ! function_exists( __NAMESPACE__ . '\\register_dashboard_feature_flags' ) ) {
21 + require_once __DIR__ . '/dashboard-policy.php';
22 +}
22 23
23 24 /**
24 - * Filter through which Subscribers section availability is resolved.
25 - */
26 -const SUBSCRIBERS_DASHBOARD_SECTION_AVAILABLE_FILTER = 'jetpack_premium_analytics_subscribers_dashboard_section_available';
27 -
28 -/**
29 - * Filter for Ads section availability.
30 - */
31 -const ADS_DASHBOARD_SECTION_AVAILABLE_FILTER = 'jetpack_premium_analytics_ads_dashboard_section_available';
32 -
33 -/**
34 - * Filter through which the preview's section scope is resolved.
35 - */
36 -const DASHBOARD_PREVIEW_SCOPE_FILTER = 'jetpack_premium_analytics_dashboard_preview_scope';
37 -
38 -/**
39 - * Section slugs the customer preview exposes as tabs. A section still rolling out to some
40 - * sites is opened through the filter instead. Widget types are registered independently of
41 - * this, as they are of the per-section availability checks.
42 - */
43 -const PREVIEW_SECTIONS = array( DASHBOARD_TRAFFIC_SECTION_ID );
44 -
45 -/**
46 25 * Registers a dashboard section.
47 26 *
48 27 * @param string $dashboard_name Dashboard identifier.
49 28 * @param string $id Section identifier.
@@ -75,62 +54,19 @@
75 54 return Dashboard_Section_Registry::get_instance()->get_available_sections( $dashboard_name );
76 55 }
77 56
78 57 /**
79 - * Whether the dashboard is running as the customer-facing preview.
80 - *
81 - * The site's own opt-in means the preview. Anything else that switches the dashboard on, the
82 - * WordPress.com blog sticker or the `jetpack_premium_analytics_enabled` filter, means us.
83 - *
84 - * @since 0.6.0
85 - *
86 - * @return bool
87 - */
88 -function is_dashboard_preview_scoped() {
89 - return (bool) get_option( Enablement_Setting::ENABLED_OPTION );
90 -}
91 -
92 -/**
93 - * Whether the preview exposes a dashboard section.
94 - *
95 - * @since 0.6.0
96 - *
97 - * @param string $dashboard_name Dashboard identifier. Only this package's own dashboard is scoped.
98 - * @param string $slug URL-facing section slug.
99 - * @return bool
100 - */
101 -function is_dashboard_section_in_preview_scope( $dashboard_name, $slug ) {
102 - $in_scope = DASHBOARD_NAME !== $dashboard_name
103 - || ! is_dashboard_preview_scoped()
104 - || in_array( $slug, PREVIEW_SECTIONS, true );
105 -
106 - /**
107 - * Filters whether the preview exposes a dashboard section.
108 - *
109 - * `__return_true` restores the whole dashboard, which is how a development or test site
110 - * sees every tab.
111 - *
112 - * @since 0.6.0
113 - *
114 - * @param bool $in_scope Whether the preview exposes the section.
115 - * @param string $slug URL-facing section slug.
116 - * @param string $dashboard_name Dashboard the section belongs to.
117 - */
118 - return (bool) apply_filters( DASHBOARD_PREVIEW_SCOPE_FILTER, $in_scope, $slug, $dashboard_name );
119 -}
120 -
121 -/**
122 58 * Slugs of the tabs the dashboard exposes, for the client's report routes.
123 59 *
124 60 * Reads the same sections the tab list does, so a report cannot outlive the tab it sits
125 - * behind. Null, never `array()`, before the registry is hydrated: an empty array is a
126 - * published scope that exposes nothing.
61 + * behind. Null, never `array()`, while nothing is registered: an empty array means no
62 + * tab is available.
127 63 *
128 64 * @since 0.6.0
129 65 *
130 66 * @return string[]|null
131 67 */
132 -function get_dashboard_preview_scope_sections() {
68 +function get_available_dashboard_section_slugs() {
133 69 $registry = Dashboard_Section_Registry::get_instance();
134 70
135 71 if ( empty( $registry->get_all_registered( DASHBOARD_NAME ) ) ) {
136 72 return null;
@@ -144,232 +80,74 @@
144 80 );
145 81 }
146 82
147 83 /**
148 - * Configures the preview scope script data.
84 + * Configures the section script data.
149 85 *
150 86 * @since 0.6.0
151 87 *
152 88 * @return void
153 89 */
154 -function configure_dashboard_preview_scope() {
155 - add_filter( 'jetpack_admin_js_script_data', __NAMESPACE__ . '\\inject_dashboard_preview_scope_script_data', 20 );
90 +function configure_dashboard_sections_script_data() {
91 + add_filter( 'jetpack_admin_js_script_data', __NAMESPACE__ . '\\inject_dashboard_sections_script_data', 20 );
156 92 }
157 93
158 94 /**
159 - * Injects the preview's section scope into JetpackScriptData.
95 + * Kept for older copies of the package, whose Dashboard_Section::is_available() calls it on every
96 + * availability check. Every section is in scope now, so the section's own rule decides.
160 97 *
161 - * The same list travels over REST for the tab bar, but a report route reads no REST before
162 - * choosing its redirect, so it reads the scope from boot data instead.
163 - *
164 98 * @since 0.6.0
99 + * @deprecated 0.10.0 The preview scope is gone.
165 100 *
166 - * @param array $data The script data passed by the assets package.
167 - * @return array
101 + * @param string $dashboard_name Dashboard identifier.
102 + * @param string $slug URL-facing section slug.
103 + * @return bool Always true.
168 104 */
169 -function inject_dashboard_preview_scope_script_data( array $data ): array {
170 - $sections = get_dashboard_preview_scope_sections();
171 -
172 - if ( null === $sections ) {
173 - return $data;
174 - }
175 -
176 - if ( ! isset( $data['premium_analytics'] ) || ! is_array( $data['premium_analytics'] ) ) {
177 - $data['premium_analytics'] = array();
178 - }
179 -
180 - $data['premium_analytics']['preview_sections'] = $sections;
181 -
182 - return $data;
105 +function is_dashboard_section_in_preview_scope( $dashboard_name, $slug ) { // phpcs:ignore VariableAnalysis.CodeAnalysis.VariableAnalysis.UnusedVariable -- Kept for the signature older copies call.
106 + return true;
183 107 }
184 108
185 109 /**
186 - * Whether the WooCommerce dashboard section should be exposed.
110 + * Kept for older copies of the package: they guard their include of this file on another
111 + * symbol and call this, so a newer copy loading first must still define it.
187 112 *
188 - * @return bool True when WooCommerce is active.
189 - */
190 -function is_woocommerce_dashboard_section_available() {
191 - $is_available = class_exists( 'WooCommerce' ) || function_exists( 'WC' );
192 -
193 - /**
194 - * Filters whether the WooCommerce dashboard section is available.
195 - *
196 - * @param bool $is_available Whether WooCommerce was detected in the current request.
197 - */
198 - return (bool) apply_filters( WOOCOMMERCE_DASHBOARD_SECTION_AVAILABLE_FILTER, $is_available );
199 -}
200 -
201 -/**
202 - * Whether the current user should be shown the WooCommerce dashboard section.
113 + * @since 0.6.0
114 + * @deprecated 0.10.0 Use configure_dashboard_sections_script_data().
203 115 *
204 - * The sibling is_woocommerce_dashboard_section_available() answers "is
205 - * WooCommerce here"; this adds "and may this reader see store data".
206 - *
207 - * @since 0.1.0
208 - *
209 - * @return bool
116 + * @return void
210 117 */
211 -function is_woocommerce_dashboard_section_available_to_current_user() {
212 - return is_woocommerce_dashboard_section_available() && Capabilities::current_user_can_view_store_reports();
118 +function configure_dashboard_preview_scope() {
119 + configure_dashboard_sections_script_data();
213 120 }
214 121
215 122 /**
216 - * Whether the Subscribers dashboard section should be exposed.
123 + * Injects the available section slugs into JetpackScriptData.
217 124 *
218 - * Sites without Jetpack have no module state to check, so the section remains
219 - * available. Modules::is_active() also returns true on WPCOM Simple.
125 + * The same list travels over REST for the tab bar, but a report route reads no REST before
126 + * choosing its redirect, so it reads the slugs from boot data instead.
220 127 *
221 - * @since 0.3.0
128 + * @since 0.6.0
222 129 *
223 - * @return bool True when the subscriptions module is active.
130 + * @param array $data The script data passed by the assets package.
131 + * @return array
224 132 */
225 -function is_subscribers_dashboard_section_available() {
226 - $is_available = ! class_exists( 'Jetpack' ) || ( new Modules() )->is_active( 'subscriptions' );
133 +function inject_dashboard_sections_script_data( array $data ): array {
134 + $sections = get_available_dashboard_section_slugs();
227 135
228 - /**
229 - * Filters whether the Subscribers dashboard section is available.
230 - *
231 - * @since 0.3.0
232 - *
233 - * @param bool $is_available Whether the subscriptions module was detected in the current request.
234 - */
235 - return (bool) apply_filters( SUBSCRIBERS_DASHBOARD_SECTION_AVAILABLE_FILTER, $is_available );
236 -}
136 + if ( null === $sections ) {
137 + return $data;
138 + }
237 139
238 -/**
239 - * Whether the Ads dashboard section is available.
240 - *
241 - * WPCOM reads the plan feature rather than the module, which is a false negative
242 - * on Atomic and meaningless on Simple. Mirrors is_videopress_available().
243 - *
244 - * @since 0.4.0
245 - *
246 - * @return bool True when the site can produce WordAds earnings.
247 - */
248 -function is_ads_dashboard_section_available() {
249 - if ( ( new Host() )->is_wpcom_platform() ) {
250 - $is_available = function_exists( 'wpcom_site_has_feature' ) && \wpcom_site_has_feature( 'wordads' );
251 - } else {
252 - $is_available = ! class_exists( 'Jetpack' ) || ( new Modules() )->is_active( 'wordads' );
140 + if ( ! isset( $data['premium_analytics'] ) || ! is_array( $data['premium_analytics'] ) ) {
141 + $data['premium_analytics'] = array();
253 142 }
254 143
255 - /**
256 - * Filters whether the Ads dashboard section is available.
257 - *
258 - * @since 0.4.0
259 - *
260 - * @param bool $is_available Whether WordAds was detected in the current request.
261 - */
262 - return (bool) apply_filters( ADS_DASHBOARD_SECTION_AVAILABLE_FILTER, $is_available );
263 -}
144 + $data['premium_analytics']['sections'] = $sections;
264 145
265 -/**
266 - * Whether the current user can access the Ads dashboard section.
267 - *
268 - * @since 0.4.0
269 - *
270 - * @return bool
271 - */
272 -function is_ads_dashboard_section_available_to_current_user() {
273 - return is_ads_dashboard_section_available() && Capabilities::current_user_can_view_ad_reports();
146 + return $data;
274 147 }
275 148
276 149 /**
277 - * Returns the default widget layout for the WooCommerce dashboard section.
278 - *
279 - * @return array Array of widget instances.
280 - */
281 -function get_woocommerce_dashboard_section_default_layout() {
282 - return get_dashboard_default_layout_for( 'woocommerce/store' );
283 -}
284 -
285 -/**
286 - * Registers the default Premium Analytics dashboard sections.
287 - *
288 - * @return void
289 - */
290 -function register_default_dashboard_sections() {
291 - $registry = Dashboard_Section_Registry::get_instance();
292 -
293 - $sections = array(
294 - 'analytics/traffic' => array(
295 - 'label' => __( 'Traffic', 'jetpack-premium-analytics-pkg' ),
296 - 'title' => __( 'Site traffic', 'jetpack-premium-analytics-pkg' ),
297 - 'order' => 10,
298 - 'default_layout' => static function () {
299 - return get_dashboard_default_layout_for( 'analytics/traffic' );
300 - },
301 - ),
302 - 'analytics/insights' => array(
303 - 'label' => __( 'Insights', 'jetpack-premium-analytics-pkg' ),
304 - 'title' => __( 'Activity insights', 'jetpack-premium-analytics-pkg' ),
305 - 'order' => 20,
306 - // Insights reads whole history: all time and single years instead of
307 - // the rolling picker, with nothing to compare them against.
308 - 'date_filter' => Dashboard_Section::DATE_FILTER_YEAR,
309 - 'date_filter_options' => array(
310 - 'with_date_comparison' => false,
311 - ),
312 - 'default_layout' => static function () {
313 - return get_dashboard_default_layout_for( 'analytics/insights' );
314 - },
315 - ),
316 - 'analytics/subscribers' => array(
317 - 'label' => __( 'Subscribers', 'jetpack-premium-analytics-pkg' ),
318 - 'title' => __( 'Subscribers stats', 'jetpack-premium-analytics-pkg' ),
319 - 'order' => 30,
320 - 'is_available' => __NAMESPACE__ . '\\is_subscribers_dashboard_section_available',
321 - 'default_layout' => static function () {
322 - return get_dashboard_default_layout_for( 'analytics/subscribers' );
323 - },
324 - ),
325 - // Store registers no heading of its own, so it falls back to the label.
326 - 'woocommerce/store' => array(
327 - 'label' => __( 'Store', 'jetpack-premium-analytics-pkg' ),
328 - 'order' => 40,
329 - 'is_available' => __NAMESPACE__ . '\\is_woocommerce_dashboard_section_available_to_current_user',
330 - // Nothing backfills historical orders to WordPress.com but the analytics
331 - // full sync. The site sections above read data it already holds.
332 - 'requires_sync' => true,
333 - 'default_layout' => __NAMESPACE__ . '\\get_woocommerce_dashboard_section_default_layout',
334 - ),
335 - 'analytics/ads' => array(
336 - 'label' => __( 'Ads', 'jetpack-premium-analytics-pkg' ),
337 - 'order' => 50,
338 - 'is_available' => __NAMESPACE__ . '\\is_ads_dashboard_section_available_to_current_user',
339 - // Only the chart supports dates, so it owns the control. No Ads widget
340 - // supports comparison.
341 - 'date_filter_options' => array(
342 - 'with_date_comparison' => false,
343 - 'with_header_date_control' => false,
344 - ),
345 - 'default_layout' => static function () {
346 - return get_dashboard_default_layout_for( 'analytics/ads' );
347 - },
348 - ),
349 - );
350 -
351 - foreach ( $sections as $id => $args ) {
352 - if ( ! $registry->is_registered( DASHBOARD_NAME, $id ) ) {
353 - register_dashboard_section( DASHBOARD_NAME, $id, $args );
354 - }
355 - }
356 -}
357 -
358 -/**
359 - * Hydrates the dashboard section registry.
360 - *
361 - * @return void
362 - */
363 -function bootstrap_dashboard_sections() {
364 - if ( did_action( 'init' ) ) {
365 - register_default_dashboard_sections();
366 - } else {
367 - add_action( 'init', __NAMESPACE__ . '\\register_default_dashboard_sections' );
368 - }
369 -}
370 -
371 -/**
372 150 * Whether the current user can access dashboard section routes.
373 151 *
374 152 * @return bool
375 153 */
@@ -564,6 +342,4 @@
564 342 ),
565 343 )
566 344 );
567 345 }
568 -
569 -bootstrap_dashboard_sections();