', esc_attr( $context ) );
// Grab the ones the user has manually sorted. Pull them out of their previous context/priority and into the one the user chose
if ( ! $already_sorted && $sorted = get_user_option( "meta-box-order_$page" ) ) {
diff --git a/wp-admin/includes/user.php b/wp-admin/includes/user.php
index 68be2ba60..2ebcb3326 100644
--- a/wp-admin/includes/user.php
+++ b/wp-admin/includes/user.php
@@ -1380,6 +1380,7 @@ class WP_Privacy_Data_Export_Requests_Table extends WP_Privacy_Requests_Table {
* @return string Email column markup.
*/
public function column_email( $item ) {
+ /** This filter is documented in wp-admin/includes/ajax-actions.php */
$exporters = apply_filters( 'wp_privacy_personal_data_exporters', array() );
$exporters_count = count( $exporters );
$request_id = $item->ID;
@@ -1420,6 +1421,7 @@ public function column_next_steps( $item ) {
esc_html_e( 'Waiting for confirmation' );
break;
case 'request-confirmed':
+ /** This filter is documented in wp-admin/includes/ajax-actions.php */
$exporters = apply_filters( 'wp_privacy_personal_data_exporters', array() );
$exporters_count = count( $exporters );
$request_id = $item->ID;
@@ -1492,6 +1494,7 @@ public function column_email( $item ) {
// Allow the administrator to "force remove" the personal data even if confirmation has not yet been received.
$status = $item->status;
if ( 'request-confirmed' !== $status ) {
+ /** This filter is documented in wp-admin/includes/ajax-actions.php */
$erasers = apply_filters( 'wp_privacy_personal_data_erasers', array() );
$erasers_count = count( $erasers );
$request_id = $item->ID;
@@ -1532,6 +1535,7 @@ public function column_next_steps( $item ) {
esc_html_e( 'Waiting for confirmation' );
break;
case 'request-confirmed':
+ /** This filter is documented in wp-admin/includes/ajax-actions.php */
$erasers = apply_filters( 'wp_privacy_personal_data_erasers', array() );
$erasers_count = count( $erasers );
$request_id = $item->ID;
diff --git a/wp-admin/privacy.php b/wp-admin/privacy.php
index 991ad72ef..9c360dd18 100644
--- a/wp-admin/privacy.php
+++ b/wp-admin/privacy.php
@@ -22,14 +22,33 @@
$privacy_policy_page_id = isset( $_POST['page_for_privacy_policy'] ) ? (int) $_POST['page_for_privacy_policy'] : 0;
update_option( 'wp_page_for_privacy_policy', $privacy_policy_page_id );
+ $privacy_page_updated_message = __( 'Privacy policy page updated successfully.' );
+
+ if ( $privacy_policy_page_id ) {
+ /*
+ * Don't always link to the menu customizer:
+ *
+ * - Unpublished pages can't be selected by default.
+ * - `WP_Customize_Nav_Menus::__construct()` checks the user's capabilities.
+ * - Themes might not "officially" support menus.
+ */
+ if (
+ 'publish' === get_post_status( $privacy_policy_page_id )
+ && current_user_can( 'edit_theme_options' )
+ && current_theme_supports( 'menus' )
+ ) {
+ $privacy_page_updated_message = sprintf(
+ /* translators: %s: URL to Customizer -> Menus */
+ __( 'Privacy policy page updated successfully. Remember to
update your menus!' ),
+ esc_url( add_query_arg( 'autofocus[panel]', 'nav_menus', admin_url( 'customize.php' ) ) )
+ );
+ }
+ }
+
add_settings_error(
'page_for_privacy_policy',
'page_for_privacy_policy',
- sprintf(
- /* translators: %s: URL to Customizer -> Menus */
- __( 'Privacy policy page updated successfully. Remember to
update your menus!' ),
- 'customize.php?autofocus[panel]=nav_menus'
- ),
+ $privacy_page_updated_message,
'updated'
);
} elseif ( 'create-privacy-page' === $action ) {
diff --git a/wp-includes/class-wp-term-query.php b/wp-includes/class-wp-term-query.php
index 6d35fe5d6..def009dc8 100644
--- a/wp-includes/class-wp-term-query.php
+++ b/wp-includes/class-wp-term-query.php
@@ -674,7 +674,7 @@ public function get_terms() {
$cache_key = "get_terms:$key:$last_changed";
$cache = wp_cache_get( $cache_key, 'terms' );
if ( false !== $cache ) {
- if ( 'all' === $_fields ) {
+ if ( 'all' === $_fields || 'all_with_object_id' === $_fields ) {
$cache = array_map( 'get_term', $cache );
}
diff --git a/wp-includes/comment-template.php b/wp-includes/comment-template.php
index 51193cec1..7bde60c51 100644
--- a/wp-includes/comment-template.php
+++ b/wp-includes/comment-template.php
@@ -2123,6 +2123,7 @@ function wp_list_comments( $args = array(), $comments = null ) {
* @since 4.5.0 The 'author', 'email', and 'url' form fields are limited to 245, 100,
* and 200 characters, respectively.
* @since 4.6.0 Introduced the 'action' argument.
+ * @since 4.9.6 Introduced the 'cookies' default comment field.
*
* @param array $args {
* Optional. Default arguments and form fields to override.
@@ -2130,9 +2131,10 @@ function wp_list_comments( $args = array(), $comments = null ) {
* @type array $fields {
* Default comment fields, filterable by default via the {@see 'comment_form_default_fields'} hook.
*
- * @type string $author Comment author field HTML.
- * @type string $email Comment author email field HTML.
- * @type string $url Comment author URL field HTML.
+ * @type string $author Comment author field HTML.
+ * @type string $email Comment author email field HTML.
+ * @type string $url Comment author URL field HTML.
+ * @type string $cookies Comment cookie opt-in field HTML.
* }
* @type string $comment_field The comment textarea field HTML.
* @type string $must_log_in HTML element for a 'must be logged in to comment' message.
diff --git a/wp-includes/functions.php b/wp-includes/functions.php
index 59fb55755..313e015ec 100644
--- a/wp-includes/functions.php
+++ b/wp-includes/functions.php
@@ -1716,17 +1716,30 @@ function path_join( $base, $path ) {
* @since 3.9.0
* @since 4.4.0 Ensures upper-case drive letters on Windows systems.
* @since 4.5.0 Allows for Windows network shares.
+ * @since 4.9.7 Allows for PHP file wrappers.
*
* @param string $path Path to normalize.
* @return string Normalized path.
*/
function wp_normalize_path( $path ) {
+ $wrapper = '';
+ if ( wp_is_stream( $path ) ) {
+ list( $wrapper, $path ) = explode( '://', $path, 2 );
+ $wrapper .= '://';
+ }
+
+ // Standardise all paths to use /
$path = str_replace( '\\', '/', $path );
+
+ // Replace multiple slashes down to a singular, allowing for network shares having two slashes.
$path = preg_replace( '|(?<=.)/+|', '/', $path );
+
+ // Windows paths should uppercase the drive letter
if ( ':' === substr( $path, 1, 1 ) ) {
$path = ucfirst( $path );
}
- return $path;
+
+ return $wrapper . $path;
}
/**
@@ -5518,6 +5531,28 @@ function wp_delete_file( $file ) {
}
}
+/**
+ * Deletes a file if its path is within the given directory.
+ *
+ * @since 4.9.7
+ *
+ * @param string $file Absolute path to the file to delete.
+ * @param string $directory Absolute path to a directory.
+ * @return bool True on success, false on failure.
+ */
+function wp_delete_file_from_directory( $file, $directory ) {
+ $real_file = realpath( wp_normalize_path( $file ) );
+ $real_directory = realpath( wp_normalize_path( $directory ) );
+
+ if ( false === $real_file || false === $real_directory || strpos( wp_normalize_path( $real_file ), trailingslashit( wp_normalize_path( $real_directory ) ) ) !== 0 ) {
+ return false;
+ }
+
+ wp_delete_file( $file );
+
+ return true;
+}
+
/**
* Outputs a small JS snippet on preview tabs/windows to remove `window.name` on unload.
*
diff --git a/wp-includes/pluggable.php b/wp-includes/pluggable.php
index 792136759..a8c03d238 100644
--- a/wp-includes/pluggable.php
+++ b/wp-includes/pluggable.php
@@ -967,6 +967,9 @@ function wp_clear_auth_cookie() {
setcookie( PASS_COOKIE, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
setcookie( USER_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
setcookie( PASS_COOKIE, ' ', time() - YEAR_IN_SECONDS, SITECOOKIEPATH, COOKIE_DOMAIN );
+
+ // Post password cookie
+ setcookie( 'wp-postpass_' . COOKIEHASH, ' ', time() - YEAR_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN );
}
endif;
diff --git a/wp-includes/pn-version.php b/wp-includes/pn-version.php
index c167e86d1..bab517b5c 100644
--- a/wp-includes/pn-version.php
+++ b/wp-includes/pn-version.php
@@ -1,3 +1,3 @@
get_row( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attachment_metadata' AND meta_value LIKE %s AND post_id <> %d", '%' . $wpdb->esc_like( $meta['thumb'] ) . '%', $post_id)) ) {
- $thumbfile = str_replace(basename($file), $meta['thumb'], $file);
- /** This filter is documented in wp-includes/functions.php */
- $thumbfile = apply_filters( 'wp_delete_file', $thumbfile );
- @ unlink( path_join($uploadpath['basedir'], $thumbfile) );
+ if ( ! $wpdb->get_row( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attachment_metadata' AND meta_value LIKE %s AND post_id <> %d", '%' . $wpdb->esc_like( $meta['thumb'] ) . '%', $post_id ) ) ) {
+ $thumbfile = str_replace( basename( $file ), $meta['thumb'], $file );
+ if ( ! empty( $thumbfile ) ) {
+ $thumbfile = path_join( $uploadpath['basedir'], $thumbfile );
+ $thumbdir = path_join( $uploadpath['basedir'], dirname( $file ) );
+
+ if ( ! wp_delete_file_from_directory( $thumbfile, $thumbdir ) ) {
+ $deleted = false;
+ }
+ }
}
}
// Remove intermediate and backup images if there are any.
if ( isset( $meta['sizes'] ) && is_array( $meta['sizes'] ) ) {
+ $intermediate_dir = path_join( $uploadpath['basedir'], dirname( $file ) );
foreach ( $meta['sizes'] as $size => $sizeinfo ) {
$intermediate_file = str_replace( basename( $file ), $sizeinfo['file'], $file );
- /** This filter is documented in wp-includes/functions.php */
- $intermediate_file = apply_filters( 'wp_delete_file', $intermediate_file );
- @ unlink( path_join( $uploadpath['basedir'], $intermediate_file ) );
+ if ( ! empty( $intermediate_file ) ) {
+ $intermediate_file = path_join( $uploadpath['basedir'], $intermediate_file );
+
+ if ( ! wp_delete_file_from_directory( $intermediate_file, $intermediate_dir ) ) {
+ $deleted = false;
+ }
+ }
}
}
- if ( is_array($backup_sizes) ) {
+ if ( is_array( $backup_sizes ) ) {
+ $del_dir = path_join( $uploadpath['basedir'], dirname( $meta['file'] ) );
foreach ( $backup_sizes as $size ) {
- $del_file = path_join( dirname($meta['file']), $size['file'] );
- /** This filter is documented in wp-includes/functions.php */
- $del_file = apply_filters( 'wp_delete_file', $del_file );
- @ unlink( path_join($uploadpath['basedir'], $del_file) );
+ $del_file = path_join( dirname( $meta['file'] ), $size['file'] );
+ if ( ! empty( $del_file ) ) {
+ $del_file = path_join( $uploadpath['basedir'], $del_file );
+
+ if ( ! wp_delete_file_from_directory( $del_file, $del_dir ) ) {
+ $deleted = false;
+ }
+ }
}
}
- wp_delete_file( $file );
-
- clean_post_cache( $post );
+ if ( ! wp_delete_file_from_directory( $file, $uploadpath['basedir'] ) ) {
+ $deleted = false;
+ }
- return $post;
+ return $deleted;
}
/**
diff --git a/wp-includes/user.php b/wp-includes/user.php
index a226efdb4..88f771377 100644
--- a/wp-includes/user.php
+++ b/wp-includes/user.php
@@ -2648,7 +2648,7 @@ function send_confirmation_on_profile_email() {
return;
}
- $hash = md5( $_POST['email'] . time() . mt_rand() );
+ $hash = md5( $_POST['email'] . time() . wp_rand() );
$new_user_email = array(
'hash' => $hash,
'newemail' => $_POST['email'],
@@ -3258,7 +3258,7 @@ function wp_send_user_request( $request_id ) {
'siteurl' => network_home_url(),
);
- /* translators: Do not translate DESCRIPTION, CONFIRM_URL, EMAIL, SITENAME, SITEURL: those are placeholders. */
+ /* translators: Do not translate DESCRIPTION, CONFIRM_URL, SITENAME, SITEURL: those are placeholders. */
$email_text = __(
'Howdy,
@@ -3272,8 +3272,6 @@ function wp_send_user_request( $request_id ) {
You can safely ignore and delete this email if you do not want to
take this action.
-This email has been sent to ###EMAIL###.
-
Regards,
All at ###SITENAME###
###SITEURL###'
@@ -3286,7 +3284,6 @@ function wp_send_user_request( $request_id ) {
*
* ###DESCRIPTION### Description of the action being performed so the user knows what the email is for.
* ###CONFIRM_URL### The link to click on to confirm the account action.
- * ###EMAIL### The email we are sending to.
* ###SITENAME### The name of the site.
* ###SITEURL### The URL to the site.
*
@@ -3429,7 +3426,7 @@ function wp_validate_user_request_key( $request_id, $key ) {
}
if ( ! $expiration_time || time() > $expiration_time ) {
- $return = new WP_Error( 'expired_key', __( 'The confirmation email has expired.' ) );
+ return new WP_Error( 'expired_key', __( 'The confirmation email has expired.' ) );
}
return true;
diff --git a/wp-includes/version.php b/wp-includes/version.php
index 9710ebbf5..f5efbfcdb 100644
--- a/wp-includes/version.php
+++ b/wp-includes/version.php
@@ -4,7 +4,7 @@
*
* @global string $wp_version
*/
-$wp_version = '4.9.6';
+$wp_version = '4.9.7';
/**
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
diff --git a/wp-includes/widgets.php b/wp-includes/widgets.php
index 1e939e173..683c7ab91 100644
--- a/wp-includes/widgets.php
+++ b/wp-includes/widgets.php
@@ -420,8 +420,9 @@ function wp_sidebar_description( $id ) {
global $wp_registered_sidebars;
- if ( isset($wp_registered_sidebars[$id]['description']) )
- return esc_html( $wp_registered_sidebars[$id]['description'] );
+ if ( isset( $wp_registered_sidebars[ $id ]['description'] ) ) {
+ return wp_kses( $wp_registered_sidebars[ $id ]['description'], 'sidebar_description' );
+ }
}
/**