diff -r 7b1b88e27a20 -r 48c4eec2b7e6 wp/wp-includes/post.php --- a/wp/wp-includes/post.php Thu Sep 29 08:06:27 2022 +0200 +++ b/wp/wp-includes/post.php Fri Sep 05 18:40:08 2025 +0200 @@ -75,9 +75,9 @@ 'labels' => array( 'name' => _x( 'Media', 'post type general name' ), 'name_admin_bar' => _x( 'Media', 'add new from admin bar' ), - 'add_new' => _x( 'Add New', 'add new media' ), + 'add_new' => __( 'Add New Media File' ), 'edit_item' => __( 'Edit Media' ), - 'view_item' => __( 'View Attachment Page' ), + 'view_item' => ( '1' === get_option( 'wp_attachment_pages_enabled' ) ) ? __( 'View Attachment Page' ) : __( 'View Media File' ), 'attributes' => __( 'Attachment Attributes' ), ), 'public' => true, @@ -202,7 +202,7 @@ 'labels' => array( 'name' => _x( 'Changesets', 'post type general name' ), 'singular_name' => _x( 'Changeset', 'post type singular name' ), - 'add_new' => _x( 'Add New', 'Customize Changeset' ), + 'add_new' => __( 'Add New Changeset' ), 'add_new_item' => __( 'Add New Changeset' ), 'new_item' => __( 'New Changeset' ), 'edit_item' => __( 'Edit Changeset' ), @@ -282,25 +282,26 @@ 'wp_block', array( 'labels' => array( - 'name' => _x( 'Reusable blocks', 'post type general name' ), - 'singular_name' => _x( 'Reusable block', 'post type singular name' ), - 'add_new' => _x( 'Add New', 'Reusable block' ), - 'add_new_item' => __( 'Add new Reusable block' ), - 'new_item' => __( 'New Reusable block' ), - 'edit_item' => __( 'Edit Reusable block' ), - 'view_item' => __( 'View Reusable block' ), - 'all_items' => __( 'All Reusable blocks' ), - 'search_items' => __( 'Search Reusable blocks' ), - 'not_found' => __( 'No reusable blocks found.' ), - 'not_found_in_trash' => __( 'No reusable blocks found in Trash.' ), - 'filter_items_list' => __( 'Filter reusable blocks list' ), - 'items_list_navigation' => __( 'Reusable blocks list navigation' ), - 'items_list' => __( 'Reusable blocks list' ), - 'item_published' => __( 'Reusable block published.' ), - 'item_published_privately' => __( 'Reusable block published privately.' ), - 'item_reverted_to_draft' => __( 'Reusable block reverted to draft.' ), - 'item_scheduled' => __( 'Reusable block scheduled.' ), - 'item_updated' => __( 'Reusable block updated.' ), + 'name' => _x( 'Patterns', 'post type general name' ), + 'singular_name' => _x( 'Pattern', 'post type singular name' ), + 'add_new' => __( 'Add New Pattern' ), + 'add_new_item' => __( 'Add New Pattern' ), + 'new_item' => __( 'New Pattern' ), + 'edit_item' => __( 'Edit Block Pattern' ), + 'view_item' => __( 'View Pattern' ), + 'view_items' => __( 'View Patterns' ), + 'all_items' => __( 'All Patterns' ), + 'search_items' => __( 'Search Patterns' ), + 'not_found' => __( 'No patterns found.' ), + 'not_found_in_trash' => __( 'No patterns found in Trash.' ), + 'filter_items_list' => __( 'Filter patterns list' ), + 'items_list_navigation' => __( 'Patterns list navigation' ), + 'items_list' => __( 'Patterns list' ), + 'item_published' => __( 'Pattern published.' ), + 'item_published_privately' => __( 'Pattern published privately.' ), + 'item_reverted_to_draft' => __( 'Pattern reverted to draft.' ), + 'item_scheduled' => __( 'Pattern scheduled.' ), + 'item_updated' => __( 'Pattern updated.' ), ), 'public' => false, '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ @@ -319,25 +320,37 @@ 'edit_posts' => 'edit_posts', 'edit_published_posts' => 'edit_published_posts', 'delete_published_posts' => 'delete_published_posts', + // Enables trashing draft posts as well. + 'delete_posts' => 'delete_posts', 'edit_others_posts' => 'edit_others_posts', 'delete_others_posts' => 'delete_others_posts', ), 'map_meta_cap' => true, 'supports' => array( 'title', + 'excerpt', 'editor', 'revisions', + 'custom-fields', ), ) ); + $template_edit_link = 'site-editor.php?' . build_query( + array( + 'postType' => '%s', + 'postId' => '%s', + 'canvas' => 'edit', + ) + ); + register_post_type( 'wp_template', array( - 'labels' => array( + 'labels' => array( 'name' => _x( 'Templates', 'post type general name' ), 'singular_name' => _x( 'Template', 'post type singular name' ), - 'add_new' => _x( 'Add New', 'Template' ), + 'add_new' => __( 'Add New Template' ), 'add_new_item' => __( 'Add New Template' ), 'new_item' => __( 'New Template' ), 'edit_item' => __( 'Edit Template' ), @@ -353,19 +366,24 @@ 'filter_items_list' => __( 'Filter templates list' ), 'items_list_navigation' => __( 'Templates list navigation' ), 'items_list' => __( 'Templates list' ), + 'item_updated' => __( 'Template updated.' ), ), - 'description' => __( 'Templates to include in your theme.' ), - 'public' => false, - '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ - 'has_archive' => false, - 'show_ui' => false, - 'show_in_menu' => false, - 'show_in_rest' => true, - 'rewrite' => false, - 'rest_base' => 'templates', - 'rest_controller_class' => 'WP_REST_Templates_Controller', - 'capability_type' => array( 'template', 'templates' ), - 'capabilities' => array( + 'description' => __( 'Templates to include in your theme.' ), + 'public' => false, + '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ + '_edit_link' => $template_edit_link, /* internal use only. don't use this when registering your own post type. */ + 'has_archive' => false, + 'show_ui' => false, + 'show_in_menu' => false, + 'show_in_rest' => true, + 'rewrite' => false, + 'rest_base' => 'templates', + 'rest_controller_class' => 'WP_REST_Templates_Controller', + 'autosave_rest_controller_class' => 'WP_REST_Template_Autosaves_Controller', + 'revisions_rest_controller_class' => 'WP_REST_Template_Revisions_Controller', + 'late_route_registration' => true, + 'capability_type' => array( 'template', 'templates' ), + 'capabilities' => array( 'create_posts' => 'edit_theme_options', 'delete_posts' => 'edit_theme_options', 'delete_others_posts' => 'edit_theme_options', @@ -379,8 +397,8 @@ 'read' => 'edit_theme_options', 'read_private_posts' => 'edit_theme_options', ), - 'map_meta_cap' => true, - 'supports' => array( + 'map_meta_cap' => true, + 'supports' => array( 'title', 'slug', 'excerpt', @@ -394,10 +412,10 @@ register_post_type( 'wp_template_part', array( - 'labels' => array( + 'labels' => array( 'name' => _x( 'Template Parts', 'post type general name' ), 'singular_name' => _x( 'Template Part', 'post type singular name' ), - 'add_new' => _x( 'Add New', 'Template Part' ), + 'add_new' => __( 'Add New Template Part' ), 'add_new_item' => __( 'Add New Template Part' ), 'new_item' => __( 'New Template Part' ), 'edit_item' => __( 'Edit Template Part' ), @@ -413,19 +431,24 @@ 'filter_items_list' => __( 'Filter template parts list' ), 'items_list_navigation' => __( 'Template parts list navigation' ), 'items_list' => __( 'Template parts list' ), + 'item_updated' => __( 'Template part updated.' ), ), - 'description' => __( 'Template parts to include in your templates.' ), - 'public' => false, - '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ - 'has_archive' => false, - 'show_ui' => false, - 'show_in_menu' => false, - 'show_in_rest' => true, - 'rewrite' => false, - 'rest_base' => 'template-parts', - 'rest_controller_class' => 'WP_REST_Templates_Controller', - 'map_meta_cap' => true, - 'capabilities' => array( + 'description' => __( 'Template parts to include in your templates.' ), + 'public' => false, + '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ + '_edit_link' => $template_edit_link, /* internal use only. don't use this when registering your own post type. */ + 'has_archive' => false, + 'show_ui' => false, + 'show_in_menu' => false, + 'show_in_rest' => true, + 'rewrite' => false, + 'rest_base' => 'template-parts', + 'rest_controller_class' => 'WP_REST_Templates_Controller', + 'autosave_rest_controller_class' => 'WP_REST_Template_Autosaves_Controller', + 'revisions_rest_controller_class' => 'WP_REST_Template_Revisions_Controller', + 'late_route_registration' => true, + 'map_meta_cap' => true, + 'capabilities' => array( 'create_posts' => 'edit_theme_options', 'delete_posts' => 'edit_theme_options', 'delete_others_posts' => 'edit_theme_options', @@ -439,7 +462,7 @@ 'read' => 'edit_theme_options', 'read_private_posts' => 'edit_theme_options', ), - 'supports' => array( + 'supports' => array( 'title', 'slug', 'excerpt', @@ -453,14 +476,19 @@ register_post_type( 'wp_global_styles', array( - 'label' => _x( 'Global Styles', 'post type general name' ), - 'description' => __( 'Global styles to include in themes.' ), - 'public' => false, - '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ - 'show_ui' => false, - 'show_in_rest' => false, - 'rewrite' => false, - 'capabilities' => array( + 'label' => _x( 'Global Styles', 'post type general name' ), + 'description' => __( 'Global styles to include in themes.' ), + 'public' => false, + '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ + '_edit_link' => '/site-editor.php?canvas=edit', /* internal use only. don't use this when registering your own post type. */ + 'show_ui' => false, + 'show_in_rest' => true, + 'rewrite' => false, + 'rest_base' => 'global-styles', + 'rest_controller_class' => 'WP_REST_Global_Styles_Controller', + 'revisions_rest_controller_class' => 'WP_REST_Global_Styles_Revisions_Controller', + 'late_route_registration' => true, + 'capabilities' => array( 'read' => 'edit_theme_options', 'create_posts' => 'edit_theme_options', 'edit_posts' => 'edit_theme_options', @@ -469,14 +497,24 @@ 'edit_others_posts' => 'edit_theme_options', 'delete_others_posts' => 'edit_theme_options', ), - 'map_meta_cap' => true, - 'supports' => array( + 'map_meta_cap' => true, + 'supports' => array( 'title', 'editor', 'revisions', ), ) ); + // Disable autosave endpoints for global styles. + remove_post_type_support( 'wp_global_styles', 'autosave' ); + + $navigation_post_edit_link = 'site-editor.php?' . build_query( + array( + 'postId' => '%s', + 'postType' => 'wp_navigation', + 'canvas' => 'edit', + ) + ); register_post_type( 'wp_navigation', @@ -484,7 +522,7 @@ 'labels' => array( 'name' => _x( 'Navigation Menus', 'post type general name' ), 'singular_name' => _x( 'Navigation Menu', 'post type singular name' ), - 'add_new' => _x( 'Add New', 'Navigation Menu' ), + 'add_new' => __( 'Add New Navigation Menu' ), 'add_new_item' => __( 'Add New Navigation Menu' ), 'new_item' => __( 'New Navigation Menu' ), 'edit_item' => __( 'Edit Navigation Menu' ), @@ -504,6 +542,7 @@ 'description' => __( 'Navigation menus that can be inserted into your site.' ), 'public' => false, '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ + '_edit_link' => $navigation_post_edit_link, /* internal use only. don't use this when registering your own post type. */ 'has_archive' => false, 'show_ui' => true, 'show_in_menu' => false, @@ -534,6 +573,70 @@ ) ); + register_post_type( + 'wp_font_family', + array( + 'labels' => array( + 'name' => __( 'Font Families' ), + 'singular_name' => __( 'Font Family' ), + ), + 'public' => false, + '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ + 'hierarchical' => false, + 'capabilities' => array( + 'read' => 'edit_theme_options', + 'read_private_posts' => 'edit_theme_options', + 'create_posts' => 'edit_theme_options', + 'publish_posts' => 'edit_theme_options', + 'edit_posts' => 'edit_theme_options', + 'edit_others_posts' => 'edit_theme_options', + 'edit_published_posts' => 'edit_theme_options', + 'delete_posts' => 'edit_theme_options', + 'delete_others_posts' => 'edit_theme_options', + 'delete_published_posts' => 'edit_theme_options', + ), + 'map_meta_cap' => true, + 'query_var' => false, + 'rewrite' => false, + 'show_in_rest' => true, + 'rest_base' => 'font-families', + 'rest_controller_class' => 'WP_REST_Font_Families_Controller', + 'supports' => array( 'title' ), + ) + ); + + register_post_type( + 'wp_font_face', + array( + 'labels' => array( + 'name' => __( 'Font Faces' ), + 'singular_name' => __( 'Font Face' ), + ), + 'public' => false, + '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ + 'hierarchical' => false, + 'capabilities' => array( + 'read' => 'edit_theme_options', + 'read_private_posts' => 'edit_theme_options', + 'create_posts' => 'edit_theme_options', + 'publish_posts' => 'edit_theme_options', + 'edit_posts' => 'edit_theme_options', + 'edit_others_posts' => 'edit_theme_options', + 'edit_published_posts' => 'edit_theme_options', + 'delete_posts' => 'edit_theme_options', + 'delete_others_posts' => 'edit_theme_options', + 'delete_published_posts' => 'edit_theme_options', + ), + 'map_meta_cap' => true, + 'query_var' => false, + 'rewrite' => false, + 'show_in_rest' => true, + 'rest_base' => 'font-families/(?P[\d]+)/font-faces', + 'rest_controller_class' => 'WP_REST_Font_Faces_Controller', + 'supports' => array( 'title' ), + ) + ); + register_post_status( 'publish', array( @@ -703,28 +806,27 @@ } /** - * Retrieve attached file path based on attachment ID. - * - * By default the path will go through the 'get_attached_file' filter, but - * passing a true to the $unfiltered argument of get_attached_file() will - * return the file path unfiltered. - * - * The function works by getting the single post meta name, named - * '_wp_attached_file' and returning it. This is a convenience function to - * prevent looking up the meta name and provide a mechanism for sending the - * attached filename through a filter. + * Retrieves attached file path based on attachment ID. + * + * By default the path will go through the {@see 'get_attached_file'} filter, but + * passing `true` to the `$unfiltered` argument will return the file path unfiltered. + * + * The function works by retrieving the `_wp_attached_file` post meta value. + * This is a convenience function to prevent looking up the meta name and provide + * a mechanism for sending the attached filename through a filter. * * @since 2.0.0 * * @param int $attachment_id Attachment ID. - * @param bool $unfiltered Optional. Whether to apply filters. Default false. + * @param bool $unfiltered Optional. Whether to skip the {@see 'get_attached_file'} filter. + * Default false. * @return string|false The file path to where the attached file should be, false otherwise. */ function get_attached_file( $attachment_id, $unfiltered = false ) { $file = get_post_meta( $attachment_id, '_wp_attached_file', true ); // If the file is relative, prepend upload dir. - if ( $file && 0 !== strpos( $file, '/' ) && ! preg_match( '|^.:\\\|', $file ) ) { + if ( $file && ! str_starts_with( $file, '/' ) && ! preg_match( '|^.:\\\|', $file ) ) { $uploads = wp_get_upload_dir(); if ( false === $uploads['error'] ) { $file = $uploads['basedir'] . "/$file"; @@ -747,7 +849,7 @@ } /** - * Update attachment file path based on attachment ID. + * Updates attachment file path based on attachment ID. * * Used to update the file path of the attachment, which uses post meta name * '_wp_attached_file' to store the path of the attachment. @@ -782,7 +884,7 @@ } /** - * Return relative path to an uploaded file. + * Returns relative path to an uploaded file. * * The path is relative to the current upload dir. * @@ -796,7 +898,7 @@ $new_path = $path; $uploads = wp_get_upload_dir(); - if ( 0 === strpos( $new_path, $uploads['basedir'] ) ) { + if ( str_starts_with( $new_path, $uploads['basedir'] ) ) { $new_path = str_replace( $uploads['basedir'], '', $new_path ); $new_path = ltrim( $new_path, '/' ); } @@ -813,7 +915,7 @@ } /** - * Retrieve all children of the post parent ID. + * Retrieves all children of the post parent ID. * * Normally, without any enhancements, the children would apply to pages. In the * context of the inner workings of WordPress, pages, posts, and attachments @@ -861,7 +963,7 @@ * @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which * correspond to a WP_Post object, an associative array, or a numeric array, * respectively. Default OBJECT. - * @return WP_Post[]|int[] Array of post objects or post IDs. + * @return WP_Post[]|array[]|int[] Array of post objects, arrays, or IDs, depending on `$output`. */ function get_children( $args = '', $output = OBJECT ) { $kids = array(); @@ -922,7 +1024,7 @@ } /** - * Get extended entry info (). + * Gets extended entry info (). * * There should not be any space after the second dash and before the word * 'more'. There can be text or space(s) after the word 'more', but won't be @@ -1056,7 +1158,7 @@ } /** - * Retrieve data from a post field based on Post ID. + * Retrieves data from a post field based on Post ID. * * Examples of the post field will be, 'post_type', 'post_status', 'post_content', * etc and based off of the post object property or key names. @@ -1090,7 +1192,7 @@ } /** - * Retrieve the mime type of an attachment based on the ID. + * Retrieves the mime type of an attachment based on the ID. * * This function can be used with any post type, but it makes more sense with * attachments. @@ -1111,7 +1213,7 @@ } /** - * Retrieve the post status based on the post ID. + * Retrieves the post status based on the post ID. * * If the post ID is of an attachment, then the parent post status will be given * instead. @@ -1122,7 +1224,10 @@ * @return string|false Post status on success, false on failure. */ function get_post_status( $post = null ) { - $post = get_post( $post ); + // Normalize the post object if necessary, skip normalization if called from get_sample_permalink(). + if ( ! $post instanceof WP_Post || ! isset( $post->filter ) || 'sample' !== $post->filter ) { + $post = get_post( $post ); + } if ( ! is_object( $post ) ) { return false; @@ -1144,6 +1249,7 @@ } elseif ( 'trash' === get_post_status( $post->post_parent ) ) { // Get parent status prior to trashing. $post_status = get_post_meta( $post->post_parent, '_wp_trash_meta_status', true ); + if ( ! $post_status ) { // Assume publish as above. $post_status = 'publish'; @@ -1178,7 +1284,7 @@ } /** - * Retrieve all of the WordPress supported post statuses. + * Retrieves all of the WordPress supported post statuses. * * Posts have a limited set of valid status values, this provides the * post_status values and descriptions. @@ -1199,7 +1305,7 @@ } /** - * Retrieve all of the WordPress support page statuses. + * Retrieves all of the WordPress support page statuses. * * Pages have a limited set of valid status values, this provides the * post_status values and descriptions. @@ -1219,12 +1325,12 @@ } /** - * Return statuses for privacy requests. + * Returns statuses for privacy requests. * * @since 4.9.6 * @access private * - * @return array + * @return string[] Array of privacy request status labels keyed by their status. */ function _wp_privacy_statuses() { return array( @@ -1236,7 +1342,7 @@ } /** - * Register a post status. Do not use before init. + * Registers a post status. Do not use before init. * * A simple function for creating or modifying a post status based on the * parameters given. The function will accept an array (second optional @@ -1254,8 +1360,10 @@ * * @type bool|string $label A descriptive name for the post status marked * for translation. Defaults to value of $post_status. - * @type bool|array $label_count Descriptive text to use for nooped plurals. - * Default array of $label, twice. + * @type array|false $label_count Nooped plural text from _n_noop() to provide the singular + * and plural forms of the label for counts. Default false + * which means the `$label` argument will be used for both + * the singular and plural forms of this label. * @type bool $exclude_from_search Whether to exclude posts with this post status * from search results. Default is value of $internal. * @type bool $_builtin Whether the status is built-in. Core-use only. @@ -1356,7 +1464,7 @@ } if ( false === $args->label_count ) { - // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralSingle,WordPress.WP.I18n.NonSingularStringLiteralPlural + // phpcs:ignore WordPress.WP.I18n.NonSingularStringLiteralSingular,WordPress.WP.I18n.NonSingularStringLiteralPlural $args->label_count = _n_noop( $args->label, $args->label ); } @@ -1366,7 +1474,7 @@ } /** - * Retrieve a post status object by name. + * Retrieves a post status object by name. * * @since 3.0.0 * @@ -1388,7 +1496,7 @@ } /** - * Get a list of post statuses. + * Gets a list of post statuses. * * @since 3.0.0 * @@ -1413,7 +1521,7 @@ } /** - * Whether the post type is hierarchical. + * Determines whether the post type is hierarchical. * * A false return value might also mean that the post type does not exist. * @@ -1492,7 +1600,7 @@ } /** - * Get a list of all registered post type objects. + * Gets a list of all registered post type objects. * * @since 2.9.0 * @@ -1502,7 +1610,7 @@ * * @param array|string $args Optional. An array of key => value arguments to match against * the post type objects. Default empty array. - * @param string $output Optional. The type of output to return. Accepts post type 'names' + * @param string $output Optional. The type of output to return. Either 'names' * or 'objects'. Default 'names'. * @param string $operator Optional. The logical operation to perform. 'or' means only one * element from the array needs to match; 'and' means all elements @@ -1544,91 +1652,97 @@ * * @global array $wp_post_types List of post types. * - * @param string $post_type Post type key. Must not exceed 20 characters and may - * only contain lowercase alphanumeric characters, dashes, - * and underscores. See sanitize_key(). + * @param string $post_type Post type key. Must not exceed 20 characters and may only contain + * lowercase alphanumeric characters, dashes, and underscores. See sanitize_key(). * @param array|string $args { * Array or string of arguments for registering a post type. * - * @type string $label Name of the post type shown in the menu. Usually plural. - * Default is value of $labels['name']. - * @type string[] $labels An array of labels for this post type. If not set, post - * labels are inherited for non-hierarchical types and page - * labels for hierarchical ones. See get_post_type_labels() for a full - * list of supported labels. - * @type string $description A short descriptive summary of what the post type is. - * Default empty. - * @type bool $public Whether a post type is intended for use publicly either via - * the admin interface or by front-end users. While the default - * settings of $exclude_from_search, $publicly_queryable, $show_ui, - * and $show_in_nav_menus are inherited from $public, each does not - * rely on this relationship and controls a very specific intention. - * Default false. - * @type bool $hierarchical Whether the post type is hierarchical (e.g. page). Default false. - * @type bool $exclude_from_search Whether to exclude posts with this post type from front end search - * results. Default is the opposite value of $public. - * @type bool $publicly_queryable Whether queries can be performed on the front end for the post type - * as part of parse_request(). Endpoints would include: - * * ?post_type={post_type_key} - * * ?{post_type_key}={single_post_slug} - * * ?{post_type_query_var}={single_post_slug} - * If not set, the default is inherited from $public. - * @type bool $show_ui Whether to generate and allow a UI for managing this post type in the - * admin. Default is value of $public. - * @type bool|string $show_in_menu Where to show the post type in the admin menu. To work, $show_ui - * must be true. If true, the post type is shown in its own top level - * menu. If false, no menu is shown. If a string of an existing top - * level menu ('tools.php' or 'edit.php?post_type=page', for example), the - * post type will be placed as a sub-menu of that. - * Default is value of $show_ui. - * @type bool $show_in_nav_menus Makes this post type available for selection in navigation menus. - * Default is value of $public. - * @type bool $show_in_admin_bar Makes this post type available via the admin bar. Default is value - * of $show_in_menu. - * @type bool $show_in_rest Whether to include the post type in the REST API. Set this to true - * for the post type to be available in the block editor. - * @type string $rest_base To change the base URL of REST API route. Default is $post_type. - * @type string $rest_namespace To change the namespace URL of REST API route. Default is wp/v2. - * @type string $rest_controller_class REST API controller class name. Default is 'WP_REST_Posts_Controller'. - * @type int $menu_position The position in the menu order the post type should appear. To work, - * $show_in_menu must be true. Default null (at the bottom). - * @type string $menu_icon The URL to the icon to be used for this menu. Pass a base64-encoded - * SVG using a data URI, which will be colored to match the color scheme - * -- this should begin with 'data:image/svg+xml;base64,'. Pass the name - * of a Dashicons helper class to use a font icon, e.g. - * 'dashicons-chart-pie'. Pass 'none' to leave div.wp-menu-image empty - * so an icon can be added via CSS. Defaults to use the posts icon. - * @type string|array $capability_type The string to use to build the read, edit, and delete capabilities. - * May be passed as an array to allow for alternative plurals when using - * this argument as a base to construct the capabilities, e.g. - * array('story', 'stories'). Default 'post'. - * @type string[] $capabilities Array of capabilities for this post type. $capability_type is used - * as a base to construct capabilities by default. - * See get_post_type_capabilities(). - * @type bool $map_meta_cap Whether to use the internal default meta capability handling. - * Default false. - * @type array $supports Core feature(s) the post type supports. Serves as an alias for calling - * add_post_type_support() directly. Core features include 'title', - * 'editor', 'comments', 'revisions', 'trackbacks', 'author', 'excerpt', - * 'page-attributes', 'thumbnail', 'custom-fields', and 'post-formats'. - * Additionally, the 'revisions' feature dictates whether the post type - * will store revisions, and the 'comments' feature dictates whether the - * comments count will show on the edit screen. A feature can also be - * specified as an array of arguments to provide additional information - * about supporting that feature. - * Example: `array( 'my_feature', array( 'field' => 'value' ) )`. - * Default is an array containing 'title' and 'editor'. - * @type callable $register_meta_box_cb Provide a callback function that sets up the meta boxes for the - * edit form. Do remove_meta_box() and add_meta_box() calls in the - * callback. Default null. - * @type string[] $taxonomies An array of taxonomy identifiers that will be registered for the - * post type. Taxonomies can be registered later with register_taxonomy() - * or register_taxonomy_for_object_type(). - * Default empty array. - * @type bool|string $has_archive Whether there should be post type archives, or if a string, the - * archive slug to use. Will generate the proper rewrite rules if - * $rewrite is enabled. Default false. - * @type bool|array $rewrite { + * @type string $label Name of the post type shown in the menu. Usually plural. + * Default is value of $labels['name']. + * @type string[] $labels An array of labels for this post type. If not set, post + * labels are inherited for non-hierarchical types and page + * labels for hierarchical ones. See get_post_type_labels() for a full + * list of supported labels. + * @type string $description A short descriptive summary of what the post type is. + * Default empty. + * @type bool $public Whether a post type is intended for use publicly either via + * the admin interface or by front-end users. While the default + * settings of $exclude_from_search, $publicly_queryable, $show_ui, + * and $show_in_nav_menus are inherited from $public, each does not + * rely on this relationship and controls a very specific intention. + * Default false. + * @type bool $hierarchical Whether the post type is hierarchical (e.g. page). Default false. + * @type bool $exclude_from_search Whether to exclude posts with this post type from front end search + * results. Default is the opposite value of $public. + * @type bool $publicly_queryable Whether queries can be performed on the front end for the post type + * as part of parse_request(). Endpoints would include: + * * ?post_type={post_type_key} + * * ?{post_type_key}={single_post_slug} + * * ?{post_type_query_var}={single_post_slug} + * If not set, the default is inherited from $public. + * @type bool $show_ui Whether to generate and allow a UI for managing this post type in the + * admin. Default is value of $public. + * @type bool|string $show_in_menu Where to show the post type in the admin menu. To work, $show_ui + * must be true. If true, the post type is shown in its own top level + * menu. If false, no menu is shown. If a string of an existing top + * level menu ('tools.php' or 'edit.php?post_type=page', for example), the + * post type will be placed as a sub-menu of that. + * Default is value of $show_ui. + * @type bool $show_in_nav_menus Makes this post type available for selection in navigation menus. + * Default is value of $public. + * @type bool $show_in_admin_bar Makes this post type available via the admin bar. Default is value + * of $show_in_menu. + * @type bool $show_in_rest Whether to include the post type in the REST API. Set this to true + * for the post type to be available in the block editor. + * @type string $rest_base To change the base URL of REST API route. Default is $post_type. + * @type string $rest_namespace To change the namespace URL of REST API route. Default is wp/v2. + * @type string $rest_controller_class REST API controller class name. Default is 'WP_REST_Posts_Controller'. + * @type string|bool $autosave_rest_controller_class REST API controller class name. Default is 'WP_REST_Autosaves_Controller'. + * @type string|bool $revisions_rest_controller_class REST API controller class name. Default is 'WP_REST_Revisions_Controller'. + * @type bool $late_route_registration A flag to direct the REST API controllers for autosave / revisions + * should be registered before/after the post type controller. + * @type int $menu_position The position in the menu order the post type should appear. To work, + * $show_in_menu must be true. Default null (at the bottom). + * @type string $menu_icon The URL to the icon to be used for this menu. Pass a base64-encoded + * SVG using a data URI, which will be colored to match the color scheme + * -- this should begin with 'data:image/svg+xml;base64,'. Pass the name + * of a Dashicons helper class to use a font icon, e.g. + * 'dashicons-chart-pie'. Pass 'none' to leave div.wp-menu-image empty + * so an icon can be added via CSS. Defaults to use the posts icon. + * @type string|array $capability_type The string to use to build the read, edit, and delete capabilities. + * May be passed as an array to allow for alternative plurals when using + * this argument as a base to construct the capabilities, e.g. + * array('story', 'stories'). Default 'post'. + * @type string[] $capabilities Array of capabilities for this post type. $capability_type is used + * as a base to construct capabilities by default. + * See get_post_type_capabilities(). + * @type bool $map_meta_cap Whether to use the internal default meta capability handling. + * Default false. + * @type array|false $supports Core feature(s) the post type supports. Serves as an alias for calling + * add_post_type_support() directly. Core features include 'title', + * 'editor', 'comments', 'revisions', 'trackbacks', 'author', 'excerpt', + * 'page-attributes', 'thumbnail', 'custom-fields', and 'post-formats'. + * Additionally, the 'revisions' feature dictates whether the post type + * will store revisions, the 'autosave' feature dictates whether the post type + * will be autosaved, and the 'comments' feature dictates whether the + * comments count will show on the edit screen. For backward compatibility reasons, + * adding 'editor' support implies 'autosave' support too. A feature can also be + * specified as an array of arguments to provide additional information + * about supporting that feature. + * Example: `array( 'my_feature', array( 'field' => 'value' ) )`. + * If false, no features will be added. + * Default is an array containing 'title' and 'editor'. + * @type callable $register_meta_box_cb Provide a callback function that sets up the meta boxes for the + * edit form. Do remove_meta_box() and add_meta_box() calls in the + * callback. Default null. + * @type string[] $taxonomies An array of taxonomy identifiers that will be registered for the + * post type. Taxonomies can be registered later with register_taxonomy() + * or register_taxonomy_for_object_type(). + * Default empty array. + * @type bool|string $has_archive Whether there should be post type archives, or if a string, the + * archive slug to use. Will generate the proper rewrite rules if + * $rewrite is enabled. Default false. + * @type bool|array $rewrite { * Triggers the handling of rewrites for this post type. To prevent rewrite, set to false. * Defaults to true, using $post_type as slug. To specify rewrite rules, an array can be * passed with any of these keys: @@ -1643,32 +1757,32 @@ * inherits from $permalink_epmask. If not specified and permalink_epmask * is not set, defaults to EP_PERMALINK. * } - * @type string|bool $query_var Sets the query_var key for this post type. Defaults to $post_type - * key. If false, a post type cannot be loaded at - * ?{query_var}={post_slug}. If specified as a string, the query - * ?{query_var_string}={post_slug} will be valid. - * @type bool $can_export Whether to allow this post type to be exported. Default true. - * @type bool $delete_with_user Whether to delete posts of this type when deleting a user. - * * If true, posts of this type belonging to the user will be moved - * to Trash when the user is deleted. - * * If false, posts of this type belonging to the user will *not* - * be trashed or deleted. - * * If not set (the default), posts are trashed if post type supports - * the 'author' feature. Otherwise posts are not trashed or deleted. - * Default null. - * @type array $template Array of blocks to use as the default initial state for an editor - * session. Each item should be an array containing block name and - * optional attributes. Default empty array. - * @type string|false $template_lock Whether the block template should be locked if $template is set. - * * If set to 'all', the user is unable to insert new blocks, - * move existing blocks and delete blocks. - * * If set to 'insert', the user is able to move existing blocks - * but is unable to insert new blocks and delete blocks. - * Default false. - * @type bool $_builtin FOR INTERNAL USE ONLY! True if this post type is a native or - * "built-in" post_type. Default false. - * @type string $_edit_link FOR INTERNAL USE ONLY! URL segment to use for edit link of - * this post type. Default 'post.php?post=%d'. + * @type string|bool $query_var Sets the query_var key for this post type. Defaults to $post_type + * key. If false, a post type cannot be loaded at + * ?{query_var}={post_slug}. If specified as a string, the query + * ?{query_var_string}={post_slug} will be valid. + * @type bool $can_export Whether to allow this post type to be exported. Default true. + * @type bool $delete_with_user Whether to delete posts of this type when deleting a user. + * * If true, posts of this type belonging to the user will be moved + * to Trash when the user is deleted. + * * If false, posts of this type belonging to the user will *not* + * be trashed or deleted. + * * If not set (the default), posts are trashed if post type supports + * the 'author' feature. Otherwise posts are not trashed or deleted. + * Default null. + * @type array $template Array of blocks to use as the default initial state for an editor + * session. Each item should be an array containing block name and + * optional attributes. Default empty array. + * @type string|false $template_lock Whether the block template should be locked if $template is set. + * * If set to 'all', the user is unable to insert new blocks, + * move existing blocks and delete blocks. + * * If set to 'insert', the user is able to move existing blocks + * but is unable to insert new blocks and delete blocks. + * Default false. + * @type bool $_builtin FOR INTERNAL USE ONLY! True if this post type is a native or + * "built-in" post_type. Default false. + * @type string $_edit_link FOR INTERNAL USE ONLY! URL segment to use for edit link of + * this post type. Default 'post.php?post=%d'. * } * @return WP_Post_Type|WP_Error The registered post type object on success, * WP_Error object on failure. @@ -1776,7 +1890,7 @@ } /** - * Build an object with all post type capabilities out of a post type object + * Builds an object with all post type capabilities out of a post type object * * Post type capabilities use the 'capability_type' argument as a base, if the * capability is not set in the 'capabilities' argument array or if the @@ -1881,7 +1995,7 @@ } /** - * Store or return a list of post type meta caps for map_meta_cap(). + * Stores or returns a list of post type meta caps for map_meta_cap(). * * @since 3.1.0 * @access private @@ -1908,9 +2022,7 @@ * - `name` - General name for the post type, usually plural. The same and overridden * by `$post_type_object->label`. Default is 'Posts' / 'Pages'. * - `singular_name` - Name for one object of this post type. Default is 'Post' / 'Page'. - * - `add_new` - Default is 'Add New' for both hierarchical and non-hierarchical types. - * When internationalizing this string, please use a {@link https://developer.wordpress.org/plugins/internationalization/how-to-internationalize-your-plugin/#disambiguation-by-context gettext context} - * matching your post type. Example: `_x( 'Add New', 'product', 'textdomain' );`. + * - `add_new` - Label for adding a new item. Default is 'Add New Post' / 'Add New Page'. * - `add_new_item` - Label for adding a new singular item. Default is 'Add New Post' / 'Add New Page'. * - `edit_item` - Label for editing a singular item. Default is 'Edit Post' / 'Edit Page'. * - `new_item` - Label for the new item page title. Default is 'New Post' / 'New Page'. @@ -1944,6 +2056,7 @@ * Default is 'Post published privately.' / 'Page published privately.' * - `item_reverted_to_draft` - Label used when an item is switched to a draft. * Default is 'Post reverted to draft.' / 'Page reverted to draft.' + * - `item_trashed` - Label used when an item is moved to Trash. Default is 'Post trashed.' / 'Page trashed.' * - `item_scheduled` - Label used when an item is scheduled for publishing. Default is 'Post scheduled.' / * 'Page scheduled.' * - `item_updated` - Label used when an item is updated. Default is 'Post updated.' / 'Page updated.' @@ -1967,6 +2080,10 @@ * `item_scheduled`, and `item_updated` labels. * @since 5.7.0 Added the `filter_by_date` label. * @since 5.8.0 Added the `item_link` and `item_link_description` labels. + * @since 6.3.0 Added the `item_trashed` label. + * @since 6.4.0 Changed default values for the `add_new` label to include the type of content. + * This matches `add_new_item` and provides more context for better accessibility. + * @since 6.6.0 Added the `template_name` label. * * @access private * @@ -1980,6 +2097,11 @@ $labels = _get_custom_object_labels( $post_type_object, $nohier_vs_hier_defaults ); + if ( ! isset( $post_type_object->labels->template_name ) && isset( $post_type_object->labels->singular_name ) ) { + /* translators: %s: Post type name. */ + $labels->template_name = sprintf( __( 'Single item: %s' ), $post_type_object->labels->singular_name ); + } + $post_type = $post_type_object->name; $default_labels = clone $labels; @@ -2011,55 +2133,59 @@ } /** - * Build an object with custom-something object (post type, taxonomy) labels + * Builds an object with custom-something object (post type, taxonomy) labels * out of a custom-something object * * @since 3.0.0 * @access private * - * @param object $object A custom-something object. + * @param object $data_object A custom-something object. * @param array $nohier_vs_hier_defaults Hierarchical vs non-hierarchical default labels. * @return object Object containing labels for the given custom-something object. */ -function _get_custom_object_labels( $object, $nohier_vs_hier_defaults ) { - $object->labels = (array) $object->labels; - - if ( isset( $object->label ) && empty( $object->labels['name'] ) ) { - $object->labels['name'] = $object->label; - } - - if ( ! isset( $object->labels['singular_name'] ) && isset( $object->labels['name'] ) ) { - $object->labels['singular_name'] = $object->labels['name']; - } - - if ( ! isset( $object->labels['name_admin_bar'] ) ) { - $object->labels['name_admin_bar'] = isset( $object->labels['singular_name'] ) ? $object->labels['singular_name'] : $object->name; - } - - if ( ! isset( $object->labels['menu_name'] ) && isset( $object->labels['name'] ) ) { - $object->labels['menu_name'] = $object->labels['name']; - } - - if ( ! isset( $object->labels['all_items'] ) && isset( $object->labels['menu_name'] ) ) { - $object->labels['all_items'] = $object->labels['menu_name']; - } - - if ( ! isset( $object->labels['archives'] ) && isset( $object->labels['all_items'] ) ) { - $object->labels['archives'] = $object->labels['all_items']; +function _get_custom_object_labels( $data_object, $nohier_vs_hier_defaults ) { + $data_object->labels = (array) $data_object->labels; + + if ( isset( $data_object->label ) && empty( $data_object->labels['name'] ) ) { + $data_object->labels['name'] = $data_object->label; + } + + if ( ! isset( $data_object->labels['singular_name'] ) && isset( $data_object->labels['name'] ) ) { + $data_object->labels['singular_name'] = $data_object->labels['name']; + } + + if ( ! isset( $data_object->labels['name_admin_bar'] ) ) { + $data_object->labels['name_admin_bar'] = + isset( $data_object->labels['singular_name'] ) + ? $data_object->labels['singular_name'] + : $data_object->name; + } + + if ( ! isset( $data_object->labels['menu_name'] ) && isset( $data_object->labels['name'] ) ) { + $data_object->labels['menu_name'] = $data_object->labels['name']; + } + + if ( ! isset( $data_object->labels['all_items'] ) && isset( $data_object->labels['menu_name'] ) ) { + $data_object->labels['all_items'] = $data_object->labels['menu_name']; + } + + if ( ! isset( $data_object->labels['archives'] ) && isset( $data_object->labels['all_items'] ) ) { + $data_object->labels['archives'] = $data_object->labels['all_items']; } $defaults = array(); foreach ( $nohier_vs_hier_defaults as $key => $value ) { - $defaults[ $key ] = $object->hierarchical ? $value[1] : $value[0]; - } - $labels = array_merge( $defaults, $object->labels ); - $object->labels = (object) $object->labels; + $defaults[ $key ] = $data_object->hierarchical ? $value[1] : $value[0]; + } + + $labels = array_merge( $defaults, $data_object->labels ); + $data_object->labels = (object) $data_object->labels; return (object) $labels; } /** - * Add submenus for post types. + * Adds submenus for post types. * * @access private * @since 3.1.0 @@ -2084,7 +2210,8 @@ * 'thumbnail', 'custom-fields', and 'post-formats'. * * Additionally, the 'revisions' feature dictates whether the post type will - * store revisions, and the 'comments' feature dictates whether the comments + * store revisions, the 'autosave' feature dictates whether the post type + * will be autosaved, and the 'comments' feature dictates whether the comments * count will show on the edit screen. * * A third, optional parameter can also be passed along with a feature to provide @@ -2125,7 +2252,7 @@ } /** - * Remove support for a feature from a post type. + * Removes support for a feature from a post type. * * @since 3.0.0 * @@ -2141,7 +2268,7 @@ } /** - * Get all the post type features + * Gets all the post type features * * @since 3.4.0 * @@ -2161,7 +2288,7 @@ } /** - * Check a post type's support for a given feature. + * Checks a post type's support for a given feature. * * @since 3.0.0 * @@ -2200,7 +2327,7 @@ } /** - * Update the post type for the post ID. + * Updates the post type for the post ID. * * The page or post cache will be cleaned for the post ID. * @@ -2241,6 +2368,7 @@ function is_post_type_viewable( $post_type ) { if ( is_scalar( $post_type ) ) { $post_type = get_post_type_object( $post_type ); + if ( ! $post_type ) { return false; } @@ -2270,7 +2398,7 @@ } /** - * Determine whether a post status is considered "viewable". + * Determines whether a post status is considered "viewable". * * For built-in post statuses such as publish and private, the 'public' value will be evaluated. * For all others, the 'publicly_queryable' value will be used. @@ -2284,6 +2412,7 @@ function is_post_status_viewable( $post_status ) { if ( is_scalar( $post_status ) ) { $post_status = get_post_status_object( $post_status ); + if ( ! $post_status ) { return false; } @@ -2317,7 +2446,7 @@ } /** - * Determine whether a post is publicly viewable. + * Determines whether a post is publicly viewable. * * Posts are considered publicly viewable if both the post status and post type * are viewable. @@ -2406,9 +2535,8 @@ $parsed_args['ignore_sticky_posts'] = true; $parsed_args['no_found_rows'] = true; - $get_posts = new WP_Query; + $get_posts = new WP_Query(); return $get_posts->query( $parsed_args ); - } // @@ -2563,7 +2691,7 @@ } /** - * Retrieve post meta fields, based on post ID. + * Retrieves post meta fields, based on post ID. * * The post meta fields are retrieved from the cache where possible, * so the function is optimized to be called more than once. @@ -2577,6 +2705,7 @@ */ function get_post_custom( $post_id = 0 ) { $post_id = absint( $post_id ); + if ( ! $post_id ) { $post_id = get_the_ID(); } @@ -2585,7 +2714,7 @@ } /** - * Retrieve meta field names for a post. + * Retrieves meta field names for a post. * * If there are no meta fields, then nothing (null) will be returned. * @@ -2608,7 +2737,7 @@ } /** - * Retrieve values for a custom post field. + * Retrieves values for a custom post field. * * The parameters must not be considered optional. All of the post meta fields * will be retrieved and only the meta field key values returned. @@ -2752,7 +2881,7 @@ } $prefixed = false; - if ( false !== strpos( $field, 'post_' ) ) { + if ( str_contains( $field, 'post_' ) ) { $prefixed = true; $field_no_prefix = str_replace( 'post_', '', $field ); } @@ -2881,7 +3010,7 @@ } /** - * Make a post sticky. + * Makes a post sticky. * * Sticky posts should be displayed at the top of the front page. * @@ -2918,7 +3047,7 @@ } /** - * Un-stick a post. + * Un-sticks a post. * * Sticky posts should be displayed at the top of the front page. * @@ -2962,7 +3091,7 @@ } /** - * Return the cache key for wp_count_posts() based on the passed arguments. + * Returns the cache key for wp_count_posts() based on the passed arguments. * * @since 3.9.0 * @access private @@ -2986,7 +3115,7 @@ } /** - * Count number of posts of a post type and if user has permissions to view. + * Counts number of posts of a post type and if user has permissions to view. * * This function provides an efficient method of finding the amount of post's * type a blog has. Another method is to count the amount of items in @@ -3002,13 +3131,14 @@ * * @param string $type Optional. Post type to retrieve count. Default 'post'. * @param string $perm Optional. 'readable' or empty. Default empty. - * @return stdClass Number of posts for each status. + * @return stdClass An object containing the number of posts for each status, + * or an empty object if the post type does not exist. */ function wp_count_posts( $type = 'post', $perm = '' ) { global $wpdb; if ( ! post_type_exists( $type ) ) { - return new stdClass; + return new stdClass(); } $cache_key = _count_posts_cache_key( $type, $perm ); @@ -3051,7 +3181,7 @@ wp_cache_set( $cache_key, $counts, 'counts' ); /** - * Modify returned post counts by status for the current post type. + * Filters the post counts by status for the current post type. * * @since 3.7.0 * @@ -3065,7 +3195,7 @@ } /** - * Count number of attachments for the mime type(s). + * Counts number of attachments for the mime type(s). * * If you set the optional mime_type parameter, then an array will still be * returned, but will only have the item you are looking for. It does not give @@ -3083,17 +3213,27 @@ function wp_count_attachments( $mime_type = '' ) { global $wpdb; - $and = wp_post_mime_type_where( $mime_type ); - $count = $wpdb->get_results( "SELECT post_mime_type, COUNT( * ) AS num_posts FROM $wpdb->posts WHERE post_type = 'attachment' AND post_status != 'trash' $and GROUP BY post_mime_type", ARRAY_A ); - - $counts = array(); - foreach ( (array) $count as $row ) { - $counts[ $row['post_mime_type'] ] = $row['num_posts']; - } - $counts['trash'] = $wpdb->get_var( "SELECT COUNT( * ) FROM $wpdb->posts WHERE post_type = 'attachment' AND post_status = 'trash' $and" ); + $cache_key = sprintf( + 'attachments%s', + ! empty( $mime_type ) ? ':' . str_replace( '/', '_', implode( '-', (array) $mime_type ) ) : '' + ); + + $counts = wp_cache_get( $cache_key, 'counts' ); + if ( false == $counts ) { + $and = wp_post_mime_type_where( $mime_type ); + $count = $wpdb->get_results( "SELECT post_mime_type, COUNT( * ) AS num_posts FROM $wpdb->posts WHERE post_type = 'attachment' AND post_status != 'trash' $and GROUP BY post_mime_type", ARRAY_A ); + + $counts = array(); + foreach ( (array) $count as $row ) { + $counts[ $row['post_mime_type'] ] = $row['num_posts']; + } + $counts['trash'] = $wpdb->get_var( "SELECT COUNT( * ) FROM $wpdb->posts WHERE post_type = 'attachment' AND post_status = 'trash' $and" ); + + wp_cache_set( $cache_key, (object) $counts, 'counts' ); + } /** - * Modify returned attachment counts by mime type. + * Filters the attachment counts by mime type. * * @since 3.7.0 * @@ -3105,7 +3245,7 @@ } /** - * Get default post mime types. + * Gets default post mime types. * * @since 2.9.0 * @since 5.3.0 Added the 'Documents', 'Spreadsheets', and 'Archives' mime type groups. @@ -3209,16 +3349,16 @@ } /** - * Check a MIME-Type against a list. - * - * If the wildcard_mime_types parameter is a string, it must be comma separated - * list. If the real_mime_types is a string, it is also comma separated to + * Checks a MIME-Type against a list. + * + * If the `$wildcard_mime_types` parameter is a string, it must be comma separated + * list. If the `$real_mime_types` is a string, it is also comma separated to * create the list. * * @since 2.5.0 * - * @param string|string[] $wildcard_mime_types Mime types, e.g. audio/mpeg or image (same as image/*) - * or flash (same as *flash*). + * @param string|string[] $wildcard_mime_types Mime types, e.g. `audio/mpeg`, `image` (same as `image/*`), + * or `flash` (same as `*flash*`). * @param string|string[] $real_mime_types Real post mime type values. * @return array array(wildcard=>array(real types)). */ @@ -3241,7 +3381,7 @@ $patternses[][ $type ] = "^$regex$"; - if ( false === strpos( $mime, '/' ) ) { + if ( ! str_contains( $mime, '/' ) ) { $patternses[][ $type ] = "^$regex/"; $patternses[][ $type ] = $regex; } @@ -3265,7 +3405,7 @@ } /** - * Convert MIME types into SQL. + * Converts MIME types into SQL. * * @since 2.5.0 * @@ -3282,7 +3422,7 @@ $post_mime_types = array_map( 'trim', explode( ',', $post_mime_types ) ); } - $wheres = array(); + $where_clauses = array(); foreach ( (array) $post_mime_types as $mime_type ) { $mime_type = preg_replace( '/\s/', '', $mime_type ); @@ -3298,7 +3438,7 @@ $mime_pattern = "$mime_group/$mime_subgroup"; } else { $mime_pattern = preg_replace( '/[^-*.a-zA-Z0-9]/', '', $mime_type ); - if ( false === strpos( $mime_pattern, '*' ) ) { + if ( ! str_contains( $mime_pattern, '*' ) ) { $mime_pattern .= '/*'; } } @@ -3309,22 +3449,22 @@ return ''; } - if ( false !== strpos( $mime_pattern, '%' ) ) { - $wheres[] = empty( $table_alias ) ? "post_mime_type LIKE '$mime_pattern'" : "$table_alias.post_mime_type LIKE '$mime_pattern'"; + if ( str_contains( $mime_pattern, '%' ) ) { + $where_clauses[] = empty( $table_alias ) ? "post_mime_type LIKE '$mime_pattern'" : "$table_alias.post_mime_type LIKE '$mime_pattern'"; } else { - $wheres[] = empty( $table_alias ) ? "post_mime_type = '$mime_pattern'" : "$table_alias.post_mime_type = '$mime_pattern'"; - } - } - - if ( ! empty( $wheres ) ) { - $where = ' AND (' . implode( ' OR ', $wheres ) . ') '; + $where_clauses[] = empty( $table_alias ) ? "post_mime_type = '$mime_pattern'" : "$table_alias.post_mime_type = '$mime_pattern'"; + } + } + + if ( ! empty( $where_clauses ) ) { + $where = ' AND (' . implode( ' OR ', $where_clauses ) . ') '; } return $where; } /** - * Trash or delete a post or page. + * Trashes or deletes a post or page. * * When the post and page is permanently deleted, everything that is tied to * it is deleted also. This includes comments, post meta fields, and terms @@ -3339,15 +3479,15 @@ * @see wp_delete_attachment() * @see wp_trash_post() * - * @param int $postid Optional. Post ID. Default 0. + * @param int $post_id Optional. Post ID. Default 0. * @param bool $force_delete Optional. Whether to bypass Trash and force deletion. * Default false. * @return WP_Post|false|null Post data on success, false or null on failure. */ -function wp_delete_post( $postid = 0, $force_delete = false ) { +function wp_delete_post( $post_id = 0, $force_delete = false ) { global $wpdb; - $post = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE ID = %d", $postid ) ); + $post = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE ID = %d", $post_id ) ); if ( ! $post ) { return $post; @@ -3355,12 +3495,15 @@ $post = get_post( $post ); - if ( ! $force_delete && ( 'post' === $post->post_type || 'page' === $post->post_type ) && 'trash' !== get_post_status( $postid ) && EMPTY_TRASH_DAYS ) { - return wp_trash_post( $postid ); + if ( ! $force_delete + && ( 'post' === $post->post_type || 'page' === $post->post_type ) + && 'trash' !== get_post_status( $post_id ) && EMPTY_TRASH_DAYS + ) { + return wp_trash_post( $post_id ); } if ( 'attachment' === $post->post_type ) { - return wp_delete_attachment( $postid, $force_delete ); + return wp_delete_attachment( $post_id, $force_delete ); } /** @@ -3368,7 +3511,7 @@ * * @since 4.4.0 * - * @param WP_Post|false|null $delete Whether to go forward with deletion. @TODO description + * @param WP_Post|false|null $delete Whether to go forward with deletion. * @param WP_Post $post Post object. * @param bool $force_delete Whether to bypass the Trash. */ @@ -3385,30 +3528,39 @@ * * @see wp_delete_post() * - * @param int $postid Post ID. - * @param WP_Post $post Post object. + * @param int $post_id Post ID. + * @param WP_Post $post Post object. */ - do_action( 'before_delete_post', $postid, $post ); - - delete_post_meta( $postid, '_wp_trash_meta_status' ); - delete_post_meta( $postid, '_wp_trash_meta_time' ); - - wp_delete_object_term_relationships( $postid, get_object_taxonomies( $post->post_type ) ); + do_action( 'before_delete_post', $post_id, $post ); + + delete_post_meta( $post_id, '_wp_trash_meta_status' ); + delete_post_meta( $post_id, '_wp_trash_meta_time' ); + + wp_delete_object_term_relationships( $post_id, get_object_taxonomies( $post->post_type ) ); $parent_data = array( 'post_parent' => $post->post_parent ); - $parent_where = array( 'post_parent' => $postid ); + $parent_where = array( 'post_parent' => $post_id ); if ( is_post_type_hierarchical( $post->post_type ) ) { // Point children of this page to its parent, also clean the cache of affected children. - $children_query = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_parent = %d AND post_type = %s", $postid, $post->post_type ); - $children = $wpdb->get_results( $children_query ); + $children_query = $wpdb->prepare( + "SELECT * FROM $wpdb->posts WHERE post_parent = %d AND post_type = %s", + $post_id, + $post->post_type + ); + + $children = $wpdb->get_results( $children_query ); + if ( $children ) { $wpdb->update( $wpdb->posts, $parent_data, $parent_where + array( 'post_type' => $post->post_type ) ); } } // Do raw query. wp_get_post_revisions() is filtered. - $revision_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'revision'", $postid ) ); + $revision_ids = $wpdb->get_col( + $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'revision'", $post_id ) + ); + // Use wp_delete_post (via wp_delete_post_revision) again. Ensures any meta/misplaced data gets cleaned up. foreach ( $revision_ids as $revision_id ) { wp_delete_post_revision( $revision_id ); @@ -3419,14 +3571,20 @@ wp_defer_comment_counting( true ); - $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d ORDER BY comment_ID DESC", $postid ) ); + $comment_ids = $wpdb->get_col( + $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d ORDER BY comment_ID DESC", $post_id ) + ); + foreach ( $comment_ids as $comment_id ) { wp_delete_comment( $comment_id, true ); } wp_defer_comment_counting( false ); - $post_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d ", $postid ) ); + $post_meta_ids = $wpdb->get_col( + $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d ", $post_id ) + ); + foreach ( $post_meta_ids as $mid ) { delete_metadata_by_mid( 'post', $mid ); } @@ -3434,15 +3592,28 @@ /** * Fires immediately before a post is deleted from the database. * + * The dynamic portion of the hook name, `$post->post_type`, refers to + * the post type slug. + * + * @since 6.6.0 + * + * @param int $post_id Post ID. + * @param WP_Post $post Post object. + */ + do_action( "delete_post_{$post->post_type}", $post_id, $post ); + + /** + * Fires immediately before a post is deleted from the database. + * * @since 1.2.0 * @since 5.5.0 Added the `$post` parameter. * - * @param int $postid Post ID. - * @param WP_Post $post Post object. + * @param int $post_id Post ID. + * @param WP_Post $post Post object. */ - do_action( 'delete_post', $postid, $post ); - - $result = $wpdb->delete( $wpdb->posts, array( 'ID' => $postid ) ); + do_action( 'delete_post', $post_id, $post ); + + $result = $wpdb->delete( $wpdb->posts, array( 'ID' => $post_id ) ); if ( ! $result ) { return false; } @@ -3450,13 +3621,26 @@ /** * Fires immediately after a post is deleted from the database. * + * The dynamic portion of the hook name, `$post->post_type`, refers to + * the post type slug. + * + * @since 6.6.0 + * + * @param int $post_id Post ID. + * @param WP_Post $post Post object. + */ + do_action( "deleted_post_{$post->post_type}", $post_id, $post ); + + /** + * Fires immediately after a post is deleted from the database. + * * @since 2.2.0 * @since 5.5.0 Added the `$post` parameter. * - * @param int $postid Post ID. - * @param WP_Post $post Post object. + * @param int $post_id Post ID. + * @param WP_Post $post Post object. */ - do_action( 'deleted_post', $postid, $post ); + do_action( 'deleted_post', $post_id, $post ); clean_post_cache( $post ); @@ -3466,7 +3650,7 @@ } } - wp_clear_scheduled_hook( 'publish_future_post', array( $postid ) ); + wp_clear_scheduled_hook( 'publish_future_post', array( $post_id ) ); /** * Fires after a post is deleted, at the conclusion of wp_delete_post(). @@ -3476,16 +3660,16 @@ * * @see wp_delete_post() * - * @param int $postid Post ID. - * @param WP_Post $post Post object. + * @param int $post_id Post ID. + * @param WP_Post $post Post object. */ - do_action( 'after_delete_post', $postid, $post ); + do_action( 'after_delete_post', $post_id, $post ); return $post; } /** - * Reset the page_on_front, show_on_front, and page_for_post settings when + * Resets the page_on_front, show_on_front, and page_for_post settings when * a linked page is deleted or trashed. * * Also ensures the post is no longer sticky. @@ -3516,7 +3700,7 @@ } /** - * Move a post or page to the Trash + * Moves a post or page to the Trash * * If Trash is disabled, the post or page is permanently deleted. * @@ -3543,15 +3727,19 @@ return false; } + $previous_status = $post->post_status; + /** * Filters whether a post trashing should take place. * * @since 4.9.0 - * - * @param bool|null $trash Whether to go forward with trashing. - * @param WP_Post $post Post object. + * @since 6.3.0 Added the `$previous_status` parameter. + * + * @param bool|null $trash Whether to go forward with trashing. + * @param WP_Post $post Post object. + * @param string $previous_status The status of the post about to be trashed. */ - $check = apply_filters( 'pre_trash_post', null, $post ); + $check = apply_filters( 'pre_trash_post', null, $post, $previous_status ); if ( null !== $check ) { return $check; @@ -3561,12 +3749,14 @@ * Fires before a post is sent to the Trash. * * @since 3.3.0 - * - * @param int $post_id Post ID. + * @since 6.3.0 Added the `$previous_status` parameter. + * + * @param int $post_id Post ID. + * @param string $previous_status The status of the post about to be trashed. */ - do_action( 'wp_trash_post', $post_id ); - - add_post_meta( $post_id, '_wp_trash_meta_status', $post->post_status ); + do_action( 'wp_trash_post', $post_id, $previous_status ); + + add_post_meta( $post_id, '_wp_trash_meta_status', $previous_status ); add_post_meta( $post_id, '_wp_trash_meta_time', time() ); $post_updated = wp_update_post( @@ -3586,10 +3776,12 @@ * Fires after a post is sent to the Trash. * * @since 2.9.0 - * - * @param int $post_id Post ID. + * @since 6.3.0 Added the `$previous_status` parameter. + * + * @param int $post_id Post ID. + * @param string $previous_status The status of the post at the point where it was trashed. */ - do_action( 'trashed_post', $post_id ); + do_action( 'trashed_post', $post_id, $previous_status ); return $post; } @@ -3623,7 +3815,7 @@ * Filters whether a post untrashing should take place. * * @since 4.9.0 - * @since 5.6.0 The `$previous_status` parameter was added. + * @since 5.6.0 Added the `$previous_status` parameter. * * @param bool|null $untrash Whether to go forward with untrashing. * @param WP_Post $post Post object. @@ -3638,7 +3830,7 @@ * Fires before a post is restored from the Trash. * * @since 2.9.0 - * @since 5.6.0 The `$previous_status` parameter was added. + * @since 5.6.0 Added the `$previous_status` parameter. * * @param int $post_id Post ID. * @param string $previous_status The status of the post at the point where it was trashed. @@ -3684,7 +3876,7 @@ * Fires after a post is restored from the Trash. * * @since 2.9.0 - * @since 5.6.0 The `$previous_status` parameter was added. + * @since 5.6.0 Added the `$previous_status` parameter. * * @param int $post_id Post ID. * @param string $previous_status The status of the post at the point where it was trashed. @@ -3756,7 +3948,7 @@ } /** - * Restore comments for a post from the Trash. + * Restores comments for a post from the Trash. * * @since 2.9.0 * @@ -3798,7 +3990,7 @@ } foreach ( $group_by_status as $status => $comments ) { - // Sanity check. This shouldn't happen. + // Confidence check. This shouldn't happen. if ( 'post-trashed' === $status ) { $status = '0'; } @@ -3821,7 +4013,7 @@ } /** - * Retrieve the list of categories for a post. + * Retrieves the list of categories for a post. * * Compatibility layer for themes and plugins. Also an easy layer of abstraction * away from the complexity of the taxonomy layer. @@ -3850,7 +4042,7 @@ } /** - * Retrieve the tags for a post. + * Retrieves the tags for a post. * * There is only one default for this function, called 'fields' and by default * is set to 'all'. There are other defaults that can be overridden in @@ -3898,7 +4090,7 @@ } /** - * Retrieve a number of recent posts. + * Retrieves a number of recent posts. * * @since 1.0.0 * @@ -3947,11 +4139,10 @@ } return $results ? $results : false; - -} - -/** - * Insert or update a post. +} + +/** + * Inserts or update a post. * * If the $postarr parameter has 'ID' set to a value, then post will be updated. * @@ -3995,10 +4186,6 @@ * Default empty. * @type string $pinged Space or carriage return-separated list of URLs that have * been pinged. Default empty. - * @type string $post_modified The date when the post was last modified. Default is - * the current time. - * @type string $post_modified_gmt The date when the post was last modified in the GMT - * timezone. Default is the current time. * @type int $post_parent Set this for the post it belongs to, if any. Default 0. * @type int $menu_order The order the post should be displayed in. Default 0. * @type string $post_mime_type The mime type of the post. Default empty. @@ -4017,6 +4204,7 @@ * child terms can have the same names with different parent terms, * so the only way to connect them is using ID. Default empty. * @type array $meta_input Array of post meta values keyed by their post meta key. Default empty. + * @type string $page_template Page template to use. * } * @param bool $wp_error Optional. Whether to return a WP_Error on failure. Default false. * @param bool $fire_after_hooks Optional. Whether to fire the after insert hooks. Default true. @@ -4059,7 +4247,7 @@ $postarr = sanitize_post( $postarr, 'db' ); // Are we updating or creating? - $post_ID = 0; + $post_id = 0; $update = false; $guid = $postarr['guid']; @@ -4067,8 +4255,8 @@ $update = true; // Get the post ID and GUID. - $post_ID = $postarr['ID']; - $post_before = get_post( $post_ID ); + $post_id = $postarr['ID']; + $post_before = get_post( $post_id ); if ( is_null( $post_before ) ) { if ( $wp_error ) { @@ -4077,8 +4265,8 @@ return 0; } - $guid = get_post_field( 'guid', $post_ID ); - $previous_status = get_post_field( 'post_status', $post_ID ); + $guid = get_post_field( 'guid', $post_id ); + $previous_status = get_post_field( 'post_status', $post_id ); } else { $previous_status = 'new'; $post_before = null; @@ -4136,6 +4324,8 @@ if ( ! empty( $postarr['post_category'] ) ) { // Filter out empty terms. $post_category = array_filter( $postarr['post_category'] ); + } elseif ( $update && ! isset( $postarr['post_category'] ) ) { + $post_category = $post_before->post_category; } // Make sure we set a valid category. @@ -4153,12 +4343,14 @@ * * For new posts check the primitive capability, for updates check the meta capability. */ - $post_type_object = get_post_type_object( $post_type ); - - if ( ! $update && 'pending' === $post_status && ! current_user_can( $post_type_object->cap->publish_posts ) ) { - $post_name = ''; - } elseif ( $update && 'pending' === $post_status && ! current_user_can( 'publish_post', $post_ID ) ) { - $post_name = ''; + if ( 'pending' === $post_status ) { + $post_type_object = get_post_type_object( $post_type ); + + if ( ! $update && $post_type_object && ! current_user_can( $post_type_object->cap->publish_posts ) ) { + $post_name = ''; + } elseif ( $update && ! current_user_can( 'publish_post', $post_id ) ) { + $post_name = ''; + } } /* @@ -4175,9 +4367,12 @@ // On updates, we need to check to see if it's using the old, fixed sanitization context. $check_name = sanitize_title( $post_name, '', 'old-save' ); - if ( $update && strtolower( urlencode( $post_name ) ) == $check_name && get_post_field( 'post_name', $post_ID ) == $check_name ) { + if ( $update + && strtolower( urlencode( $post_name ) ) === $check_name + && get_post_field( 'post_name', $post_id ) === $check_name + ) { $post_name = $check_name; - } else { // new post, or slug has changed. + } else { // New post, or slug has changed. $post_name = sanitize_title( $post_name ); } } @@ -4187,6 +4382,7 @@ * if none are provided, the date will be set to now. */ $post_date = wp_resolve_post_date( $postarr['post_date'], $postarr['post_date_gmt'] ); + if ( ! $post_date ) { if ( $wp_error ) { return new WP_Error( 'invalid_date', __( 'Invalid date.' ) ); @@ -4269,7 +4465,7 @@ $new_postarr = array_merge( array( - 'ID' => $post_ID, + 'ID' => $post_id, ), compact( array_diff( array_keys( $defaults ), array( 'context', 'filter' ) ) ) ); @@ -4280,21 +4476,21 @@ * @since 3.1.0 * * @param int $post_parent Post parent ID. - * @param int $post_ID Post ID. + * @param int $post_id Post ID. * @param array $new_postarr Array of parsed post data. * @param array $postarr Array of sanitized, but otherwise unmodified post data. */ - $post_parent = apply_filters( 'wp_insert_post_parent', $post_parent, $post_ID, $new_postarr, $postarr ); + $post_parent = apply_filters( 'wp_insert_post_parent', $post_parent, $post_id, $new_postarr, $postarr ); /* * If the post is being untrashed and it has a desired slug stored in post meta, * reassign it. */ if ( 'trash' === $previous_status && 'trash' !== $post_status ) { - $desired_post_slug = get_post_meta( $post_ID, '_wp_desired_post_slug', true ); + $desired_post_slug = get_post_meta( $post_id, '_wp_desired_post_slug', true ); if ( $desired_post_slug ) { - delete_post_meta( $post_ID, '_wp_desired_post_slug' ); + delete_post_meta( $post_id, '_wp_desired_post_slug' ); $post_name = $desired_post_slug; } } @@ -4308,27 +4504,49 @@ * * @param bool $add_trashed_suffix Whether to attempt to add the suffix. * @param string $post_name The name of the post being updated. - * @param int $post_ID Post ID. + * @param int $post_id Post ID. */ - $add_trashed_suffix = apply_filters( 'add_trashed_suffix_to_trashed_posts', true, $post_name, $post_ID ); + $add_trashed_suffix = apply_filters( 'add_trashed_suffix_to_trashed_posts', true, $post_name, $post_id ); if ( $add_trashed_suffix ) { - wp_add_trashed_suffix_to_post_name_for_trashed_posts( $post_name, $post_ID ); + wp_add_trashed_suffix_to_post_name_for_trashed_posts( $post_name, $post_id ); } } // When trashing an existing post, change its slug to allow non-trashed posts to use it. if ( 'trash' === $post_status && 'trash' !== $previous_status && 'new' !== $previous_status ) { - $post_name = wp_add_trashed_suffix_to_post_name_for_post( $post_ID ); - } - - $post_name = wp_unique_post_slug( $post_name, $post_ID, $post_status, $post_type, $post_parent ); + $post_name = wp_add_trashed_suffix_to_post_name_for_post( $post_id ); + } + + $post_name = wp_unique_post_slug( $post_name, $post_id, $post_status, $post_type, $post_parent ); // Don't unslash. $post_mime_type = isset( $postarr['post_mime_type'] ) ? $postarr['post_mime_type'] : ''; // Expected_slashed (everything!). - $data = compact( 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_content_filtered', 'post_title', 'post_excerpt', 'post_status', 'post_type', 'comment_status', 'ping_status', 'post_password', 'post_name', 'to_ping', 'pinged', 'post_modified', 'post_modified_gmt', 'post_parent', 'menu_order', 'post_mime_type', 'guid' ); + $data = compact( + 'post_author', + 'post_date', + 'post_date_gmt', + 'post_content', + 'post_content_filtered', + 'post_title', + 'post_excerpt', + 'post_status', + 'post_type', + 'comment_status', + 'ping_status', + 'post_password', + 'post_name', + 'to_ping', + 'pinged', + 'post_modified', + 'post_modified_gmt', + 'post_parent', + 'menu_order', + 'post_mime_type', + 'guid' + ); $emoji_fields = array( 'post_title', 'post_content', 'post_excerpt' ); @@ -4375,7 +4593,7 @@ } $data = wp_unslash( $data ); - $where = array( 'ID' => $post_ID ); + $where = array( 'ID' => $post_id ); if ( $update ) { /** @@ -4383,10 +4601,10 @@ * * @since 2.5.0 * - * @param int $post_ID Post ID. + * @param int $post_id Post ID. * @param array $data Array of unslashed post data. */ - do_action( 'pre_post_update', $post_ID, $data ); + do_action( 'pre_post_update', $post_id, $data ); if ( false === $wpdb->update( $wpdb->posts, $data, $where ) ) { if ( $wp_error ) { @@ -4425,25 +4643,25 @@ } } - $post_ID = (int) $wpdb->insert_id; - - // Use the newly generated $post_ID. - $where = array( 'ID' => $post_ID ); + $post_id = (int) $wpdb->insert_id; + + // Use the newly generated $post_id. + $where = array( 'ID' => $post_id ); } if ( empty( $data['post_name'] ) && ! in_array( $data['post_status'], array( 'draft', 'pending', 'auto-draft' ), true ) ) { - $data['post_name'] = wp_unique_post_slug( sanitize_title( $data['post_title'], $post_ID ), $post_ID, $data['post_status'], $post_type, $post_parent ); + $data['post_name'] = wp_unique_post_slug( sanitize_title( $data['post_title'], $post_id ), $post_id, $data['post_status'], $post_type, $post_parent ); $wpdb->update( $wpdb->posts, array( 'post_name' => $data['post_name'] ), $where ); - clean_post_cache( $post_ID ); + clean_post_cache( $post_id ); } if ( is_object_in_taxonomy( $post_type, 'category' ) ) { - wp_set_post_categories( $post_ID, $post_category ); + wp_set_post_categories( $post_id, $post_category ); } if ( isset( $postarr['tags_input'] ) && is_object_in_taxonomy( $post_type, 'post_tag' ) ) { - wp_set_post_tags( $post_ID, $postarr['tags_input'] ); + wp_set_post_tags( $post_id, $postarr['tags_input'] ); } // Add default term for all associated custom taxonomies. @@ -4458,7 +4676,7 @@ } // Passed custom taxonomy list overwrites the existing list if not empty. - $terms = wp_get_object_terms( $post_ID, $taxonomy, array( 'fields' => 'ids' ) ); + $terms = wp_get_object_terms( $post_id, $taxonomy, array( 'fields' => 'ids' ) ); if ( ! empty( $terms ) && empty( $postarr['tax_input'][ $taxonomy ] ) ) { $postarr['tax_input'][ $taxonomy ] = $terms; } @@ -4490,31 +4708,31 @@ } if ( current_user_can( $taxonomy_obj->cap->assign_terms ) ) { - wp_set_post_terms( $post_ID, $tags, $taxonomy ); + wp_set_post_terms( $post_id, $tags, $taxonomy ); } } } if ( ! empty( $postarr['meta_input'] ) ) { foreach ( $postarr['meta_input'] as $field => $value ) { - update_post_meta( $post_ID, $field, $value ); - } - } - - $current_guid = get_post_field( 'guid', $post_ID ); + update_post_meta( $post_id, $field, $value ); + } + } + + $current_guid = get_post_field( 'guid', $post_id ); // Set GUID. if ( ! $update && '' === $current_guid ) { - $wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post_ID ) ), $where ); + $wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post_id ) ), $where ); } if ( 'attachment' === $postarr['post_type'] ) { if ( ! empty( $postarr['file'] ) ) { - update_attached_file( $post_ID, $postarr['file'] ); + update_attached_file( $post_id, $postarr['file'] ); } if ( ! empty( $postarr['context'] ) ) { - add_post_meta( $post_ID, '_wp_attachment_context', $postarr['context'], true ); + add_post_meta( $post_id, '_wp_attachment_context', $postarr['context'], true ); } } @@ -4523,9 +4741,9 @@ $thumbnail_support = current_theme_supports( 'post-thumbnails', $post_type ) && post_type_supports( $post_type, 'thumbnail' ) || 'revision' === $post_type; if ( ! $thumbnail_support && 'attachment' === $post_type && $post_mime_type ) { - if ( wp_attachment_is( 'audio', $post_ID ) ) { + if ( wp_attachment_is( 'audio', $post_id ) ) { $thumbnail_support = post_type_supports( 'attachment:audio', 'thumbnail' ) || current_theme_supports( 'post-thumbnails', 'attachment:audio' ); - } elseif ( wp_attachment_is( 'video', $post_ID ) ) { + } elseif ( wp_attachment_is( 'video', $post_id ) ) { $thumbnail_support = post_type_supports( 'attachment:video', 'thumbnail' ) || current_theme_supports( 'post-thumbnails', 'attachment:video' ); } } @@ -4533,16 +4751,16 @@ if ( $thumbnail_support ) { $thumbnail_id = (int) $postarr['_thumbnail_id']; if ( -1 === $thumbnail_id ) { - delete_post_thumbnail( $post_ID ); + delete_post_thumbnail( $post_id ); } else { - set_post_thumbnail( $post_ID, $thumbnail_id ); + set_post_thumbnail( $post_id, $thumbnail_id ); } } } - clean_post_cache( $post_ID ); - - $post = get_post( $post_ID ); + clean_post_cache( $post_id ); + + $post = get_post( $post_id ); if ( ! empty( $postarr['page_template'] ) ) { $post->page_template = $postarr['page_template']; @@ -4553,9 +4771,9 @@ return new WP_Error( 'invalid_page_template', __( 'Invalid page template.' ) ); } - update_post_meta( $post_ID, '_wp_page_template', 'default' ); + update_post_meta( $post_id, '_wp_page_template', 'default' ); } else { - update_post_meta( $post_ID, '_wp_page_template', $postarr['page_template'] ); + update_post_meta( $post_id, '_wp_page_template', $postarr['page_template'] ); } } @@ -4568,22 +4786,22 @@ * * @since 2.0.0 * - * @param int $post_ID Attachment ID. + * @param int $post_id Attachment ID. */ - do_action( 'edit_attachment', $post_ID ); - - $post_after = get_post( $post_ID ); + do_action( 'edit_attachment', $post_id ); + + $post_after = get_post( $post_id ); /** * Fires once an existing attachment has been updated. * * @since 4.4.0 * - * @param int $post_ID Post ID. + * @param int $post_id Post ID. * @param WP_Post $post_after Post object following the update. * @param WP_Post $post_before Post object before the update. */ - do_action( 'attachment_updated', $post_ID, $post_after, $post_before ); + do_action( 'attachment_updated', $post_id, $post_after, $post_before ); } else { /** @@ -4591,12 +4809,12 @@ * * @since 2.0.0 * - * @param int $post_ID Attachment ID. + * @param int $post_id Attachment ID. */ - do_action( 'add_attachment', $post_ID ); - } - - return $post_ID; + do_action( 'add_attachment', $post_id ); + } + + return $post_id; } if ( $update ) { @@ -4613,33 +4831,33 @@ * * @since 5.1.0 * - * @param int $post_ID Post ID. + * @param int $post_id Post ID. * @param WP_Post $post Post object. */ - do_action( "edit_post_{$post->post_type}", $post_ID, $post ); + do_action( "edit_post_{$post->post_type}", $post_id, $post ); /** * Fires once an existing post has been updated. * * @since 1.2.0 * - * @param int $post_ID Post ID. + * @param int $post_id Post ID. * @param WP_Post $post Post object. */ - do_action( 'edit_post', $post_ID, $post ); - - $post_after = get_post( $post_ID ); + do_action( 'edit_post', $post_id, $post ); + + $post_after = get_post( $post_id ); /** * Fires once an existing post has been updated. * * @since 3.0.0 * - * @param int $post_ID Post ID. + * @param int $post_id Post ID. * @param WP_Post $post_after Post object following the update. * @param WP_Post $post_before Post object before the update. */ - do_action( 'post_updated', $post_ID, $post_after, $post_before ); + do_action( 'post_updated', $post_id, $post_after, $post_before ); } /** @@ -4655,43 +4873,43 @@ * * @since 3.7.0 * - * @param int $post_ID Post ID. + * @param int $post_id Post ID. * @param WP_Post $post Post object. * @param bool $update Whether this is an existing post being updated. */ - do_action( "save_post_{$post->post_type}", $post_ID, $post, $update ); + do_action( "save_post_{$post->post_type}", $post_id, $post, $update ); /** * Fires once a post has been saved. * * @since 1.5.0 * - * @param int $post_ID Post ID. + * @param int $post_id Post ID. * @param WP_Post $post Post object. * @param bool $update Whether this is an existing post being updated. */ - do_action( 'save_post', $post_ID, $post, $update ); + do_action( 'save_post', $post_id, $post, $update ); /** * Fires once a post has been saved. * * @since 2.0.0 * - * @param int $post_ID Post ID. + * @param int $post_id Post ID. * @param WP_Post $post Post object. * @param bool $update Whether this is an existing post being updated. */ - do_action( 'wp_insert_post', $post_ID, $post, $update ); + do_action( 'wp_insert_post', $post_id, $post, $update ); if ( $fire_after_hooks ) { wp_after_insert_post( $post, $update, $post_before ); } - return $post_ID; -} - -/** - * Update a post with new post data. + return $post_id; +} + +/** + * Updates a post with new post data. * * The date does not have to be set for drafts. You can set the date and it will * not be overridden. @@ -4776,7 +4994,7 @@ } /** - * Publish a post by transitioning the post status. + * Publishes a post by transitioning the post status. * * @since 2.1.0 * @@ -4853,17 +5071,17 @@ } /** - * Publish future post and make sure post ID has future post status. + * Publishes future post and make sure post ID has future post status. * * Invoked by cron 'publish_future_post' event. This safeguard prevents cron * from publishing drafts, etc. * * @since 2.5.0 * - * @param int|WP_Post $post_id Post ID or post object. - */ -function check_and_publish_future_post( $post_id ) { - $post = get_post( $post_id ); + * @param int|WP_Post $post Post ID or post object. + */ +function check_and_publish_future_post( $post ) { + $post = get_post( $post ); if ( ! $post ) { return; @@ -4877,13 +5095,13 @@ // Uh oh, someone jumped the gun! if ( $time > time() ) { - wp_clear_scheduled_hook( 'publish_future_post', array( $post_id ) ); // Clear anything else in the system. - wp_schedule_single_event( $time, 'publish_future_post', array( $post_id ) ); + wp_clear_scheduled_hook( 'publish_future_post', array( $post->ID ) ); // Clear anything else in the system. + wp_schedule_single_event( $time, 'publish_future_post', array( $post->ID ) ); return; } // wp_publish_post() returns no meaningful value. - wp_publish_post( $post_id ); + wp_publish_post( $post->ID ); } /** @@ -4896,8 +5114,8 @@ * * @since 5.7.0 * - * @param string $post_date The date in mysql format. - * @param string $post_date_gmt The GMT date in mysql format. + * @param string $post_date The date in mysql format (`Y-m-d H:i:s`). + * @param string $post_date_gmt The GMT date in mysql format (`Y-m-d H:i:s`). * @return string|false A valid Gregorian-calendar date string, or false on failure. */ function wp_resolve_post_date( $post_date = '', $post_date_gmt = '' ) { @@ -4911,9 +5129,9 @@ } // Validate the date. - $month = substr( $post_date, 5, 2 ); - $day = substr( $post_date, 8, 2 ); - $year = substr( $post_date, 0, 4 ); + $month = (int) substr( $post_date, 5, 2 ); + $day = (int) substr( $post_date, 8, 2 ); + $year = (int) substr( $post_date, 0, 4 ); $valid_date = wp_checkdate( $month, $day, $year, $post_date ); @@ -4932,13 +5150,13 @@ * @global WP_Rewrite $wp_rewrite WordPress rewrite component. * * @param string $slug The desired slug (post_name). - * @param int $post_ID Post ID. + * @param int $post_id Post ID. * @param string $post_status No uniqueness checks are made if the post is still draft or pending. * @param string $post_type Post type. * @param int $post_parent Post parent ID. * @return string Unique slug for the post, based on $post_name (with a -1, -2, etc. suffix) */ -function wp_unique_post_slug( $slug, $post_ID, $post_status, $post_type, $post_parent ) { +function wp_unique_post_slug( $slug, $post_id, $post_status, $post_type, $post_parent ) { if ( in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ), true ) || ( 'inherit' === $post_status && 'revision' === $post_type ) || 'user_request' === $post_type ) { @@ -4955,12 +5173,12 @@ * * @param string|null $override_slug Short-circuit return value. * @param string $slug The desired slug (post_name). - * @param int $post_ID Post ID. + * @param int $post_id Post ID. * @param string $post_status The post status. * @param string $post_type Post type. * @param int $post_parent Post parent ID. */ - $override_slug = apply_filters( 'pre_wp_unique_post_slug', null, $slug, $post_ID, $post_status, $post_type, $post_parent ); + $override_slug = apply_filters( 'pre_wp_unique_post_slug', null, $slug, $post_id, $post_status, $post_type, $post_parent ); if ( null !== $override_slug ) { return $override_slug; } @@ -4977,7 +5195,7 @@ if ( 'attachment' === $post_type ) { // Attachment slugs must be unique across all types. $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND ID != %d LIMIT 1"; - $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_ID ) ); + $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_id ) ); /** * Filters whether the post slug would make a bad attachment slug. @@ -4996,8 +5214,8 @@ $suffix = 2; do { $alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix"; - $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_ID ) ); - $suffix++; + $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_id ) ); + ++$suffix; } while ( $post_name_check ); $slug = $alt_post_name; } @@ -5011,7 +5229,7 @@ * namespace than posts so page slugs are allowed to overlap post slugs. */ $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( %s, 'attachment' ) AND ID != %d AND post_parent = %d LIMIT 1"; - $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID, $post_parent ) ); + $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_id, $post_parent ) ); /** * Filters whether the post slug would make a bad hierarchical post slug. @@ -5033,17 +5251,17 @@ $suffix = 2; do { $alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix"; - $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_type, $post_ID, $post_parent ) ); - $suffix++; + $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_type, $post_id, $post_parent ) ); + ++$suffix; } while ( $post_name_check ); $slug = $alt_post_name; } } else { // Post slugs must be unique across all posts. $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d LIMIT 1"; - $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID ) ); - - $post = get_post( $post_ID ); + $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_id ) ); + + $post = get_post( $post_id ); // Prevent new post slugs that could result in URLs that conflict with date archives. $conflicts_with_date_archive = false; @@ -5089,8 +5307,8 @@ $suffix = 2; do { $alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix"; - $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_type, $post_ID ) ); - $suffix++; + $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_type, $post_id ) ); + ++$suffix; } while ( $post_name_check ); $slug = $alt_post_name; } @@ -5102,17 +5320,17 @@ * @since 3.3.0 * * @param string $slug The post slug. - * @param int $post_ID Post ID. + * @param int $post_id Post ID. * @param string $post_status The post status. * @param string $post_type Post type. * @param int $post_parent Post parent ID * @param string $original_slug The original post slug. */ - return apply_filters( 'wp_unique_post_slug', $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug ); -} - -/** - * Truncate a post slug. + return apply_filters( 'wp_unique_post_slug', $slug, $post_id, $post_status, $post_type, $post_parent, $original_slug ); +} + +/** + * Truncates a post slug. * * @since 3.6.0 * @access private @@ -5137,7 +5355,7 @@ } /** - * Add tags to a post. + * Adds tags to a post. * * @see wp_set_post_tags() * @@ -5153,7 +5371,7 @@ } /** - * Set the tags for a post. + * Sets the tags for a post. * * @since 2.3.0 * @@ -5171,14 +5389,14 @@ } /** - * Set the terms for a post. + * Sets the terms for a post. * * @since 2.8.0 * * @see wp_set_object_terms() * * @param int $post_id Optional. The Post ID. Does not default to the ID of the global $post. - * @param string|array $tags Optional. An array of terms to set for the post, or a string of terms + * @param string|array $terms Optional. An array of terms to set for the post, or a string of terms * separated by commas. Hierarchical taxonomies must always pass IDs rather * than names so that children with the same names but different parents * aren't confused. Default empty. @@ -5187,23 +5405,23 @@ * replace the terms with the new terms. Default false. * @return array|false|WP_Error Array of term taxonomy IDs of affected terms. WP_Error or false on failure. */ -function wp_set_post_terms( $post_id = 0, $tags = '', $taxonomy = 'post_tag', $append = false ) { +function wp_set_post_terms( $post_id = 0, $terms = '', $taxonomy = 'post_tag', $append = false ) { $post_id = (int) $post_id; if ( ! $post_id ) { return false; } - if ( empty( $tags ) ) { - $tags = array(); - } - - if ( ! is_array( $tags ) ) { + if ( empty( $terms ) ) { + $terms = array(); + } + + if ( ! is_array( $terms ) ) { $comma = _x( ',', 'tag delimiter' ); if ( ',' !== $comma ) { - $tags = str_replace( $comma, ',', $tags ); - } - $tags = explode( ',', trim( $tags, " \n\t\r\0\x0B," ) ); + $terms = str_replace( $comma, ',', $terms ); + } + $terms = explode( ',', trim( $terms, " \n\t\r\0\x0B," ) ); } /* @@ -5211,20 +5429,20 @@ * children with the same names but different parents aren't confused. */ if ( is_taxonomy_hierarchical( $taxonomy ) ) { - $tags = array_unique( array_map( 'intval', $tags ) ); - } - - return wp_set_object_terms( $post_id, $tags, $taxonomy, $append ); -} - -/** - * Set categories for a post. + $terms = array_unique( array_map( 'intval', $terms ) ); + } + + return wp_set_object_terms( $post_id, $terms, $taxonomy, $append ); +} + +/** + * Sets categories for a post. * * If no categories are provided, the default category is used. * * @since 2.1.0 * - * @param int $post_ID Optional. The Post ID. Does not default to the ID + * @param int $post_id Optional. The Post ID. Does not default to the ID * of the global $post. Default 0. * @param int[]|int $post_categories Optional. List of category IDs, or the ID of a single category. * Default empty array. @@ -5232,10 +5450,10 @@ * If false, replace the categories with the new categories. * @return array|false|WP_Error Array of term taxonomy IDs of affected categories. WP_Error or false on failure. */ -function wp_set_post_categories( $post_ID = 0, $post_categories = array(), $append = false ) { - $post_ID = (int) $post_ID; - $post_type = get_post_type( $post_ID ); - $post_status = get_post_status( $post_ID ); +function wp_set_post_categories( $post_id = 0, $post_categories = array(), $append = false ) { + $post_id = (int) $post_id; + $post_type = get_post_type( $post_id ); + $post_status = get_post_status( $post_id ); // If $post_categories isn't already an array, make it one. $post_categories = (array) $post_categories; @@ -5266,7 +5484,7 @@ return true; } - return wp_set_post_terms( $post_ID, $post_categories, 'category', $append ); + return wp_set_post_terms( $post_id, $post_categories, 'category', $append ); } /** @@ -5373,6 +5591,7 @@ */ function wp_after_insert_post( $post, $update, $post_before ) { $post = get_post( $post ); + if ( ! $post ) { return; } @@ -5398,22 +5617,22 @@ // /** - * Add a URL to those already pinged. + * Adds a URL to those already pinged. * * @since 1.5.0 - * @since 4.7.0 `$post_id` can be a WP_Post object. + * @since 4.7.0 `$post` can be a WP_Post object. * @since 4.7.0 `$uri` can be an array of URIs. * * @global wpdb $wpdb WordPress database abstraction object. * - * @param int|WP_Post $post_id Post object or ID. - * @param string|array $uri Ping URI or array of URIs. + * @param int|WP_Post $post Post ID or post object. + * @param string|array $uri Ping URI or array of URIs. * @return int|false How many rows were updated. */ -function add_ping( $post_id, $uri ) { +function add_ping( $post, $uri ) { global $wpdb; - $post = get_post( $post_id ); + $post = get_post( $post ); if ( ! $post ) { return false; @@ -5444,7 +5663,7 @@ } /** - * Retrieve enclosures already enclosed for a post. + * Retrieves enclosures already enclosed for a post. * * @since 1.5.0 * @@ -5480,17 +5699,17 @@ } /** - * Retrieve URLs already pinged for a post. + * Retrieves URLs already pinged for a post. * * @since 1.5.0 * - * @since 4.7.0 `$post_id` can be a WP_Post object. - * - * @param int|WP_Post $post_id Post ID or object. + * @since 4.7.0 `$post` can be a WP_Post object. + * + * @param int|WP_Post $post Post ID or object. * @return string[]|false Array of URLs already pinged for the given post, false if the post is not found. */ -function get_pung( $post_id ) { - $post = get_post( $post_id ); +function get_pung( $post ) { + $post = get_post( $post ); if ( ! $post ) { return false; @@ -5510,16 +5729,16 @@ } /** - * Retrieve URLs that need to be pinged. + * Retrieves URLs that need to be pinged. * * @since 1.5.0 - * @since 4.7.0 `$post_id` can be a WP_Post object. - * - * @param int|WP_Post $post_id Post Object or ID + * @since 4.7.0 `$post` can be a WP_Post object. + * + * @param int|WP_Post $post Post ID or post object. * @return string[]|false List of URLs yet to ping. */ -function get_to_ping( $post_id ) { - $post = get_post( $post_id ); +function get_to_ping( $post ) { + $post = get_post( $post ); if ( ! $post ) { return false; @@ -5539,7 +5758,7 @@ } /** - * Do trackbacks for a list of URLs. + * Does trackbacks for a list of URLs. * * @since 1.0.0 * @@ -5571,7 +5790,7 @@ // /** - * Get a list of page IDs. + * Gets a list of page IDs. * * @since 2.0.0 * @@ -5632,7 +5851,7 @@ $hash = md5( $page_path . serialize( $post_type ) ); $cache_key = "get_page_by_path:$hash:$last_changed"; - $cached = wp_cache_get( $cache_key, 'posts' ); + $cached = wp_cache_get( $cache_key, 'post-queries' ); if ( false !== $cached ) { // Special case: '0' is a bad `$page_path`. if ( '0' === $cached || 0 === $cached ) { @@ -5681,7 +5900,7 @@ * ensuring each matches the post ancestry. */ while ( 0 != $p->post_parent && isset( $pages[ $p->post_parent ] ) ) { - $count++; + ++$count; $parent = $pages[ $p->post_parent ]; if ( ! isset( $revparts[ $count ] ) || $parent->post_name != $revparts[ $count ] ) { break; @@ -5689,7 +5908,7 @@ $p = $parent; } - if ( 0 == $p->post_parent && count( $revparts ) == $count + 1 && $p->post_name == $revparts[ $count ] ) { + if ( 0 == $p->post_parent && count( $revparts ) === $count + 1 && $p->post_name == $revparts[ $count ] ) { $foundid = $page->ID; if ( $page->post_type == $post_type ) { break; @@ -5699,7 +5918,7 @@ } // We cache misses as well as hits. - wp_cache_set( $cache_key, $foundid, 'posts' ); + wp_cache_set( $cache_key, $foundid, 'post-queries' ); if ( $foundid ) { return get_post( $foundid, $output ); @@ -5709,74 +5928,15 @@ } /** - * Retrieve a page given its title. - * - * If more than one post uses the same title, the post with the smallest ID will be returned. - * Be careful: in case of more than one post having the same title, it will check the oldest - * publication date, not the smallest ID. - * - * Because this function uses the MySQL '=' comparison, $page_title will usually be matched - * as case-insensitive with default collation. - * - * @since 2.1.0 - * @since 3.0.0 The `$post_type` parameter was added. - * - * @global wpdb $wpdb WordPress database abstraction object. - * - * @param string $page_title Page title. - * @param string $output Optional. The required return type. One of OBJECT, ARRAY_A, or ARRAY_N, which - * correspond to a WP_Post object, an associative array, or a numeric array, - * respectively. Default OBJECT. - * @param string|array $post_type Optional. Post type or array of post types. Default 'page'. - * @return WP_Post|array|null WP_Post (or array) on success, or null on failure. - */ -function get_page_by_title( $page_title, $output = OBJECT, $post_type = 'page' ) { - global $wpdb; - - if ( is_array( $post_type ) ) { - $post_type = esc_sql( $post_type ); - $post_type_in_string = "'" . implode( "','", $post_type ) . "'"; - $sql = $wpdb->prepare( - " - SELECT ID - FROM $wpdb->posts - WHERE post_title = %s - AND post_type IN ($post_type_in_string) - ", - $page_title - ); - } else { - $sql = $wpdb->prepare( - " - SELECT ID - FROM $wpdb->posts - WHERE post_title = %s - AND post_type = %s - ", - $page_title, - $post_type - ); - } - - $page = $wpdb->get_var( $sql ); - - if ( $page ) { - return get_post( $page, $output ); - } - - return null; -} - -/** - * Identify descendants of a given page ID in a list of page objects. + * Identifies descendants of a given page ID in a list of page objects. * * Descendants are identified from the `$pages` array passed to the function. No database queries are performed. * * @since 1.5.1 * - * @param int $page_id Page ID. - * @param array $pages List of page objects from which descendants should be identified. - * @return array List of page children. + * @param int $page_id Page ID. + * @param WP_Post[] $pages List of page objects from which descendants should be identified. + * @return WP_Post[] List of page children. */ function get_page_children( $page_id, $pages ) { // Build a hash of ID -> children. @@ -5808,7 +5968,7 @@ } /** - * Order the pages with children under parents in a flat list. + * Orders the pages with children under parents in a flat list. * * It uses auxiliary structure to hold parent-children relationships and * runs in O(N) complexity @@ -5837,7 +5997,7 @@ } /** - * Traverse and return all the nested children post names of a root page. + * Traverses and return all the nested children post names of a root page. * * $children contains parent-children relations * @@ -5860,7 +6020,7 @@ } /** - * Build the URI path for a page. + * Builds the URI path for a page. * * Sub pages will be in the "directory" under the parent page post name. * @@ -5900,11 +6060,10 @@ } /** - * Retrieve an array of pages (or hierarchical post type items). - * - * @global wpdb $wpdb WordPress database abstraction object. + * Retrieves an array of pages (or hierarchical post type items). * * @since 1.5.0 + * @since 6.3.0 Use WP_Query internally. * * @param array|string $args { * Optional. Array or string of arguments to retrieve pages. @@ -5939,13 +6098,11 @@ * @type string|array $post_status A comma-separated list or array of post statuses to include. * Default 'publish'. * } - * @return WP_Post[]|int[]|false Array of pages (or hierarchical post type items). Boolean false if the - * specified post type is not hierarchical or the specified status is not - * supported by the post type. + * @return WP_Post[]|false Array of pages (or hierarchical post type items). Boolean false if the + * specified post type is not hierarchical or the specified status is not + * supported by the post type. */ function get_pages( $args = array() ) { - global $wpdb; - $defaults = array( 'child_of' => 0, 'sort_order' => 'ASC', @@ -5994,50 +6151,35 @@ return false; } - // $args can be whatever, only use the args defined in defaults to compute the key. - $key = md5( serialize( wp_array_slice_assoc( $parsed_args, array_keys( $defaults ) ) ) ); - $last_changed = wp_cache_get_last_changed( 'posts' ); - - $cache_key = "get_pages:$key:$last_changed"; - $cache = wp_cache_get( $cache_key, 'posts' ); - if ( false !== $cache ) { - _prime_post_caches( $cache, false, false ); - - // Convert to WP_Post instances. - $pages = array_map( 'get_post', $cache ); - /** This filter is documented in wp-includes/post.php */ - $pages = apply_filters( 'get_pages', $pages, $parsed_args ); - - return $pages; - } - - $inclusions = ''; + $query_args = array( + 'orderby' => 'post_title', + 'order' => 'ASC', + 'post__not_in' => wp_parse_id_list( $exclude ), + 'meta_key' => $meta_key, + 'meta_value' => $meta_value, + 'posts_per_page' => -1, + 'offset' => $offset, + 'post_type' => $parsed_args['post_type'], + 'post_status' => $post_status, + 'update_post_term_cache' => false, + 'update_post_meta_cache' => false, + 'ignore_sticky_posts' => true, + 'no_found_rows' => true, + ); + if ( ! empty( $parsed_args['include'] ) ) { - $child_of = 0; // Ignore child_of, parent, exclude, meta_key, and meta_value params if using include. - $parent = -1; - $exclude = ''; - $meta_key = ''; - $meta_value = ''; - $hierarchical = false; - $incpages = wp_parse_id_list( $parsed_args['include'] ); - if ( ! empty( $incpages ) ) { - $inclusions = ' AND ID IN (' . implode( ',', $incpages ) . ')'; - } - } - - $exclusions = ''; - if ( ! empty( $exclude ) ) { - $expages = wp_parse_id_list( $exclude ); - if ( ! empty( $expages ) ) { - $exclusions = ' AND ID NOT IN (' . implode( ',', $expages ) . ')'; - } - } - - $author_query = ''; + $child_of = 0; // Ignore child_of, parent, exclude, meta_key, and meta_value params if using include. + $parent = -1; + unset( $query_args['post__not_in'], $query_args['meta_key'], $query_args['meta_value'] ); + $hierarchical = false; + $query_args['post__in'] = wp_parse_id_list( $parsed_args['include'] ); + } + if ( ! empty( $parsed_args['authors'] ) ) { $post_authors = wp_parse_list( $parsed_args['authors'] ); if ( ! empty( $post_authors ) ) { + $query_args['author__in'] = array(); foreach ( $post_authors as $post_author ) { // Do we have an author id or an author login? if ( 0 == (int) $post_author ) { @@ -6050,136 +6192,61 @@ } $post_author = $post_author->ID; } - - if ( '' === $author_query ) { - $author_query = $wpdb->prepare( ' post_author = %d ', $post_author ); - } else { - $author_query .= $wpdb->prepare( ' OR post_author = %d ', $post_author ); - } - } - if ( '' !== $author_query ) { - $author_query = " AND ($author_query)"; + $query_args['author__in'][] = (int) $post_author; } } } - $join = ''; - $where = "$exclusions $inclusions "; - if ( '' !== $meta_key || '' !== $meta_value ) { - $join = " LEFT JOIN $wpdb->postmeta ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id )"; - - // meta_key and meta_value might be slashed. - $meta_key = wp_unslash( $meta_key ); - $meta_value = wp_unslash( $meta_value ); - if ( '' !== $meta_key ) { - $where .= $wpdb->prepare( " AND $wpdb->postmeta.meta_key = %s", $meta_key ); - } - if ( '' !== $meta_value ) { - $where .= $wpdb->prepare( " AND $wpdb->postmeta.meta_value = %s", $meta_value ); - } - } - if ( is_array( $parent ) ) { - $post_parent__in = implode( ',', array_map( 'absint', (array) $parent ) ); + $post_parent__in = array_map( 'absint', (array) $parent ); if ( ! empty( $post_parent__in ) ) { - $where .= " AND post_parent IN ($post_parent__in)"; + $query_args['post_parent__in'] = $post_parent__in; } } elseif ( $parent >= 0 ) { - $where .= $wpdb->prepare( ' AND post_parent = %d ', $parent ); - } - - if ( 1 === count( $post_status ) ) { - $where_post_type = $wpdb->prepare( 'post_type = %s AND post_status = %s', $parsed_args['post_type'], reset( $post_status ) ); - } else { - $post_status = implode( "', '", $post_status ); - $where_post_type = $wpdb->prepare( "post_type = %s AND post_status IN ('$post_status')", $parsed_args['post_type'] ); - } - - $orderby_array = array(); - $allowed_keys = array( - 'author', - 'post_author', - 'date', - 'post_date', - 'title', - 'post_title', - 'name', - 'post_name', - 'modified', - 'post_modified', - 'modified_gmt', - 'post_modified_gmt', - 'menu_order', - 'parent', - 'post_parent', - 'ID', - 'rand', - 'comment_count', + $query_args['post_parent'] = $parent; + } + + /* + * Maintain backward compatibility for `sort_column` key. + * Additionally to `WP_Query`, it has been supporting the `post_modified_gmt` field, so this logic will translate + * it to `post_modified` which should result in the same order given the two dates in the fields match. + */ + $orderby = wp_parse_list( $parsed_args['sort_column'] ); + $orderby = array_map( + static function ( $orderby_field ) { + $orderby_field = trim( $orderby_field ); + if ( 'post_modified_gmt' === $orderby_field || 'modified_gmt' === $orderby_field ) { + $orderby_field = str_replace( '_gmt', '', $orderby_field ); + } + return $orderby_field; + }, + $orderby ); - - foreach ( explode( ',', $parsed_args['sort_column'] ) as $orderby ) { - $orderby = trim( $orderby ); - if ( ! in_array( $orderby, $allowed_keys, true ) ) { - continue; - } - - switch ( $orderby ) { - case 'menu_order': - break; - case 'ID': - $orderby = "$wpdb->posts.ID"; - break; - case 'rand': - $orderby = 'RAND()'; - break; - case 'comment_count': - $orderby = "$wpdb->posts.comment_count"; - break; - default: - if ( 0 === strpos( $orderby, 'post_' ) ) { - $orderby = "$wpdb->posts." . $orderby; - } else { - $orderby = "$wpdb->posts.post_" . $orderby; - } - } - - $orderby_array[] = $orderby; - - } - $sort_column = ! empty( $orderby_array ) ? implode( ',', $orderby_array ) : "$wpdb->posts.post_title"; - - $sort_order = strtoupper( $parsed_args['sort_order'] ); - if ( '' !== $sort_order && ! in_array( $sort_order, array( 'ASC', 'DESC' ), true ) ) { - $sort_order = 'ASC'; - } - - $query = "SELECT * FROM $wpdb->posts $join WHERE ($where_post_type) $where "; - $query .= $author_query; - $query .= ' ORDER BY ' . $sort_column . ' ' . $sort_order; + if ( $orderby ) { + $query_args['orderby'] = array_fill_keys( $orderby, $parsed_args['sort_order'] ); + } + + $order = $parsed_args['sort_order']; + if ( $order ) { + $query_args['order'] = $order; + } if ( ! empty( $number ) ) { - $query .= ' LIMIT ' . $offset . ',' . $number; - } - - $pages = $wpdb->get_results( $query ); - - if ( empty( $pages ) ) { - wp_cache_set( $cache_key, array(), 'posts' ); - - /** This filter is documented in wp-includes/post.php */ - $pages = apply_filters( 'get_pages', array(), $parsed_args ); - - return $pages; - } - - // Sanitize before caching so it'll only get done once. - $num_pages = count( $pages ); - for ( $i = 0; $i < $num_pages; $i++ ) { - $pages[ $i ] = sanitize_post( $pages[ $i ], 'raw' ); - } - - // Update cache. - update_post_cache( $pages ); + $query_args['posts_per_page'] = $number; + } + + /** + * Filters query arguments passed to WP_Query in get_pages. + * + * @since 6.3.0 + * + * @param array $query_args Array of arguments passed to WP_Query. + * @param array $parsed_args Array of get_pages() arguments. + */ + $query_args = apply_filters( 'get_pages_query_args', $query_args, $parsed_args ); + + $pages = new WP_Query(); + $pages = $pages->query( $query_args ); if ( $child_of || $hierarchical ) { $pages = get_page_children( $child_of, $pages ); @@ -6202,16 +6269,6 @@ } } - $page_structure = array(); - foreach ( $pages as $page ) { - $page_structure[] = $page->ID; - } - - wp_cache_set( $cache_key, $page_structure, 'posts' ); - - // Convert to WP_Post instances. - $pages = array_map( 'get_post', $pages ); - /** * Filters the retrieved list of pages. * @@ -6240,10 +6297,10 @@ * @return bool True on success, false on failure. */ function is_local_attachment( $url ) { - if ( strpos( $url, home_url() ) === false ) { + if ( ! str_contains( $url, home_url() ) ) { return false; } - if ( strpos( $url, home_url( '/?attachment_id=' ) ) !== false ) { + if ( str_contains( $url, home_url( '/?attachment_id=' ) ) ) { return true; } @@ -6258,7 +6315,7 @@ } /** - * Insert an attachment. + * Inserts an attachment. * * If you set the 'ID' in the $args parameter, it will mean that you are * updating and attempt to update the attachment. You can also set the @@ -6278,13 +6335,13 @@ * @see wp_insert_post() * * @param string|array $args Arguments for inserting an attachment. - * @param string|false $file Optional. Filename. - * @param int $parent Optional. Parent post ID. + * @param string|false $file Optional. Filename. Default false. + * @param int $parent_post_id Optional. Parent post ID or 0 for no parent. Default 0. * @param bool $wp_error Optional. Whether to return a WP_Error on failure. Default false. * @param bool $fire_after_hooks Optional. Whether to fire the after insert hooks. Default true. * @return int|WP_Error The attachment ID on success. The value 0 or WP_Error on failure. */ -function wp_insert_attachment( $args, $file = false, $parent = 0, $wp_error = false, $fire_after_hooks = true ) { +function wp_insert_attachment( $args, $file = false, $parent_post_id = 0, $wp_error = false, $fire_after_hooks = true ) { $defaults = array( 'file' => $file, 'post_parent' => 0, @@ -6292,8 +6349,8 @@ $data = wp_parse_args( $args, $defaults ); - if ( ! empty( $parent ) ) { - $data['post_parent'] = $parent; + if ( ! empty( $parent_post_id ) ) { + $data['post_parent'] = $parent_post_id; } $data['post_type'] = 'attachment'; @@ -6302,7 +6359,7 @@ } /** - * Trash or delete an attachment. + * Trashes or deletes an attachment. * * When an attachment is permanently deleted, the file will also be removed. * Deletion removes all post meta fields, taxonomy, comments, etc. associated @@ -6344,7 +6401,7 @@ * * @since 5.5.0 * - * @param WP_Post|false|null $delete Whether to go forward with deletion. @TODO description + * @param WP_Post|false|null $delete Whether to go forward with deletion. * @param WP_Post $post Post object. * @param bool $force_delete Whether to bypass the Trash. */ @@ -6506,6 +6563,7 @@ * Retrieves attachment metadata for attachment ID. * * @since 2.1.0 + * @since 6.0.0 The `$filesize` value was added to the returned array. * * @param int $attachment_id Attachment post ID. Defaults to global $post. * @param bool $unfiltered Optional. If true, filters are not run. Default false. @@ -6590,7 +6648,7 @@ } /** - * Retrieve the URL for an attachment. + * Retrieves the URL for an attachment. * * @since 2.1.0 * @@ -6622,10 +6680,10 @@ $uploads = wp_get_upload_dir(); if ( $uploads && false === $uploads['error'] ) { // Check that the upload base exists in the file location. - if ( 0 === strpos( $file, $uploads['basedir'] ) ) { + if ( str_starts_with( $file, $uploads['basedir'] ) ) { // Replace file location with url location. $url = str_replace( $uploads['basedir'], $uploads['baseurl'], $file ); - } elseif ( false !== strpos( $file, 'wp-content/uploads' ) ) { + } elseif ( str_contains( $file, 'wp-content/uploads' ) ) { // Get the directory name relative to the basedir (back compat for pre-2.7 uploads). $url = trailingslashit( $uploads['baseurl'] . '/' . _wp_get_attachment_relative_path( $file ) ) . wp_basename( $file ); } else { @@ -6699,87 +6757,36 @@ } /** - * Retrieve thumbnail for an attachment. + * Retrieves URL for an attachment thumbnail. * * @since 2.1.0 - * - * @param int $post_id Optional. Attachment ID. Default is the ID of the global `$post`. - * @return string|false Thumbnail file path on success, false on failure. - */ -function wp_get_attachment_thumb_file( $post_id = 0 ) { - $post_id = (int) $post_id; - $post = get_post( $post_id ); - - if ( ! $post ) { - return false; - } - - $imagedata = wp_get_attachment_metadata( $post->ID ); - if ( ! is_array( $imagedata ) ) { - return false; - } - - $file = get_attached_file( $post->ID ); - - if ( ! empty( $imagedata['thumb'] ) ) { - $thumbfile = str_replace( wp_basename( $file ), $imagedata['thumb'], $file ); - if ( file_exists( $thumbfile ) ) { - /** - * Filters the attachment thumbnail file path. - * - * @since 2.1.0 - * - * @param string $thumbfile File path to the attachment thumbnail. - * @param int $post_id Attachment ID. - */ - return apply_filters( 'wp_get_attachment_thumb_file', $thumbfile, $post->ID ); - } - } - return false; -} - -/** - * Retrieve URL for an attachment thumbnail. - * - * @since 2.1.0 + * @since 6.1.0 Changed to use wp_get_attachment_image_url(). * * @param int $post_id Optional. Attachment ID. Default is the ID of the global `$post`. * @return string|false Thumbnail URL on success, false on failure. */ function wp_get_attachment_thumb_url( $post_id = 0 ) { $post_id = (int) $post_id; - $post = get_post( $post_id ); - - if ( ! $post ) { - return false; - } - - $url = wp_get_attachment_url( $post->ID ); - if ( ! $url ) { + + /* + * This uses image_downsize() which also looks for the (very) old format $image_meta['thumb'] + * when the newer format $image_meta['sizes']['thumbnail'] doesn't exist. + */ + $thumbnail_url = wp_get_attachment_image_url( $post_id, 'thumbnail' ); + + if ( empty( $thumbnail_url ) ) { return false; } - $sized = image_downsize( $post_id, 'thumbnail' ); - if ( $sized ) { - return $sized[0]; - } - - $thumb = wp_get_attachment_thumb_file( $post->ID ); - if ( ! $thumb ) { - return false; - } - - $url = str_replace( wp_basename( $url ), wp_basename( $thumb ), $url ); - /** * Filters the attachment thumbnail URL. * * @since 2.1.0 * - * @param string $url URL for the attachment thumbnail. - * @param int $post_id Attachment ID. + * @param string $thumbnail_url URL for the attachment thumbnail. + * @param int $post_id Attachment ID. */ - return apply_filters( 'wp_get_attachment_thumb_url', $url, $post->ID ); + return apply_filters( 'wp_get_attachment_thumb_url', $thumbnail_url, $post_id ); } /** @@ -6787,9 +6794,9 @@ * * @since 4.2.0 * - * @param string $type Attachment type. Accepts 'image', 'audio', or 'video'. + * @param string $type Attachment type. Accepts `image`, `audio`, `video`, or a file extension. * @param int|WP_Post $post Optional. Attachment ID or object. Default is global $post. - * @return bool True if one of the accepted types, false otherwise. + * @return bool True if an accepted type or a matching file extension, false otherwise. */ function wp_attachment_is( $type, $post = null ) { $post = get_post( $post ); @@ -6804,7 +6811,7 @@ return false; } - if ( 0 === strpos( $post->post_mime_type, $type . '/' ) ) { + if ( str_starts_with( $post->post_mime_type, $type . '/' ) ) { return true; } @@ -6822,7 +6829,7 @@ switch ( $type ) { case 'image': - $image_exts = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'webp' ); + $image_exts = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png', 'webp', 'avif' ); return in_array( $ext, $image_exts, true ); case 'audio': @@ -6855,18 +6862,25 @@ } /** - * Retrieve the icon for a MIME type or attachment. + * Retrieves the icon for a MIME type or attachment. * * @since 2.1.0 - * - * @param string|int $mime MIME type or attachment ID. + * @since 6.5.0 Added the `$preferred_ext` parameter. + * + * @param string|int $mime MIME type or attachment ID. + * @param string $preferred_ext File format to prefer in return. Default '.png'. * @return string|false Icon, false otherwise. */ -function wp_mime_type_icon( $mime = 0 ) { +function wp_mime_type_icon( $mime = 0, $preferred_ext = '.png' ) { if ( ! is_numeric( $mime ) ) { $icon = wp_cache_get( "mime_type_icon_$mime" ); } + // Check if preferred file format variable is present and is a validly formatted file extension. + if ( ! empty( $preferred_ext ) && is_string( $preferred_ext ) && ! str_starts_with( $preferred_ext, '.' ) ) { + $preferred_ext = '.' . strtolower( $preferred_ext ); + } + $post_id = 0; if ( empty( $icon ) ) { $post_mimes = array(); @@ -6922,6 +6936,7 @@ */ $dirs = apply_filters( 'icon_dirs', array( $icon_dir => $icon_dir_uri ) ); $icon_files = array(); + $all_icons = array(); while ( $dirs ) { $keys = array_keys( $dirs ); $dir = array_shift( $keys ); @@ -6930,22 +6945,29 @@ if ( $dh ) { while ( false !== $file = readdir( $dh ) ) { $file = wp_basename( $file ); - if ( '.' === substr( $file, 0, 1 ) ) { + if ( str_starts_with( $file, '.' ) ) { continue; } $ext = strtolower( substr( $file, -4 ) ); - if ( ! in_array( $ext, array( '.png', '.gif', '.jpg' ), true ) ) { + if ( ! in_array( $ext, array( '.svg', '.png', '.gif', '.jpg' ), true ) ) { if ( is_dir( "$dir/$file" ) ) { $dirs[ "$dir/$file" ] = "$uri/$file"; } continue; } - $icon_files[ "$dir/$file" ] = "$uri/$file"; + $all_icons[ "$dir/$file" ] = "$uri/$file"; + if ( $ext === $preferred_ext ) { + $icon_files[ "$dir/$file" ] = "$uri/$file"; + } } closedir( $dh ); } } + // If directory only contained icons of a non-preferred format, return those. + if ( empty( $icon_files ) ) { + $icon_files = $all_icons; + } wp_cache_add( 'icon_files', $icon_files, 'default', 600 ); } @@ -6993,7 +7015,7 @@ } /** - * Check for changed slugs for published post objects and save the old slug. + * Checks for changed slugs for published post objects and save the old slug. * * The function is used when a post object of any type is updated, * by comparing the current and previous post objects. @@ -7008,8 +7030,8 @@ * @since 2.1.0 * * @param int $post_id Post ID. - * @param WP_Post $post The Post Object - * @param WP_Post $post_before The Previous Post Object + * @param WP_Post $post The post object. + * @param WP_Post $post_before The previous post object. */ function wp_check_for_changed_slugs( $post_id, $post, $post_before ) { // Don't bother if it hasn't changed. @@ -7036,7 +7058,7 @@ } /** - * Check for changed dates for published post objects and save the old date. + * Checks for changed dates for published post objects and save the old date. * * The function is used when a post object of any type is updated, * by comparing the current and previous post objects. @@ -7051,8 +7073,8 @@ * @since 4.9.3 * * @param int $post_id Post ID. - * @param WP_Post $post The Post Object - * @param WP_Post $post_before The Previous Post Object + * @param WP_Post $post The post object. + * @param WP_Post $post_before The previous post object. */ function wp_check_for_changed_dates( $post_id, $post, $post_before ) { $previous_date = gmdate( 'Y-m-d', strtotime( $post_before->post_date ) ); @@ -7082,7 +7104,7 @@ } /** - * Retrieve the private post SQL based on capability. + * Retrieves the private post SQL based on capability. * * This function provides a standardized way to appropriately select on the * post_status of a post type. The function will return a piece of SQL code @@ -7100,7 +7122,7 @@ } /** - * Retrieve the post SQL based on capability, author, and type. + * Retrieves the post SQL based on capability, author, and type. * * @since 3.0.0 * @since 4.3.0 Introduced the ability to pass an array of post types to `$post_type`. @@ -7128,6 +7150,7 @@ $post_type_clauses = array(); foreach ( $post_types as $post_type ) { $post_type_obj = get_post_type_object( $post_type ); + if ( ! $post_type_obj ) { continue; } @@ -7142,12 +7165,14 @@ * @param string $cap Capability. */ $cap = apply_filters_deprecated( 'pub_priv_sql_capability', array( '' ), '3.2.0' ); + if ( ! $cap ) { $cap = current_user_can( $post_type_obj->cap->read_private_posts ); } // Only need to check the cap if $public_only is false. $post_status_sql = "post_status = 'publish'"; + if ( false === $public_only ) { if ( $cap ) { // Does the user have the capability to view private posts? Guess so. @@ -7220,7 +7245,7 @@ } /** - * Get the most recent time that a post on the site was modified. + * Gets the most recent time that a post on the site was modified. * * The server timezone is the default and is the difference between GMT and * server time. The 'blog' value is just when the last post was modified. @@ -7391,6 +7416,7 @@ } wp_cache_delete( $post->ID, 'posts' ); + wp_cache_delete( 'post_parent:' . (string) $post->ID, 'posts' ); wp_cache_delete( $post->ID, 'post_meta' ); clean_object_term_cache( $post->ID, $post->post_type ); @@ -7420,15 +7446,15 @@ do_action( 'clean_page_cache', $post->ID ); } - wp_cache_set( 'last_changed', microtime(), 'posts' ); -} - -/** - * Call major cache updating functions for list of Post objects. + wp_cache_set_posts_last_changed(); +} + +/** + * Updates post, term, and metadata caches for a list of post objects. * * @since 1.5.0 * - * @param WP_Post[] $posts Array of Post objects + * @param WP_Post[] $posts Array of post objects (passed by reference). * @param string $post_type Optional. Post type. Default 'post'. * @param bool $update_term_cache Optional. Whether to update the term cache. Default true. * @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true. @@ -7475,7 +7501,48 @@ } /** - * Updates metadata cache for list of post IDs. + * Updates post author user caches for a list of post objects. + * + * @since 6.1.0 + * + * @param WP_Post[] $posts Array of post objects. + */ +function update_post_author_caches( $posts ) { + /* + * cache_users() is a pluggable function so is not available prior + * to the `plugins_loaded` hook firing. This is to ensure against + * fatal errors when the function is not available. + */ + if ( ! function_exists( 'cache_users' ) ) { + return; + } + + $author_ids = wp_list_pluck( $posts, 'post_author' ); + $author_ids = array_map( 'absint', $author_ids ); + $author_ids = array_unique( array_filter( $author_ids ) ); + + cache_users( $author_ids ); +} + +/** + * Updates parent post caches for a list of post objects. + * + * @since 6.1.0 + * + * @param WP_Post[] $posts Array of post objects. + */ +function update_post_parent_caches( $posts ) { + $parent_ids = wp_list_pluck( $posts, 'post_parent' ); + $parent_ids = array_map( 'absint', $parent_ids ); + $parent_ids = array_unique( array_filter( $parent_ids ) ); + + if ( ! empty( $parent_ids ) ) { + _prime_post_caches( $parent_ids, false ); + } +} + +/** + * Updates metadata cache for a list of post IDs. * * Performs SQL query to retrieve the metadata for the post IDs and updates the * metadata cache for the posts. Therefore, the functions, which call this @@ -7657,14 +7724,16 @@ */ function wp_get_post_parent_id( $post = null ) { $post = get_post( $post ); + if ( ! $post || is_wp_error( $post ) ) { return false; } + return (int) $post->post_parent; } /** - * Check the given subset of the post hierarchy for hierarchy loops. + * Checks the given subset of the post hierarchy for hierarchy loops. * * Prevents loops from forming and breaks those that it finds. Attached * to the {@see 'wp_insert_post_parent'} filter. @@ -7674,37 +7743,37 @@ * @see wp_find_hierarchy_loop() * * @param int $post_parent ID of the parent for the post we're checking. - * @param int $post_ID ID of the post we're checking. + * @param int $post_id ID of the post we're checking. * @return int The new post_parent for the post, 0 otherwise. */ -function wp_check_post_hierarchy_for_loops( $post_parent, $post_ID ) { +function wp_check_post_hierarchy_for_loops( $post_parent, $post_id ) { // Nothing fancy here - bail. if ( ! $post_parent ) { return 0; } // New post can't cause a loop. - if ( ! $post_ID ) { + if ( ! $post_id ) { return $post_parent; } // Can't be its own parent. - if ( $post_parent == $post_ID ) { + if ( $post_parent == $post_id ) { return 0; } // Now look for larger loops. - $loop = wp_find_hierarchy_loop( 'wp_get_post_parent_id', $post_ID, $post_parent ); + $loop = wp_find_hierarchy_loop( 'wp_get_post_parent_id', $post_id, $post_parent ); if ( ! $loop ) { return $post_parent; // No loop. } // Setting $post_parent to the given value causes a loop. - if ( isset( $loop[ $post_ID ] ) ) { + if ( isset( $loop[ $post_id ] ) ) { return 0; } - // There's a loop, but it doesn't contain $post_ID. Break the loop. + // There's a loop, but it doesn't contain $post_id. Break the loop. foreach ( array_keys( $loop ) as $loop_member ) { wp_update_post( array( @@ -7756,7 +7825,7 @@ } /** - * Delete auto-drafts for new posts that are > 7 days old. + * Deletes auto-drafts for new posts that are > 7 days old. * * @since 3.4.0 * @@ -7778,11 +7847,11 @@ * * @since 4.5.0 * - * @param array $posts Array of WP_Post objects. + * @param WP_Post[] $posts Array of WP_Post objects. */ function wp_queue_posts_for_term_meta_lazyload( $posts ) { $post_type_taxonomies = array(); - $term_ids = array(); + $prime_post_terms = array(); foreach ( $posts as $post ) { if ( ! ( $post instanceof WP_Post ) ) { continue; @@ -7793,26 +7862,36 @@ } foreach ( $post_type_taxonomies[ $post->post_type ] as $taxonomy ) { - // Term cache should already be primed by `update_post_term_cache()`. - $terms = get_object_term_cache( $post->ID, $taxonomy ); - if ( false !== $terms ) { - foreach ( $terms as $term ) { - if ( ! in_array( $term->term_id, $term_ids, true ) ) { - $term_ids[] = $term->term_id; + $prime_post_terms[ $taxonomy ][] = $post->ID; + } + } + + $term_ids = array(); + if ( $prime_post_terms ) { + foreach ( $prime_post_terms as $taxonomy => $post_ids ) { + $cached_term_ids = wp_cache_get_multiple( $post_ids, "{$taxonomy}_relationships" ); + if ( is_array( $cached_term_ids ) ) { + $cached_term_ids = array_filter( $cached_term_ids ); + foreach ( $cached_term_ids as $_term_ids ) { + // Backward compatibility for if a plugin is putting objects into the cache, rather than IDs. + foreach ( $_term_ids as $term_id ) { + if ( is_numeric( $term_id ) ) { + $term_ids[] = (int) $term_id; + } elseif ( isset( $term_id->term_id ) ) { + $term_ids[] = (int) $term_id->term_id; + } } } } } - } - - if ( $term_ids ) { - $lazyloader = wp_metadata_lazyloader(); - $lazyloader->queue_objects( 'term', $term_ids ); - } -} - -/** - * Update the custom taxonomies' term counts when a post's status is changed. + $term_ids = array_unique( $term_ids ); + } + + wp_lazyload_term_meta( $term_ids ); +} + +/** + * Updates the custom taxonomies' term counts when a post's status is changed. * * For example, default posts term counts (for custom taxonomies) don't include * private / draft posts. @@ -7836,13 +7915,15 @@ * Adds any posts from the given IDs to the cache that do not already exist in cache. * * @since 3.4.0 - * @access private - * - * @see update_post_caches() + * @since 6.1.0 This function is no longer marked as "private". + * + * @see update_post_cache() + * @see update_postmeta_cache() + * @see update_object_term_cache() * * @global wpdb $wpdb WordPress database abstraction object. * - * @param array $ids ID list. + * @param int[] $ids ID list. * @param bool $update_term_cache Optional. Whether to update the term cache. Default true. * @param bool $update_meta_cache Optional. Whether to update the meta cache. Default true. */ @@ -7853,7 +7934,67 @@ if ( ! empty( $non_cached_ids ) ) { $fresh_posts = $wpdb->get_results( sprintf( "SELECT $wpdb->posts.* FROM $wpdb->posts WHERE ID IN (%s)", implode( ',', $non_cached_ids ) ) ); - update_post_caches( $fresh_posts, 'any', $update_term_cache, $update_meta_cache ); + if ( $fresh_posts ) { + // Despite the name, update_post_cache() expects an array rather than a single post. + update_post_cache( $fresh_posts ); + } + } + + if ( $update_meta_cache ) { + update_postmeta_cache( $ids ); + } + + if ( $update_term_cache ) { + $post_types = array_map( 'get_post_type', $ids ); + $post_types = array_unique( $post_types ); + update_object_term_cache( $ids, $post_types ); + } +} + +/** + * Prime the cache containing the parent ID of various post objects. + * + * @since 6.4.0 + * + * @global wpdb $wpdb WordPress database abstraction object. + * + * @param int[] $ids ID list. + */ +function _prime_post_parent_id_caches( array $ids ) { + global $wpdb; + + $ids = array_filter( $ids, '_validate_cache_id' ); + $ids = array_unique( array_map( 'intval', $ids ), SORT_NUMERIC ); + + if ( empty( $ids ) ) { + return; + } + + $cache_keys = array(); + foreach ( $ids as $id ) { + $cache_keys[ $id ] = 'post_parent:' . (string) $id; + } + + $cached_data = wp_cache_get_multiple( array_values( $cache_keys ), 'posts' ); + + $non_cached_ids = array(); + foreach ( $cache_keys as $id => $cache_key ) { + if ( false === $cached_data[ $cache_key ] ) { + $non_cached_ids[] = $id; + } + } + + if ( ! empty( $non_cached_ids ) ) { + $fresh_posts = $wpdb->get_results( sprintf( "SELECT $wpdb->posts.ID, $wpdb->posts.post_parent FROM $wpdb->posts WHERE ID IN (%s)", implode( ',', $non_cached_ids ) ) ); + + if ( $fresh_posts ) { + $post_parent_data = array(); + foreach ( $fresh_posts as $fresh_post ) { + $post_parent_data[ 'post_parent:' . (string) $fresh_post->ID ] = (int) $fresh_post->post_parent; + } + + wp_cache_add_multiple( $post_parent_data, 'posts' ); + } } } @@ -7869,16 +8010,16 @@ * @access private * * @param string $post_name Post slug. - * @param int $post_ID Optional. Post ID that should be ignored. Default 0. - */ -function wp_add_trashed_suffix_to_post_name_for_trashed_posts( $post_name, $post_ID = 0 ) { + * @param int $post_id Optional. Post ID that should be ignored. Default 0. + */ +function wp_add_trashed_suffix_to_post_name_for_trashed_posts( $post_name, $post_id = 0 ) { $trashed_posts_with_desired_slug = get_posts( array( 'name' => $post_name, 'post_status' => 'trash', 'post_type' => 'any', 'nopaging' => true, - 'post__not_in' => array( $post_ID ), + 'post__not_in' => array( $post_id ), ) ); @@ -7900,6 +8041,8 @@ * @since 4.5.0 * @access private * + * @global wpdb $wpdb WordPress database abstraction object. + * * @param WP_Post $post The post. * @return string New slug for the post. */ @@ -7908,7 +8051,7 @@ $post = get_post( $post ); - if ( '__trashed' === substr( $post->post_name, -9 ) ) { + if ( str_ends_with( $post->post_name, '__trashed' ) ) { return $post->post_name; } add_post_meta( $post->ID, '_wp_desired_post_slug', $post->post_name ); @@ -7919,59 +8062,43 @@ } /** - * Filters the SQL clauses of an attachment query to include filenames. - * - * @since 4.7.0 - * @access private - * - * @global wpdb $wpdb WordPress database abstraction object. - * - * @param string[] $clauses An array including WHERE, GROUP BY, JOIN, ORDER BY, - * DISTINCT, fields (SELECT), and LIMITS clauses. - * @return string[] The modified array of clauses. - */ -function _filter_query_attachment_filenames( $clauses ) { - global $wpdb; - remove_filter( 'posts_clauses', __FUNCTION__ ); - - // Add a LEFT JOIN of the postmeta table so we don't trample existing JOINs. - $clauses['join'] .= " LEFT JOIN {$wpdb->postmeta} AS sq1 ON ( {$wpdb->posts}.ID = sq1.post_id AND sq1.meta_key = '_wp_attached_file' )"; - - $clauses['groupby'] = "{$wpdb->posts}.ID"; - - $clauses['where'] = preg_replace( - "/\({$wpdb->posts}.post_content (NOT LIKE|LIKE) (\'[^']+\')\)/", - '$0 OR ( sq1.meta_value $1 $2 )', - $clauses['where'] - ); - - return $clauses; -} - -/** * Sets the last changed time for the 'posts' cache group. * * @since 5.0.0 */ function wp_cache_set_posts_last_changed() { - wp_cache_set( 'last_changed', microtime(), 'posts' ); -} - -/** - * Get all available post MIME types for a given post type. + wp_cache_set_last_changed( 'posts' ); +} + +/** + * Gets all available post MIME types for a given post type. * * @since 2.5.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param string $type - * @return mixed + * @return string[] An array of MIME types. */ function get_available_post_mime_types( $type = 'attachment' ) { global $wpdb; - $types = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT post_mime_type FROM $wpdb->posts WHERE post_type = %s", $type ) ); - return $types; + /** + * Filters the list of available post MIME types for the given post type. + * + * @since 6.4.0 + * + * @param string[]|null $mime_types An array of MIME types. Default null. + * @param string $type The post type name. Usually 'attachment' but can be any post type. + */ + $mime_types = apply_filters( 'pre_get_available_post_mime_types', null, $type ); + + if ( ! is_array( $mime_types ) ) { + $mime_types = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT post_mime_type FROM $wpdb->posts WHERE post_type = %s AND post_mime_type != ''", $type ) ); + } + + // Remove nulls from returned $mime_types. + return array_values( array_filter( $mime_types ) ); } /** @@ -8015,7 +8142,7 @@ } /** - * Retrieve the URL to an original attachment image. + * Retrieves the URL to an original attachment image. * * Similar to `wp_get_attachment_url()` however some images may have been * processed after uploading. In this case this function returns the URL @@ -8057,7 +8184,7 @@ } /** - * Filter callback which sets the status of an untrashed post to its previous status. + * Filters callback which sets the status of an untrashed post to its previous status. * * This can be used as a callback on the `wp_untrash_post_status` filter. * @@ -8071,3 +8198,100 @@ function wp_untrash_post_set_previous_status( $new_status, $post_id, $previous_status ) { return $previous_status; } + +/** + * Returns whether the post can be edited in the block editor. + * + * @since 5.0.0 + * @since 6.1.0 Moved to wp-includes from wp-admin. + * + * @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 ( is_admin() && 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 + * @since 6.1.0 Moved to wp-includes from wp-admin. + * + * @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 ); +} + +/** + * Registers any additional post meta fields. + * + * @since 6.3.0 Adds `wp_pattern_sync_status` meta field to the wp_block post type so an unsynced option can be added. + * + * @link https://github.com/WordPress/gutenberg/pull/51144 + */ +function wp_create_initial_post_meta() { + register_post_meta( + 'wp_block', + 'wp_pattern_sync_status', + array( + 'sanitize_callback' => 'sanitize_text_field', + 'single' => true, + 'type' => 'string', + 'show_in_rest' => array( + 'schema' => array( + 'type' => 'string', + 'enum' => array( 'partial', 'unsynced' ), + ), + ), + ) + ); +}