PluginProbe
Jetpack – WP Security, Backup, Speed, & Growth / 16.3
Jetpack – WP Security, Backup, Speed, & Growth v16.3
16.3 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 All 508 releases
← All changes | jetpack_vendor/automattic/jetpack-premium-analytics/src/widget-availability.php +201 -0 16.2-beta → 16.3 View file →
@@ -1,0 +1,201 @@
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' );