PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 11.1.0-beta.2
WooCommerce v11.1.0-beta.2
11.1.0 11.1.0-rc.2 11.1.0-rc.1 11.1.0-beta.2 11.1.0-beta.1 11.0.1 11.0.0 11.0.0-rc.3 11.0.0-rc.2 11.0.0-rc.1 11.0.0-beta.2 11.0.0-beta.1 10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 7.8.0-beta.1 7.8.0-beta.2 7.8.0-rc.1 7.8.0-rc.2 7.8.1 7.8.2 7.8.3 7.8.4 7.9.0 7.9.0-beta.1 7.9.0-beta.2 7.9.0-rc.2 7.9.0-rc.3 7.9.1 7.9.2 8.0.0 8.0.0-beta.1 8.0.0-beta.2 8.0.0-rc.1 8.0.0-rc.2 8.0.1 8.0.2 8.0.3 8.0.4 8.0.5 8.1.0 8.1.0-beta.1 8.1.0-rc.1 8.1.0-rc.2 8.1.1 8.1.2 8.1.3 8.1.4 8.2.0 8.2.0-beta.1 8.2.0-rc.1 8.2.0-rc.2 8.2.1 8.2.2 8.2.3 8.2.4 8.2.5 8.3.0 8.3.0-beta.1 8.3.0-rc.1 8.3.0-rc.2 8.3.1 8.3.2 8.3.3 8.3.4 8.4.0 8.4.0-beta.1 8.4.0-rc.1 8.4.1 8.4.2 8.4.3 8.5.0 8.5.0-beta.1 8.5.0-rc.1 8.5.1 8.5.2 8.5.3 8.5.4 8.5.5 8.6.0 8.6.0-beta.1 8.6.0-rc.1 8.6.1 8.6.2 8.6.3 8.6.4 8.7.0 8.7.0-beta.1 8.7.0-beta.2 8.7.0-rc.1 8.7.1 8.7.2 8.7.3 8.8.0 8.8.0-beta.1 8.8.0-rc.1 8.8.1 8.8.2 8.8.3 8.8.4 8.8.5 8.8.6 8.8.7 8.9.0 8.9.0-beta.1 8.9.0-rc.1 8.9.1 8.9.2 8.9.3 8.9.4 8.9.5 9.0.0 9.0.0-beta.1 9.0.0-beta.2 9.0.0-rc.1 9.0.1 9.0.2 9.0.3 9.0.4 9.1.0 9.1.0-beta.1 9.1.0-rc.1 9.1.1 9.1.2 9.1.3 9.1.4 9.1.5 9.1.6 9.2.0 9.2.0-beta.1 9.2.0-rc.1 9.2.1 9.2.2 9.2.3 9.2.4 9.2.5 9.3.0 9.3.0-beta.1 9.3.0-rc.1 9.3.1 9.3.2 9.3.3 9.3.4 9.3.5 9.3.6 9.4.0 9.4.0-beta.1 9.4.0-beta.2 9.4.0-rc.1 9.4.0-rc.2 9.4.0-rc.3 9.4.0-rc.4 9.4.1 9.4.2 9.4.3 9.4.4 9.4.5 9.5.0 9.5.0-beta.1 9.5.0-beta.2 9.5.0-rc.1 9.5.1 9.5.2 9.5.3 9.5.4 9.6.0 9.6.0-beta.1 9.6.0-beta.2 9.6.0-rc.1 9.6.1 9.6.2 9.6.3 9.6.4 9.7.0 9.7.0-beta.1 9.7.0-rc.1 9.7.1 9.7.2 9.7.3 9.8.0 9.8.0-beta.1 9.8.0-rc.1 9.8.1 9.8.2 9.8.3 9.8.4 9.8.5 9.8.6 9.8.7 9.9.0 9.9.0-beta.1 9.9.0-rc.1 9.9.1 9.9.2 9.9.3 9.9.4 9.9.5 9.9.6 9.9.7 3.7.3 7.1.2 3.8.0 7.2.0 3.8.0-beta.1 7.2.0-beta.1 3.8.0-rc.1 7.2.0-beta.2 3.8.0-rc.2 7.2.0-rc.1 3.8.1 7.2.0-rc.2 3.8.2 7.2.1 3.8.3 7.2.2 3.9.0 7.2.3 3.9.0-beta.1 7.2.4 3.9.0-beta.2 7.3.0 3.9.0-rc.1 7.3.0-beta.1 3.9.0-rc.2 7.3.0-beta.2 3.9.0-rc.3 7.3.0-rc.1 3.9.0-rc.4 7.3.0-rc.2 3.9.1 7.3.1 3.9.2 7.4.0 3.9.3 7.4.0-beta.1 3.9.4 7.4.0-beta.2 3.9.5 7.4.0-rc.1 4.0.0 7.4.0-rc.2 4.0.0-beta.1 7.4.1 4.0.0-rc.1 7.4.2 4.0.0-rc.2 7.5.0 4.0.1 7.5.0-beta.1 4.0.2 7.5.0-beta.2 4.0.3 7.5.0-rc.1 4.0.4 7.5.1 4.1.0 7.5.2 4.1.0-beta.1 7.6.0 4.1.0-beta.2 7.6.0-beta.1 4.1.0-rc.1 7.6.0-beta.2 4.1.0-rc.2 7.6.0-rc.1 4.1.1 7.6.0-rc.2 4.1.2 7.6.0-rc.3 4.1.3 7.6.1 4.1.4 7.6.2 4.2.0 7.7.0 4.2.0-RC.1 7.7.0-beta.1 4.2.0-RC.2 7.7.0-beta.2 4.2.0-beta.1 7.7.0-rc.1 4.2.1 7.7.1 4.2.2 7.7.2 4.2.3 7.7.3 4.2.4 7.8.0 4.2.5 4.3.0 4.3.0-beta.1 4.3.0-rc.1 4.3.0-rc.2 4.3.0-rc.3 4.3.1 4.3.2 4.3.3 4.3.4 4.3.5 4.3.6 4.4.0 4.4.0-beta.1 4.4.0-rc.1 4.4.1 4.4.2 4.4.3 4.4.4 4.5.0 4.5.0-beta.1 4.5.0-rc.1 4.5.0-rc.3 4.5.1 4.5.2 4.5.3 4.5.4 4.5.5 4.6.0 4.6.0-beta.1 4.6.0-rc.1 4.6.1 4.6.2 4.6.3 4.6.4 4.6.5 4.7.0 4.7.0-beta.1 4.7.0-beta.2 4.7.0-rc.1 4.7.1 4.7.1-beta.1 4.7.2 4.7.3 4.7.4 4.8.0 4.8.0-beta.1 4.8.0-rc.1 4.8.0-rc.2 4.8.1 4.8.2 4.8.3 4.9.0 4.9.0-beta.1 4.9.0-rc.1 4.9.0-rc.2 4.9.1 4.9.2 4.9.3 4.9.4 4.9.5 5.0.0 5.0.0-beta.1 5.0.0-beta.2 5.0.0-rc.1 5.0.0-rc.2 5.0.0-rc.3 5.0.1 5.0.2 5.0.3 5.1.0 5.1.0-beta.1 5.1.0-rc.1 trunk 5.1.1 10.0.0 5.1.2 10.0.0-rc.1 5.1.3 10.0.0-rc.2 5.2.0 10.0.1 5.2.0-beta.1 10.0.2 5.2.0-rc.1 10.0.3 5.2.0-rc.2 10.0.4 5.2.1 10.0.5 5.2.2 10.0.6 5.2.3 10.1.0 5.2.4 10.1.0-rc.1 5.2.5 10.1.0-rc.2 5.3.0 10.1.0-rc.3 5.3.0-beta.1 10.1.0-rc.4 5.3.0-rc.1 10.1.1 5.3.0-rc.2 10.1.2 5.3.1 10.1.3 5.3.2 10.1.4 5.3.3 10.2.0 5.4.0 10.2.0-beta.1 5.4.0-beta.1 10.2.0-beta.2 5.4.0-rc.1 10.2.0-rc.1 5.4.1 10.2.1 5.4.2 10.2.2 5.4.3 10.2.3 5.4.4 10.2.4 5.4.5 10.3.0 5.5.0 10.3.0-beta.1 5.5.0-beta.1 10.3.0-beta.2 5.5.0-rc.1 10.3.0-rc.1 5.5.0-rc.2 10.3.0-rc.2 5.5.1 10.3.1 5.5.2 10.3.2 5.5.3 10.3.3 5.5.4 10.3.4 5.5.5 10.3.5 5.6.0 10.3.6 5.6.0-beta.1 10.3.7 5.6.0-rc.1 10.3.8 5.6.0-rc.2 10.4.0 5.6.1 10.4.0-beta.1 5.6.2 10.4.0-beta.2 5.6.3 10.4.0-rc.1 5.7.0 10.4.1 5.7.0-beta.1 10.4.2 5.7.0-rc.1 10.4.3 5.7.1 10.4.4 5.7.2 10.5.0 5.7.3 10.5.0-beta.1 5.8.0 10.5.0-beta.2 5.8.0-beta.1 10.5.0-rc.1 5.8.0-beta.2 10.5.0-rc.2 5.8.0-rc.1 10.5.0-rc.3 5.8.1 10.5.1 5.8.2 10.5.2 5.9.0 10.5.3 5.9.0-beta.1 10.6.0 5.9.0-rc.1 10.6.0-beta.1 5.9.0-rc.2 10.6.0-beta.2 5.9.1 10.6.0-rc.1 5.9.2 10.6.1 6.0.0 10.6.2 6.0.0-beta.1 10.7.0 6.0.0-rc.1 10.7.0-beta.1 6.0.1 10.7.0-beta.2 6.0.2 10.7.0-rc.1 6.1.0 3.0.0 6.1.0-beta.1 3.0.1 6.1.0-rc.1 3.0.2 6.1.0-rc.2 3.0.3 6.1.1 3.0.4 6.1.2 3.0.5 6.1.3 3.0.6 6.2.0 3.0.7 6.2.0-beta.1 3.0.8 6.2.0-rc.1 3.0.9 6.2.0-rc.2 3.1.0 6.2.1 3.1.1 6.2.2 3.1.2 6.2.3 3.2.0 6.3.0 3.2.1 6.3.0-beta.1 3.2.2 6.3.0-rc.1 3.2.3 6.3.0-rc.2 3.2.4 6.3.1 3.2.5 6.3.2 3.2.6 6.4.0 3.3.0 6.4.0-beta.1 3.3.1 6.4.0-rc.1 3.3.2 6.4.1 3.3.2-rc.1 6.4.2 3.3.3 6.5.0 3.3.4 6.5.0-beta.1 3.3.5 6.5.0-rc.1 3.3.6 6.5.0-rc.2 3.4.0 6.5.1 3.4.0-beta.1 6.5.2 3.4.0-rc.2 6.6.0 3.4.1 6.6.0-beta.1 3.4.2 6.6.0-rc.1 3.4.3 6.6.0-rc.2 3.4.4 6.6.1 3.4.5 6.6.2 3.4.6 6.7.0 3.4.7 6.7.0-beta.1 3.4.8 6.7.0-beta.2 3.5.0 6.7.0-rc.1 3.5.0-beta.1 6.7.1 3.5.0-rc.1 6.8.0 3.5.0-rc.2 6.8.0-beta.1 3.5.1 6.8.0-beta.2 3.5.10 6.8.0-rc.1 3.5.2 6.8.1 3.5.3 6.8.2 3.5.4 6.8.3 3.5.5 6.9.0 3.5.6 6.9.0-beta.1 3.5.7 6.9.0-beta.2 3.5.8 6.9.0-rc.1 3.5.9 6.9.1 3.6.0 6.9.2 3.6.0-beta.1 6.9.3 3.6.0-rc.1 6.9.4 3.6.0-rc.2 6.9.5 3.6.0-rc.3 7.0.0 3.6.1 7.0.0-beta.1 3.6.2 7.0.0-beta.2 3.6.3 7.0.0-beta.3 3.6.4 7.0.0-rc.1 3.6.5 7.0.0-rc.2 3.6.6 7.0.1 3.6.7 7.0.2 3.7.0 7.1.0 3.7.0-beta.1 7.1.0-beta.1 3.7.0-rc.1 7.1.0-beta.2 3.7.0-rc.2 7.1.0-rc.1 3.7.1 7.1.0-rc.2 3.7.2 7.1.1
woocommerce / src / Admin / Features / Features.php
woocommerce / src / Admin / Features Last commit date
Blueprint 2 weeks ago Fulfillments 2 months ago MarketingRecommendations 5 months ago Navigation 1 year ago OnboardingTasks 2 weeks ago PaymentGatewaySuggestions 1 year ago ProductBlockEditor 1 month ago ShippingPartnerSuggestions 3 months ago Features.php 2 weeks ago LaunchYourStore.php 1 month ago Onboarding.php 1 year ago TransientNotices.php 3 years ago
Features.php
673 lines
1 <?php
2 /**
3 * Features loader for features developed in WooCommerce Admin.
4 */
5
6 namespace Automattic\WooCommerce\Admin\Features;
7
8 use Automattic\WooCommerce\Admin\PageController;
9 use Automattic\WooCommerce\Internal\Admin\Analytics;
10 use Automattic\WooCommerce\Internal\Admin\Loader;
11 use Automattic\WooCommerce\Internal\Admin\RemoteInboxNotifications;
12 use Automattic\WooCommerce\Internal\Admin\WCAdminAssets;
13 use Automattic\WooCommerce\Utilities\FeaturesUtil;
14
15 /**
16 * Features Class.
17 */
18 class Features {
19 /**
20 * Class instance.
21 *
22 * @var Loader instance
23 */
24 protected static $instance = null;
25
26 /**
27 * Version metadata for WC Admin feature flags kept for backward compatibility.
28 *
29 * Keep this dictionary in sync with RETIRED_FEATURE_FLAGS in
30 * plugins/woocommerce/client/admin/client/utils/features/retired-feature-flags.ts.
31 *
32 * @var array<string, array{deprecated_since: string, removed_in: ?string}>
33 */
34 private static $retired_feature_compatibility_versions = array(
35 'activity-panels' => array(
36 'deprecated_since' => '11.1.0',
37 'removed_in' => null,
38 ),
39 'analytics' => array(
40 'deprecated_since' => '11.1.0',
41 'removed_in' => null,
42 ),
43 'analytics-scheduled-import' => array(
44 'deprecated_since' => '11.1.0',
45 'removed_in' => null,
46 ),
47 'experimental-iapi-mini-cart' => array(
48 'deprecated_since' => '11.1.0',
49 'removed_in' => null,
50 ),
51 'coupons' => array(
52 'deprecated_since' => '11.1.0',
53 'removed_in' => null,
54 ),
55 'core-profiler' => array(
56 'deprecated_since' => '11.1.0',
57 'removed_in' => null,
58 ),
59 'customize-store' => array(
60 'deprecated_since' => '11.1.0',
61 'removed_in' => null,
62 ),
63 'customer-effort-score-tracks' => array(
64 'deprecated_since' => '11.1.0',
65 'removed_in' => null,
66 ),
67 'import-products-task' => array(
68 'deprecated_since' => '11.1.0',
69 'removed_in' => null,
70 ),
71 'experimental-fashion-sample-products' => array(
72 'deprecated_since' => '11.1.0',
73 'removed_in' => null,
74 ),
75 'shipping-smart-defaults' => array(
76 'deprecated_since' => '11.1.0',
77 'removed_in' => null,
78 ),
79 'shipping-setting-tour' => array(
80 'deprecated_since' => '11.1.0',
81 'removed_in' => null,
82 ),
83 'homescreen' => array(
84 'deprecated_since' => '11.1.0',
85 'removed_in' => null,
86 ),
87 'marketing' => array(
88 'deprecated_since' => '11.1.0',
89 'removed_in' => null,
90 ),
91 'mobile-app-banner' => array(
92 'deprecated_since' => '11.1.0',
93 'removed_in' => null,
94 ),
95 'onboarding' => array(
96 'deprecated_since' => '11.1.0',
97 'removed_in' => null,
98 ),
99 'onboarding-tasks' => array(
100 'deprecated_since' => '11.1.0',
101 'removed_in' => null,
102 ),
103 'pattern-toolkit-full-composability' => array(
104 'deprecated_since' => '11.1.0',
105 'removed_in' => null,
106 ),
107 'payment-gateway-suggestions' => array(
108 'deprecated_since' => '11.1.0',
109 'removed_in' => null,
110 ),
111 'product-custom-fields' => array(
112 'deprecated_since' => '11.1.0',
113 'removed_in' => null,
114 ),
115 'printful' => array(
116 'deprecated_since' => '11.1.0',
117 'removed_in' => null,
118 ),
119 'remote-inbox-notifications' => array(
120 'deprecated_since' => '11.1.0',
121 'removed_in' => null,
122 ),
123 'remote-free-extensions' => array(
124 'deprecated_since' => '11.1.0',
125 'removed_in' => null,
126 ),
127 'shipping-label-banner' => array(
128 'deprecated_since' => '11.1.0',
129 'removed_in' => null,
130 ),
131 'subscriptions' => array(
132 'deprecated_since' => '11.1.0',
133 'removed_in' => null,
134 ),
135 'transient-notices' => array(
136 'deprecated_since' => '11.1.0',
137 'removed_in' => null,
138 ),
139 'wc-pay-promotion' => array(
140 'deprecated_since' => '11.1.0',
141 'removed_in' => null,
142 ),
143 'wc-pay-welcome-page' => array(
144 'deprecated_since' => '11.1.0',
145 'removed_in' => null,
146 ),
147 'woo-mobile-welcome' => array(
148 'deprecated_since' => '11.1.0',
149 'removed_in' => null,
150 ),
151 'launch-your-store' => array(
152 'deprecated_since' => '11.1.0',
153 'removed_in' => null,
154 ),
155 );
156
157 /**
158 * Get class instance.
159 */
160 public static function get_instance() {
161 if ( ! self::$instance ) {
162 self::$instance = new self();
163 }
164 return self::$instance;
165 }
166
167 /**
168 * Constructor.
169 */
170 public function __construct() {
171
172 $this->register_internal_class_aliases();
173
174 if ( ! self::should_load_features() ) {
175 return;
176 }
177
178 // Load feature before WooCommerce update hooks.
179 add_action( 'init', array( __CLASS__, 'load_features' ), 4 );
180 add_action( 'admin_enqueue_scripts', array( __CLASS__, 'load_scripts' ), 15 );
181 add_filter( 'admin_body_class', array( __CLASS__, 'add_admin_body_classes' ) );
182 }
183
184 /**
185 * Gets a build configured array of enabled WooCommerce Admin features/sections, but does not respect optionally disabled features.
186 *
187 * @return array Enabled Woocommerce Admin features/sections.
188 */
189 public static function get_features() {
190 return apply_filters( 'woocommerce_admin_features', array() );
191 }
192
193 /**
194 * Gets the optional feature options as an associative array that can be toggled on or off.
195 *
196 * @deprecated 11.1.0 Use FeaturesUtil::feature_is_enabled() to check if a feature is enabled.
197 *
198 * @return array
199 */
200 public static function get_optional_feature_options() {
201 wc_deprecated_function( __METHOD__, '11.1.0', 'FeaturesUtil::feature_is_enabled()' );
202
203 return array(
204 'analytics' => Analytics::TOGGLE_OPTION_NAME,
205 'remote-inbox-notifications' => RemoteInboxNotifications::TOGGLE_OPTION_NAME,
206 );
207 }
208
209 /**
210 * Returns if a specific wc-admin feature exists in the current environment.
211 *
212 * @param string $feature Feature slug.
213 * @return bool Returns true if the feature exists.
214 */
215 public static function exists( $feature ) {
216 $is_legacy_compatibility_feature = self::is_legacy_compatibility_feature( $feature );
217
218 if ( $is_legacy_compatibility_feature ) {
219 self::warn_legacy_feature_compatibility_usage( __METHOD__, $feature );
220 }
221
222 $features = $is_legacy_compatibility_feature
223 ? self::get_features_with_legacy_compatibility_defaults()
224 : self::get_features();
225 return in_array( $feature, $features, true );
226 }
227
228 /**
229 * Get the feature class as a string.
230 *
231 * @param string $feature Feature name.
232 * @return string|null
233 */
234 public static function get_feature_class( $feature ) {
235 $feature = str_replace( '-', '', ucwords( strtolower( $feature ), '-' ) );
236 $feature_class = 'Automattic\\WooCommerce\\Admin\\Features\\' . $feature;
237
238 $should_autoload_class = self::should_load_features();
239
240 if ( class_exists( $feature_class, $should_autoload_class ) ) {
241 return $feature_class;
242 }
243
244 // Handle features contained in subdirectory.
245 if ( class_exists( $feature_class . '\\Init', $should_autoload_class ) ) {
246 return $feature_class . '\\Init';
247 }
248
249 return null;
250 }
251
252 /**
253 * Class loader for enabled WooCommerce Admin features/sections.
254 */
255 public static function load_features() {
256 if ( ! self::should_load_features() ) {
257 return;
258 }
259
260 $always_loaded_feature_classes = array(
261 \Automattic\WooCommerce\Internal\Admin\ActivityPanels::class,
262 \Automattic\WooCommerce\Internal\Admin\Analytics::class,
263 \Automattic\WooCommerce\Internal\Admin\Coupons::class,
264 \Automattic\WooCommerce\Internal\Admin\CustomerEffortScoreTracks::class,
265 \Automattic\WooCommerce\Internal\Admin\Homescreen::class,
266 \Automattic\WooCommerce\Internal\Admin\Marketing::class,
267 \Automattic\WooCommerce\Internal\Admin\MobileAppBanner::class,
268 \Automattic\WooCommerce\Admin\Features\OnboardingTasks\Init::class,
269 \Automattic\WooCommerce\Internal\Admin\RemoteInboxNotifications::class,
270 \Automattic\WooCommerce\Internal\Admin\RemoteFreeExtensions\Init::class,
271 \Automattic\WooCommerce\Internal\Admin\ShippingLabelBanner::class,
272 \Automattic\WooCommerce\Admin\Features\PaymentGatewaySuggestions\Init::class,
273 \Automattic\WooCommerce\Admin\Features\TransientNotices::class,
274 \Automattic\WooCommerce\Internal\Admin\WCPayPromotion\Init::class,
275 \Automattic\WooCommerce\Internal\Admin\WcPayWelcomePage::class,
276 \Automattic\WooCommerce\Admin\Features\LaunchYourStore::class,
277 );
278
279 foreach ( $always_loaded_feature_classes as $feature_class ) {
280 new $feature_class();
281 }
282
283 $features = self::get_features();
284 foreach ( $features as $feature ) {
285 $feature_class = self::get_feature_class( $feature );
286
287 if ( ! $feature_class ) {
288 continue;
289 }
290
291 foreach ( $always_loaded_feature_classes as $loaded_feature_class ) {
292 if ( is_a( $feature_class, $loaded_feature_class, true ) ) {
293 // Skip the outer features loop because this feature was already loaded.
294 continue 2;
295 }
296 }
297
298 new $feature_class();
299 }
300
301 if ( FeaturesUtil::feature_is_enabled( 'blueprint' ) ) {
302 new \Automattic\WooCommerce\Admin\Features\Blueprint\Init();
303 }
304
305 if ( FeaturesUtil::feature_is_enabled( 'order-detail-redesign' ) ) {
306 new \Automattic\WooCommerce\Internal\Features\OrderDetailRedesign\Init();
307 }
308 }
309
310 /**
311 * Gets a build configured array of enabled WooCommerce Admin respecting optionally disabled features.
312 *
313 * @return array Enabled Woocommerce Admin features/sections.
314 */
315 public static function get_available_features() {
316 $features = self::get_features_with_legacy_compatibility_defaults();
317 $optional_feature_keys = array( 'analytics', 'remote-inbox-notifications' );
318 $legacy_compatibility_values = self::get_legacy_feature_compatibility_values();
319 $unavailable_features = array();
320 $available_compatibility_keys = array_keys( array_filter( $legacy_compatibility_values ) );
321
322 $features = array_values( array_unique( array_merge( $features, $available_compatibility_keys ) ) );
323
324 /**
325 * Filter allowing WooCommerce Admin optional features to be disabled.
326 *
327 * @param bool $disabled False.
328 */
329 if ( apply_filters( 'woocommerce_admin_disabled', false ) ) {
330 return array_values( array_diff( $features, $optional_feature_keys ) );
331 }
332
333 if (
334 in_array( 'analytics', $features, true ) &&
335 ! FeaturesUtil::feature_is_enabled( 'analytics' )
336 ) {
337 $unavailable_features[] = 'analytics';
338 }
339
340 if (
341 in_array( 'remote-inbox-notifications', $features, true ) &&
342 'yes' !== get_option( RemoteInboxNotifications::TOGGLE_OPTION_NAME, 'yes' )
343 ) {
344 $unavailable_features[] = 'remote-inbox-notifications';
345 }
346
347 return array_values( array_diff( $features, $unavailable_features ) );
348 }
349
350 /**
351 * Check if a feature is enabled.
352 *
353 * @param string $feature Feature slug.
354 * @return bool
355 */
356 public static function is_enabled( $feature ) {
357 if ( self::is_legacy_compatibility_feature( $feature ) ) {
358 self::warn_legacy_feature_compatibility_usage( __METHOD__, $feature );
359 }
360
361 $available_features = self::get_available_features();
362 return in_array( $feature, $available_features, true );
363 }
364
365 /**
366 * Enable a toggleable optional feature.
367 *
368 * @deprecated 11.1.0 Use FeaturesUtil::feature_is_enabled() to check if a feature is enabled.
369 *
370 * @param string $feature Feature name.
371 * @return bool
372 */
373 public static function enable( $feature ) {
374 wc_deprecated_function( __METHOD__, '11.1.0', 'FeaturesUtil::feature_is_enabled()' );
375
376 if ( 'analytics' === $feature ) {
377 update_option( Analytics::TOGGLE_OPTION_NAME, 'yes' );
378 return true;
379 }
380
381 if ( 'remote-inbox-notifications' === $feature ) {
382 update_option( RemoteInboxNotifications::TOGGLE_OPTION_NAME, 'yes' );
383 return true;
384 }
385
386 return false;
387 }
388
389 /**
390 * Disable a toggleable optional feature.
391 *
392 * @deprecated 11.1.0 Use FeaturesUtil::feature_is_enabled() to check if a feature is enabled.
393 *
394 * @param string $feature Feature name.
395 * @return bool
396 */
397 public static function disable( $feature ) {
398 wc_deprecated_function( __METHOD__, '11.1.0', 'FeaturesUtil::feature_is_enabled()' );
399
400 if ( 'analytics' === $feature ) {
401 update_option( Analytics::TOGGLE_OPTION_NAME, 'no' );
402 return true;
403 }
404
405 if ( 'remote-inbox-notifications' === $feature ) {
406 update_option( RemoteInboxNotifications::TOGGLE_OPTION_NAME, 'no' );
407 return true;
408 }
409
410 return false;
411 }
412
413 /**
414 * Adds the Features section to the advanced tab of WooCommerce Settings
415 *
416 * @deprecated 7.0 The WooCommerce Admin features are now handled by the WooCommerce features engine (see the FeaturesController class).
417 *
418 * @param array $sections Sections.
419 * @return array
420 */
421 public static function add_features_section( $sections ) {
422 return $sections;
423 }
424
425 /**
426 * Adds the Features settings.
427 *
428 * @deprecated 7.0 The WooCommerce Admin features are now handled by the WooCommerce features engine (see the FeaturesController class).
429 *
430 * @param array $settings Settings.
431 * @param string $current_section Current section slug.
432 * @return array
433 */
434 public static function add_features_settings( $settings, $current_section ) {
435 return $settings;
436 }
437
438 /**
439 * Loads the required scripts on the correct pages.
440 */
441 public static function load_scripts() {
442 if ( ! PageController::is_admin_or_embed_page() ) {
443 return;
444 }
445
446 $available_features = self::get_available_features();
447 $enabled_features = array();
448 foreach ( self::get_features() as $key ) {
449 $enabled_features[ $key ] = in_array( $key, $available_features, true );
450 }
451
452 $enabled_features = array_merge( $enabled_features, self::get_legacy_feature_compatibility_values() );
453
454 wp_add_inline_script( WC_ADMIN_APP, 'window.wcAdminFeatures = ' . wp_json_encode( $enabled_features, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ), 'before' );
455 }
456
457
458 /**
459 * Adds body classes to the main wp-admin wrapper, allowing us to better target elements in specific scenarios.
460 *
461 * @param string $admin_body_class Body class to add.
462 */
463 public static function add_admin_body_classes( $admin_body_class = '' ) {
464 if ( ! PageController::is_admin_or_embed_page() ) {
465 return $admin_body_class;
466 }
467
468 $classes = explode( ' ', trim( $admin_body_class ) );
469
470 foreach ( self::get_available_features() as $feature_key ) {
471 $classes[] = sanitize_html_class( 'woocommerce-feature-enabled-' . $feature_key );
472 }
473
474 $admin_body_class = implode( ' ', array_unique( $classes ) );
475 return " $admin_body_class ";
476 }
477
478 /**
479 * Gets legacy feature flag compatibility values.
480 *
481 * This method is intended for passive compatibility paths, such as script globals
482 * and filtering shared settings, where emitting deprecation notices would warn on
483 * every admin page load.
484 *
485 * @since 11.1.0
486 * @return array<string, bool>
487 */
488 public static function get_legacy_feature_compatibility_values() {
489 $compatibility_values = array_merge(
490 array_fill_keys( array_keys( self::$retired_feature_compatibility_versions ), true ),
491 array(
492 'analytics' => FeaturesUtil::feature_is_enabled( 'analytics' ),
493 'remote-inbox-notifications' => 'yes' === get_option( RemoteInboxNotifications::TOGGLE_OPTION_NAME, 'yes' ),
494 )
495 );
496
497 return array_intersect_key(
498 $compatibility_values,
499 array_flip( self::get_features_with_legacy_compatibility_defaults() )
500 );
501 }
502
503 /**
504 * Gets default legacy feature flag compatibility values before public filtering.
505 *
506 * @return array<string, bool>
507 */
508 private static function get_legacy_feature_compatibility_defaults() {
509 return array_merge(
510 array_fill_keys( array_keys( self::$retired_feature_compatibility_versions ), true ),
511 array(
512 'analytics' => true,
513 'remote-inbox-notifications' => true,
514 )
515 );
516 }
517
518 /**
519 * Gets WooCommerce Admin features with legacy compatibility defaults before public filtering.
520 *
521 * @return array Enabled Woocommerce Admin features/sections.
522 */
523 private static function get_features_with_legacy_compatibility_defaults() {
524 /**
525 * Filter allowing WooCommerce Admin features to be changed after legacy compatibility defaults are seeded.
526 *
527 * @since 11.1.0
528 *
529 * @param array $features Array of feature slugs.
530 */
531 return apply_filters( 'woocommerce_admin_features', array_keys( self::get_legacy_feature_compatibility_defaults() ) );
532 }
533
534 /**
535 * Checks if Analytics was disabled by legacy WooCommerce Admin filters.
536 *
537 * @return bool True if Analytics was disabled by legacy filters.
538 */
539 public static function is_analytics_disabled_by_legacy_filters(): bool {
540 /**
541 * Filter allowing WooCommerce Admin optional features to be disabled.
542 *
543 * @param bool $disabled False.
544 */
545 if ( apply_filters( 'woocommerce_admin_disabled', false ) ) { // phpcs:ignore WooCommerce.Commenting.CommentHooks.MissingSinceComment
546 return true;
547 }
548
549 return ! in_array( 'analytics', self::get_features_with_legacy_compatibility_defaults(), true );
550 }
551
552 /**
553 * Checks if a feature slug is supported only by the legacy compatibility shim.
554 *
555 * @param string $feature Feature slug.
556 * @return bool
557 */
558 private static function is_legacy_compatibility_feature( $feature ) {
559 return array_key_exists( $feature, self::get_legacy_feature_compatibility_defaults() );
560 }
561
562 /**
563 * Gets version metadata for a legacy feature flag shim.
564 *
565 * @param string $feature Feature slug.
566 * @return array{deprecated_since: string, removed_in: ?string}|null
567 */
568 private static function get_legacy_feature_compatibility_versions( $feature ) {
569 return self::$retired_feature_compatibility_versions[ $feature ] ?? null;
570 }
571
572 /**
573 * Gets the WooCommerce version where a legacy feature flag shim was deprecated.
574 *
575 * @param string $feature Feature slug.
576 * @return string|null
577 */
578 private static function get_legacy_feature_compatibility_deprecation_version( $feature ) {
579 $versions = self::get_legacy_feature_compatibility_versions( $feature );
580 return $versions['deprecated_since'] ?? null;
581 }
582
583 /**
584 * Gets the WooCommerce version where a legacy feature flag shim will be removed.
585 *
586 * @param string $feature Feature slug.
587 * @return string|null
588 */
589 private static function get_legacy_feature_compatibility_removal_version( $feature ) {
590 $versions = self::get_legacy_feature_compatibility_versions( $feature );
591 return $versions['removed_in'] ?? null;
592 }
593
594 /**
595 * Emits a deprecation notice for a direct legacy feature flag shim lookup.
596 *
597 * @param string $method Method name.
598 * @param string $feature Feature slug.
599 */
600 private static function warn_legacy_feature_compatibility_usage( $method, $feature ): void {
601 $deprecation_version = self::get_legacy_feature_compatibility_deprecation_version( $feature );
602 $removal_version = self::get_legacy_feature_compatibility_removal_version( $feature );
603
604 if ( ! $deprecation_version ) {
605 return;
606 }
607
608 wc_deprecated_function(
609 sprintf( "%s( '%s' )", $method, $feature ),
610 $deprecation_version,
611 sprintf(
612 'direct feature behavior checks. The %1$s WC Admin feature flag shim will be removed in %2$s.',
613 $feature,
614 $removal_version ? 'WooCommerce ' . $removal_version : 'a future version of WooCommerce'
615 )
616 );
617 }
618
619 /**
620 * Alias internal features classes to make them backward compatible.
621 * We've moved our feature classes to src-internal as part of merging this
622 * repository with WooCommerce Core to form a monorepo.
623 * See https://wp.me/p90Yrv-2HY for details.
624 */
625 private function register_internal_class_aliases() {
626 $aliases = array(
627 // new class => original class (this will be aliased).
628 'Automattic\WooCommerce\Internal\Admin\WCPayPromotion\Init' => 'Automattic\WooCommerce\Admin\Features\WcPayPromotion\Init',
629 'Automattic\WooCommerce\Internal\Admin\RemoteFreeExtensions\Init' => 'Automattic\WooCommerce\Admin\Features\RemoteFreeExtensions\Init',
630 'Automattic\WooCommerce\Internal\Admin\ActivityPanels' => 'Automattic\WooCommerce\Admin\Features\ActivityPanels',
631 'Automattic\WooCommerce\Internal\Admin\Analytics' => 'Automattic\WooCommerce\Admin\Features\Analytics',
632 'Automattic\WooCommerce\Internal\Admin\Coupons' => 'Automattic\WooCommerce\Admin\Features\Coupons',
633 'Automattic\WooCommerce\Internal\Admin\CouponsMovedTrait' => 'Automattic\WooCommerce\Admin\Features\CouponsMovedTrait',
634 'Automattic\WooCommerce\Internal\Admin\CustomerEffortScoreTracks' => 'Automattic\WooCommerce\Admin\Features\CustomerEffortScoreTracks',
635 'Automattic\WooCommerce\Internal\Admin\Homescreen' => 'Automattic\WooCommerce\Admin\Features\Homescreen',
636 'Automattic\WooCommerce\Internal\Admin\Marketing' => 'Automattic\WooCommerce\Admin\Features\Marketing',
637 'Automattic\WooCommerce\Internal\Admin\MobileAppBanner' => 'Automattic\WooCommerce\Admin\Features\MobileAppBanner',
638 'Automattic\WooCommerce\Internal\Admin\RemoteInboxNotifications' => 'Automattic\WooCommerce\Admin\Features\RemoteInboxNotifications',
639 'Automattic\WooCommerce\Internal\Admin\ShippingLabelBanner' => 'Automattic\WooCommerce\Admin\Features\ShippingLabelBanner',
640 'Automattic\WooCommerce\Internal\Admin\ShippingLabelBannerDisplayRules' => 'Automattic\WooCommerce\Admin\Features\ShippingLabelBannerDisplayRules',
641 'Automattic\WooCommerce\Internal\Admin\WcPayWelcomePage' => 'Automattic\WooCommerce\Admin\Features\WcPayWelcomePage',
642 );
643 foreach ( $aliases as $new_class => $orig_class ) {
644 class_alias( $new_class, $orig_class );
645 }
646 }
647
648 /**
649 * Check if we're in an admin context where features should be loaded.
650 *
651 * @return boolean
652 */
653 private static function should_load_features() {
654 $should_load = (
655 is_admin() ||
656 wp_doing_ajax() ||
657 wp_doing_cron() ||
658 ( defined( 'WP_CLI' ) && WP_CLI ) ||
659 ( WC()->is_rest_api_request() && ! WC()->is_store_api_request() ) ||
660 // Allow features to be loaded in frontend for admin users. This is needed for the use case such as the coming soon footer banner.
661 current_user_can( 'manage_woocommerce' )
662 );
663
664 /**
665 * Filter to determine if admin features should be loaded.
666 *
667 * @since 9.6.0
668 * @param boolean $should_load Whether admin features should be loaded. It defaults to true when the current request is in an admin context.
669 */
670 return apply_filters( 'woocommerce_admin_should_load_features', $should_load );
671 }
672 }
673