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. */ private function enqueue_script_to_fix_plugin_list_html( $current_screen ): void { if ( 'plugins' !== $current_screen->id ) { return; } wc_enqueue_js( " 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.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. */ private 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(); $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' ), $this->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 */ 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 */ private 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->features ) 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'] ) ); } } }