diff -r 7b1b88e27a20 -r 48c4eec2b7e6 wp/wp-admin/includes/post.php --- a/wp/wp-admin/includes/post.php Thu Sep 29 08:06:27 2022 +0200 +++ b/wp/wp-admin/includes/post.php Fri Sep 05 18:40:08 2025 +0200 @@ -72,7 +72,7 @@ } } - if ( isset( $post_data['user_ID'] ) && ( $post_data['post_author'] != $post_data['user_ID'] ) + if ( isset( $post_data['user_ID'] ) && ( $post_data['post_author'] !== $post_data['user_ID'] ) && ! current_user_can( $ptype->cap->edit_others_posts ) ) { if ( $update ) { @@ -135,8 +135,10 @@ $published_statuses = array( 'publish', 'future' ); - // Posts 'submitted for approval' are submitted to $_POST the same as if they were being published. - // Change status from 'publish' to 'pending' if user lacks permissions to publish or to resave published posts. + /* + * Posts 'submitted for approval' are submitted to $_POST the same as if they were being published. + * Change status from 'publish' to 'pending' if user lacks permissions to publish or to resave published posts. + */ if ( isset( $post_data['post_status'] ) && ( in_array( $post_data['post_status'], $published_statuses, true ) && ! current_user_can( $ptype->cap->publish_posts ) ) @@ -163,7 +165,7 @@ } foreach ( array( 'aa', 'mm', 'jj', 'hh', 'mn' ) as $timeunit ) { - if ( ! empty( $post_data[ 'hidden_' . $timeunit ] ) && $post_data[ 'hidden_' . $timeunit ] != $post_data[ $timeunit ] ) { + if ( ! empty( $post_data[ 'hidden_' . $timeunit ] ) && $post_data[ 'hidden_' . $timeunit ] !== $post_data[ $timeunit ] ) { $post_data['edit_date'] = '1'; break; } @@ -191,7 +193,19 @@ return new WP_Error( 'invalid_date', __( 'Invalid date.' ) ); } - $post_data['post_date_gmt'] = get_gmt_from_date( $post_data['post_date'] ); + /* + * Only assign a post date if the user has explicitly set a new value. + * See #59125 and #19907. + */ + $previous_date = $post_id ? get_post_field( 'post_date', $post_id ) : false; + if ( $previous_date && $previous_date !== $post_data['post_date'] ) { + $post_data['edit_date'] = true; + $post_data['post_date_gmt'] = get_gmt_from_date( $post_data['post_date'] ); + } else { + $post_data['edit_date'] = false; + unset( $post_data['post_date'] ); + unset( $post_data['post_date_gmt'] ); + } } if ( isset( $post_data['post_category'] ) ) { @@ -252,8 +266,8 @@ // Clear out any data in internal vars. unset( $post_data['filter'] ); - $post_ID = (int) $post_data['post_ID']; - $post = get_post( $post_ID ); + $post_id = (int) $post_data['post_ID']; + $post = get_post( $post_id ); $post_data['post_type'] = $post->post_type; $post_data['post_mime_type'] = $post->post_mime_type; @@ -267,7 +281,7 @@ } $ptype = get_post_type_object( $post_data['post_type'] ); - if ( ! current_user_can( 'edit_post', $post_ID ) ) { + if ( ! current_user_can( 'edit_post', $post_id ) ) { if ( 'page' === $post_data['post_type'] ) { wp_die( __( 'Sorry, you are not allowed to edit this page.' ) ); } else { @@ -277,7 +291,7 @@ if ( post_type_supports( $ptype->name, 'revisions' ) ) { $revisions = wp_get_post_revisions( - $post_ID, + $post_id, array( 'order' => 'ASC', 'posts_per_page' => 1, @@ -287,7 +301,7 @@ // Check if the revisions have been upgraded. if ( $revisions && _wp_get_post_revision_version( $revision ) < 1 ) { - _wp_upgrade_revisions_of_post( $post, wp_get_post_revisions( $post_ID ) ); + _wp_upgrade_revisions_of_post( $post, wp_get_post_revisions( $post_id ) ); } } @@ -315,14 +329,14 @@ // Post formats. if ( isset( $post_data['post_format'] ) ) { - set_post_format( $post_ID, $post_data['post_format'] ); + set_post_format( $post_id, $post_data['post_format'] ); } $format_meta_urls = array( 'url', 'link_url', 'quote_source_url' ); foreach ( $format_meta_urls as $format_meta_url ) { $keyed = '_format_' . $format_meta_url; if ( isset( $post_data[ $keyed ] ) ) { - update_post_meta( $post_ID, $keyed, wp_slash( esc_url_raw( wp_unslash( $post_data[ $keyed ] ) ) ) ); + update_post_meta( $post_id, $keyed, wp_slash( sanitize_url( wp_unslash( $post_data[ $keyed ] ) ) ) ); } } @@ -332,15 +346,15 @@ $keyed = '_format_' . $key; if ( isset( $post_data[ $keyed ] ) ) { if ( current_user_can( 'unfiltered_html' ) ) { - update_post_meta( $post_ID, $keyed, $post_data[ $keyed ] ); + update_post_meta( $post_id, $keyed, $post_data[ $keyed ] ); } else { - update_post_meta( $post_ID, $keyed, wp_filter_post_kses( $post_data[ $keyed ] ) ); + update_post_meta( $post_id, $keyed, wp_filter_post_kses( $post_data[ $keyed ] ) ); } } } if ( 'attachment' === $post_data['post_type'] && preg_match( '#^(audio|video)/#', $post_data['post_mime_type'] ) ) { - $id3data = wp_get_attachment_metadata( $post_ID ); + $id3data = wp_get_attachment_metadata( $post_id ); if ( ! is_array( $id3data ) ) { $id3data = array(); } @@ -350,7 +364,7 @@ $id3data[ $key ] = sanitize_text_field( wp_unslash( $post_data[ 'id3_' . $key ] ) ); } } - wp_update_attachment_metadata( $post_ID, $id3data ); + wp_update_attachment_metadata( $post_id, $id3data ); } // Meta stuff. @@ -360,15 +374,23 @@ if ( ! $meta ) { continue; } - if ( $meta->post_id != $post_ID ) { + + if ( (int) $meta->post_id !== $post_id ) { continue; } - if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'edit_post_meta', $post_ID, $meta->meta_key ) ) { + + if ( is_protected_meta( $meta->meta_key, 'post' ) + || ! current_user_can( 'edit_post_meta', $post_id, $meta->meta_key ) + ) { continue; } - if ( is_protected_meta( $value['key'], 'post' ) || ! current_user_can( 'edit_post_meta', $post_ID, $value['key'] ) ) { + + if ( is_protected_meta( $value['key'], 'post' ) + || ! current_user_can( 'edit_post_meta', $post_id, $value['key'] ) + ) { continue; } + update_meta( $key, $value['key'], $value['value'] ); } } @@ -379,12 +401,17 @@ if ( ! $meta ) { continue; } - if ( $meta->post_id != $post_ID ) { + + if ( (int) $meta->post_id !== $post_id ) { continue; } - if ( is_protected_meta( $meta->meta_key, 'post' ) || ! current_user_can( 'delete_post_meta', $post_ID, $meta->meta_key ) ) { + + if ( is_protected_meta( $meta->meta_key, 'post' ) + || ! current_user_can( 'delete_post_meta', $post_id, $meta->meta_key ) + ) { continue; } + delete_meta( $key ); } } @@ -394,15 +421,15 @@ if ( isset( $post_data['_wp_attachment_image_alt'] ) ) { $image_alt = wp_unslash( $post_data['_wp_attachment_image_alt'] ); - if ( get_post_meta( $post_ID, '_wp_attachment_image_alt', true ) !== $image_alt ) { + if ( get_post_meta( $post_id, '_wp_attachment_image_alt', true ) !== $image_alt ) { $image_alt = wp_strip_all_tags( $image_alt, true ); // update_post_meta() expects slashed. - update_post_meta( $post_ID, '_wp_attachment_image_alt', wp_slash( $image_alt ) ); + update_post_meta( $post_id, '_wp_attachment_image_alt', wp_slash( $image_alt ) ); } } - $attachment_data = isset( $post_data['attachments'][ $post_ID ] ) ? $post_data['attachments'][ $post_ID ] : array(); + $attachment_data = isset( $post_data['attachments'][ $post_id ] ) ? $post_data['attachments'][ $post_id ] : array(); /** This filter is documented in wp-admin/includes/media.php */ $translated = apply_filters( 'attachment_fields_to_save', $translated, $attachment_data ); @@ -419,13 +446,13 @@ } } - add_meta( $post_ID ); - - update_post_meta( $post_ID, '_edit_last', get_current_user_id() ); + add_meta( $post_id ); + + update_post_meta( $post_id, '_edit_last', get_current_user_id() ); $success = wp_update_post( $translated ); - // If the save failed, see if we can sanity check the main fields and try again. + // If the save failed, see if we can confidence check the main fields and try again. if ( ! $success && is_callable( array( $wpdb, 'strip_invalid_text_for_column' ) ) ) { $fields = array( 'post_title', 'post_content', 'post_excerpt' ); @@ -439,19 +466,19 @@ } // Now that we have an ID we can fix any attachment anchor hrefs. - _fix_attachment_links( $post_ID ); - - wp_set_post_lock( $post_ID ); + _fix_attachment_links( $post_id ); + + wp_set_post_lock( $post_id ); if ( current_user_can( $ptype->cap->edit_others_posts ) && current_user_can( $ptype->cap->publish_posts ) ) { if ( ! empty( $post_data['sticky'] ) ) { - stick_post( $post_ID ); + stick_post( $post_id ); } else { - unstick_post( $post_ID ); + unstick_post( $post_id ); } } - return $post_ID; + return $post_id; } /** @@ -489,7 +516,7 @@ } } - if ( -1 == $post_data['_status'] ) { + if ( '-1' === $post_data['_status'] ) { $post_data['post_status'] = null; unset( $post_data['post_status'] ); } else { @@ -505,7 +532,7 @@ } } - $post_IDs = array_map( 'intval', (array) $post_data['post'] ); + $post_ids = array_map( 'intval', (array) $post_data['post'] ); $reset = array( 'post_author', @@ -523,7 +550,7 @@ ); foreach ( $reset as $field ) { - if ( isset( $post_data[ $field ] ) && ( '' === $post_data[ $field ] || -1 == $post_data[ $field ] ) ) { + if ( isset( $post_data[ $field ] ) && ( '' === $post_data[ $field ] || '-1' === $post_data[ $field ] ) ) { unset( $post_data[ $field ] ); } } @@ -542,6 +569,7 @@ if ( empty( $terms ) ) { continue; } + if ( is_taxonomy_hierarchical( $tax_name ) ) { $tax_input[ $tax_name ] = array_map( 'absint', $terms ); } else { @@ -576,29 +604,35 @@ $locked = array(); $shared_post_data = $post_data; - foreach ( $post_IDs as $post_ID ) { + foreach ( $post_ids as $post_id ) { // Start with fresh post data with each iteration. $post_data = $shared_post_data; - $post_type_object = get_post_type_object( get_post_type( $post_ID ) ); + $post_type_object = get_post_type_object( get_post_type( $post_id ) ); if ( ! isset( $post_type_object ) - || ( isset( $children ) && in_array( $post_ID, $children, true ) ) - || ! current_user_can( 'edit_post', $post_ID ) + || ( isset( $children ) && in_array( $post_id, $children, true ) ) + || ! current_user_can( 'edit_post', $post_id ) ) { - $skipped[] = $post_ID; + $skipped[] = $post_id; + continue; + } + + if ( wp_check_post_lock( $post_id ) ) { + $locked[] = $post_id; continue; } - if ( wp_check_post_lock( $post_ID ) ) { - $locked[] = $post_ID; - continue; - } - - $post = get_post( $post_ID ); + $post = get_post( $post_id ); $tax_names = get_object_taxonomies( $post ); + foreach ( $tax_names as $tax_name ) { $taxonomy_obj = get_taxonomy( $tax_name ); + + if ( ! $taxonomy_obj->show_in_quick_edit ) { + continue; + } + if ( isset( $tax_input[ $tax_name ] ) && current_user_can( $taxonomy_obj->cap->assign_terms ) ) { $new_terms = $tax_input[ $tax_name ]; } else { @@ -606,21 +640,34 @@ } if ( $taxonomy_obj->hierarchical ) { - $current_terms = (array) wp_get_object_terms( $post_ID, $tax_name, array( 'fields' => 'ids' ) ); + $current_terms = (array) wp_get_object_terms( $post_id, $tax_name, array( 'fields' => 'ids' ) ); } else { - $current_terms = (array) wp_get_object_terms( $post_ID, $tax_name, array( 'fields' => 'names' ) ); + $current_terms = (array) wp_get_object_terms( $post_id, $tax_name, array( 'fields' => 'names' ) ); } $post_data['tax_input'][ $tax_name ] = array_merge( $current_terms, $new_terms ); } if ( isset( $new_cats ) && in_array( 'category', $tax_names, true ) ) { - $cats = (array) wp_get_post_categories( $post_ID ); - $post_data['post_category'] = array_unique( array_merge( $cats, $new_cats ) ); + $cats = (array) wp_get_post_categories( $post_id ); + + if ( + isset( $post_data['indeterminate_post_category'] ) + && is_array( $post_data['indeterminate_post_category'] ) + ) { + $indeterminate_post_category = $post_data['indeterminate_post_category']; + } else { + $indeterminate_post_category = array(); + } + + $indeterminate_cats = array_intersect( $cats, $indeterminate_post_category ); + $determinate_cats = array_diff( $new_cats, $indeterminate_post_category ); + $post_data['post_category'] = array_unique( array_merge( $indeterminate_cats, $determinate_cats ) ); + unset( $post_data['tax_input']['category'] ); } - $post_data['post_ID'] = $post_ID; + $post_data['post_ID'] = $post_id; $post_data['post_type'] = $post->post_type; $post_data['post_mime_type'] = $post->post_mime_type; @@ -632,31 +679,50 @@ $post_data = _wp_translate_postdata( true, $post_data ); if ( is_wp_error( $post_data ) ) { - $skipped[] = $post_ID; + $skipped[] = $post_id; continue; } $post_data = _wp_get_allowed_postdata( $post_data ); if ( isset( $shared_post_data['post_format'] ) ) { - set_post_format( $post_ID, $shared_post_data['post_format'] ); + set_post_format( $post_id, $shared_post_data['post_format'] ); } // Prevent wp_insert_post() from overwriting post format with the old data. unset( $post_data['tax_input']['post_format'] ); + // Reset post date of scheduled post to be published. + if ( + in_array( $post->post_status, array( 'future', 'draft' ), true ) && + 'publish' === $post_data['post_status'] + ) { + $post_data['post_date'] = current_time( 'mysql' ); + $post_data['post_date_gmt'] = ''; + } + $post_id = wp_update_post( $post_data ); update_post_meta( $post_id, '_edit_last', get_current_user_id() ); $updated[] = $post_id; if ( isset( $post_data['sticky'] ) && current_user_can( $ptype->cap->edit_others_posts ) ) { if ( 'sticky' === $post_data['sticky'] ) { - stick_post( $post_ID ); + stick_post( $post_id ); } else { - unstick_post( $post_ID ); + unstick_post( $post_id ); } } } + /** + * Fires after processing the post data for bulk edit. + * + * @since 6.3.0 + * + * @param int[] $updated An array of updated post IDs. + * @param array $shared_post_data Associative array containing the post data. + */ + do_action( 'bulk_edit_posts', $updated, $shared_post_data ); + return array( 'updated' => $updated, 'skipped' => $skipped, @@ -710,7 +776,7 @@ wp_schedule_event( time(), 'daily', 'wp_scheduled_auto_draft_delete' ); } } else { - $post = new stdClass; + $post = new stdClass(); $post->ID = 0; $post->post_author = ''; $post->post_date = ''; @@ -881,25 +947,25 @@ $translated = _wp_get_allowed_postdata( $translated ); // Create the post. - $post_ID = wp_insert_post( $translated ); - if ( is_wp_error( $post_ID ) ) { - return $post_ID; + $post_id = wp_insert_post( $translated ); + if ( is_wp_error( $post_id ) ) { + return $post_id; } - if ( empty( $post_ID ) ) { + if ( empty( $post_id ) ) { return 0; } - add_meta( $post_ID ); - - add_post_meta( $post_ID, '_edit_last', $GLOBALS['current_user']->ID ); + add_meta( $post_id ); + + add_post_meta( $post_id, '_edit_last', $GLOBALS['current_user']->ID ); // Now that we have an ID we can fix any attachment anchor hrefs. - _fix_attachment_links( $post_ID ); - - wp_set_post_lock( $post_ID ); - - return $post_ID; + _fix_attachment_links( $post_id ); + + wp_set_post_lock( $post_id ); + + return $post_id; } /** @@ -927,11 +993,11 @@ * * @since 1.2.0 * - * @param int $post_ID + * @param int $post_id * @return int|bool */ -function add_meta( $post_ID ) { - $post_ID = (int) $post_ID; +function add_meta( $post_id ) { + $post_id = (int) $post_id; $metakeyselect = isset( $_POST['metakeyselect'] ) ? wp_unslash( trim( $_POST['metakeyselect'] ) ) : ''; $metakeyinput = isset( $_POST['metakeyinput'] ) ? wp_unslash( trim( $_POST['metakeyinput'] ) ) : ''; @@ -953,13 +1019,13 @@ $metakey = $metakeyinput; // Default. } - if ( is_protected_meta( $metakey, 'post' ) || ! current_user_can( 'add_post_meta', $post_ID, $metakey ) ) { + if ( is_protected_meta( $metakey, 'post' ) || ! current_user_can( 'add_post_meta', $post_id, $metakey ) ) { return false; } $metakey = wp_slash( $metakey ); - return add_post_meta( $post_ID, $metakey, $metavalue ); + return add_post_meta( $post_id, $metakey, $metavalue ); } return false; @@ -990,11 +1056,10 @@ global $wpdb; $keys = $wpdb->get_col( - " - SELECT meta_key - FROM $wpdb->postmeta - GROUP BY meta_key - ORDER BY meta_key" + "SELECT meta_key + FROM $wpdb->postmeta + GROUP BY meta_key + ORDER BY meta_key" ); return $keys; @@ -1019,7 +1084,7 @@ * * @global wpdb $wpdb WordPress database abstraction object. * - * @param int $postid A post ID. + * @param int $post_id A post ID. * @return array[] { * Array of meta data arrays for the given post ID. * @@ -1033,7 +1098,7 @@ * } * } */ -function has_meta( $postid ) { +function has_meta( $post_id ) { global $wpdb; return $wpdb->get_results( @@ -1041,7 +1106,7 @@ "SELECT meta_key, meta_value, meta_id, post_id FROM $wpdb->postmeta WHERE post_id = %d ORDER BY meta_key,meta_id", - $postid + $post_id ), ARRAY_A ); @@ -1074,7 +1139,7 @@ * @since 2.3.0 * @access private * - * @param int|object $post Post ID or post object. + * @param int|WP_Post $post Post ID or post object. * @return void|int|WP_Error Void if nothing fixed. 0 or WP_Error on update failure. The post ID on update success. */ function _fix_attachment_links( $post ) { @@ -1106,7 +1171,7 @@ $url_id = (int) $url_match[2]; $rel_id = (int) $rel_match[1]; - if ( ! $url_id || ! $rel_id || $url_id != $rel_id || strpos( $url_match[0], $site_url ) === false ) { + if ( ! $url_id || ! $rel_id || $url_id != $rel_id || ! str_contains( $url_match[0], $site_url ) ) { continue; } @@ -1134,9 +1199,9 @@ * @return string[] An array of all the statuses for the supplied post type. */ function get_available_post_statuses( $type = 'post' ) { - $stati = wp_count_posts( $type ); - - return array_keys( get_object_vars( $stati ) ); + $statuses = wp_count_posts( $type ); + + return array_keys( get_object_vars( $statuses ) ); } /** @@ -1152,9 +1217,11 @@ if ( false === $q ) { $q = $_GET; } - $q['m'] = isset( $q['m'] ) ? (int) $q['m'] : 0; - $q['cat'] = isset( $q['cat'] ) ? (int) $q['cat'] : 0; - $post_stati = get_post_stati(); + + $q['m'] = isset( $q['m'] ) ? (int) $q['m'] : 0; + $q['cat'] = isset( $q['cat'] ) ? (int) $q['cat'] : 0; + + $post_statuses = get_post_stati(); if ( isset( $q['post_type'] ) && in_array( $q['post_type'], get_post_types(), true ) ) { $post_type = $q['post_type']; @@ -1166,7 +1233,7 @@ $post_status = ''; $perm = ''; - if ( isset( $q['post_status'] ) && in_array( $q['post_status'], $post_stati, true ) ) { + if ( isset( $q['post_status'] ) && in_array( $q['post_status'], $post_statuses, true ) ) { $post_status = $q['post_status']; $perm = 'readable'; } @@ -1302,7 +1369,7 @@ // Filter query clauses to include filenames. if ( isset( $q['s'] ) ) { - add_filter( 'posts_clauses', '_filter_query_attachment_filenames' ); + add_filter( 'wp_allow_query_attachment_by_filename', '__return_true' ); } return $q; @@ -1337,7 +1404,7 @@ * @return string Space-separated string of class names. */ function postbox_classes( $box_id, $screen_id ) { - if ( isset( $_GET['edit'] ) && $_GET['edit'] == $box_id ) { + if ( isset( $_GET['edit'] ) && $_GET['edit'] === $box_id ) { $classes = array( '' ); } elseif ( get_user_option( 'closedpostboxes_' . $screen_id ) ) { $closed = get_user_option( 'closedpostboxes_' . $screen_id ); @@ -1370,7 +1437,7 @@ * * @since 2.5.0 * - * @param int|WP_Post $id Post ID or post object. + * @param int|WP_Post $post Post ID or post object. * @param string|null $title Optional. Title to override the post's current title * when generating the post name. Default null. * @param string|null $name Optional. Name to override the post name. Default null. @@ -1381,8 +1448,9 @@ * @type string $1 The post name. * } */ -function get_sample_permalink( $id, $title = null, $name = null ) { - $post = get_post( $id ); +function get_sample_permalink( $post, $title = null, $name = null ) { + $post = get_post( $post ); + if ( ! $post ) { return array( '', '' ); } @@ -1392,15 +1460,18 @@ $original_status = $post->post_status; $original_date = $post->post_date; $original_name = $post->post_name; + $original_filter = $post->filter; // Hack: get_permalink() would return plain permalink for drafts, so we will fake that our post is published. - if ( in_array( $post->post_status, array( 'draft', 'pending', 'future' ), true ) ) { + if ( in_array( $post->post_status, array( 'auto-draft', 'draft', 'pending', 'future' ), true ) ) { $post->post_status = 'publish'; $post->post_name = sanitize_title( $post->post_name ? $post->post_name : $post->post_title, $post->ID ); } - // If the user wants to set a new name -- override the current one. - // Note: if empty name is supplied -- use the title instead, see #6072. + /* + * If the user wants to set a new name -- override the current one. + * Note: if empty name is supplied -- use the title instead, see #6072. + */ if ( ! is_null( $name ) ) { $post->post_name = sanitize_title( $name ? $name : $title, $post->ID ); } @@ -1436,7 +1507,7 @@ $post->post_status = $original_status; $post->post_date = $original_date; $post->post_name = $original_name; - unset( $post->filter ); + $post->filter = $original_filter; /** * Filters the sample permalink. @@ -1462,13 +1533,14 @@ * * @since 2.5.0 * - * @param int|WP_Post $id Post ID or post object. + * @param int|WP_Post $post Post ID or post object. * @param string|null $new_title Optional. New title. Default null. * @param string|null $new_slug Optional. New slug. Default null. * @return string The HTML of the sample permalink slug editor. */ -function get_sample_permalink_html( $id, $new_title = null, $new_slug = null ) { - $post = get_post( $id ); +function get_sample_permalink_html( $post, $new_title = null, $new_slug = null ) { + $post = get_post( $post ); + if ( ! $post ) { return ''; } @@ -1493,7 +1565,7 @@ } // Permalinks without a post/page name placeholder don't have anything to edit. - if ( false === strpos( $permalink, '%postname%' ) && false === strpos( $permalink, '%pagename%' ) ) { + if ( ! str_contains( $permalink, '%postname%' ) && ! str_contains( $permalink, '%pagename%' ) ) { $return = '' . __( 'Permalink:' ) . "\n"; if ( false !== $view_link ) { @@ -1505,7 +1577,7 @@ // Encourage a pretty permalink setting. if ( ! get_option( 'permalink_structure' ) && current_user_can( 'manage_options' ) - && ! ( 'page' === get_option( 'show_on_front' ) && get_option( 'page_on_front' ) == $id ) + && ! ( 'page' === get_option( 'show_on_front' ) && (int) get_option( 'page_on_front' ) === $post->ID ) ) { $return .= '' . __( 'Change Permalink Structure' ) . "\n"; } @@ -1532,11 +1604,11 @@ * @since 2.9.0 * @since 4.4.0 Added `$post` parameter. * - * @param string $return Sample permalink HTML markup. - * @param int $post_id Post ID. - * @param string $new_title New sample permalink title. - * @param string $new_slug New sample permalink slug. - * @param WP_Post $post Post object. + * @param string $return Sample permalink HTML markup. + * @param int $post_id Post ID. + * @param string|null $new_title New sample permalink title. + * @param string|null $new_slug New sample permalink slug. + * @param WP_Post $post Post object. */ $return = apply_filters( 'get_sample_permalink_html', $return, $post->ID, $new_title, $new_slug, $post ); @@ -1622,24 +1694,26 @@ * * @since 2.5.0 * - * @param int|WP_Post $post_id ID or object of the post to check for editing. + * @param int|WP_Post $post ID or object of the post to check for editing. * @return int|false ID of the user with lock. False if the post does not exist, post is not locked, * the user with lock does not exist, or the post is locked by current user. */ -function wp_check_post_lock( $post_id ) { - $post = get_post( $post_id ); +function wp_check_post_lock( $post ) { + $post = get_post( $post ); + if ( ! $post ) { return false; } $lock = get_post_meta( $post->ID, '_edit_lock', true ); + if ( ! $lock ) { return false; } $lock = explode( ':', $lock ); $time = $lock[0]; - $user = isset( $lock[1] ) ? $lock[1] : get_post_meta( $post->ID, '_edit_last', true ); + $user = isset( $lock[1] ) ? (int) $lock[1] : (int) get_post_meta( $post->ID, '_edit_last', true ); if ( ! get_userdata( $user ) ) { return false; @@ -1648,7 +1722,7 @@ /** This filter is documented in wp-admin/includes/ajax-actions.php */ $time_window = apply_filters( 'wp_check_post_lock_window', 150 ); - if ( $time && $time > time() - $time_window && get_current_user_id() != $user ) { + if ( $time && $time > time() - $time_window && get_current_user_id() !== $user ) { return $user; } @@ -1660,7 +1734,7 @@ * * @since 2.5.0 * - * @param int|WP_Post $post_id ID or object of the post being edited. + * @param int|WP_Post $post ID or object of the post being edited. * @return array|false { * Array of the lock time and user ID. False if the post does not exist, or there * is no current user. @@ -1669,14 +1743,16 @@ * @type int $1 The ID of the current user. * } */ -function wp_set_post_lock( $post_id ) { - $post = get_post( $post_id ); +function wp_set_post_lock( $post ) { + $post = get_post( $post ); + if ( ! $post ) { return false; } $user_id = get_current_user_id(); - if ( 0 == $user_id ) { + + if ( 0 === $user_id ) { return false; } @@ -1695,12 +1771,14 @@ */ function _admin_notice_post_locked() { $post = get_post(); + if ( ! $post ) { return; } $user = null; $user_id = wp_check_post_lock( $post->ID ); + if ( $user_id ) { $user = get_userdata( $user_id ); } @@ -1727,7 +1805,7 @@ } $sendback = wp_get_referer(); - if ( $locked && $sendback && false === strpos( $sendback, 'post.php' ) && false === strpos( $sendback, 'post-new.php' ) ) { + if ( $locked && $sendback && ! str_contains( $sendback, 'post.php' ) && ! str_contains( $sendback, 'post-new.php' ) ) { $sendback_text = __( 'Go back' ); } else { @@ -1751,7 +1829,7 @@ if ( $locked ) { $query_args = array(); if ( get_post_type_object( $post->post_type )->public ) { - if ( 'publish' === $post->post_status || $user->ID != $post->post_author ) { + if ( 'publish' === $post->post_status || $user->ID !== (int) $post->post_author ) { // Latest content is in autosave. $nonce = wp_create_nonce( 'post_preview_' . $post->ID ); $query_args['preview_id'] = $post->ID; @@ -1902,11 +1980,12 @@ * Fires before an autosave is stored. * * @since 4.1.0 + * @since 6.4.0 The `$is_update` parameter was added to indicate if the autosave is being updated or was newly created. * * @param array $new_autosave Post array - the autosave that is about to be saved. + * @param bool $is_update Whether this is an existing autosave. */ - do_action( 'wp_creating_autosave', $new_autosave ); - + do_action( 'wp_creating_autosave', $new_autosave, true ); return wp_update_post( $new_autosave ); } @@ -1914,7 +1993,68 @@ $post_data = wp_unslash( $post_data ); // Otherwise create the new autosave as a special post revision. - return _wp_put_post_revision( $post_data, true ); + $revision = _wp_put_post_revision( $post_data, true ); + + if ( ! is_wp_error( $revision ) && 0 !== $revision ) { + + /** This action is documented in wp-admin/includes/post.php */ + do_action( 'wp_creating_autosave', get_post( $revision, ARRAY_A ), false ); + } + + return $revision; +} + +/** + * Autosave the revisioned meta fields. + * + * Iterates through the revisioned meta fields and checks each to see if they are set, + * and have a changed value. If so, the meta value is saved and attached to the autosave. + * + * @since 6.4.0 + * + * @param array $new_autosave The new post data being autosaved. + */ +function wp_autosave_post_revisioned_meta_fields( $new_autosave ) { + /* + * The post data arrives as either $_POST['data']['wp_autosave'] or the $_POST + * itself. This sets $posted_data to the correct variable. + * + * Ignoring sanitization to avoid altering meta. Ignoring the nonce check because + * this is hooked on inner core hooks where a valid nonce was already checked. + */ + $posted_data = isset( $_POST['data']['wp_autosave'] ) ? $_POST['data']['wp_autosave'] : $_POST; + + $post_type = get_post_type( $new_autosave['post_parent'] ); + + /* + * Go thru the revisioned meta keys and save them as part of the autosave, if + * the meta key is part of the posted data, the meta value is not blank and + * the the meta value has changes from the last autosaved value. + */ + foreach ( wp_post_revision_meta_keys( $post_type ) as $meta_key ) { + + if ( + isset( $posted_data[ $meta_key ] ) && + get_post_meta( $new_autosave['ID'], $meta_key, true ) !== wp_unslash( $posted_data[ $meta_key ] ) + ) { + /* + * Use the underlying delete_metadata() and add_metadata() functions + * vs delete_post_meta() and add_post_meta() to make sure we're working + * with the actual revision meta. + */ + delete_metadata( 'post', $new_autosave['ID'], $meta_key ); + + /* + * One last check to ensure meta value not empty(). + */ + if ( ! empty( $posted_data[ $meta_key ] ) ) { + /* + * Add the revisions meta data to the autosave. + */ + add_metadata( 'post', $new_autosave['ID'], $meta_key, $posted_data[ $meta_key ] ); + } + } + } } /** @@ -1926,10 +2066,11 @@ */ function post_preview() { - $post_ID = (int) $_POST['post_ID']; - $_POST['ID'] = $post_ID; - - $post = get_post( $post_ID ); + $post_id = (int) $_POST['post_ID']; + $_POST['ID'] = $post_id; + + $post = get_post( $post_id ); + if ( ! $post ) { wp_die( __( 'Sorry, you are not allowed to edit this post.' ) ); } @@ -1940,7 +2081,7 @@ $is_autosave = false; - if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() == $post->post_author + if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() === (int) $post->post_author && ( 'draft' === $post->post_status || 'auto-draft' === $post->post_status ) ) { $saved_post_id = edit_post(); @@ -2015,14 +2156,16 @@ $post_data['post_category'] = explode( ',', $post_data['catslist'] ); } - if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() == $post->post_author + if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() === (int) $post->post_author && ( 'auto-draft' === $post->post_status || 'draft' === $post->post_status ) ) { // Drafts and auto-drafts are just overwritten by autosave for the same user if the post is not locked. return edit_post( wp_slash( $post_data ) ); } else { - // Non-drafts or other users' drafts are not overwritten. - // The autosave is stored in a special post revision for each user. + /* + * Non-drafts or other users' drafts are not overwritten. + * The autosave is stored in a special post revision for each user. + */ return wp_create_post_autosave( wp_slash( $post_data ) ); } } @@ -2038,19 +2181,19 @@ if ( isset( $_POST['save'] ) || isset( $_POST['publish'] ) ) { $status = get_post_status( $post_id ); - if ( isset( $_POST['publish'] ) ) { - switch ( $status ) { - case 'pending': - $message = 8; - break; - case 'future': - $message = 9; - break; - default: - $message = 6; - } - } else { - $message = 'draft' === $status ? 10 : 1; + switch ( $status ) { + case 'pending': + $message = 8; + break; + case 'future': + $message = 9; + break; + case 'draft': + $message = 10; + break; + default: + $message = isset( $_POST['publish'] ) ? 6 : 1; + break; } $location = add_query_arg( 'message', $message, get_edit_post_link( $post_id, 'url' ) ); @@ -2142,82 +2285,14 @@ } /** - * Returns whether the post can be edited in the block editor. - * - * @since 5.0.0 - * - * @param int|WP_Post $post Post ID or WP_Post object. - * @return bool Whether the post can be edited in the block editor. - */ -function use_block_editor_for_post( $post ) { - $post = get_post( $post ); - - if ( ! $post ) { - return false; - } - - // We're in the meta box loader, so don't use the block editor. - if ( isset( $_GET['meta-box-loader'] ) ) { - check_admin_referer( 'meta-box-loader', 'meta-box-loader-nonce' ); - return false; - } - - $use_block_editor = use_block_editor_for_post_type( $post->post_type ); - - /** - * Filters whether a post is able to be edited in the block editor. - * - * @since 5.0.0 - * - * @param bool $use_block_editor Whether the post can be edited or not. - * @param WP_Post $post The post being checked. - */ - return apply_filters( 'use_block_editor_for_post', $use_block_editor, $post ); -} - -/** - * Returns whether a post type is compatible with the block editor. - * - * The block editor depends on the REST API, and if the post type is not shown in the - * REST API, then it won't work with the block editor. - * - * @since 5.0.0 - * - * @param string $post_type The post type. - * @return bool Whether the post type can be edited with the block editor. - */ -function use_block_editor_for_post_type( $post_type ) { - if ( ! post_type_exists( $post_type ) ) { - return false; - } - - if ( ! post_type_supports( $post_type, 'editor' ) ) { - return false; - } - - $post_type_object = get_post_type_object( $post_type ); - if ( $post_type_object && ! $post_type_object->show_in_rest ) { - return false; - } - - /** - * Filters whether a post is able to be edited in the block editor. - * - * @since 5.0.0 - * - * @param bool $use_block_editor Whether the post type can be edited or not. Default true. - * @param string $post_type The post type being checked. - */ - return apply_filters( 'use_block_editor_for_post_type', true, $post_type ); -} - -/** * Prepares server-registered blocks for the block editor. * * Returns an associative array of registered block data keyed by block name. Data includes properties * of a block relevant for client registration. * * @since 5.0.0 + * @since 6.3.0 Added `selectors` field. + * @since 6.4.0 Added `block_hooks` field. * * @return array An associative array of registered block data. */ @@ -2232,6 +2307,8 @@ 'attributes' => 'attributes', 'provides_context' => 'providesContext', 'uses_context' => 'usesContext', + 'block_hooks' => 'blockHooks', + 'selectors' => 'selectors', 'supports' => 'supports', 'category' => 'category', 'styles' => 'styles', @@ -2241,6 +2318,7 @@ 'keywords' => 'keywords', 'example' => 'example', 'variations' => 'variations', + 'allowed_blocks' => 'allowedBlocks', ); foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) { @@ -2264,6 +2342,10 @@ * Renders the meta boxes forms. * * @since 5.0.0 + * + * @global WP_Post $post Global post object. + * @global WP_Screen $current_screen WordPress current screen object. + * @global array $wp_meta_boxes Global meta box state. */ function the_block_editor_meta_boxes() { global $post, $current_screen, $wp_meta_boxes; @@ -2326,7 +2408,7 @@ $meta_boxes = (array) $wp_meta_boxes[ $current_screen->id ][ $location ][ $priority ]; foreach ( $meta_boxes as $meta_box ) { - if ( false == $meta_box || ! $meta_box['title'] ) { + if ( false === $meta_box || ! $meta_box['title'] ) { continue; } @@ -2389,6 +2471,50 @@ wp_add_inline_script( 'wp-lists', $script ); } + /* + * Refresh nonces used by the meta box loader. + * + * The logic is very similar to that provided by post.js for the classic editor. + */ + $script = "( function( $ ) { + var check, timeout; + + function schedule() { + check = false; + window.clearTimeout( timeout ); + timeout = window.setTimeout( function() { check = true; }, 300000 ); + } + + $( document ).on( 'heartbeat-send.wp-refresh-nonces', function( e, data ) { + var post_id, \$authCheck = $( '#wp-auth-check-wrap' ); + + if ( check || ( \$authCheck.length && ! \$authCheck.hasClass( 'hidden' ) ) ) { + if ( ( post_id = $( '#post_ID' ).val() ) && $( '#_wpnonce' ).val() ) { + data['wp-refresh-metabox-loader-nonces'] = { + post_id: post_id + }; + } + } + }).on( 'heartbeat-tick.wp-refresh-nonces', function( e, data ) { + var nonces = data['wp-refresh-metabox-loader-nonces']; + + if ( nonces ) { + if ( nonces.replace ) { + if ( nonces.replace.metabox_loader_nonce && window._wpMetaBoxUrl && wp.url ) { + window._wpMetaBoxUrl= wp.url.addQueryArgs( window._wpMetaBoxUrl, { 'meta-box-loader-nonce': nonces.replace.metabox_loader_nonce } ); + } + + if ( nonces.replace._wpnonce ) { + $( '#_wpnonce' ).val( nonces.replace._wpnonce ); + } + } + } + }).ready( function() { + schedule(); + }); + } )( jQuery );"; + wp_add_inline_script( 'heartbeat', $script ); + // Reset meta box data. $wp_meta_boxes = $_original_meta_boxes; } @@ -2428,7 +2554,7 @@ $classic_elements = wp_html_split( $classic_output ); $hidden_inputs = ''; foreach ( $classic_elements as $element ) { - if ( 0 !== strpos( $element, '