PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3-a.7
Jetpack – WP Security, Backup, Speed, & Growth v16.3-a.7
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 13.8.3 All 506 releases
jetpack / jetpack_vendor / automattic / jetpack-premium-analytics / src / widget-availability.php

widget-availability.php in Jetpack – WP Security, Backup, Speed, & Growth 16.3-a.7, at jetpack_vendor/automattic/jetpack-premium-analytics/src/widget-availability.php

202 lines 7.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Widget availability policy (consumer layer): drops developer-only, platform-unsupported,
4 * plugin-gated and capability-gated candidates from a manifest at registry time, over the
5 * neutral hooks in widget-types.php. A dropped candidate never registers, so every reader sees
6 * the same set; a type registered one by one with register_widget_type() skips this policy.
7 *
8 * @package automattic/jetpack-premium-analytics
9 */
10
11 namespace Automattic\Jetpack\PremiumAnalytics;
12
13 require_once __DIR__ . '/widget-types.php';
14 require_once __DIR__ . '/widget-type-support.php';
15
16 /**
17 * Widget categories that are only meaningful with WooCommerce active.
18 */
19 const WOOCOMMERCE_WIDGET_CATEGORIES = array( 'store', 'orders', 'coupons' );
20
21 /**
22 * Widget categories that are only meaningful with WooCommerce Bookings active.
23 *
24 * Checked independently of WOOCOMMERCE_WIDGET_CATEGORIES: the Bookings
25 * extension cannot run without WooCommerce, so its presence implies both.
26 */
27 const WOOCOMMERCE_BOOKINGS_WIDGET_CATEGORIES = array( 'bookings' );
28
29 /**
30 * Widget categories whose data counts as a store report — by data source, not subject
31 * matter: each reaches WPCOM via the proxy's `analytics` prefix (gated on
32 * `view_woocommerce_reports`), including `visitors`, which reads `sessions/…` from it.
33 */
34 const STORE_REPORT_WIDGET_CATEGORIES = array( 'store', 'orders', 'coupons', 'bookings', 'visitors' );
35
36 /**
37 * Removes developer-only candidates in production.
38 *
39 * Split from the hook callback so both branches are testable without touching
40 * the global environment.
41 *
42 * @param array $widget_candidates Manifest candidates, each with a `name` and `category`.
43 * @param string $environment Site environment type.
44 * @return array The candidates, minus developer-only types in production.
45 */
46 function remove_dev_only_widget_types( $widget_candidates, $environment ) {
47 if ( 'production' !== $environment ) {
48 return $widget_candidates;
49 }
50
51 return array_values(
52 array_filter(
53 $widget_candidates,
54 static function ( $widget ) {
55 return 'developer' !== ( $widget['category'] ?? '' );
56 }
57 )
58 );
59 }
60
61 /**
62 * Registry-time callback: hides developer-only types in production.
63 *
64 * Defaults to `production`; a site opts in via `WP_ENVIRONMENT_TYPE`
65 * (`local`, `development`, `staging`).
66 *
67 * @param array $widget_candidates Manifest candidates.
68 * @return array The candidates, minus developer-only types in production.
69 */
70 function filter_registrable_widget_types_by_environment( $widget_candidates ) {
71 return remove_dev_only_widget_types( $widget_candidates, wp_get_environment_type() );
72 }
73
74 add_filter( REGISTRABLE_WIDGET_TYPES_FILTER, __NAMESPACE__ . '\\filter_registrable_widget_types_by_environment' );
75
76 /**
77 * Applies shared type-level availability at registry time.
78 *
79 * @param array $widget_candidates Manifest candidates.
80 * @return array Filtered candidates.
81 */
82 function filter_registrable_widget_types_by_availability( $widget_candidates ) {
83 return remove_unsupported_widget_items(
84 $widget_candidates,
85 'name',
86 get_widget_support_context()
87 );
88 }
89
90 add_filter( REGISTRABLE_WIDGET_TYPES_FILTER, __NAMESPACE__ . '\\filter_registrable_widget_types_by_availability' );
91
92 /**
93 * Removes candidates whose commerce category lacks its backing plugin.
94 *
95 * Split from the hook callback so the branches are testable without touching
96 * global plugin state.
97 *
98 * @param array $widget_candidates Manifest candidates, each with a `category`.
99 * @param bool $woocommerce_available Whether WooCommerce is available.
100 * @param bool $bookings_available Whether WooCommerce Bookings is available.
101 * @return array The candidates, minus commerce categories missing their plugin.
102 */
103 function remove_plugin_gated_widget_types( $widget_candidates, $woocommerce_available, $bookings_available ) {
104 return array_values(
105 array_filter(
106 $widget_candidates,
107 static function ( $widget ) use ( $woocommerce_available, $bookings_available ) {
108 $category = $widget['category'] ?? '';
109
110 if ( ! $woocommerce_available && in_array( $category, WOOCOMMERCE_WIDGET_CATEGORIES, true ) ) {
111 return false;
112 }
113
114 if ( ! $bookings_available && in_array( $category, WOOCOMMERCE_BOOKINGS_WIDGET_CATEGORIES, true ) ) {
115 return false;
116 }
117
118 return true;
119 }
120 )
121 );
122 }
123
124 /**
125 * Whether the WooCommerce Bookings extension is active.
126 *
127 * Mirrors the detection in woocommerce-analytics' Bookings sync module;
128 * `is_plugin_active()` only exists in admin contexts, hence the guard.
129 *
130 * @return bool Whether WooCommerce Bookings was detected in the current request.
131 */
132 function is_bookings_plugin_active() {
133 return class_exists( 'WC_Bookings' )
134 || ( function_exists( 'is_plugin_active' ) && \is_plugin_active( 'woocommerce-bookings/woocommerce-bookings.php' ) );
135 }
136
137 /**
138 * Registry-time callback: hides commerce categories without their plugin, reading
139 * WooCommerce availability through the store section's signal so section and widgets
140 * agree; both entry points load default-dashboard-sections.php before the registry hydrates.
141 *
142 * @param array $widget_candidates Manifest candidates.
143 * @return array The candidates, minus commerce categories missing their plugin.
144 */
145 function filter_registrable_widget_types_by_plugin( $widget_candidates ) {
146 return remove_plugin_gated_widget_types(
147 $widget_candidates,
148 is_woocommerce_dashboard_section_available(),
149 is_bookings_plugin_active()
150 );
151 }
152
153 add_filter( REGISTRABLE_WIDGET_TYPES_FILTER, __NAMESPACE__ . '\\filter_registrable_widget_types_by_plugin' );
154
155 // Subscriber widgets stay registered even when their section is hidden: their data
156 // doesn't depend on the local module, and unregistering would break instances placed elsewhere.
157
158 /**
159 * Removes candidates the reader could not load data for anyway.
160 *
161 * Split from the hook callback so both branches are testable without a user.
162 *
163 * @since 0.1.0
164 *
165 * @param array $widget_candidates Manifest candidates, each with a `category`.
166 * @param bool $can_view_store_reports Whether the reader may see the store reports.
167 * @return array The candidates, minus the store-report categories for readers who can't.
168 */
169 function remove_capability_gated_widget_types( $widget_candidates, $can_view_store_reports ) {
170 if ( $can_view_store_reports ) {
171 return $widget_candidates;
172 }
173
174 return array_values(
175 array_filter(
176 $widget_candidates,
177 static function ( $widget ) {
178 return ! in_array( $widget['category'] ?? '', STORE_REPORT_WIDGET_CATEGORIES, true );
179 }
180 )
181 );
182 }
183
184 /**
185 * Registry-time callback: hides widgets whose data the reader cannot fetch — a
186 * `view_stats` reader would only collect 403s from the proxy's `analytics` prefix.
187 * The registry is request-scoped, so filtering on the current user is safe here.
188 *
189 * @since 0.1.0
190 *
191 * @param array $widget_candidates Manifest candidates.
192 * @return array The candidates, minus the store-report categories for readers who can't see them.
193 */
194 function filter_registrable_widget_types_by_capability( $widget_candidates ) {
195 return remove_capability_gated_widget_types(
196 $widget_candidates,
197 Capabilities::current_user_can_view_store_reports()
198 );
199 }
200
201 add_filter( REGISTRABLE_WIDGET_TYPES_FILTER, __NAMESPACE__ . '\\filter_registrable_widget_types_by_capability' );
202