PluginProbe ʕ •ᴥ•ʔ
WooCommerce / 10.8.0-beta.2
WooCommerce v10.8.0-beta.2
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 / Utilities / PluginUtil.php
woocommerce / src / Utilities Last commit date
ArrayUtil.php 1 year ago CallbackUtil.php 4 months ago DiscountsUtil.php 2 years ago FeaturesUtil.php 4 months ago I18nUtil.php 3 years ago LoggingUtil.php 1 year ago MetaDataUtil.php 4 weeks ago NumberUtil.php 10 months ago OrderUtil.php 6 months ago PluginUtil.php 7 months ago RestApiUtil.php 6 months ago ShippingUtil.php 1 year ago StringUtil.php 1 year ago TimeUtil.php 2 years ago
PluginUtil.php
342 lines
1 <?php
2 /**
3 * A class of utilities for dealing with plugins.
4 */
5
6 namespace Automattic\WooCommerce\Utilities;
7
8 use Automattic\WooCommerce\Enums\FeaturePluginCompatibility;
9 use Automattic\WooCommerce\Internal\Features\FeaturesController;
10 use Automattic\WooCommerce\Internal\Utilities\PluginInstaller;
11 use Automattic\WooCommerce\Proxies\LegacyProxy;
12
13 /**
14 * A class of utilities for dealing with plugins.
15 */
16 class PluginUtil {
17
18 /**
19 * The LegacyProxy instance to use.
20 *
21 * @var LegacyProxy
22 */
23 private $proxy;
24
25 /**
26 * The cached list of WooCommerce aware plugin ids.
27 *
28 * @var null|array
29 */
30 private $woocommerce_aware_plugins = null;
31
32 /**
33 * The cached list of enabled WooCommerce aware plugin ids.
34 *
35 * @var null|array
36 */
37 private $woocommerce_aware_active_plugins = null;
38
39 /**
40 * List of plugins excluded from feature compatibility warnings in UI.
41 *
42 * @var string[]
43 */
44 private $plugins_excluded_from_compatibility_ui;
45
46 /**
47 * Creates a new instance of the class.
48 */
49 public function __construct() {
50 add_action( 'activated_plugin', array( $this, 'handle_plugin_de_activation' ), 10, 0 );
51 add_action( 'deactivated_plugin', array( $this, 'handle_plugin_de_activation' ), 10, 0 );
52
53 $this->plugins_excluded_from_compatibility_ui = array( 'woocommerce-legacy-rest-api/woocommerce-legacy-rest-api.php' );
54 }
55
56 /**
57 * Initialize the class instance.
58 *
59 * @internal
60 *
61 * @param LegacyProxy $proxy The instance of LegacyProxy to use.
62 */
63 final public function init( LegacyProxy $proxy ) {
64 $this->proxy = $proxy;
65 require_once ABSPATH . WPINC . '/plugin.php';
66 }
67
68 /**
69 * Wrapper for WP's private `wp_get_active_and_valid_plugins` and `wp_get_active_network_plugins` functions.
70 *
71 * This combines the results of the two functions to get a list of all plugins that are active within a site.
72 * It's more useful than just retrieving the option values because it also validates that the plugin files exist.
73 * This wrapper is also a hedge against backward-incompatible changes since both of the WP methods are marked as
74 * being "@access private", so if need be we can update our methods here to preserve functionality.
75 *
76 * Note that the doc block for `wp_get_active_and_valid_plugins` says it returns "Array of paths to plugin files
77 * relative to the plugins directory", but it actually returns absolute paths.
78 *
79 * @return string[] Array of plugin basenames (paths relative to the plugin directory).
80 */
81 public function get_all_active_valid_plugins() {
82 $local = wp_get_active_and_valid_plugins();
83
84 if ( is_multisite() ) {
85 require_once ABSPATH . WPINC . '/ms-load.php';
86 $network = wp_get_active_network_plugins();
87 } else {
88 $network = array();
89 }
90
91 $all = array_merge( $local, $network );
92 $all = array_unique( $all );
93 $all = array_map( 'plugin_basename', $all );
94 sort( $all );
95
96 return $all;
97 }
98
99 /**
100 * Get a list with the names of the WordPress plugins that are WooCommerce aware
101 * (they have a "WC tested up to" header).
102 *
103 * @param bool $active_only True to return only active plugins, false to return all the active plugins.
104 * @return string[] A list of plugin ids (path/file.php).
105 */
106 public function get_woocommerce_aware_plugins( bool $active_only = false ): array {
107 if ( is_null( $this->woocommerce_aware_plugins ) ) {
108 // In case `get_plugins` was called much earlier in the request (before our headers could be injected), we
109 // invalidate the plugin cache list.
110 wp_cache_delete( 'plugins', 'plugins' );
111 $all_plugins = $this->proxy->call_function( 'get_plugins' );
112
113 $this->woocommerce_aware_plugins =
114 array_keys(
115 array_filter(
116 $all_plugins,
117 array( $this, 'is_woocommerce_aware_plugin' )
118 )
119 );
120
121 $this->woocommerce_aware_active_plugins =
122 array_values(
123 array_filter(
124 $this->woocommerce_aware_plugins,
125 function ( $plugin_name ) {
126 return $this->proxy->call_function( 'is_plugin_active', $plugin_name );
127 }
128 )
129 );
130 }
131
132 return $active_only ? $this->woocommerce_aware_active_plugins : $this->woocommerce_aware_plugins;
133 }
134
135 /**
136 * Get the printable name of a plugin.
137 *
138 * @param string $plugin_id Plugin id (path/file.php).
139 * @return string Printable plugin name, or the plugin id itself if printable name is not available.
140 */
141 public function get_plugin_name( string $plugin_id ): string {
142 $plugin_data = $this->proxy->call_function( 'get_plugin_data', WP_PLUGIN_DIR . DIRECTORY_SEPARATOR . $plugin_id );
143 return $plugin_data['Name'] ?? $plugin_id;
144 }
145
146 /**
147 * Check if a plugin is WooCommerce aware.
148 *
149 * @param string|array $plugin_file_or_data Plugin id (path/file.php) or plugin data (as returned by get_plugins).
150 * @return bool True if the plugin exists and is WooCommerce aware.
151 * @throws \Exception The input is neither a string nor an array.
152 */
153 public function is_woocommerce_aware_plugin( $plugin_file_or_data ): bool {
154 if ( is_string( $plugin_file_or_data ) ) {
155 return in_array( $plugin_file_or_data, $this->get_woocommerce_aware_plugins(), true );
156 } elseif ( is_array( $plugin_file_or_data ) ) {
157 return '' !== ( $plugin_file_or_data['WC tested up to'] ?? '' );
158 } else {
159 throw new \Exception( 'is_woocommerce_aware_plugin requires a plugin name or an array of plugin data as input' );
160 }
161 }
162
163 /**
164 * Match plugin identifier passed as a parameter with the output from `get_plugins()`.
165 *
166 * @param string $plugin_file Plugin identifier, either 'my-plugin/my-plugin.php', or output from __FILE__.
167 *
168 * @return string|false Key from the array returned by `get_plugins` if matched. False if no match.
169 */
170 public function get_wp_plugin_id( $plugin_file ) {
171 $wp_plugins = array_keys( $this->proxy->call_function( 'get_plugins' ) );
172
173 // Try to match plugin_basename().
174 $plugin_basename = $this->proxy->call_function( 'plugin_basename', $plugin_file );
175 if ( in_array( $plugin_basename, $wp_plugins, true ) ) {
176 return $plugin_basename;
177 }
178
179 // Try to match by the my-file/my-file.php (dir + file name), then by my-file.php (file name only).
180 $plugin_file = str_replace( array( '\\', '/' ), DIRECTORY_SEPARATOR, $plugin_file );
181 $file_name_parts = explode( DIRECTORY_SEPARATOR, $plugin_file );
182 $file_name = array_pop( $file_name_parts );
183 $directory_name = array_pop( $file_name_parts );
184 $full_matches = array();
185 $partial_matches = array();
186 foreach ( $wp_plugins as $wp_plugin ) {
187 if ( false !== strpos( $wp_plugin, $directory_name . DIRECTORY_SEPARATOR . $file_name ) ) {
188 $full_matches[] = $wp_plugin;
189 }
190
191 if ( ! empty( $file_name ) && false !== strpos( $wp_plugin, $file_name ) ) {
192 $partial_matches[] = $wp_plugin;
193 }
194 }
195
196 if ( 1 === count( $full_matches ) ) {
197 return $full_matches[0];
198 }
199
200 if ( 1 === count( $partial_matches ) ) {
201 return $partial_matches[0];
202 }
203
204 return false;
205 }
206
207 /**
208 * Handle plugin activation and deactivation by clearing the WooCommerce aware plugin ids cache.
209 *
210 * @internal For exclusive usage of WooCommerce core, backwards compatibility not guaranteed.
211 */
212 public function handle_plugin_de_activation(): void {
213 $this->woocommerce_aware_plugins = null;
214 $this->woocommerce_aware_active_plugins = null;
215 }
216
217 /**
218 * Utility method to generate warning string for incompatible features based on active plugins.
219 *
220 * Additionally, this method will manually print a warning message on the HPOS feature if both
221 * the Legacy REST API and HPOS are active.
222 *
223 * @param string $feature_id Feature id.
224 * @param array $plugin_feature_info Array of plugin feature info, as provided by FeaturesController->get_compatible_plugins_for_feature().
225 *
226 * @return string Warning string.
227 */
228 public function generate_incompatible_plugin_feature_warning( string $feature_id, array $plugin_feature_info ): string {
229 $incompatibles = $this->get_items_considered_incompatible( $feature_id, $plugin_feature_info );
230 $incompatibles = array_filter( $incompatibles, 'is_plugin_active' );
231 $incompatibles = array_values( array_diff( $incompatibles, $this->get_plugins_excluded_from_compatibility_ui() ) );
232 $incompatible_count = count( $incompatibles );
233
234 $feature_warnings = array();
235 if ( 'custom_order_tables' === $feature_id && 'yes' === get_option( 'woocommerce_api_enabled' ) ) {
236 if ( is_plugin_active( 'woocommerce-legacy-rest-api/woocommerce-legacy-rest-api.php' ) ) {
237 $legacy_api_and_hpos_incompatibility_warning_text =
238 sprintf(
239 // translators: %s is a URL.
240 __( '⚠ <b><a target="_blank" href="%s">The Legacy REST API plugin</a> is installed and active on this site.</b> Please be aware that the WooCommerce Legacy REST API is <b>not</b> compatible with HPOS.', 'woocommerce' ),
241 'https://wordpress.org/plugins/woocommerce-legacy-rest-api/'
242 );
243 } else {
244 $legacy_api_and_hpos_incompatibility_warning_text =
245 sprintf(
246 // translators: %s is a URL.
247 __( '⚠ <b><a target="_blank" href="%s">The Legacy REST API</a> is active on this site.</b> Please be aware that the WooCommerce Legacy REST API is <b>not</b> compatible with HPOS.', 'woocommerce' ),
248 admin_url( 'admin.php?page=wc-settings&tab=advanced&section=legacy_api' )
249 );
250 }
251
252 /**
253 * Filter to modify the warning text that appears in the HPOS section of the features settings page
254 * when both the Legacy REST API is active (via WooCommerce core or via the Legacy REST API plugin)
255 * and the orders table is in use as the primary data store for orders.
256 *
257 * @param string $legacy_api_and_hpos_incompatibility_warning_text Original warning text.
258 * @returns string|null Actual warning text to use, or null to suppress the warning.
259 *
260 * @since 8.9.0
261 */
262 $legacy_api_and_hpos_incompatibility_warning_text = apply_filters( 'woocommerce_legacy_api_and_hpos_incompatibility_warning_text', $legacy_api_and_hpos_incompatibility_warning_text );
263
264 if ( ! is_null( $legacy_api_and_hpos_incompatibility_warning_text ) ) {
265 $feature_warnings[] = $legacy_api_and_hpos_incompatibility_warning_text . "\n";
266 }
267 }
268
269 if ( $incompatible_count > 0 ) {
270 if ( 1 === $incompatible_count ) {
271 /* translators: %s = printable plugin name */
272 $feature_warnings[] = sprintf( __( '⚠ 1 Incompatible plugin detected (%s).', 'woocommerce' ), $this->get_plugin_name( $incompatibles[0] ) );
273 } elseif ( 2 === $incompatible_count ) {
274 $feature_warnings[] = sprintf(
275 /* translators: %1\$s, %2\$s = printable plugin names */
276 __( '⚠ 2 Incompatible plugins detected (%1$s and %2$s).', 'woocommerce' ),
277 $this->get_plugin_name( $incompatibles[0] ),
278 $this->get_plugin_name( $incompatibles[1] )
279 );
280 } else {
281 $feature_warnings[] = sprintf(
282 /* translators: %1\$s, %2\$s = printable plugin names, %3\$d = plugins count */
283 _n(
284 '⚠ Incompatible plugins detected (%1$s, %2$s and %3$d other).',
285 '⚠ Incompatible plugins detected (%1$s and %2$s plugins and %3$d others).',
286 $incompatible_count - 2,
287 'woocommerce'
288 ),
289 $this->get_plugin_name( $incompatibles[0] ),
290 $this->get_plugin_name( $incompatibles[1] ),
291 $incompatible_count - 2
292 );
293 }
294
295 $incompatible_plugins_url = add_query_arg(
296 array(
297 'plugin_status' => 'incompatible_with_feature',
298 'feature_id' => $feature_id,
299 ),
300 admin_url( 'plugins.php' )
301 );
302
303 $feature_warnings[] = sprintf(
304 /* translators: %1$s opening link tag %2$s closing link tag. */
305 __( '%1$sView and manage%2$s', 'woocommerce' ),
306 '<a href="' . esc_url( $incompatible_plugins_url ) . '">',
307 '</a>'
308 );
309 }
310
311 return str_replace( "\n", '<br>', implode( "\n", $feature_warnings ) );
312 }
313
314 /**
315 * Filter plugin/feature compatibility info, returning the names of the plugins/features that are considered incompatible.
316 * "Uncertain" information will be included or not depending on the value of the value of the 'default_plugin_compatibility'
317 * flag in the feature definition (default is 'compatible').
318 *
319 * @param string $feature_id Feature id.
320 * @param array $compatibility_info Array containing "compatible', 'incompatible' and 'uncertain' keys.
321 * @return array Items in 'incompatible' and 'uncertain' if plugins are incompatible by default with the feature; only items in 'incompatible' otherwise.
322 */
323 public function get_items_considered_incompatible( string $feature_id, array $compatibility_info ): array {
324 $incompatible_by_default = FeaturePluginCompatibility::COMPATIBLE !== wc_get_container()->get( FeaturesController::class )->get_default_plugin_compatibility( $feature_id );
325
326 return $incompatible_by_default ?
327 array_merge( $compatibility_info['incompatible'], $compatibility_info['uncertain'] ) :
328 $compatibility_info['incompatible'];
329 }
330
331 /**
332 * Get the names of the plugins that are excluded from the feature compatibility UI.
333 * These plugins won't be considered as incompatible with any existing feature for the purposes
334 * of displaying compatibility warning in UI, even if they declare incompatibilities explicitly.
335 *
336 * @return string[] Plugin names relative to the root plugins directory.
337 */
338 public function get_plugins_excluded_from_compatibility_ui() {
339 return $this->plugins_excluded_from_compatibility_ui;
340 }
341 }
342