diff options
Diffstat (limited to 'wp-admin/includes/class-wp-plugins-list-table.php')
-rw-r--r-- | wp-admin/includes/class-wp-plugins-list-table.php | 378 |
1 files changed, 320 insertions, 58 deletions
diff --git a/wp-admin/includes/class-wp-plugins-list-table.php b/wp-admin/includes/class-wp-plugins-list-table.php index 5c92fba..4cc0132 100644 --- a/wp-admin/includes/class-wp-plugins-list-table.php +++ b/wp-admin/includes/class-wp-plugins-list-table.php @@ -607,11 +607,11 @@ class WP_Plugins_List_Table extends WP_List_Table { $actions = array(); if ( 'active' !== $status ) { - $actions['activate-selected'] = $this->screen->in_admin( 'network' ) ? __( 'Network Activate' ) : __( 'Activate' ); + $actions['activate-selected'] = $this->screen->in_admin( 'network' ) ? _x( 'Network Activate', 'plugin' ) : _x( 'Activate', 'plugin' ); } if ( 'inactive' !== $status && 'recent' !== $status ) { - $actions['deactivate-selected'] = $this->screen->in_admin( 'network' ) ? __( 'Network Deactivate' ) : __( 'Deactivate' ); + $actions['deactivate-selected'] = $this->screen->in_admin( 'network' ) ? _x( 'Network Deactivate', 'plugin' ) : _x( 'Deactivate', 'plugin' ); } if ( ! is_multisite() || $this->screen->in_admin( 'network' ) ) { @@ -754,6 +754,11 @@ class WP_Plugins_List_Table extends WP_List_Table { $compatible_php = is_php_version_compatible( $requires_php ); $compatible_wp = is_wp_version_compatible( $requires_wp ); + $has_dependents = WP_Plugin_Dependencies::has_dependents( $plugin_file ); + $has_active_dependents = WP_Plugin_Dependencies::has_active_dependents( $plugin_file ); + $has_unmet_dependencies = WP_Plugin_Dependencies::has_unmet_dependencies( $plugin_file ); + $has_circular_dependency = WP_Plugin_Dependencies::has_circular_dependency( $plugin_file ); + if ( 'mustuse' === $context ) { $is_active = true; } elseif ( 'dropins' === $context ) { @@ -796,26 +801,53 @@ class WP_Plugins_List_Table extends WP_List_Table { if ( $screen->in_admin( 'network' ) ) { if ( $is_active ) { if ( current_user_can( 'manage_network_plugins' ) ) { - $actions['deactivate'] = sprintf( - '<a href="%s" id="deactivate-%s" aria-label="%s">%s</a>', - wp_nonce_url( 'plugins.php?action=deactivate&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'deactivate-plugin_' . $plugin_file ), - esc_attr( $plugin_id_attr ), - /* translators: %s: Plugin name. */ - esc_attr( sprintf( _x( 'Network Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ), - __( 'Network Deactivate' ) - ); + if ( $has_active_dependents ) { + $actions['deactivate'] = __( 'Deactivate' ) . + '<span class="screen-reader-text">' . + __( 'You cannot deactivate this plugin as other plugins require it.' ) . + '</span>'; + + } else { + $deactivate_url = 'plugins.php?action=deactivate' . + '&plugin=' . urlencode( $plugin_file ) . + '&plugin_status=' . $context . + '&paged=' . $page . + '&s=' . $s; + + $actions['deactivate'] = sprintf( + '<a href="%s" id="deactivate-%s" aria-label="%s">%s</a>', + wp_nonce_url( $deactivate_url, 'deactivate-plugin_' . $plugin_file ), + esc_attr( $plugin_id_attr ), + /* translators: %s: Plugin name. */ + esc_attr( sprintf( _x( 'Network Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ), + _x( 'Network Deactivate', 'plugin' ) + ); + } } } else { if ( current_user_can( 'manage_network_plugins' ) ) { if ( $compatible_php && $compatible_wp ) { - $actions['activate'] = sprintf( - '<a href="%s" id="activate-%s" class="edit" aria-label="%s">%s</a>', - wp_nonce_url( 'plugins.php?action=activate&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'activate-plugin_' . $plugin_file ), - esc_attr( $plugin_id_attr ), - /* translators: %s: Plugin name. */ - esc_attr( sprintf( _x( 'Network Activate %s', 'plugin' ), $plugin_data['Name'] ) ), - __( 'Network Activate' ) - ); + if ( $has_unmet_dependencies ) { + $actions['activate'] = _x( 'Network Activate', 'plugin' ) . + '<span class="screen-reader-text">' . + __( 'You cannot activate this plugin as it has unmet requirements.' ) . + '</span>'; + } else { + $activate_url = 'plugins.php?action=activate' . + '&plugin=' . urlencode( $plugin_file ) . + '&plugin_status=' . $context . + '&paged=' . $page . + '&s=' . $s; + + $actions['activate'] = sprintf( + '<a href="%s" id="activate-%s" class="edit" aria-label="%s">%s</a>', + wp_nonce_url( $activate_url, 'activate-plugin_' . $plugin_file ), + esc_attr( $plugin_id_attr ), + /* translators: %s: Plugin name. */ + esc_attr( sprintf( _x( 'Network Activate %s', 'plugin' ), $plugin_data['Name'] ) ), + _x( 'Network Activate', 'plugin' ) + ); + } } else { $actions['activate'] = sprintf( '<span>%s</span>', @@ -825,14 +857,27 @@ class WP_Plugins_List_Table extends WP_List_Table { } if ( current_user_can( 'delete_plugins' ) && ! is_plugin_active( $plugin_file ) ) { - $actions['delete'] = sprintf( - '<a href="%s" id="delete-%s" class="delete" aria-label="%s">%s</a>', - wp_nonce_url( 'plugins.php?action=delete-selected&checked[]=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'bulk-plugins' ), - esc_attr( $plugin_id_attr ), - /* translators: %s: Plugin name. */ - esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ), - __( 'Delete' ) - ); + if ( $has_dependents && ! $has_circular_dependency ) { + $actions['delete'] = __( 'Delete' ) . + '<span class="screen-reader-text">' . + __( 'You cannot delete this plugin as other plugins require it.' ) . + '</span>'; + } else { + $delete_url = 'plugins.php?action=delete-selected' . + '&checked[]=' . urlencode( $plugin_file ) . + '&plugin_status=' . $context . + '&paged=' . $page . + '&s=' . $s; + + $actions['delete'] = sprintf( + '<a href="%s" id="delete-%s" class="delete" aria-label="%s">%s</a>', + wp_nonce_url( $delete_url, 'bulk-plugins' ), + esc_attr( $plugin_id_attr ), + /* translators: %s: Plugin name. */ + esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ), + __( 'Delete' ) + ); + } } } } else { @@ -846,20 +891,39 @@ class WP_Plugins_List_Table extends WP_List_Table { ); } elseif ( $is_active ) { if ( current_user_can( 'deactivate_plugin', $plugin_file ) ) { - $actions['deactivate'] = sprintf( - '<a href="%s" id="deactivate-%s" aria-label="%s">%s</a>', - wp_nonce_url( 'plugins.php?action=deactivate&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'deactivate-plugin_' . $plugin_file ), - esc_attr( $plugin_id_attr ), - /* translators: %s: Plugin name. */ - esc_attr( sprintf( _x( 'Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ), - __( 'Deactivate' ) - ); + if ( $has_active_dependents ) { + $actions['deactivate'] = __( 'Deactivate' ) . + '<span class="screen-reader-text">' . + __( 'You cannot deactivate this plugin as other plugins depend on it.' ) . + '</span>'; + } else { + $deactivate_url = 'plugins.php?action=deactivate' . + '&plugin=' . urlencode( $plugin_file ) . + '&plugin_status=' . $context . + '&paged=' . $page . + '&s=' . $s; + + $actions['deactivate'] = sprintf( + '<a href="%s" id="deactivate-%s" aria-label="%s">%s</a>', + wp_nonce_url( $deactivate_url, 'deactivate-plugin_' . $plugin_file ), + esc_attr( $plugin_id_attr ), + /* translators: %s: Plugin name. */ + esc_attr( sprintf( _x( 'Deactivate %s', 'plugin' ), $plugin_data['Name'] ) ), + __( 'Deactivate' ) + ); + } } if ( current_user_can( 'resume_plugin', $plugin_file ) && is_plugin_paused( $plugin_file ) ) { + $resume_url = 'plugins.php?action=resume' . + '&plugin=' . urlencode( $plugin_file ) . + '&plugin_status=' . $context . + '&paged=' . $page . + '&s=' . $s; + $actions['resume'] = sprintf( '<a href="%s" id="resume-%s" class="resume-link" aria-label="%s">%s</a>', - wp_nonce_url( 'plugins.php?action=resume&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'resume-plugin_' . $plugin_file ), + wp_nonce_url( $resume_url, 'resume-plugin_' . $plugin_file ), esc_attr( $plugin_id_attr ), /* translators: %s: Plugin name. */ esc_attr( sprintf( _x( 'Resume %s', 'plugin' ), $plugin_data['Name'] ) ), @@ -869,14 +933,27 @@ class WP_Plugins_List_Table extends WP_List_Table { } else { if ( current_user_can( 'activate_plugin', $plugin_file ) ) { if ( $compatible_php && $compatible_wp ) { - $actions['activate'] = sprintf( - '<a href="%s" id="activate-%s" class="edit" aria-label="%s">%s</a>', - wp_nonce_url( 'plugins.php?action=activate&plugin=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'activate-plugin_' . $plugin_file ), - esc_attr( $plugin_id_attr ), - /* translators: %s: Plugin name. */ - esc_attr( sprintf( _x( 'Activate %s', 'plugin' ), $plugin_data['Name'] ) ), - __( 'Activate' ) - ); + if ( $has_unmet_dependencies ) { + $actions['activate'] = _x( 'Activate', 'plugin' ) . + '<span class="screen-reader-text">' . + __( 'You cannot activate this plugin as it has unmet requirements.' ) . + '</span>'; + } else { + $activate_url = 'plugins.php?action=activate' . + '&plugin=' . urlencode( $plugin_file ) . + '&plugin_status=' . $context . + '&paged=' . $page . + '&s=' . $s; + + $actions['activate'] = sprintf( + '<a href="%s" id="activate-%s" class="edit" aria-label="%s">%s</a>', + wp_nonce_url( $activate_url, 'activate-plugin_' . $plugin_file ), + esc_attr( $plugin_id_attr ), + /* translators: %s: Plugin name. */ + esc_attr( sprintf( _x( 'Activate %s', 'plugin' ), $plugin_data['Name'] ) ), + _x( 'Activate', 'plugin' ) + ); + } } else { $actions['activate'] = sprintf( '<span>%s</span>', @@ -886,14 +963,27 @@ class WP_Plugins_List_Table extends WP_List_Table { } if ( ! is_multisite() && current_user_can( 'delete_plugins' ) ) { - $actions['delete'] = sprintf( - '<a href="%s" id="delete-%s" class="delete" aria-label="%s">%s</a>', - wp_nonce_url( 'plugins.php?action=delete-selected&checked[]=' . urlencode( $plugin_file ) . '&plugin_status=' . $context . '&paged=' . $page . '&s=' . $s, 'bulk-plugins' ), - esc_attr( $plugin_id_attr ), - /* translators: %s: Plugin name. */ - esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ), - __( 'Delete' ) - ); + if ( $has_dependents && ! $has_circular_dependency ) { + $actions['delete'] = __( 'Delete' ) . + '<span class="screen-reader-text">' . + __( 'You cannot delete this plugin as other plugins require it.' ) . + '</span>'; + } else { + $delete_url = 'plugins.php?action=delete-selected' . + '&checked[]=' . urlencode( $plugin_file ) . + '&plugin_status=' . $context . + '&paged=' . $page . + '&s=' . $s; + + $actions['delete'] = sprintf( + '<a href="%s" id="delete-%s" class="delete" aria-label="%s">%s</a>', + wp_nonce_url( $delete_url, 'bulk-plugins' ), + esc_attr( $plugin_id_attr ), + /* translators: %s: Plugin name. */ + esc_attr( sprintf( _x( 'Delete %s', 'plugin' ), $plugin_data['Name'] ) ), + __( 'Delete' ) + ); + } } } // End if $is_active. } // End if $screen->in_admin( 'network' ). @@ -988,17 +1078,28 @@ class WP_Plugins_List_Table extends WP_List_Table { $class = $is_active ? 'active' : 'inactive'; $checkbox_id = 'checkbox_' . md5( $plugin_file ); + $disabled = ''; + + if ( $has_dependents || $has_unmet_dependencies ) { + $disabled = 'disabled'; + } - if ( $restrict_network_active || $restrict_network_only || in_array( $status, array( 'mustuse', 'dropins' ), true ) || ! $compatible_php ) { + if ( + $restrict_network_active || + $restrict_network_only || + in_array( $status, array( 'mustuse', 'dropins' ), true ) || + ! $compatible_php + ) { $checkbox = ''; } else { $checkbox = sprintf( - '<input type="checkbox" name="checked[]" value="%1$s" id="%2$s" />' . - '<label for="%2$s"><span class="screen-reader-text">%3$s</span></label>', - esc_attr( $plugin_file ), + '<label class="label-covers-full-cell" for="%1$s">' . + '<span class="screen-reader-text">%2$s</span></label>' . + '<input type="checkbox" name="checked[]" value="%3$s" id="%1$s" ' . $disabled . '/>', $checkbox_id, /* translators: Hidden accessibility text. %s: Plugin name. */ - sprintf( __( 'Select %s' ), $plugin_data['Name'] ) + sprintf( __( 'Select %s' ), $plugin_data['Name'] ), + esc_attr( $plugin_file ) ); } @@ -1007,8 +1108,11 @@ class WP_Plugins_List_Table extends WP_List_Table { $plugin_name = $plugin_data['Name']; } - if ( ! empty( $totals['upgrade'] ) && ! empty( $plugin_data['update'] ) - || ! $compatible_php || ! $compatible_wp + if ( + ! empty( $totals['upgrade'] ) && + ! empty( $plugin_data['update'] ) || + ! $compatible_php || + ! $compatible_wp ) { $class .= ' update'; } @@ -1057,15 +1161,19 @@ class WP_Plugins_List_Table extends WP_List_Table { <div class='$class second plugin-version-author-uri'>"; $plugin_meta = array(); + if ( ! empty( $plugin_data['Version'] ) ) { /* translators: %s: Plugin version number. */ $plugin_meta[] = sprintf( __( 'Version %s' ), $plugin_data['Version'] ); } + if ( ! empty( $plugin_data['Author'] ) ) { $author = $plugin_data['Author']; + if ( ! empty( $plugin_data['AuthorURI'] ) ) { $author = '<a href="' . $plugin_data['AuthorURI'] . '">' . $plugin_data['Author'] . '</a>'; } + /* translators: %s: Plugin author name. */ $plugin_meta[] = sprintf( __( 'By %s' ), $author ); } @@ -1149,6 +1257,24 @@ class WP_Plugins_List_Table extends WP_List_Table { echo '</div>'; + if ( $has_dependents ) { + $this->add_dependents_to_dependency_plugin_row( $plugin_file ); + } + + if ( WP_Plugin_Dependencies::has_dependencies( $plugin_file ) ) { + $this->add_dependencies_to_dependent_plugin_row( $plugin_file ); + } + + /** + * Fires after plugin row meta. + * + * @since 6.5.0 + * + * @param string $plugin_file Refer to {@see 'plugin_row_meta'} filter. + * @param array $plugin_data Refer to {@see 'plugin_row_meta'} filter. + */ + do_action( 'after_plugin_row_meta', $plugin_file, $plugin_data ); + if ( $paused ) { $notice_text = __( 'This plugin failed to load properly and is paused during recovery mode.' ); @@ -1391,4 +1517,140 @@ class WP_Plugins_List_Table extends WP_List_Table { protected function get_primary_column_name() { return 'name'; } + + /** + * Prints a list of other plugins that depend on the plugin. + * + * @since 6.5.0 + * + * @param string $dependency The dependency's filepath, relative to the plugins directory. + */ + protected function add_dependents_to_dependency_plugin_row( $dependency ) { + $dependent_names = WP_Plugin_Dependencies::get_dependent_names( $dependency ); + + if ( empty( $dependent_names ) ) { + return; + } + + $dependency_note = __( 'Note: This plugin cannot be deactivated or deleted until the plugins that require it are deactivated or deleted.' ); + + $comma = wp_get_list_item_separator(); + $required_by = sprintf( + /* translators: %s: List of dependencies. */ + __( '<strong>Required by:</strong> %s' ), + implode( $comma, $dependent_names ) + ); + + printf( + '<div class="required-by"><p>%1$s</p><p>%2$s</p></div>', + $required_by, + $dependency_note + ); + } + + /** + * Prints a list of other plugins that the plugin depends on. + * + * @since 6.5.0 + * + * @param string $dependent The dependent plugin's filepath, relative to the plugins directory. + */ + protected function add_dependencies_to_dependent_plugin_row( $dependent ) { + $dependency_names = WP_Plugin_Dependencies::get_dependency_names( $dependent ); + + if ( array() === $dependency_names ) { + return; + } + + $links = array(); + foreach ( $dependency_names as $slug => $name ) { + $links[] = $this->get_dependency_view_details_link( $name, $slug ); + } + + $is_active = is_multisite() ? is_plugin_active_for_network( $dependent ) : is_plugin_active( $dependent ); + $comma = wp_get_list_item_separator(); + $requires = sprintf( + /* translators: %s: List of dependency names. */ + __( '<strong>Requires:</strong> %s' ), + implode( $comma, $links ) + ); + + $notice = ''; + $error_message = ''; + if ( WP_Plugin_Dependencies::has_unmet_dependencies( $dependent ) ) { + if ( $is_active ) { + $error_message = __( 'This plugin is active but may not function correctly because required plugins are missing or inactive.' ); + } else { + $error_message = __( 'This plugin cannot be activated because required plugins are missing or inactive.' ); + } + $notice = wp_get_admin_notice( + $error_message, + array( + 'type' => 'error', + 'additional_classes' => array( 'inline', 'notice-alt' ), + ) + ); + } + + printf( + '<div class="requires"><p>%1$s</p><p>%2$s</p></div>', + $requires, + $notice + ); + } + + /** + * Returns a 'View details' like link for a dependency. + * + * @since 6.5.0 + * + * @param string $name The dependency's name. + * @param string $slug The dependency's slug. + * @return string A 'View details' link for the dependency. + */ + protected function get_dependency_view_details_link( $name, $slug ) { + $dependency_data = WP_Plugin_Dependencies::get_dependency_data( $slug ); + + if ( false === $dependency_data + || $name === $slug + || $name !== $dependency_data['name'] + || empty( $dependency_data['version'] ) + ) { + return $name; + } + + return $this->get_view_details_link( $name, $slug ); + } + + /** + * Returns a 'View details' link for the plugin. + * + * @since 6.5.0 + * + * @param string $name The plugin's name. + * @param string $slug The plugin's slug. + * @return string A 'View details' link for the plugin. + */ + protected function get_view_details_link( $name, $slug ) { + $url = add_query_arg( + array( + 'tab' => 'plugin-information', + 'plugin' => $slug, + 'TB_iframe' => 'true', + 'width' => '600', + 'height' => '550', + ), + network_admin_url( 'plugin-install.php' ) + ); + + $name_attr = esc_attr( $name ); + return sprintf( + "<a href='%s' class='thickbox open-plugin-details-modal' aria-label='%s' data-title='%s'>%s</a>", + esc_url( $url ), + /* translators: %s: Plugin name. */ + sprintf( __( 'More information about %s' ), $name_attr ), + $name_attr, + esc_html( $name ) + ); + } } |