From fa9a33d818470a5796f0ff8797f98b510ed8de18 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 09:57:30 +0200 Subject: Merging upstream version 6.5+dfsg1. Signed-off-by: Daniel Baumann --- wp-admin/includes/ajax-actions.php | 116 ++++--- wp-admin/includes/class-custom-background.php | 7 +- wp-admin/includes/class-custom-image-header.php | 16 +- wp-admin/includes/class-file-upload-upgrader.php | 19 +- wp-admin/includes/class-ftp.php | 2 +- wp-admin/includes/class-language-pack-upgrader.php | 5 + wp-admin/includes/class-pclzip.php | 2 +- wp-admin/includes/class-plugin-installer-skin.php | 2 +- wp-admin/includes/class-plugin-upgrader.php | 2 +- wp-admin/includes/class-theme-installer-skin.php | 2 +- wp-admin/includes/class-theme-upgrader-skin.php | 2 +- wp-admin/includes/class-theme-upgrader.php | 70 +++- .../class-wp-application-passwords-list-table.php | 2 +- wp-admin/includes/class-wp-comments-list-table.php | 34 +- wp-admin/includes/class-wp-debug-data.php | 8 + wp-admin/includes/class-wp-filesystem-base.php | 2 +- wp-admin/includes/class-wp-filesystem-direct.php | 2 +- wp-admin/includes/class-wp-filesystem-ftpext.php | 2 +- .../includes/class-wp-filesystem-ftpsockets.php | 2 +- wp-admin/includes/class-wp-filesystem-ssh2.php | 6 +- wp-admin/includes/class-wp-list-table.php | 33 +- wp-admin/includes/class-wp-ms-sites-list-table.php | 10 +- wp-admin/includes/class-wp-ms-users-list-table.php | 9 +- .../class-wp-plugin-install-list-table.php | 189 +++++------ wp-admin/includes/class-wp-plugins-list-table.php | 378 +++++++++++++++++---- .../includes/class-wp-privacy-policy-content.php | 32 +- wp-admin/includes/class-wp-screen.php | 2 +- wp-admin/includes/class-wp-site-health.php | 6 +- wp-admin/includes/class-wp-site-icon.php | 3 + .../includes/class-wp-theme-install-list-table.php | 2 +- wp-admin/includes/class-wp-themes-list-table.php | 2 +- wp-admin/includes/class-wp-upgrader-skin.php | 9 +- wp-admin/includes/class-wp-users-list-table.php | 2 +- wp-admin/includes/dashboard.php | 29 +- wp-admin/includes/deprecated.php | 4 +- wp-admin/includes/export.php | 46 +++ wp-admin/includes/file.php | 37 +- wp-admin/includes/image-edit.php | 11 + wp-admin/includes/image.php | 99 +++++- wp-admin/includes/media.php | 15 +- wp-admin/includes/menu.php | 6 +- wp-admin/includes/meta-boxes.php | 2 +- wp-admin/includes/misc.php | 63 ++-- wp-admin/includes/ms.php | 4 +- wp-admin/includes/nav-menu.php | 2 +- wp-admin/includes/options.php | 2 + wp-admin/includes/plugin-install.php | 175 ++++++++-- wp-admin/includes/plugin.php | 146 ++++++-- wp-admin/includes/post.php | 20 +- wp-admin/includes/revision.php | 2 +- wp-admin/includes/schema.php | 1 + wp-admin/includes/template.php | 58 ++-- wp-admin/includes/theme-install.php | 2 +- wp-admin/includes/theme.php | 28 +- wp-admin/includes/update-core.php | 139 +------- wp-admin/includes/update.php | 4 +- wp-admin/includes/upgrade.php | 109 +++--- wp-admin/includes/user.php | 16 +- 58 files changed, 1340 insertions(+), 660 deletions(-) (limited to 'wp-admin/includes') diff --git a/wp-admin/includes/ajax-actions.php b/wp-admin/includes/ajax-actions.php index 69f5fd4..30aab70 100644 --- a/wp-admin/includes/ajax-actions.php +++ b/wp-admin/includes/ajax-actions.php @@ -561,8 +561,8 @@ function _wp_ajax_delete_comment_response( $comment_id, $delta = -1 ) { 'postId' => $comment ? $comment->comment_post_ID : '', /* translators: %s: Number of comments. */ 'total_items_i18n' => sprintf( _n( '%s item', '%s items', $total ), number_format_i18n( $total ) ), - 'total_pages' => ceil( $total / $per_page ), - 'total_pages_i18n' => number_format_i18n( ceil( $total / $per_page ) ), + 'total_pages' => (int) ceil( $total / $per_page ), + 'total_pages_i18n' => number_format_i18n( (int) ceil( $total / $per_page ) ), 'total' => $total, 'time' => $time, 'in_moderation' => $counts->moderated, @@ -1628,8 +1628,13 @@ function wp_ajax_add_meta() { $post_data['post_type'] = $post->post_type; $post_data['post_status'] = 'draft'; $now = time(); - /* translators: 1: Post creation date, 2: Post creation time. */ - $post_data['post_title'] = sprintf( __( 'Draft created on %1$s at %2$s' ), gmdate( __( 'F j, Y' ), $now ), gmdate( __( 'g:i a' ), $now ) ); + + $post_data['post_title'] = sprintf( + /* translators: 1: Post creation date, 2: Post creation time. */ + __( 'Draft created on %1$s at %2$s' ), + gmdate( __( 'F j, Y' ), $now ), + gmdate( __( 'g:i a' ), $now ) + ); $pid = edit_post( $post_data ); @@ -3084,10 +3089,10 @@ function wp_ajax_query_attachments() { $posts_per_page = (int) $attachments_query->get( 'posts_per_page' ); - $max_pages = $posts_per_page ? ceil( $total_posts / $posts_per_page ) : 0; + $max_pages = $posts_per_page ? (int) ceil( $total_posts / $posts_per_page ) : 0; header( 'X-WP-Total: ' . (int) $total_posts ); - header( 'X-WP-TotalPages: ' . (int) $max_pages ); + header( 'X-WP-TotalPages: ' . $max_pages ); wp_send_json_success( $posts ); } @@ -4030,9 +4035,10 @@ function wp_ajax_crop_image() { } /** This filter is documented in wp-admin/includes/class-custom-image-header.php */ - $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication. - $attachment = $wp_site_icon->create_attachment_object( $cropped, $attachment_id ); - unset( $attachment['ID'] ); + $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication. + + // Copy attachment properties. + $attachment = wp_copy_parent_attachment_properties( $cropped, $attachment_id, $context ); // Update the attachment. add_filter( 'intermediate_image_sizes_advanced', array( $wp_site_icon, 'additional_sizes' ) ); @@ -4060,46 +4066,8 @@ function wp_ajax_crop_image() { /** This filter is documented in wp-admin/includes/class-custom-image-header.php */ $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication. - $parent_url = wp_get_attachment_url( $attachment_id ); - $parent_basename = wp_basename( $parent_url ); - $url = str_replace( $parent_basename, wp_basename( $cropped ), $parent_url ); - - $size = wp_getimagesize( $cropped ); - $image_type = ( $size ) ? $size['mime'] : 'image/jpeg'; - - // Get the original image's post to pre-populate the cropped image. - $original_attachment = get_post( $attachment_id ); - $sanitized_post_title = sanitize_file_name( $original_attachment->post_title ); - $use_original_title = ( - ( '' !== trim( $original_attachment->post_title ) ) && - /* - * Check if the original image has a title other than the "filename" default, - * meaning the image had a title when originally uploaded or its title was edited. - */ - ( $parent_basename !== $sanitized_post_title ) && - ( pathinfo( $parent_basename, PATHINFO_FILENAME ) !== $sanitized_post_title ) - ); - $use_original_description = ( '' !== trim( $original_attachment->post_content ) ); - - $attachment = array( - 'post_title' => $use_original_title ? $original_attachment->post_title : wp_basename( $cropped ), - 'post_content' => $use_original_description ? $original_attachment->post_content : $url, - 'post_mime_type' => $image_type, - 'guid' => $url, - 'context' => $context, - ); - - // Copy the image caption attribute (post_excerpt field) from the original image. - if ( '' !== trim( $original_attachment->post_excerpt ) ) { - $attachment['post_excerpt'] = $original_attachment->post_excerpt; - } - - // Copy the image alt text attribute from the original image. - if ( '' !== trim( $original_attachment->_wp_attachment_image_alt ) ) { - $attachment['meta_input'] = array( - '_wp_attachment_image_alt' => wp_slash( $original_attachment->_wp_attachment_image_alt ), - ); - } + // Copy attachment properties. + $attachment = wp_copy_parent_attachment_properties( $cropped, $attachment_id, $context ); $attachment_id = wp_insert_attachment( $attachment, $cropped ); $metadata = wp_generate_attachment_metadata( $attachment_id, $cropped ); @@ -4573,6 +4541,56 @@ function wp_ajax_install_plugin() { wp_send_json_success( $status ); } +/** + * Handles activating a plugin via AJAX. + * + * @since 6.5.0 + */ +function wp_ajax_activate_plugin() { + check_ajax_referer( 'updates' ); + + if ( empty( $_POST['name'] ) || empty( $_POST['slug'] ) || empty( $_POST['plugin'] ) ) { + wp_send_json_error( + array( + 'slug' => '', + 'pluginName' => '', + 'plugin' => '', + 'errorCode' => 'no_plugin_specified', + 'errorMessage' => __( 'No plugin specified.' ), + ) + ); + } + + $status = array( + 'activate' => 'plugin', + 'slug' => wp_unslash( $_POST['slug'] ), + 'pluginName' => wp_unslash( $_POST['name'] ), + 'plugin' => wp_unslash( $_POST['plugin'] ), + ); + + if ( ! current_user_can( 'activate_plugin', $status['plugin'] ) ) { + $status['errorMessage'] = __( 'Sorry, you are not allowed to activate plugins on this site.' ); + wp_send_json_error( $status ); + } + + if ( is_plugin_active( $status['plugin'] ) ) { + $status['errorMessage'] = sprintf( + /* translators: %s: Plugin name. */ + __( '%s is already active.' ), + $status['pluginName'] + ); + } + + $activated = activate_plugin( $status['plugin'] ); + + if ( is_wp_error( $activated ) ) { + $status['errorMessage'] = $activated->get_error_message(); + wp_send_json_error( $status ); + } + + wp_send_json_success( $status ); +} + /** * Handles updating a plugin via AJAX. * diff --git a/wp-admin/includes/class-custom-background.php b/wp-admin/includes/class-custom-background.php index 2eb3ccf..1d1cce7 100644 --- a/wp-admin/includes/class-custom-background.php +++ b/wp-admin/includes/class-custom-background.php @@ -42,8 +42,11 @@ class Custom_Background { * Constructor - Registers administration header callback. * * @since 3.0.0 - * @param callable $admin_header_callback - * @param callable $admin_image_div_callback Optional custom image div output callback. + * + * @param callable $admin_header_callback Optional. Administration header callback. + * Default empty string. + * @param callable $admin_image_div_callback Optional. Custom image div output callback. + * Default empty string. */ public function __construct( $admin_header_callback = '', $admin_image_div_callback = '' ) { $this->admin_header_callback = $admin_header_callback; diff --git a/wp-admin/includes/class-custom-image-header.php b/wp-admin/includes/class-custom-image-header.php index ee3bcb1..20f1959 100644 --- a/wp-admin/includes/class-custom-image-header.php +++ b/wp-admin/includes/class-custom-image-header.php @@ -47,11 +47,13 @@ class Custom_Image_Header { private $updated; /** - * Constructor - Register administration header callback. + * Constructor - Registers administration header callback. * * @since 2.1.0 - * @param callable $admin_header_callback - * @param callable $admin_image_div_callback Optional custom image div output callback. + * + * @param callable $admin_header_callback Administration header callback. + * @param callable $admin_image_div_callback Optional. Custom image div output callback. + * Default empty string. */ public function __construct( $admin_header_callback, $admin_image_div_callback = '' ) { $this->admin_header_callback = $admin_header_callback; @@ -932,7 +934,7 @@ endif;

- +
@@ -1075,7 +1077,7 @@ endif; /** This filter is documented in wp-admin/includes/class-custom-image-header.php */ $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication. - $attachment = $this->create_attachment_object( $cropped, $attachment_id ); + $attachment = wp_copy_parent_attachment_properties( $cropped, $attachment_id, 'custom-header' ); if ( ! empty( $_POST['create-new-attachment'] ) ) { unset( $attachment['ID'] ); @@ -1312,12 +1314,14 @@ endif; * Creates an attachment 'object'. * * @since 3.9.0 + * @deprecated 6.5.0 * * @param string $cropped Cropped image URL. * @param int $parent_attachment_id Attachment ID of parent image. * @return array An array with attachment object data. */ final public function create_attachment_object( $cropped, $parent_attachment_id ) { + _deprecated_function( __METHOD__, '6.5.0', 'wp_copy_parent_attachment_properties()' ); $parent = get_post( $parent_attachment_id ); $parent_url = wp_get_attachment_url( $parent->ID ); $url = str_replace( wp_basename( $parent_url ), wp_basename( $cropped ), $parent_url ); @@ -1419,7 +1423,7 @@ endif; /** This filter is documented in wp-admin/includes/class-custom-image-header.php */ $cropped = apply_filters( 'wp_create_file_in_uploads', $cropped, $attachment_id ); // For replication. - $attachment = $this->create_attachment_object( $cropped, $attachment_id ); + $attachment = wp_copy_parent_attachment_properties( $cropped, $attachment_id, 'custom-header' ); $previous = $this->get_previous_crop( $attachment ); diff --git a/wp-admin/includes/class-file-upload-upgrader.php b/wp-admin/includes/class-file-upload-upgrader.php index e625615..1201c6d 100644 --- a/wp-admin/includes/class-file-upload-upgrader.php +++ b/wp-admin/includes/class-file-upload-upgrader.php @@ -70,24 +70,7 @@ class File_Upload_Upgrader { } if ( 'pluginzip' === $form || 'themezip' === $form ) { - $archive_is_valid = false; - - /** This filter is documented in wp-admin/includes/file.php */ - if ( class_exists( 'ZipArchive', false ) && apply_filters( 'unzip_file_use_ziparchive', true ) ) { - $archive = new ZipArchive(); - $archive_is_valid = $archive->open( $file['file'], ZIPARCHIVE::CHECKCONS ); - - if ( true === $archive_is_valid ) { - $archive->close(); - } - } else { - require_once ABSPATH . 'wp-admin/includes/class-pclzip.php'; - - $archive = new PclZip( $file['file'] ); - $archive_is_valid = is_array( $archive->properties() ); - } - - if ( true !== $archive_is_valid ) { + if ( ! wp_zip_file_is_valid( $file['file'] ) ) { wp_delete_file( $file['file'] ); wp_die( __( 'Incompatible Archive.' ) ); } diff --git a/wp-admin/includes/class-ftp.php b/wp-admin/includes/class-ftp.php index 7658a0b..1b29783 100644 --- a/wp-admin/includes/class-ftp.php +++ b/wp-admin/includes/class-ftp.php @@ -483,7 +483,7 @@ class ftp_base { $this->PushError("restore", "cannot restore in ASCII mode"); return FALSE; } - if(!$this->_exec("REST ".$from, "resore")) return FALSE; + if(!$this->_exec("REST ".$from, "restore")) return FALSE; if(!$this->_checkCode()) return FALSE; return TRUE; } diff --git a/wp-admin/includes/class-language-pack-upgrader.php b/wp-admin/includes/class-language-pack-upgrader.php index 3c3d42a..855dbe6 100644 --- a/wp-admin/includes/class-language-pack-upgrader.php +++ b/wp-admin/includes/class-language-pack-upgrader.php @@ -409,12 +409,16 @@ class Language_Pack_Upgrader extends WP_Upgrader { $files = array( $remote_destination . $language_update->language . '.po', $remote_destination . $language_update->language . '.mo', + $remote_destination . $language_update->language . '.l10n.php', $remote_destination . 'admin-' . $language_update->language . '.po', $remote_destination . 'admin-' . $language_update->language . '.mo', + $remote_destination . 'admin-' . $language_update->language . '.l10n.php', $remote_destination . 'admin-network-' . $language_update->language . '.po', $remote_destination . 'admin-network-' . $language_update->language . '.mo', + $remote_destination . 'admin-network-' . $language_update->language . '.l10n.php', $remote_destination . 'continents-cities-' . $language_update->language . '.po', $remote_destination . 'continents-cities-' . $language_update->language . '.mo', + $remote_destination . 'continents-cities-' . $language_update->language . '.l10n.php', ); $json_translation_files = glob( $language_directory . $language_update->language . '-*.json' ); @@ -427,6 +431,7 @@ class Language_Pack_Upgrader extends WP_Upgrader { $files = array( $remote_destination . $language_update->slug . '-' . $language_update->language . '.po', $remote_destination . $language_update->slug . '-' . $language_update->language . '.mo', + $remote_destination . $language_update->slug . '-' . $language_update->language . '.l10n.php', ); $language_directory = $language_directory . $language_update->type . 's/'; diff --git a/wp-admin/includes/class-pclzip.php b/wp-admin/includes/class-pclzip.php index 3fdade5..963f311 100644 --- a/wp-admin/includes/class-pclzip.php +++ b/wp-admin/includes/class-pclzip.php @@ -1854,7 +1854,7 @@ $p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] = floor($v_memory_limit_int*PCLZIP_TEMPORARY_FILE_RATIO); - // ----- Sanity check : No threshold if value lower than 1M + // ----- Confidence check : No threshold if value lower than 1M if ($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD] < 1048576) { unset($p_options[PCLZIP_OPT_TEMP_FILE_THRESHOLD]); } diff --git a/wp-admin/includes/class-plugin-installer-skin.php b/wp-admin/includes/class-plugin-installer-skin.php index ed165ed..20cd15f 100644 --- a/wp-admin/includes/class-plugin-installer-skin.php +++ b/wp-admin/includes/class-plugin-installer-skin.php @@ -121,7 +121,7 @@ class Plugin_Installer_Skin extends WP_Upgrader_Skin { $install_actions['network_activate'] = sprintf( '%s', wp_nonce_url( 'plugins.php?action=activate&networkwide=1&plugin=' . urlencode( $plugin_file ), 'activate-plugin_' . $plugin_file ), - __( 'Network Activate' ) + _x( 'Network Activate', 'plugin' ) ); unset( $install_actions['activate_plugin'] ); } diff --git a/wp-admin/includes/class-plugin-upgrader.php b/wp-admin/includes/class-plugin-upgrader.php index 02743f6..091cfeb 100644 --- a/wp-admin/includes/class-plugin-upgrader.php +++ b/wp-admin/includes/class-plugin-upgrader.php @@ -472,7 +472,7 @@ class Plugin_Upgrader extends WP_Upgrader { } $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit( WP_CONTENT_DIR ), $source ); - if ( ! is_dir( $working_directory ) ) { // Sanity check, if the above fails, let's not prevent installation. + if ( ! is_dir( $working_directory ) ) { // Confidence check, if the above fails, let's not prevent installation. return $source; } diff --git a/wp-admin/includes/class-theme-installer-skin.php b/wp-admin/includes/class-theme-installer-skin.php index 99fe322..d83e038 100644 --- a/wp-admin/includes/class-theme-installer-skin.php +++ b/wp-admin/includes/class-theme-installer-skin.php @@ -138,7 +138,7 @@ class Theme_Installer_Skin extends WP_Upgrader_Skin { '' . '%s', esc_url( $activate_link ), - __( 'Activate' ), + _x( 'Activate', 'theme' ), /* translators: Hidden accessibility text. %s: Theme name. */ sprintf( _x( 'Activate “%s”', 'theme' ), $name ) ); diff --git a/wp-admin/includes/class-theme-upgrader-skin.php b/wp-admin/includes/class-theme-upgrader-skin.php index 97d76a8..cd4c6c6 100644 --- a/wp-admin/includes/class-theme-upgrader-skin.php +++ b/wp-admin/includes/class-theme-upgrader-skin.php @@ -110,7 +110,7 @@ class Theme_Upgrader_Skin extends WP_Upgrader_Skin { '' . '%s', esc_url( $activate_link ), - __( 'Activate' ), + _x( 'Activate', 'theme' ), /* translators: Hidden accessibility text. %s: Theme name. */ sprintf( _x( 'Activate “%s”', 'theme' ), $name ) ); diff --git a/wp-admin/includes/class-theme-upgrader.php b/wp-admin/includes/class-theme-upgrader.php index 12bd477..869bf64 100644 --- a/wp-admin/includes/class-theme-upgrader.php +++ b/wp-admin/includes/class-theme-upgrader.php @@ -371,6 +371,8 @@ class Theme_Upgrader extends WP_Upgrader { * @since 3.0.0 * @since 3.7.0 The `$args` parameter was added, making clearing the update cache optional. * + * @global string $wp_version The WordPress version string. + * * @param string[] $themes Array of the theme slugs. * @param array $args { * Optional. Other arguments for upgrading several themes at once. Default empty array. @@ -381,6 +383,8 @@ class Theme_Upgrader extends WP_Upgrader { * @return array[]|false An array of results, or false if unable to connect to the filesystem. */ public function bulk_upgrade( $themes, $args = array() ) { + global $wp_version; + $defaults = array( 'clear_update_cache' => true, ); @@ -442,23 +446,55 @@ class Theme_Upgrader extends WP_Upgrader { // Get the URL to the zip file. $r = $current->response[ $theme ]; - $result = $this->run( - array( - 'package' => $r['package'], - 'destination' => get_theme_root( $theme ), - 'clear_destination' => true, - 'clear_working' => true, - 'is_multi' => true, - 'hook_extra' => array( - 'theme' => $theme, - 'temp_backup' => array( - 'slug' => $theme, - 'src' => get_theme_root( $theme ), - 'dir' => 'themes', + if ( isset( $r['requires'] ) && ! is_wp_version_compatible( $r['requires'] ) ) { + $result = new WP_Error( + 'incompatible_wp_required_version', + sprintf( + /* translators: 1: Current WordPress version, 2: WordPress version required by the new theme version. */ + __( 'Your WordPress version is %1$s, however the new theme version requires %2$s.' ), + $wp_version, + $r['requires'] + ) + ); + + $this->skin->before( $result ); + $this->skin->error( $result ); + $this->skin->after(); + } elseif ( isset( $r['requires_php'] ) && ! is_php_version_compatible( $r['requires_php'] ) ) { + $result = new WP_Error( + 'incompatible_php_required_version', + sprintf( + /* translators: 1: Current PHP version, 2: PHP version required by the new theme version. */ + __( 'The PHP version on your server is %1$s, however the new theme version requires %2$s.' ), + PHP_VERSION, + $r['requires_php'] + ) + ); + + $this->skin->before( $result ); + $this->skin->error( $result ); + $this->skin->after(); + } else { + add_filter( 'upgrader_source_selection', array( $this, 'check_package' ) ); + $result = $this->run( + array( + 'package' => $r['package'], + 'destination' => get_theme_root( $theme ), + 'clear_destination' => true, + 'clear_working' => true, + 'is_multi' => true, + 'hook_extra' => array( + 'theme' => $theme, + 'temp_backup' => array( + 'slug' => $theme, + 'src' => get_theme_root( $theme ), + 'dir' => 'themes', + ), ), - ), - ) - ); + ) + ); + remove_filter( 'upgrader_source_selection', array( $this, 'check_package' ) ); + } $results[ $theme ] = $result; @@ -538,7 +574,7 @@ class Theme_Upgrader extends WP_Upgrader { // Check that the folder contains a valid theme. $working_directory = str_replace( $wp_filesystem->wp_content_dir(), trailingslashit( WP_CONTENT_DIR ), $source ); - if ( ! is_dir( $working_directory ) ) { // Sanity check, if the above fails, let's not prevent installation. + if ( ! is_dir( $working_directory ) ) { // Confidence check, if the above fails, let's not prevent installation. return $source; } diff --git a/wp-admin/includes/class-wp-application-passwords-list-table.php b/wp-admin/includes/class-wp-application-passwords-list-table.php index 6c0bf26..9a60853 100644 --- a/wp-admin/includes/class-wp-application-passwords-list-table.php +++ b/wp-admin/includes/class-wp-application-passwords-list-table.php @@ -146,7 +146,7 @@ class WP_Application_Passwords_List_Table extends WP_List_Table { * * @since 5.6.0 * - * @param string $which The location of the bulk actions: 'top' or 'bottom'. + * @param string $which The location of the bulk actions: Either 'top' or 'bottom'. */ protected function display_tablenav( $which ) { ?> diff --git a/wp-admin/includes/class-wp-comments-list-table.php b/wp-admin/includes/class-wp-comments-list-table.php index 1433818..d4970e2 100644 --- a/wp-admin/includes/class-wp-comments-list-table.php +++ b/wp-admin/includes/class-wp-comments-list-table.php @@ -437,7 +437,7 @@ class WP_Comments_List_Table extends WP_List_Table { * @since 5.6.0 The `$which` parameter was added. * * @param string $comment_status The comment status name. Default 'All'. - * @param string $which The location of the extra table nav markup: 'top' or 'bottom'. + * @param string $which The location of the extra table nav markup: Either 'top' or 'bottom'. */ do_action( 'manage_comments_nav', $comment_status, $which ); @@ -638,8 +638,23 @@ class WP_Comments_List_Table extends WP_List_Table { public function single_row( $item ) { global $post, $comment; + // Restores the more descriptive, specific name for use within this method. $comment = $item; + if ( $comment->comment_post_ID > 0 ) { + $post = get_post( $comment->comment_post_ID ); + } + + $edit_post_cap = $post ? 'edit_post' : 'edit_posts'; + + if ( ! current_user_can( $edit_post_cap, $comment->comment_post_ID ) + && ( post_password_required( $comment->comment_post_ID ) + || ! current_user_can( 'read_post', $comment->comment_post_ID ) ) + ) { + // The user has no access to the post and thus cannot see the comments. + return false; + } + $the_comment_class = wp_get_comment_status( $comment ); if ( ! $the_comment_class ) { @@ -648,25 +663,8 @@ class WP_Comments_List_Table extends WP_List_Table { $the_comment_class = implode( ' ', get_comment_class( $the_comment_class, $comment, $comment->comment_post_ID ) ); - if ( $comment->comment_post_ID > 0 ) { - $post = get_post( $comment->comment_post_ID ); - } - $this->user_can = current_user_can( 'edit_comment', $comment->comment_ID ); - $edit_post_cap = $post ? 'edit_post' : 'edit_posts'; - if ( - current_user_can( $edit_post_cap, $comment->comment_post_ID ) || - ( - empty( $post->post_password ) && - current_user_can( 'read_post', $comment->comment_post_ID ) - ) - ) { - // The user has access to the post - } else { - return false; - } - echo ""; $this->single_row_columns( $comment ); echo "\n"; diff --git a/wp-admin/includes/class-wp-debug-data.php b/wp-admin/includes/class-wp-debug-data.php index d83c873..ed6a9e7 100644 --- a/wp-admin/includes/class-wp-debug-data.php +++ b/wp-admin/includes/class-wp-debug-data.php @@ -381,6 +381,14 @@ class WP_Debug_Data { // Conditionally add debug information for multisite setups. if ( is_multisite() ) { + $site_id = get_current_blog_id(); + + $info['wp-core']['fields']['site_id'] = array( + 'label' => __( 'Site ID' ), + 'value' => $site_id, + 'debug' => $site_id, + ); + $network_query = new WP_Network_Query(); $network_ids = $network_query->query( array( diff --git a/wp-admin/includes/class-wp-filesystem-base.php b/wp-admin/includes/class-wp-filesystem-base.php index 8b29127..125c2d3 100644 --- a/wp-admin/includes/class-wp-filesystem-base.php +++ b/wp-admin/includes/class-wp-filesystem-base.php @@ -836,7 +836,7 @@ class WP_Filesystem_Base { * @return array|false { * Array of arrays containing file information. False if unable to list directory contents. * - * @type array $0... { + * @type array ...$0 { * Array of file information. Note that some elements may not be available on all filesystems. * * @type string $name Name of the file or directory. diff --git a/wp-admin/includes/class-wp-filesystem-direct.php b/wp-admin/includes/class-wp-filesystem-direct.php index 9fdfeb9..2efd5b0 100644 --- a/wp-admin/includes/class-wp-filesystem-direct.php +++ b/wp-admin/includes/class-wp-filesystem-direct.php @@ -599,7 +599,7 @@ class WP_Filesystem_Direct extends WP_Filesystem_Base { * @return array|false { * Array of arrays containing file information. False if unable to list directory contents. * - * @type array $0... { + * @type array ...$0 { * Array of file information. Note that some elements may not be available on all filesystems. * * @type string $name Name of the file or directory. diff --git a/wp-admin/includes/class-wp-filesystem-ftpext.php b/wp-admin/includes/class-wp-filesystem-ftpext.php index 7db0685..0294720 100644 --- a/wp-admin/includes/class-wp-filesystem-ftpext.php +++ b/wp-admin/includes/class-wp-filesystem-ftpext.php @@ -731,7 +731,7 @@ class WP_Filesystem_FTPext extends WP_Filesystem_Base { * @return array|false { * Array of arrays containing file information. False if unable to list directory contents. * - * @type array $0... { + * @type array ...$0 { * Array of file information. Note that some elements may not be available on all filesystems. * * @type string $name Name of the file or directory. diff --git a/wp-admin/includes/class-wp-filesystem-ftpsockets.php b/wp-admin/includes/class-wp-filesystem-ftpsockets.php index c69d801..9a37d88 100644 --- a/wp-admin/includes/class-wp-filesystem-ftpsockets.php +++ b/wp-admin/includes/class-wp-filesystem-ftpsockets.php @@ -625,7 +625,7 @@ class WP_Filesystem_ftpsockets extends WP_Filesystem_Base { * @return array|false { * Array of arrays containing file information. False if unable to list directory contents. * - * @type array $0... { + * @type array ...$0 { * Array of file information. Note that some elements may not be available on all filesystems. * * @type string $name Name of the file or directory. diff --git a/wp-admin/includes/class-wp-filesystem-ssh2.php b/wp-admin/includes/class-wp-filesystem-ssh2.php index d68f143..9e0cb88 100644 --- a/wp-admin/includes/class-wp-filesystem-ssh2.php +++ b/wp-admin/includes/class-wp-filesystem-ssh2.php @@ -4,9 +4,9 @@ * * To use this class you must follow these steps for PHP 5.2.6+ * - * @contrib http://kevin.vanzonneveld.net/techblog/article/make_ssh_connections_with_php/ - Installation Notes + * {@link http://kevin.vanzonneveld.net/techblog/article/make_ssh_connections_with_php/ - Installation Notes} * - * Compile libssh2 (Note: Only 0.14 is officaly working with PHP 5.2.6+ right now, But many users have found the latest versions work) + * Compile libssh2 (Note: Only 0.14 is officially working with PHP 5.2.6+ right now, But many users have found the latest versions work) * * cd /usr/src * wget https://www.libssh2.org/download/libssh2-0.14.tar.gz @@ -745,7 +745,7 @@ class WP_Filesystem_SSH2 extends WP_Filesystem_Base { * @return array|false { * Array of arrays containing file information. False if unable to list directory contents. * - * @type array $0... { + * @type array ...$0 { * Array of file information. Note that some elements may not be available on all filesystems. * * @type string $name Name of the file or directory. diff --git a/wp-admin/includes/class-wp-list-table.php b/wp-admin/includes/class-wp-list-table.php index b3aebd9..4e6aaa4 100644 --- a/wp-admin/includes/class-wp-list-table.php +++ b/wp-admin/includes/class-wp-list-table.php @@ -319,7 +319,7 @@ class WP_List_Table { ); if ( ! $args['total_pages'] && $args['per_page'] > 0 ) { - $args['total_pages'] = ceil( $args['total_items'] / $args['per_page'] ); + $args['total_pages'] = (int) ceil( $args['total_items'] / $args['per_page'] ); } // Redirect if page number is invalid and headers are not already sent. @@ -564,7 +564,7 @@ class WP_List_Table { * * @since 3.1.0 * - * @param string $which The location of the bulk actions: 'top' or 'bottom'. + * @param string $which The location of the bulk actions: Either 'top' or 'bottom'. * This is designated as optional for backward compatibility. */ protected function bulk_actions( $which = '' ) { @@ -828,6 +828,17 @@ class WP_List_Table { * @param int $pending_comments Number of pending comments. */ protected function comments_bubble( $post_id, $pending_comments ) { + $post_object = get_post( $post_id ); + $edit_post_cap = $post_object ? 'edit_post' : 'edit_posts'; + + if ( ! current_user_can( $edit_post_cap, $post_id ) + && ( post_password_required( $post_id ) + || ! current_user_can( 'read_post', $post_id ) ) + ) { + // The user has no access to the post and thus cannot see the comments. + return false; + } + $approved_comments = get_comments_number(); $approved_comments_number = number_format_i18n( $approved_comments ); @@ -851,20 +862,6 @@ class WP_List_Table { $pending_comments_number ); - $post_object = get_post( $post_id ); - $edit_post_cap = $post_object ? 'edit_post' : 'edit_posts'; - if ( - current_user_can( $edit_post_cap, $post_id ) || - ( - empty( $post_object->post_password ) && - current_user_can( 'read_post', $post_id ) - ) - ) { - // The user has access to the post and thus can see comments - } else { - return false; - } - if ( ! $approved_comments && ! $pending_comments ) { // No comments at all. printf( @@ -1015,7 +1012,7 @@ class WP_List_Table { * * @since 3.1.0 * - * @param string $which + * @param string $which The location of the pagination: Either 'top' or 'bottom'. */ protected function pagination( $which ) { if ( empty( $this->_pagination_args ) ) { @@ -1666,7 +1663,7 @@ class WP_List_Table { * Generates the table navigation above or below the table * * @since 3.1.0 - * @param string $which + * @param string $which The location of the navigation: Either 'top' or 'bottom'. */ protected function display_tablenav( $which ) { if ( 'top' === $which ) { diff --git a/wp-admin/includes/class-wp-ms-sites-list-table.php b/wp-admin/includes/class-wp-ms-sites-list-table.php index ffa2231..c28a454 100644 --- a/wp-admin/includes/class-wp-ms-sites-list-table.php +++ b/wp-admin/includes/class-wp-ms-sites-list-table.php @@ -302,7 +302,7 @@ class WP_MS_Sites_List_Table extends WP_List_Table { /** * @global string $mode List table view mode. * - * @param string $which The location of the pagination nav markup: 'top' or 'bottom'. + * @param string $which The location of the pagination nav markup: Either 'top' or 'bottom'. */ protected function pagination( $which ) { global $mode; @@ -319,7 +319,7 @@ class WP_MS_Sites_List_Table extends WP_List_Table { * * @since 5.3.0 * - * @param string $which The location of the extra table nav markup: 'top' or 'bottom'. + * @param string $which The location of the extra table nav markup: Either 'top' or 'bottom'. */ protected function extra_tablenav( $which ) { ?> @@ -333,7 +333,7 @@ class WP_MS_Sites_List_Table extends WP_List_Table { * * @since 5.3.0 * - * @param string $which The location of the extra table nav markup: 'top' or 'bottom'. + * @param string $which The location of the extra table nav markup: Either 'top' or 'bottom'. */ do_action( 'restrict_manage_sites', $which ); @@ -353,7 +353,7 @@ class WP_MS_Sites_List_Table extends WP_List_Table { * * @since 5.3.0 * - * @param string $which The location of the extra table nav markup: 'top' or 'bottom'. + * @param string $which The location of the extra table nav markup: Either 'top' or 'bottom'. */ do_action( 'manage_sites_extra_tablenav', $which ); } @@ -756,7 +756,7 @@ class WP_MS_Sites_List_Table extends WP_List_Table { 'activateblog_' . $blog['blog_id'] ) ), - __( 'Activate' ) + _x( 'Activate', 'site' ) ); } else { $actions['deactivate'] = sprintf( diff --git a/wp-admin/includes/class-wp-ms-users-list-table.php b/wp-admin/includes/class-wp-ms-users-list-table.php index ec12321..d02d380 100644 --- a/wp-admin/includes/class-wp-ms-users-list-table.php +++ b/wp-admin/includes/class-wp-ms-users-list-table.php @@ -383,25 +383,30 @@ class WP_MS_Users_List_Table extends WP_List_Table { $path = ( '/' === $site->path ) ? '' : $site->path; $site_classes = array( 'site-' . $site->site_id ); + /** - * Filters the span class for a site listing on the mulisite user list table. + * Filters the span class for a site listing on the multisite user list table. * * @since 5.2.0 * - * @param string[] $site_classes Array of class names used within the span tag. Default "site-#" with the site's network ID. + * @param string[] $site_classes Array of class names used within the span tag. + * Default "site-#" with the site's network ID. * @param int $site_id Site ID. * @param int $network_id Network ID. * @param WP_User $user WP_User object. */ $site_classes = apply_filters( 'ms_user_list_site_class', $site_classes, $site->userblog_id, $site->site_id, $user ); + if ( is_array( $site_classes ) && ! empty( $site_classes ) ) { $site_classes = array_map( 'sanitize_html_class', array_unique( $site_classes ) ); echo ''; } else { echo ''; } + echo '' . str_replace( '.' . get_network()->domain, '', $site->domain . $path ) . ''; echo ' '; + $actions = array(); $actions['edit'] = '' . __( 'Edit' ) . ''; diff --git a/wp-admin/includes/class-wp-plugin-install-list-table.php b/wp-admin/includes/class-wp-plugin-install-list-table.php index 7823f00..f3452a7 100644 --- a/wp-admin/includes/class-wp-plugin-install-list-table.php +++ b/wp-admin/includes/class-wp-plugin-install-list-table.php @@ -555,102 +555,7 @@ class WP_Plugin_Install_List_Table extends WP_List_Table { $action_links = array(); - if ( current_user_can( 'install_plugins' ) || current_user_can( 'update_plugins' ) ) { - $status = install_plugin_install_status( $plugin ); - - switch ( $status['status'] ) { - case 'install': - if ( $status['url'] ) { - if ( $compatible_php && $compatible_wp ) { - $action_links[] = sprintf( - '%s', - esc_attr( $plugin['slug'] ), - esc_url( $status['url'] ), - /* translators: %s: Plugin name and version. */ - esc_attr( sprintf( _x( 'Install %s now', 'plugin' ), $name ) ), - esc_attr( $name ), - __( 'Install Now' ) - ); - } else { - $action_links[] = sprintf( - '', - _x( 'Cannot Install', 'plugin' ) - ); - } - } - break; - - case 'update_available': - if ( $status['url'] ) { - if ( $compatible_php && $compatible_wp ) { - $action_links[] = sprintf( - '%s', - esc_attr( $status['file'] ), - esc_attr( $plugin['slug'] ), - esc_url( $status['url'] ), - /* translators: %s: Plugin name and version. */ - esc_attr( sprintf( _x( 'Update %s now', 'plugin' ), $name ) ), - esc_attr( $name ), - __( 'Update Now' ) - ); - } else { - $action_links[] = sprintf( - '', - _x( 'Cannot Update', 'plugin' ) - ); - } - } - break; - - case 'latest_installed': - case 'newer_installed': - if ( is_plugin_active( $status['file'] ) ) { - $action_links[] = sprintf( - '', - _x( 'Active', 'plugin' ) - ); - } elseif ( current_user_can( 'activate_plugin', $status['file'] ) ) { - if ( $compatible_php && $compatible_wp ) { - $button_text = __( 'Activate' ); - /* translators: %s: Plugin name. */ - $button_label = _x( 'Activate %s', 'plugin' ); - $activate_url = add_query_arg( - array( - '_wpnonce' => wp_create_nonce( 'activate-plugin_' . $status['file'] ), - 'action' => 'activate', - 'plugin' => $status['file'], - ), - network_admin_url( 'plugins.php' ) - ); - - if ( is_network_admin() ) { - $button_text = __( 'Network Activate' ); - /* translators: %s: Plugin name. */ - $button_label = _x( 'Network Activate %s', 'plugin' ); - $activate_url = add_query_arg( array( 'networkwide' => 1 ), $activate_url ); - } - - $action_links[] = sprintf( - '%3$s', - esc_url( $activate_url ), - esc_attr( sprintf( $button_label, $plugin['name'] ) ), - $button_text - ); - } else { - $action_links[] = sprintf( - '', - _x( 'Cannot Activate', 'plugin' ) - ); - } - } else { - $action_links[] = sprintf( - '', - _x( 'Installed', 'plugin' ) - ); - } - break; - } - } + $action_links[] = wp_get_plugin_action_button( $name, $plugin, $compatible_php, $compatible_wp ); $details_link = self_admin_url( 'plugin-install.php?tab=plugin-information&plugin=' . $plugin['slug'] . @@ -769,6 +674,12 @@ class WP_Plugin_Install_List_Table extends WP_List_Table {

+ get_dependencies_notice( $plugin ); + if ( ! empty( $dependencies_notice ) ) { + echo $dependencies_notice; + } + ?>
'; } } + + /** + * Returns a notice containing a list of dependencies required by the plugin. + * + * @since 6.5.0 + * + * @param array $plugin_data An array of plugin data. See {@see plugins_api()} + * for the list of possible values. + * @return string A notice containing a list of dependencies required by the plugin, + * or an empty string if none is required. + */ + protected function get_dependencies_notice( $plugin_data ) { + if ( empty( $plugin_data['requires_plugins'] ) ) { + return ''; + } + + $no_name_markup = '
%s
'; + $has_name_markup = '
%s %s
'; + + $dependencies_list = ''; + foreach ( $plugin_data['requires_plugins'] as $dependency ) { + $dependency_data = WP_Plugin_Dependencies::get_dependency_data( $dependency ); + + if ( + false !== $dependency_data && + ! empty( $dependency_data['name'] ) && + ! empty( $dependency_data['slug'] ) && + ! empty( $dependency_data['version'] ) + ) { + $more_details_link = $this->get_more_details_link( $dependency_data['name'], $dependency_data['slug'] ); + $dependencies_list .= sprintf( $has_name_markup, esc_html( $dependency_data['name'] ), $more_details_link ); + continue; + } + + $result = plugins_api( 'plugin_information', array( 'slug' => $dependency ) ); + + if ( ! empty( $result->name ) ) { + $more_details_link = $this->get_more_details_link( $result->name, $result->slug ); + $dependencies_list .= sprintf( $has_name_markup, esc_html( $result->name ), $more_details_link ); + continue; + } + + $dependencies_list .= sprintf( $no_name_markup, esc_html( $dependency ) ); + } + + $dependencies_notice = sprintf( + '

%s

%s
', + '' . __( 'Additional plugins are required' ) . '', + $dependencies_list + ); + + return $dependencies_notice; + } + + /** + * Creates a 'More 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 The 'More details' link for the plugin. + */ + protected function get_more_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' ) + ); + + $more_details_link = sprintf( + '%4$s', + esc_url( $url ), + /* translators: %s: Plugin name. */ + sprintf( __( 'More information about %s' ), esc_html( $name ) ), + esc_attr( $name ), + __( 'More Details' ) + ); + + return $more_details_link; + } } 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( - '%s', - 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' ) . + '' . + __( 'You cannot deactivate this plugin as other plugins require it.' ) . + ''; + + } else { + $deactivate_url = 'plugins.php?action=deactivate' . + '&plugin=' . urlencode( $plugin_file ) . + '&plugin_status=' . $context . + '&paged=' . $page . + '&s=' . $s; + + $actions['deactivate'] = sprintf( + '%s', + 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( - '%s', - 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' ) . + '' . + __( 'You cannot activate this plugin as it has unmet requirements.' ) . + ''; + } else { + $activate_url = 'plugins.php?action=activate' . + '&plugin=' . urlencode( $plugin_file ) . + '&plugin_status=' . $context . + '&paged=' . $page . + '&s=' . $s; + + $actions['activate'] = sprintf( + '%s', + 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( '%s', @@ -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( - '%s', - 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' ) . + '' . + __( 'You cannot delete this plugin as other plugins require it.' ) . + ''; + } else { + $delete_url = 'plugins.php?action=delete-selected' . + '&checked[]=' . urlencode( $plugin_file ) . + '&plugin_status=' . $context . + '&paged=' . $page . + '&s=' . $s; + + $actions['delete'] = sprintf( + '%s', + 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( - '%s', - 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' ) . + '' . + __( 'You cannot deactivate this plugin as other plugins depend on it.' ) . + ''; + } else { + $deactivate_url = 'plugins.php?action=deactivate' . + '&plugin=' . urlencode( $plugin_file ) . + '&plugin_status=' . $context . + '&paged=' . $page . + '&s=' . $s; + + $actions['deactivate'] = sprintf( + '%s', + 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( '%s', - 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( - '%s', - 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' ) . + '' . + __( 'You cannot activate this plugin as it has unmet requirements.' ) . + ''; + } else { + $activate_url = 'plugins.php?action=activate' . + '&plugin=' . urlencode( $plugin_file ) . + '&plugin_status=' . $context . + '&paged=' . $page . + '&s=' . $s; + + $actions['activate'] = sprintf( + '%s', + 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( '%s', @@ -886,14 +963,27 @@ class WP_Plugins_List_Table extends WP_List_Table { } if ( ! is_multisite() && current_user_can( 'delete_plugins' ) ) { - $actions['delete'] = sprintf( - '%s', - 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' ) . + '' . + __( 'You cannot delete this plugin as other plugins require it.' ) . + ''; + } else { + $delete_url = 'plugins.php?action=delete-selected' . + '&checked[]=' . urlencode( $plugin_file ) . + '&plugin_status=' . $context . + '&paged=' . $page . + '&s=' . $s; + + $actions['delete'] = sprintf( + '%s', + 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( - '' . - '', - esc_attr( $plugin_file ), + '' . + '', $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 {
"; $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 = '' . $plugin_data['Author'] . ''; } + /* 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 '
'; + 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. */ + __( 'Required by: %s' ), + implode( $comma, $dependent_names ) + ); + + printf( + '

%1$s

%2$s

', + $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. */ + __( 'Requires: %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( + '

%1$s

%2$s

', + $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( + "%s", + esc_url( $url ), + /* translators: %s: Plugin name. */ + sprintf( __( 'More information about %s' ), $name_attr ), + $name_attr, + esc_html( $name ) + ); + } } diff --git a/wp-admin/includes/class-wp-privacy-policy-content.php b/wp-admin/includes/class-wp-privacy-policy-content.php index a64d043..e0775f7 100644 --- a/wp-admin/includes/class-wp-privacy-policy-content.php +++ b/wp-admin/includes/class-wp-privacy-policy-content.php @@ -466,7 +466,7 @@ final class WP_Privacy_Policy_Content { } /* translators: Default privacy policy heading. */ - $strings[] = '

' . __( 'Who we are' ) . '

'; + $strings[] = '

' . __( 'Who we are' ) . '

'; if ( $description ) { /* translators: Privacy policy tutorial. */ @@ -494,7 +494,7 @@ final class WP_Privacy_Policy_Content { } /* translators: Default privacy policy heading. */ - $strings[] = '

' . __( 'Comments' ) . '

'; + $strings[] = '

' . __( 'Comments' ) . '

'; if ( $description ) { /* translators: Privacy policy tutorial. */ @@ -507,7 +507,7 @@ final class WP_Privacy_Policy_Content { } /* translators: Default privacy policy heading. */ - $strings[] = '

' . __( 'Media' ) . '

'; + $strings[] = '

' . __( 'Media' ) . '

'; if ( $description ) { /* translators: Privacy policy tutorial. */ @@ -525,11 +525,11 @@ final class WP_Privacy_Policy_Content { } /* translators: Default privacy policy heading. */ - $strings[] = '

' . __( 'Cookies' ) . '

'; + $strings[] = '

' . __( 'Cookies' ) . '

'; if ( $description ) { /* translators: Privacy policy tutorial. */ - $strings[] = '

' . __( 'In this subsection you should list the cookies your web site uses, including those set by your plugins, social media, and analytics. We have provided the cookies which WordPress installs by default.' ) . '

'; + $strings[] = '

' . __( 'In this subsection you should list the cookies your website uses, including those set by your plugins, social media, and analytics. We have provided the cookies which WordPress installs by default.' ) . '

'; } else { /* translators: Default privacy policy text. */ $strings[] = '

' . $suggested_text . __( 'If you leave a comment on our site you may opt-in to saving your name, email address and website in cookies. These are for your convenience so that you do not have to fill in your details again when you leave another comment. These cookies will last for one year.' ) . '

'; @@ -543,7 +543,7 @@ final class WP_Privacy_Policy_Content { if ( ! $description ) { /* translators: Default privacy policy heading. */ - $strings[] = '

' . __( 'Embedded content from other websites' ) . '

'; + $strings[] = '

' . __( 'Embedded content from other websites' ) . '

'; /* translators: Default privacy policy text. */ $strings[] = '

' . $suggested_text . __( 'Articles on this site may include embedded content (e.g. videos, images, articles, etc.). Embedded content from other websites behaves in the exact same way as if the visitor has visited the other website.' ) . '

'; /* translators: Default privacy policy text. */ @@ -560,7 +560,7 @@ final class WP_Privacy_Policy_Content { } /* translators: Default privacy policy heading. */ - $strings[] = '

' . __( 'Who we share your data with' ) . '

'; + $strings[] = '

' . __( 'Who we share your data with' ) . '

'; if ( $description ) { /* translators: Privacy policy tutorial. */ @@ -573,11 +573,11 @@ final class WP_Privacy_Policy_Content { } /* translators: Default privacy policy heading. */ - $strings[] = '

' . __( 'How long we retain your data' ) . '

'; + $strings[] = '

' . __( 'How long we retain your data' ) . '

'; if ( $description ) { /* translators: Privacy policy tutorial. */ - $strings[] = '

' . __( 'In this section you should explain how long you retain personal data collected or processed by the web site. While it is your responsibility to come up with the schedule of how long you keep each dataset for and why you keep it, that information does need to be listed here. For example, you may want to say that you keep contact form entries for six months, analytics records for a year, and customer purchase records for ten years.' ) . '

'; + $strings[] = '

' . __( 'In this section you should explain how long you retain personal data collected or processed by the website. While it is your responsibility to come up with the schedule of how long you keep each dataset for and why you keep it, that information does need to be listed here. For example, you may want to say that you keep contact form entries for six months, analytics records for a year, and customer purchase records for ten years.' ) . '

'; } else { /* translators: Default privacy policy text. */ $strings[] = '

' . $suggested_text . __( 'If you leave a comment, the comment and its metadata are retained indefinitely. This is so we can recognize and approve any follow-up comments automatically instead of holding them in a moderation queue.' ) . '

'; @@ -586,7 +586,7 @@ final class WP_Privacy_Policy_Content { } /* translators: Default privacy policy heading. */ - $strings[] = '

' . __( 'What rights you have over your data' ) . '

'; + $strings[] = '

' . __( 'What rights you have over your data' ) . '

'; if ( $description ) { /* translators: Privacy policy tutorial. */ @@ -597,7 +597,7 @@ final class WP_Privacy_Policy_Content { } /* translators: Default privacy policy heading. */ - $strings[] = '

' . __( 'Where your data is sent' ) . '

'; + $strings[] = '

' . __( 'Where your data is sent' ) . '

'; if ( $description ) { /* translators: Privacy policy tutorial. */ @@ -641,14 +641,14 @@ final class WP_Privacy_Policy_Content { /* translators: Default privacy policy heading. */ $strings[] = '

' . __( 'What third parties we receive data from' ) . '

'; /* translators: Privacy policy tutorial. */ - $strings[] = '

' . __( 'If your web site receives data about users from third parties, including advertisers, this information must be included within the section of your privacy policy dealing with third party data.' ) . '

'; + $strings[] = '

' . __( 'If your website receives data about users from third parties, including advertisers, this information must be included within the section of your privacy policy dealing with third party data.' ) . '

'; } if ( $description ) { /* translators: Default privacy policy heading. */ $strings[] = '

' . __( 'What automated decision making and/or profiling we do with user data' ) . '

'; /* translators: Privacy policy tutorial. */ - $strings[] = '

' . __( 'If your web site provides a service which includes automated decision making - for example, allowing customers to apply for credit, or aggregating their data into an advertising profile - you must note that this is taking place, and include information about how that information is used, what decisions are made with that aggregated data, and what rights users have over decisions made without human intervention.' ) . '

'; + $strings[] = '

' . __( 'If your website provides a service which includes automated decision making - for example, allowing customers to apply for credit, or aggregating their data into an advertising profile - you must note that this is taking place, and include information about how that information is used, what decisions are made with that aggregated data, and what rights users have over decisions made without human intervention.' ) . '

'; } if ( $description ) { @@ -662,11 +662,11 @@ final class WP_Privacy_Policy_Content { if ( $blocks ) { foreach ( $strings as $key => $string ) { if ( str_starts_with( $string, '

' ) ) { - $strings[ $key ] = '' . $string . ''; + $strings[ $key ] = "\n" . $string . "\n\n"; } - if ( str_starts_with( $string, '

' ) ) { - $strings[ $key ] = '' . $string . ''; + if ( str_starts_with( $string, '

\n"; } } } diff --git a/wp-admin/includes/class-wp-screen.php b/wp-admin/includes/class-wp-screen.php index 739a182..67bceef 100644 --- a/wp-admin/includes/class-wp-screen.php +++ b/wp-admin/includes/class-wp-screen.php @@ -1279,7 +1279,7 @@ final class WP_Screen { diff --git a/wp-admin/includes/class-wp-site-health.php b/wp-admin/includes/class-wp-site-health.php index b73e1e7..da6c81e 100644 --- a/wp-admin/includes/class-wp-site-health.php +++ b/wp-admin/includes/class-wp-site-health.php @@ -17,7 +17,7 @@ class WP_Site_Health { public $is_mariadb = false; private $mysql_server_version = ''; private $mysql_required_version = '5.5'; - private $mysql_recommended_version = '5.7'; + private $mysql_recommended_version = '8.0'; private $mariadb_recommended_version = '10.4'; public $php_memory_limit; @@ -1288,13 +1288,9 @@ class WP_Site_Health { * * @since 5.2.0 * - * @global wpdb $wpdb WordPress database abstraction object. - * * @return array The test results. */ public function get_test_utf8mb4_support() { - global $wpdb; - if ( ! $this->mysql_server_version ) { $this->prepare_sql_data(); } diff --git a/wp-admin/includes/class-wp-site-icon.php b/wp-admin/includes/class-wp-site-icon.php index ff41771..d14ead3 100644 --- a/wp-admin/includes/class-wp-site-icon.php +++ b/wp-admin/includes/class-wp-site-icon.php @@ -78,12 +78,15 @@ class WP_Site_Icon { * Creates an attachment 'object'. * * @since 4.3.0 + * @deprecated 6.5.0 * * @param string $cropped Cropped image URL. * @param int $parent_attachment_id Attachment ID of parent image. * @return array An array with attachment object data. */ public function create_attachment_object( $cropped, $parent_attachment_id ) { + _deprecated_function( __METHOD__, '6.5.0', 'wp_copy_parent_attachment_properties()' ); + $parent = get_post( $parent_attachment_id ); $parent_url = wp_get_attachment_url( $parent->ID ); $url = str_replace( wp_basename( $parent_url ), wp_basename( $cropped ), $parent_url ); diff --git a/wp-admin/includes/class-wp-theme-install-list-table.php b/wp-admin/includes/class-wp-theme-install-list-table.php index 318d8d8..945fb6e 100644 --- a/wp-admin/includes/class-wp-theme-install-list-table.php +++ b/wp-admin/includes/class-wp-theme-install-list-table.php @@ -333,7 +333,7 @@ class WP_Theme_Install_List_Table extends WP_Themes_List_Table { esc_url( wp_nonce_url( $install_url, 'install-theme_' . $theme->slug ) ), /* translators: %s: Theme name. */ esc_attr( sprintf( _x( 'Install %s', 'theme' ), $name ) ), - __( 'Install Now' ) + _x( 'Install Now', 'theme' ) ); break; } diff --git a/wp-admin/includes/class-wp-themes-list-table.php b/wp-admin/includes/class-wp-themes-list-table.php index 8d385f9..fce3620 100644 --- a/wp-admin/includes/class-wp-themes-list-table.php +++ b/wp-admin/includes/class-wp-themes-list-table.php @@ -212,7 +212,7 @@ class WP_Themes_List_Table extends WP_List_Table { $activate_link, /* translators: %s: Theme name. */ esc_attr( sprintf( _x( 'Activate “%s”', 'theme' ), $title ) ), - __( 'Activate' ) + _x( 'Activate', 'theme' ) ); if ( current_user_can( 'edit_theme_options' ) && current_user_can( 'customize' ) ) { diff --git a/wp-admin/includes/class-wp-upgrader-skin.php b/wp-admin/includes/class-wp-upgrader-skin.php index 598724f..83b4ba4 100644 --- a/wp-admin/includes/class-wp-upgrader-skin.php +++ b/wp-admin/includes/class-wp-upgrader-skin.php @@ -240,7 +240,14 @@ class WP_Upgrader_Skin { if ( defined( 'IFRAME_REQUEST' ) ) { echo ''; } else { diff --git a/wp-admin/includes/class-wp-users-list-table.php b/wp-admin/includes/class-wp-users-list-table.php index ecb8eb4..8dfe3ce 100644 --- a/wp-admin/includes/class-wp-users-list-table.php +++ b/wp-admin/includes/class-wp-users-list-table.php @@ -163,7 +163,7 @@ class WP_Users_List_Table extends WP_List_Table { * with this table. * * Provides a list of roles and user count for that role for easy - * Filtersing of the user table. + * filtering of the user table. * * @since 3.1.0 * diff --git a/wp-admin/includes/dashboard.php b/wp-admin/includes/dashboard.php index 5b50423..2aeaa24 100644 --- a/wp-admin/includes/dashboard.php +++ b/wp-admin/includes/dashboard.php @@ -1088,7 +1088,11 @@ function wp_dashboard_recent_comments( $total_items = 5 ) { } foreach ( $possible as $comment ) { - if ( ! current_user_can( 'read_post', $comment->comment_post_ID ) ) { + if ( ! current_user_can( 'edit_post', $comment->comment_post_ID ) + && ( post_password_required( $comment->comment_post_ID ) + || ! current_user_can( 'read_post', $comment->comment_post_ID ) ) + ) { + // The user has no access to the post and thus cannot see the comments. continue; } @@ -1109,16 +1113,7 @@ function wp_dashboard_recent_comments( $total_items = 5 ) { echo '
    '; foreach ( $comments as $comment ) { - $comment_post = get_post( $comment->comment_post_ID ); - if ( - current_user_can( 'edit_post', $comment->comment_post_ID ) || - ( - empty( $comment_post->post_password ) && - current_user_can( 'read_post', $comment->comment_post_ID ) - ) - ) { - _wp_dashboard_recent_comments_row( $comment ); - } + _wp_dashboard_recent_comments_row( $comment ); } echo '
'; @@ -1577,7 +1572,11 @@ function wp_dashboard_primary() { * * @param string $link The widget's secondary link URL. */ - 'link' => apply_filters( 'dashboard_secondary_link', __( 'https://planet.wordpress.org/' ) ), + 'link' => apply_filters( + 'dashboard_secondary_link', + /* translators: Link to the Planet website of the locale. */ + __( 'https://planet.wordpress.org/' ) + ), /** * Filters the secondary feed URL for the 'WordPress Events and News' dashboard widget. @@ -1586,7 +1585,11 @@ function wp_dashboard_primary() { * * @param string $url The widget's secondary feed URL. */ - 'url' => apply_filters( 'dashboard_secondary_feed', __( 'https://planet.wordpress.org/feed/' ) ), + 'url' => apply_filters( + 'dashboard_secondary_feed', + /* translators: Link to the Planet feed of the locale. */ + __( 'https://planet.wordpress.org/feed/' ) + ), /** * Filters the secondary link title for the 'WordPress Events and News' dashboard widget. diff --git a/wp-admin/includes/deprecated.php b/wp-admin/includes/deprecated.php index d588ad4..f7805b4 100644 --- a/wp-admin/includes/deprecated.php +++ b/wp-admin/includes/deprecated.php @@ -1152,7 +1152,7 @@ function wp_nav_menu_locations_meta_box() { /** * This was once used to kick-off the Core Updater. * - * Deprecated in favor of instantating a Core_Upgrader instance directly, + * Deprecated in favor of instantiating a Core_Upgrader instance directly, * and calling the 'upgrade' method. * * @since 2.7.0 @@ -1174,7 +1174,7 @@ function wp_update_core($current, $feedback = '') { /** * This was once used to kick-off the Plugin Updater. * - * Deprecated in favor of instantating a Plugin_Upgrader instance directly, + * Deprecated in favor of instantiating a Plugin_Upgrader instance directly, * and calling the 'upgrade' method. * Unused since 2.8.0. * diff --git a/wp-admin/includes/export.php b/wp-admin/includes/export.php index 9610ac8..d05f98f 100644 --- a/wp-admin/includes/export.php +++ b/wp-admin/includes/export.php @@ -144,6 +144,52 @@ function export_wp( $args = array() ) { // Grab a snapshot of post IDs, just in case it changes during the export. $post_ids = $wpdb->get_col( "SELECT ID FROM {$wpdb->posts} $join WHERE $where" ); + // Get IDs for the attachments of each post, unless all content is already being exported. + if ( ! in_array( $args['content'], array( 'all', 'attachment' ), true ) ) { + // Array to hold all additional IDs (attachments and thumbnails). + $additional_ids = array(); + + // Create a copy of the post IDs array to avoid modifying the original array. + $processing_ids = $post_ids; + + while ( $next_posts = array_splice( $processing_ids, 0, 20 ) ) { + $posts_in = array_map( 'absint', $next_posts ); + $placeholders = array_fill( 0, count( $posts_in ), '%d' ); + + // Create a string for the placeholders. + $in_placeholder = implode( ',', $placeholders ); + + // Prepare the SQL statement for attachment ids. + $attachment_ids = $wpdb->get_col( + $wpdb->prepare( + " + SELECT ID + FROM $wpdb->posts + WHERE post_parent IN ($in_placeholder) AND post_type = 'attachment' + ", + $posts_in + ) + ); + + $thumbnails_ids = $wpdb->get_col( + $wpdb->prepare( + " + SELECT meta_value + FROM $wpdb->postmeta + WHERE $wpdb->postmeta.post_id IN ($in_placeholder) + AND $wpdb->postmeta.meta_key = '_thumbnail_id' + ", + $posts_in + ) + ); + + $additional_ids = array_merge( $additional_ids, $attachment_ids, $thumbnails_ids ); + } + + // Merge the additional IDs back with the original post IDs after processing all posts + $post_ids = array_unique( array_merge( $post_ids, $additional_ids ) ); + } + /* * Get the requested terms ready, empty unless posts filtered by category * or all content. diff --git a/wp-admin/includes/file.php b/wp-admin/includes/file.php index 600ddc2..5832569 100644 --- a/wp-admin/includes/file.php +++ b/wp-admin/includes/file.php @@ -656,7 +656,7 @@ function wp_edit_theme_plugin_file( $args ) { /** * Returns a filename of a temporary unique file. * - * Please note that the calling function must unlink() this itself. + * Please note that the calling function must delete or move the file. * * The filename is based off the passed parameter or defaults to the current unix timestamp, * while the directory can either be passed as well, or by leaving it blank, default to a writable @@ -1139,7 +1139,7 @@ function wp_handle_sideload( &$file, $overrides = false, $time = null ) { /** * Downloads a URL to a local temporary file using the WordPress HTTP API. * - * Please note that the calling function must unlink() the file. + * Please note that the calling function must delete or move the file. * * @since 2.5.0 * @since 5.2.0 Signature Verification with SoftFail was added. @@ -1153,7 +1153,7 @@ function wp_handle_sideload( &$file, $overrides = false, $time = null ) { * @return string|WP_Error Filename on success, WP_Error on failure. */ function download_url( $url, $timeout = 300, $signature_verification = false ) { - // WARNING: The file is not automatically deleted, the script must unlink() the file. + // WARNING: The file is not automatically deleted, the script must delete or move the file. if ( ! $url ) { return new WP_Error( 'http_no_url', __( 'Invalid URL Provided.' ) ); } @@ -1563,6 +1563,37 @@ function wp_trusted_keys() { return apply_filters( 'wp_trusted_keys', $trusted_keys ); } +/** + * Determines whether the given file is a valid ZIP file. + * + * This function does not test to ensure that a file exists. Non-existent files + * are not valid ZIPs, so those will also return false. + * + * @since 6.4.4 + * + * @param string $file Full path to the ZIP file. + * @return bool Whether the file is a valid ZIP file. + */ +function wp_zip_file_is_valid( $file ) { + /** This filter is documented in wp-admin/includes/file.php */ + if ( class_exists( 'ZipArchive', false ) && apply_filters( 'unzip_file_use_ziparchive', true ) ) { + $archive = new ZipArchive(); + $archive_is_valid = $archive->open( $file, ZipArchive::CHECKCONS ); + if ( true === $archive_is_valid ) { + $archive->close(); + return true; + } + } + + // Fall through to PclZip if ZipArchive is not available, or encountered an error opening the file. + require_once ABSPATH . 'wp-admin/includes/class-pclzip.php'; + + $archive = new PclZip( $file ); + $archive_is_valid = is_array( $archive->properties() ); + + return $archive_is_valid; +} + /** * Unzips a specified ZIP file to a location on the filesystem via the WordPress * Filesystem Abstraction. diff --git a/wp-admin/includes/image-edit.php b/wp-admin/includes/image-edit.php index 739b09f..2d150e6 100644 --- a/wp-admin/includes/image-edit.php +++ b/wp-admin/includes/image-edit.php @@ -390,6 +390,12 @@ function wp_stream_image( $image, $mime_type, $attachment_id ) { return imagewebp( $image, null, 90 ); } return false; + case 'image/avif': + if ( function_exists( 'imageavif' ) ) { + header( 'Content-Type: image/avif' ); + return imageavif( $image, null, 90 ); + } + return false; default: return false; } @@ -494,6 +500,11 @@ function wp_save_image_file( $filename, $image, $mime_type, $post_id ) { return imagewebp( $image, $filename ); } return false; + case 'image/avif': + if ( function_exists( 'imageavif' ) ) { + return imageavif( $image, $filename ); + } + return false; default: return false; } diff --git a/wp-admin/includes/image.php b/wp-admin/includes/image.php index 2bdcc50..69f58d5 100644 --- a/wp-admin/includes/image.php +++ b/wp-admin/includes/image.php @@ -482,6 +482,62 @@ function _wp_make_subsizes( $new_sizes, $file, $image_meta, $attachment_id ) { return $image_meta; } +/** + * Copy parent attachment properties to newly cropped image. + * + * @since 6.5.0 + * + * @param string $cropped Path to the cropped image file. + * @param int $parent_attachment_id Parent file Attachment ID. + * @param string $context Control calling the function. + * @return array Properties of attachment. + */ +function wp_copy_parent_attachment_properties( $cropped, $parent_attachment_id, $context = '' ) { + $parent = get_post( $parent_attachment_id ); + $parent_url = wp_get_attachment_url( $parent->ID ); + $parent_basename = wp_basename( $parent_url ); + $url = str_replace( wp_basename( $parent_url ), wp_basename( $cropped ), $parent_url ); + + $size = wp_getimagesize( $cropped ); + $image_type = $size ? $size['mime'] : 'image/jpeg'; + + $sanitized_post_title = sanitize_file_name( $parent->post_title ); + $use_original_title = ( + ( '' !== trim( $parent->post_title ) ) && + /* + * Check if the original image has a title other than the "filename" default, + * meaning the image had a title when originally uploaded or its title was edited. + */ + ( $parent_basename !== $sanitized_post_title ) && + ( pathinfo( $parent_basename, PATHINFO_FILENAME ) !== $sanitized_post_title ) + ); + $use_original_description = ( '' !== trim( $parent->post_content ) ); + + $attachment = array( + 'post_title' => $use_original_title ? $parent->post_title : wp_basename( $cropped ), + 'post_content' => $use_original_description ? $parent->post_content : $url, + 'post_mime_type' => $image_type, + 'guid' => $url, + 'context' => $context, + ); + + // Copy the image caption attribute (post_excerpt field) from the original image. + if ( '' !== trim( $parent->post_excerpt ) ) { + $attachment['post_excerpt'] = $parent->post_excerpt; + } + + // Copy the image alt text attribute from the original image. + if ( '' !== trim( $parent->_wp_attachment_image_alt ) ) { + $attachment['meta_input'] = array( + '_wp_attachment_image_alt' => wp_slash( $parent->_wp_attachment_image_alt ), + ); + } + + $attachment['post_parent'] = $parent_attachment_id; + + return $attachment; +} + /** * Generates attachment meta data and create image sub-sizes for images. * @@ -863,22 +919,51 @@ function wp_read_image_metadata( $file ) { $exif = array(); } + $exif_description = ''; + $exif_usercomment = ''; if ( ! empty( $exif['ImageDescription'] ) ) { + $exif_description = trim( $exif['ImageDescription'] ); + } + + if ( ! empty( $exif['COMPUTED']['UserComment'] ) ) { + $exif_usercomment = trim( $exif['COMPUTED']['UserComment'] ); + } + + if ( $exif_description ) { mbstring_binary_safe_encoding(); - $description_length = strlen( $exif['ImageDescription'] ); + $description_length = strlen( $exif_description ); reset_mbstring_encoding(); - if ( empty( $meta['title'] ) && $description_length < 80 ) { // Assume the title is stored in ImageDescription. - $meta['title'] = trim( $exif['ImageDescription'] ); + $meta['title'] = $exif_description; } - if ( empty( $meta['caption'] ) && ! empty( $exif['COMPUTED']['UserComment'] ) ) { - $meta['caption'] = trim( $exif['COMPUTED']['UserComment'] ); + // If both user comments and description are present. + if ( empty( $meta['caption'] ) && $exif_description && $exif_usercomment ) { + if ( ! empty( $meta['title'] ) && $exif_description === $meta['title'] ) { + $caption = $exif_usercomment; + } else { + if ( $exif_description === $exif_usercomment ) { + $caption = $exif_description; + } else { + $caption = trim( $exif_description . ' ' . $exif_usercomment ); + } + } + $meta['caption'] = $caption; + } + + if ( empty( $meta['caption'] ) && $exif_usercomment ) { + $meta['caption'] = $exif_usercomment; } if ( empty( $meta['caption'] ) ) { - $meta['caption'] = trim( $exif['ImageDescription'] ); + $meta['caption'] = $exif_description; + } + } elseif ( empty( $meta['caption'] ) && $exif_usercomment ) { + $meta['caption'] = $exif_usercomment; + $description_length = strlen( $exif_usercomment ); + if ( empty( $meta['title'] ) && $description_length < 80 ) { + $meta['title'] = trim( $exif_usercomment ); } } elseif ( empty( $meta['caption'] ) && ! empty( $exif['Comments'] ) ) { $meta['caption'] = trim( $exif['Comments'] ); @@ -977,7 +1062,7 @@ function file_is_valid_image( $path ) { * @return bool True if suitable, false if not suitable. */ function file_is_displayable_image( $path ) { - $displayable_image_types = array( IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_BMP, IMAGETYPE_ICO, IMAGETYPE_WEBP ); + $displayable_image_types = array( IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG, IMAGETYPE_BMP, IMAGETYPE_ICO, IMAGETYPE_WEBP, IMAGETYPE_AVIF ); $info = wp_getimagesize( $path ); if ( empty( $info ) ) { diff --git a/wp-admin/includes/media.php b/wp-admin/includes/media.php index e7b4c10..3de25dc 100644 --- a/wp-admin/includes/media.php +++ b/wp-admin/includes/media.php @@ -527,12 +527,14 @@ function media_handle_sideload( $file_array, $post_id = 0, $desc = null, $post_d * @since 5.3.0 Formalized the existing and already documented `...$args` parameter * by adding it to the function signature. * - * @global int $body_id + * @global string $body_id * * @param callable $content_func Function that outputs the content. * @param mixed ...$args Optional additional parameters to pass to the callback function when it's called. */ function wp_iframe( $content_func, ...$args ) { + global $body_id; + _wp_admin_html_begin(); ?> <?php bloginfo( 'name' ); ?> › <?php _e( 'Uploads' ); ?> — <?php _e( 'WordPress' ); ?> @@ -603,8 +605,8 @@ function wp_iframe( $content_func, ...$args ) { $body_id_attr = ''; - if ( isset( $GLOBALS['body_id'] ) ) { - $body_id_attr = ' id="' . $GLOBALS['body_id'] . '"'; + if ( isset( $body_id ) ) { + $body_id_attr = ' id="' . $body_id . '"'; } ?> @@ -2196,6 +2198,11 @@ function media_upload_form( $errors = null ) { $plupload_init['webp_upload_error'] = true; } + // Check if AVIF images can be edited. + if ( ! wp_image_editor_supports( array( 'mime_type' => 'image/avif' ) ) ) { + $plupload_init['avif_upload_error'] = true; + } + /** * Filters the default Plupload settings. * @@ -2825,7 +2832,7 @@ function media_upload_library_form( $errors ) { 'format' => '', 'prev_text' => __( '«' ), 'next_text' => __( '»' ), - 'total' => ceil( $wp_query->found_posts / 10 ), + 'total' => (int) ceil( $wp_query->found_posts / 10 ), 'current' => $q['paged'], ) ); diff --git a/wp-admin/includes/menu.php b/wp-admin/includes/menu.php index da1b2eb..3cf4a5f 100644 --- a/wp-admin/includes/menu.php +++ b/wp-admin/includes/menu.php @@ -13,8 +13,8 @@ if ( is_network_admin() ) { * * The hook fires before menus and sub-menus are removed based on user privileges. * - * @private * @since 3.1.0 + * @access private */ do_action( '_network_admin_menu' ); } elseif ( is_user_admin() ) { @@ -24,8 +24,8 @@ if ( is_network_admin() ) { * * The hook fires before menus and sub-menus are removed based on user privileges. * - * @private * @since 3.1.0 + * @access private */ do_action( '_user_admin_menu' ); } else { @@ -35,8 +35,8 @@ if ( is_network_admin() ) { * * The hook fires before menus and sub-menus are removed based on user privileges. * - * @private * @since 2.2.0 + * @access private */ do_action( '_admin_menu' ); } diff --git a/wp-admin/includes/meta-boxes.php b/wp-admin/includes/meta-boxes.php index 5228076..387910e 100644 --- a/wp-admin/includes/meta-boxes.php +++ b/wp-admin/includes/meta-boxes.php @@ -434,7 +434,7 @@ function attachment_submit_meta_box( $post ) { post_date ) ), diff --git a/wp-admin/includes/misc.php b/wp-admin/includes/misc.php index 0902820..f950821 100644 --- a/wp-admin/includes/misc.php +++ b/wp-admin/includes/misc.php @@ -39,11 +39,12 @@ function got_mod_rewrite() { * @since 3.7.0 * * @global bool $is_nginx + * @global bool $is_caddy * * @return bool Whether the server supports URL rewriting. */ function got_url_rewrite() { - $got_url_rewrite = ( got_mod_rewrite() || $GLOBALS['is_nginx'] || iis7_supports_permalinks() ); + $got_url_rewrite = ( got_mod_rewrite() || $GLOBALS['is_nginx'] || $GLOBALS['is_caddy'] || iis7_supports_permalinks() ); /** * Filters whether URL rewriting is available. @@ -1038,17 +1039,15 @@ function admin_color_scheme_picker( $user_id ) { - - - colors as $html_color ) { - ?> - - + colors as $html_color ) { ?> - -
 
+
 
+ +