our rendering of the "plugin is incompatible with X features" warning:
*
* - If the plugin info
has "update", our
will render nicely right after it; but then
* our own "plugin-update-tr" class will draw an additional line before the "needs update" warning.
* - If not, the plugin info
will render its lower border line right before our compatibility info
.
*
* This small script fixes this by adding the "update" class to the plugin info
if it doesn't have it
* (so no extra line before our
), or removing 'plugin-update-tr' from our
otherwise
* (and then some extra manual tweaking of margins is needed).
*
* @param string $current_screen The current screen object.
*
* @internal For exclusive usage of WooCommerce core, backwards compatibility not guaranteed.
*/
public function enqueue_script_to_fix_plugin_list_html( $current_screen ): void {
if ( 'plugins' !== $current_screen->id ) {
return;
}
$handle = 'wc-features-fix-plugin-list-html';
wp_register_script( $handle, '', array(), WC_VERSION, array( 'in_footer' => true ) );
wp_enqueue_script( $handle );
wp_add_inline_script(
$handle,
"
const warningRows = document.querySelectorAll('tr[data-plugin-row-type=\"feature-incomp-warn\"]');
for(const warningRow of warningRows) {
const pluginName = warningRow.getAttribute('data-plugin');
const pluginInfoRow = document.querySelector('tr.active[data-plugin=\"' + pluginName + '\"]:not(.plugin-update-tr), tr.inactive[data-plugin=\"' + pluginName + '\"]:not(.plugin-update-tr)');
if(!pluginInfoRow) {
continue;
}
if(pluginInfoRow.classList.contains('update')) {
warningRow.classList.remove('plugin-update-tr');
warningRow.querySelector('.notice').style.margin = '5px 10px 15px 30px';
}
else {
pluginInfoRow.classList.add('update');
}
}
"
);
}
/**
* Handler for the 'views_plugins' hook that shows the links to the different views in the plugins page.
* If we come from a "Manage incompatible plugins" in the features page we'll show just two views:
* "All" (so that it's easy to go back to a known state) and "Incompatible with X".
* We'll skip the rest of the views since the counts are wrong anyway, as we are modifying
* the plugins list via the 'all_plugins' filter.
*
* @param array $views An array of view ids => view links.
* @return string[] The actual views array to use.
*
* @internal For exclusive usage of WooCommerce core, backwards compatibility not guaranteed.
*/
public function handle_plugins_page_views_list( $views ): array {
// phpcs:disable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
if ( 'incompatible_with_feature' !== ArrayUtil::get_value_or_default( $_GET, 'plugin_status' ) ) {
return $views;
}
$feature_id = $_GET['feature_id'] ?? 'all';
if ( 'all' !== $feature_id && ! $this->feature_exists( $feature_id ) ) {
return $views;
}
// phpcs:enable WordPress.Security.NonceVerification, WordPress.Security.ValidatedSanitizedInput
$all_items = get_plugins();
$features = $this->get_feature_definitions();
$incompatible_plugins_count = count( $this->filter_plugins_list( $all_items ) );
$incompatible_text =
'all' === $feature_id
? __( 'Incompatible with WooCommerce features', 'woocommerce' )
/* translators: %s = name of a WooCommerce feature */
: sprintf( __( "Incompatible with '%s'", 'woocommerce' ), $features[ $feature_id ]['name'] );
$incompatible_link = "{$incompatible_text} ({$incompatible_plugins_count})";
$all_plugins_count = count( $all_items );
$all_text = __( 'All', 'woocommerce' );
$all_link = "{$all_text} ({$all_plugins_count})";
return array(
'all' => $all_link,
'incompatible_with_feature' => $incompatible_link,
);
}
/**
* Set the feature nonce to be sent from client side.
*
* @param array $settings Component settings.
*
* @return array
*
* @internal For exclusive usage of WooCommerce core, backwards compatibility not guaranteed.
*/
public function set_change_feature_enable_nonce( $settings ) {
$settings['_feature_nonce'] = wp_create_nonce( 'change_feature_enable' );
return $settings;
}
/**
* Changes the feature given it's id, a toggle value and nonce as a query param.
*
* `/wp-admin/post.php?product_block_editor=1&_feature_nonce=1234`, 1 for on
* `/wp-admin/post.php?product_block_editor=0&_feature_nonce=1234`, 0 for off
*
* @internal For exclusive usage of WooCommerce core, backwards compatibility not guaranteed.
*/
public function change_feature_enable_from_query_params(): void {
if ( ! current_user_can( 'manage_woocommerce' ) ) {
return;
}
$is_feature_nonce_invalid = ( ! isset( $_GET['_feature_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_GET['_feature_nonce'] ) ), 'change_feature_enable' ) );
$query_params_to_remove = array( '_feature_nonce' );
foreach ( array_keys( $this->get_feature_definitions() ) as $feature_id ) {
if ( isset( $_GET[ $feature_id ] ) && is_numeric( $_GET[ $feature_id ] ) ) {
$value = absint( $_GET[ $feature_id ] );
if ( $is_feature_nonce_invalid ) {
wp_die( esc_html__( 'Action failed. Please refresh the page and retry.', 'woocommerce' ) );
return;
}
if ( 1 === $value ) {
$this->change_feature_enable( $feature_id, true );
} elseif ( 0 === $value ) {
$this->change_feature_enable( $feature_id, false );
}
$query_params_to_remove[] = $feature_id;
}
}
if ( count( $query_params_to_remove ) > 1 && isset( $_SERVER['REQUEST_URI'] ) ) {
// phpcs:disable WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
wp_safe_redirect( remove_query_arg( $query_params_to_remove, $_SERVER['REQUEST_URI'] ) );
}
}
/**
* Display the email improvements feedback notice to render CES modal in.
*
* @param string $feature_id The feature id.
* @param bool $is_enabled Whether the feature is enabled.
*
* @internal For exclusive usage of WooCommerce core, backwards compatibility not guaranteed.
*/
public function display_email_improvements_feedback_notice( $feature_id, $is_enabled ): void {
if ( 'email_improvements' === $feature_id && ! $is_enabled ) {
set_transient( 'wc_settings_email_improvements_reverted', 'yes', 15 );
add_action(
'admin_notices',
function () {
echo '';
}
);
}
}
/**
* Check if the email improvements feature is enabled in preview mode in Settings > Emails.
* This is used to force the email improvements feature without affecting shoppers.
*
* @param string $feature_id The feature id.
* @return bool Whether the email improvements feature is enabled in preview mode.
*/
private function is_preview_email_improvements_enabled( string $feature_id ): bool {
if ( 'email_improvements' !== $feature_id ) {
return false;
}
/**
* This filter is documented in templates/emails/email-styles.php
*
* @since 9.9.0
* @param bool $is_email_preview Whether the email is being previewed.
*/
$is_email_preview = apply_filters( 'woocommerce_is_email_preview', false );
if ( $is_email_preview ) {
return get_transient( EmailPreview::TRANSIENT_PREVIEW_EMAIL_IMPROVEMENTS ) === 'yes';
}
return false;
}
}