diff -r 3d4e9c994f10 -r a86126ab1dd4 wp/wp-includes/post.php --- a/wp/wp-includes/post.php Tue Oct 22 16:11:46 2019 +0200 +++ b/wp/wp-includes/post.php Tue Dec 15 13:49:49 2020 +0100 @@ -7,7 +7,7 @@ */ // -// Post Type Registration +// Post Type registration. // /** @@ -30,6 +30,7 @@ 'capability_type' => 'post', 'map_meta_cap' => true, 'menu_position' => 5, + 'menu_icon' => 'dashicons-admin-post', 'hierarchical' => false, 'rewrite' => false, 'query_var' => false, @@ -54,6 +55,7 @@ 'capability_type' => 'page', 'map_meta_cap' => true, 'menu_position' => 20, + 'menu_icon' => 'dashicons-admin-page', 'hierarchical' => true, 'rewrite' => false, 'query_var' => false, @@ -85,6 +87,7 @@ 'create_posts' => 'upload_files', ), 'map_meta_cap' => true, + 'menu_icon' => 'dashicons-admin-media', 'hierarchical' => false, 'rewrite' => false, 'query_var' => false, @@ -312,7 +315,11 @@ 'label' => _x( 'Published', 'post status' ), 'public' => true, '_builtin' => true, /* internal use only. */ - 'label_count' => _n_noop( 'Published (%s)', 'Published (%s)' ), + /* translators: %s: Number of published posts. */ + 'label_count' => _n_noop( + 'Published (%s)', + 'Published (%s)' + ), ) ); @@ -322,27 +329,41 @@ 'label' => _x( 'Scheduled', 'post status' ), 'protected' => true, '_builtin' => true, /* internal use only. */ - 'label_count' => _n_noop( 'Scheduled (%s)', 'Scheduled (%s)' ), + /* translators: %s: Number of scheduled posts. */ + 'label_count' => _n_noop( + 'Scheduled (%s)', + 'Scheduled (%s)' + ), ) ); register_post_status( 'draft', array( - 'label' => _x( 'Draft', 'post status' ), - 'protected' => true, - '_builtin' => true, /* internal use only. */ - 'label_count' => _n_noop( 'Draft (%s)', 'Drafts (%s)' ), + 'label' => _x( 'Draft', 'post status' ), + 'protected' => true, + '_builtin' => true, /* internal use only. */ + /* translators: %s: Number of draft posts. */ + 'label_count' => _n_noop( + 'Draft (%s)', + 'Drafts (%s)' + ), + 'date_floating' => true, ) ); register_post_status( 'pending', array( - 'label' => _x( 'Pending', 'post status' ), - 'protected' => true, - '_builtin' => true, /* internal use only. */ - 'label_count' => _n_noop( 'Pending (%s)', 'Pending (%s)' ), + 'label' => _x( 'Pending', 'post status' ), + 'protected' => true, + '_builtin' => true, /* internal use only. */ + /* translators: %s: Number of pending posts. */ + 'label_count' => _n_noop( + 'Pending (%s)', + 'Pending (%s)' + ), + 'date_floating' => true, ) ); @@ -352,7 +373,11 @@ 'label' => _x( 'Private', 'post status' ), 'private' => true, '_builtin' => true, /* internal use only. */ - 'label_count' => _n_noop( 'Private (%s)', 'Private (%s)' ), + /* translators: %s: Number of private posts. */ + 'label_count' => _n_noop( + 'Private (%s)', + 'Private (%s)' + ), ) ); @@ -362,7 +387,11 @@ 'label' => _x( 'Trash', 'post status' ), 'internal' => true, '_builtin' => true, /* internal use only. */ - 'label_count' => _n_noop( 'Trash (%s)', 'Trash (%s)' ), + /* translators: %s: Number of trashed posts. */ + 'label_count' => _n_noop( + 'Trash (%s)', + 'Trash (%s)' + ), 'show_in_admin_status_list' => true, ) ); @@ -370,9 +399,10 @@ register_post_status( 'auto-draft', array( - 'label' => 'auto-draft', - 'internal' => true, - '_builtin' => true, /* internal use only. */ + 'label' => 'auto-draft', + 'internal' => true, + '_builtin' => true, /* internal use only. */ + 'date_floating' => true, ) ); @@ -392,7 +422,11 @@ 'label' => _x( 'Pending', 'request status' ), 'internal' => true, '_builtin' => true, /* internal use only. */ - 'label_count' => _n_noop( 'Pending (%s)', 'Pending (%s)' ), + /* translators: %s: Number of pending requests. */ + 'label_count' => _n_noop( + 'Pending (%s)', + 'Pending (%s)' + ), 'exclude_from_search' => false, ) ); @@ -403,7 +437,11 @@ 'label' => _x( 'Confirmed', 'request status' ), 'internal' => true, '_builtin' => true, /* internal use only. */ - 'label_count' => _n_noop( 'Confirmed (%s)', 'Confirmed (%s)' ), + /* translators: %s: Number of confirmed requests. */ + 'label_count' => _n_noop( + 'Confirmed (%s)', + 'Confirmed (%s)' + ), 'exclude_from_search' => false, ) ); @@ -414,7 +452,11 @@ 'label' => _x( 'Failed', 'request status' ), 'internal' => true, '_builtin' => true, /* internal use only. */ - 'label_count' => _n_noop( 'Failed (%s)', 'Failed (%s)' ), + /* translators: %s: Number of failed requests. */ + 'label_count' => _n_noop( + 'Failed (%s)', + 'Failed (%s)' + ), 'exclude_from_search' => false, ) ); @@ -425,7 +467,11 @@ 'label' => _x( 'Completed', 'request status' ), 'internal' => true, '_builtin' => true, /* internal use only. */ - 'label_count' => _n_noop( 'Completed (%s)', 'Completed (%s)' ), + /* translators: %s: Number of completed requests. */ + 'label_count' => _n_noop( + 'Completed (%s)', + 'Completed (%s)' + ), 'exclude_from_search' => false, ) ); @@ -453,8 +499,11 @@ $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 ) && ( ( $uploads = wp_get_upload_dir() ) && false === $uploads['error'] ) ) { - $file = $uploads['basedir'] . "/$file"; + if ( $file && 0 !== strpos( $file, '/' ) && ! preg_match( '|^.:\\\|', $file ) ) { + $uploads = wp_get_upload_dir(); + if ( false === $uploads['error'] ) { + $file = $uploads['basedir'] . "/$file"; + } } if ( $unfiltered ) { @@ -466,8 +515,8 @@ * * @since 2.1.0 * - * @param string $file Path to attached file. - * @param int $attachment_id Attachment ID. + * @param string|false $file The file path to where the attached file should be, false otherwise. + * @param int $attachment_id Attachment ID. */ return apply_filters( 'get_attached_file', $file, $attachment_id ); } @@ -499,7 +548,8 @@ */ $file = apply_filters( 'update_attached_file', $file, $attachment_id ); - if ( $file = _wp_relative_upload_path( $file ) ) { + $file = _wp_relative_upload_path( $file ); + if ( $file ) { return update_post_meta( $attachment_id, '_wp_attached_file', $file ); } else { return delete_post_meta( $attachment_id, '_wp_attached_file' ); @@ -580,13 +630,13 @@ * @see get_posts() * @todo Check validity of description. * - * @global WP_Post $post + * @global WP_Post $post Global post object. * * @param mixed $args Optional. User defined arguments for replacing the defaults. Default empty. - * @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 array Array of children, where the type of each element is determined by $output parameter. - * Empty array on failure. + * @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. */ function get_children( $args = '', $output = OBJECT ) { $kids = array(); @@ -609,15 +659,15 @@ 'post_parent' => 0, ); - $r = wp_parse_args( $args, $defaults ); - - $children = get_posts( $r ); + $parsed_args = wp_parse_args( $args, $defaults ); + + $children = get_posts( $parsed_args ); if ( ! $children ) { return $kids; } - if ( ! empty( $r['fields'] ) ) { + if ( ! empty( $parsed_args['fields'] ) ) { return $children; } @@ -627,15 +677,15 @@ $kids[ $child->ID ] = $children[ $key ]; } - if ( $output == OBJECT ) { + if ( OBJECT == $output ) { return $kids; - } elseif ( $output == ARRAY_A ) { + } elseif ( ARRAY_A == $output ) { $weeuns = array(); foreach ( (array) $kids as $kid ) { $weeuns[ $kid->ID ] = get_object_vars( $kids[ $kid->ID ] ); } return $weeuns; - } elseif ( $output == ARRAY_N ) { + } elseif ( ARRAY_N == $output ) { $babes = array(); foreach ( (array) $kids as $kid ) { $babes[ $kid->ID ] = array_values( get_object_vars( $kids[ $kid->ID ] ) ); @@ -660,10 +710,16 @@ * @since 1.0.0 * * @param string $post Post content. - * @return array Post before ('main'), after ('extended'), and custom read more ('more_text'). + * @return string[] { + * Extended entry info. + * + * @type string $main Content before the more tag. + * @type string $extended Content after the more tag. + * @type string $more_text Custom read more text, or empty string. + * } */ function get_extended( $post ) { - //Match the new style more links. + // Match the new style more links. if ( preg_match( '//', $post, $matches ) ) { list($main, $extended) = explode( $matches[0], $post, 2 ); $more_text = $matches[1]; @@ -673,7 +729,7 @@ $more_text = ''; } - // leading and trailing whitespace. + // Leading and trailing whitespace. $main = preg_replace( '/^[\s]*(.*)[\s]*$/', '\\1', $main ); $extended = preg_replace( '/^[\s]*(.*)[\s]*$/', '\\1', $extended ); $more_text = preg_replace( '/^[\s]*(.*)[\s]*$/', '\\1', $more_text ); @@ -693,11 +749,12 @@ * * @since 1.5.1 * - * @global WP_Post $post + * @global WP_Post $post Global post object. * * @param int|WP_Post|null $post Optional. Post ID or post object. Defaults to global $post. - * @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 $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 $filter Optional. Type of filter to apply. Accepts 'raw', 'edit', 'db', * or 'display'. Default 'raw'. * @return WP_Post|array|null Type corresponding to $output on success or null on failure. @@ -714,7 +771,7 @@ if ( empty( $post->filter ) ) { $_post = sanitize_post( $post, 'raw' ); $_post = new WP_Post( $_post ); - } elseif ( 'raw' == $post->filter ) { + } elseif ( 'raw' === $post->filter ) { $_post = new WP_Post( $post ); } else { $_post = WP_Post::get_instance( $post->ID ); @@ -729,9 +786,9 @@ $_post = $_post->filter( $filter ); - if ( $output == ARRAY_A ) { + if ( ARRAY_A == $output ) { return $_post->to_array(); - } elseif ( $output == ARRAY_N ) { + } elseif ( ARRAY_N == $output ) { return array_values( $_post->to_array() ); } @@ -744,7 +801,7 @@ * @since 2.5.0 * * @param int|WP_Post $post Post ID or post object. - * @return array Ancestor IDs or empty array if none are found. + * @return int[] Ancestor IDs or empty array if none are found. */ function get_post_ancestors( $post ) { $post = get_post( $post ); @@ -755,15 +812,17 @@ $ancestors = array(); - $id = $ancestors[] = $post->post_parent; + $id = $post->post_parent; + $ancestors[] = $id; while ( $ancestor = get_post( $id ) ) { // Loop detection: If the ancestor has been seen before, break. - if ( empty( $ancestor->post_parent ) || ( $ancestor->post_parent == $post->ID ) || in_array( $ancestor->post_parent, $ancestors ) ) { + if ( empty( $ancestor->post_parent ) || ( $ancestor->post_parent == $post->ID ) || in_array( $ancestor->post_parent, $ancestors, true ) ) { break; } - $id = $ancestors[] = $ancestor->post_parent; + $id = $ancestor->post_parent; + $ancestors[] = $id; } return $ancestors; @@ -842,20 +901,20 @@ return false; } - if ( 'attachment' == $post->post_type ) { - if ( 'private' == $post->post_status ) { + if ( 'attachment' === $post->post_type ) { + if ( 'private' === $post->post_status ) { return 'private'; } // Unattached attachments are assumed to be published. - if ( ( 'inherit' == $post->post_status ) && ( 0 == $post->post_parent ) ) { + if ( ( 'inherit' === $post->post_status ) && ( 0 == $post->post_parent ) ) { return 'publish'; } // Inherit status from the parent. if ( $post->post_parent && ( $post->ID != $post->post_parent ) ) { $parent_post_status = get_post_status( $post->post_parent ); - if ( 'trash' == $parent_post_status ) { + if ( 'trash' === $parent_post_status ) { return get_post_meta( $post->post_parent, '_wp_trash_meta_status', true ); } else { return $parent_post_status; @@ -882,7 +941,7 @@ * * @since 2.5.0 * - * @return array List of post statuses. + * @return string[] Array of post status labels keyed by their status. */ function get_post_statuses() { $status = array( @@ -903,7 +962,7 @@ * * @since 2.5.0 * - * @return array List of page statuses. + * @return string[] Array of page status labels keyed by their status. */ function get_page_statuses() { $status = array( @@ -925,10 +984,10 @@ */ function _wp_privacy_statuses() { return array( - 'request-pending' => __( 'Pending' ), // Pending confirmation from user. - 'request-confirmed' => __( 'Confirmed' ), // User has confirmed the action. - 'request-failed' => __( 'Failed' ), // User failed to confirm the action. - 'request-completed' => __( 'Completed' ), // Admin has handled the request. + 'request-pending' => _x( 'Pending', 'request status' ), // Pending confirmation from user. + 'request-confirmed' => _x( 'Confirmed', 'request status' ), // User has confirmed the action. + 'request-failed' => _x( 'Failed', 'request status' ), // User failed to confirm the action. + 'request-completed' => _x( 'Completed', 'request status' ), // Admin has handled the request. ); } @@ -942,16 +1001,17 @@ * Arguments prefixed with an _underscore shouldn't be used by plugins and themes. * * @since 3.0.0 + * * @global array $wp_post_statuses Inserts new post status object into the list * - * @param string $post_status Name of the post status. + * @param string $post_status Name of the post status. * @param array|string $args { * Optional. Array or string of post status arguments. * * @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 + * Default array of $label, twice. * @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. @@ -967,11 +1027,14 @@ * @type bool $publicly_queryable Whether posts with this status should be publicly- * queryable. Default is value of $public. * @type bool $show_in_admin_all_list Whether to include posts in the edit listing for - * their post type. Default is value of $internal. + * their post type. Default is the opposite value + * of $internal. * @type bool $show_in_admin_status_list Show in the list of statuses with post counts at * the top of the edit listings, * e.g. All (12) | Published (9) | My Custom Status (2) - * Default is value of $internal. + * Default is the opposite value of $internal. + * @type bool $date_floating Whether the post has a floating creation date. + * Default to false. * } * @return object */ @@ -995,6 +1058,7 @@ 'publicly_queryable' => null, 'show_in_admin_status_list' => null, 'show_in_admin_all_list' => null, + 'date_floating' => null, ); $args = wp_parse_args( $args, $defaults ); $args = (object) $args; @@ -1039,6 +1103,10 @@ $args->show_in_admin_status_list = ! $args->internal; } + if ( null === $args->date_floating ) { + $args->date_floating = false; + } + if ( false === $args->label ) { $args->label = $post_status; } @@ -1095,7 +1163,7 @@ function get_post_stati( $args = array(), $output = 'names', $operator = 'and' ) { global $wp_post_statuses; - $field = ( 'names' == $output ) ? 'name' : false; + $field = ( 'names' === $output ) ? 'name' : false; return wp_filter_object_list( $wp_post_statuses, $args, $operator, $field ); } @@ -1148,7 +1216,8 @@ * @return string|false Post type on success, false on failure. */ function get_post_type( $post = null ) { - if ( $post = get_post( $post ) ) { + $post = get_post( $post ); + if ( $post ) { return $post->post_type; } @@ -1199,7 +1268,7 @@ function get_post_types( $args = array(), $output = 'names', $operator = 'and' ) { global $wp_post_types; - $field = ( 'names' == $output ) ? 'name' : false; + $field = ( 'names' === $output ) ? 'name' : false; return wp_filter_object_list( $wp_post_types, $args, $operator, $field ); } @@ -1225,12 +1294,13 @@ * @since 4.6.0 Post type object returned is now an instance of `WP_Post_Type`. * @since 4.7.0 Introduced `show_in_rest`, `rest_base` and `rest_controller_class` * arguments to register the post type in REST API. - * + * @since 5.3.0 The `supports` argument will now accept an array of arguments for a feature. + * . * @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. * @@ -1259,17 +1329,18 @@ * 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 $show_in_menu Where to show the post type in the admin menu. To work, $show_ui + * @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 (eg. 'tools.php' or 'edit.php?post_type=page'), 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 $public. + * 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 add the post type route in the REST API 'wp/v2' namespace. + * @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_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, @@ -1295,8 +1366,11 @@ * '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. Defaults is an array - * containing 'title' and 'editor'. + * 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. @@ -1328,7 +1402,7 @@ * ?{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 + * posts of this type belonging to the user will be moved to Trash * when then 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('author'). Otherwise posts @@ -1338,7 +1412,8 @@ * @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, or an error object. + * @return WP_Post_Type|WP_Error The registered post type object on success, + * WP_Error object on failure. */ function register_post_type( $post_type, $args = array() ) { global $wp_post_types; @@ -1347,7 +1422,7 @@ $wp_post_types = array(); } - // Sanitize post type name + // Sanitize post type name. $post_type = sanitize_key( $post_type ); if ( empty( $post_type ) || strlen( $post_type ) > 20 ) { @@ -1437,7 +1512,7 @@ * Otherwise, an 's' will be added to the value for the plural form. After * registration, capability_type will always be a string of the singular value. * - * By default, seven keys are accepted as part of the capabilities array: + * By default, eight keys are accepted as part of the capabilities array: * * - edit_post, read_post, and delete_post are meta capabilities, which are then * generally mapped to corresponding primitive capabilities depending on the @@ -1449,17 +1524,17 @@ * - edit_others_posts - Controls whether objects of this type owned by other users * can be edited. If the post type does not support an author, then this will * behave like edit_posts. + * - delete_posts - Controls whether objects of this post type can be deleted. * - publish_posts - Controls publishing objects of this post type. * - read_private_posts - Controls whether private objects can be read. * - * These four primitive capabilities are checked in core in various locations. - * There are also seven other primitive capabilities which are not referenced + * These five primitive capabilities are checked in core in various locations. + * There are also six other primitive capabilities which are not referenced * directly in core, except in map_meta_cap(), which takes the three aforementioned * meta capabilities and translates them into one or more primitive capabilities * that must then be checked against the user or role, depending on the context. * * - read - Controls whether objects of this post type can be read. - * - delete_posts - Controls whether objects of this post type can be deleted. * - delete_private_posts - Controls whether private objects can be deleted. * - delete_published_posts - Controls whether published objects can be deleted. * - delete_others_posts - Controls whether objects owned by other users can be @@ -1473,6 +1548,7 @@ * argument set to true (default is false). * * @since 3.0.0 + * @since 5.4.0 'delete_posts' is included in default capabilities. * * @see register_post_type() * @see map_meta_cap() @@ -1489,13 +1565,14 @@ list( $singular_base, $plural_base ) = $args->capability_type; $default_capabilities = array( - // Meta capabilities + // Meta capabilities. 'edit_post' => 'edit_' . $singular_base, 'read_post' => 'read_' . $singular_base, 'delete_post' => 'delete_' . $singular_base, // Primitive capabilities used outside of map_meta_cap(): 'edit_posts' => 'edit_' . $plural_base, 'edit_others_posts' => 'edit_others_' . $plural_base, + 'delete_posts' => 'delete_' . $plural_base, 'publish_posts' => 'publish_' . $plural_base, 'read_private_posts' => 'read_private_' . $plural_base, ); @@ -1504,7 +1581,6 @@ if ( $args->map_meta_cap ) { $default_capabilities_for_mapping = array( 'read' => 'read', - 'delete_posts' => 'delete_' . $plural_base, 'delete_private_posts' => 'delete_private_' . $plural_base, 'delete_published_posts' => 'delete_published_' . $plural_base, 'delete_others_posts' => 'delete_others_' . $plural_base, @@ -1537,13 +1613,13 @@ * * @global array $post_type_meta_caps Used to store meta capabilities. * - * @param array $capabilities Post type meta capabilities. + * @param string[] $capabilities Post type meta capabilities. */ function _post_type_meta_capabilities( $capabilities = null ) { global $post_type_meta_caps; foreach ( $capabilities as $core => $custom ) { - if ( in_array( $core, array( 'read_post', 'delete_post', 'edit_post' ) ) ) { + if ( in_array( $core, array( 'read_post', 'delete_post', 'edit_post' ), true ) ) { $post_type_meta_caps[ $custom ] = $core; } } @@ -1558,7 +1634,7 @@ * 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://codex.wordpress.org/I18n_for_WordPress_Developers#Disambiguation_by_context gettext context} + * 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_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'. @@ -1567,7 +1643,7 @@ * - `view_items` - Label for viewing post type archives. Default is 'View Posts' / 'View Pages'. * - `search_items` - Label for searching plural items. Default is 'Search Posts' / 'Search Pages'. * - `not_found` - Label used when no items are found. Default is 'No posts found' / 'No pages found'. - * - `not_found_in_trash` - Label used when no items are in the trash. Default is 'No posts found in Trash' / + * - `not_found_in_trash` - Label used when no items are in the Trash. Default is 'No posts found in Trash' / * 'No pages found in Trash'. * - `parent_item_colon` - Label used to prefix parents of hierarchical items. Not used on non-hierarchical * post types. Default is 'Parent Page:'. @@ -1577,7 +1653,7 @@ * - `insert_into_item` - Label for the media frame button. Default is 'Insert into post' / 'Insert into page'. * - `uploaded_to_this_item` - Label for the media frame filter. Default is 'Uploaded to this post' / * 'Uploaded to this page'. - * - `featured_image` - Label for the Featured Image meta box title. Default is 'Featured Image'. + * - `featured_image` - Label for the featured image meta box title. Default is 'Featured image'. * - `set_featured_image` - Label for setting the featured image. Default is 'Set featured image'. * - `remove_featured_image` - Label for removing the featured image. Default is 'Remove featured image'. * - `use_featured_image` - Label in the media frame for using a featured image. Default is 'Use as featured image'. @@ -1635,7 +1711,7 @@ 'attributes' => array( __( 'Post Attributes' ), __( 'Page Attributes' ) ), 'insert_into_item' => array( __( 'Insert into post' ), __( 'Insert into page' ) ), 'uploaded_to_this_item' => array( __( 'Uploaded to this post' ), __( 'Uploaded to this page' ) ), - 'featured_image' => array( _x( 'Featured Image', 'post' ), _x( 'Featured Image', 'page' ) ), + 'featured_image' => array( _x( 'Featured image', 'post' ), _x( 'Featured image', 'page' ) ), 'set_featured_image' => array( _x( 'Set featured image', 'post' ), _x( 'Set featured image', 'page' ) ), 'remove_featured_image' => array( _x( 'Remove featured image', 'post' ), _x( 'Remove featured image', 'page' ) ), 'use_featured_image' => array( _x( 'Use as featured image', 'post' ), _x( 'Use as featured image', 'page' ) ), @@ -1734,7 +1810,7 @@ foreach ( get_post_types( array( 'show_ui' => true ) ) as $ptype ) { $ptype_obj = get_post_type_object( $ptype ); // Sub-menus only. - if ( ! $ptype_obj->show_in_menu || $ptype_obj->show_in_menu === true ) { + if ( ! $ptype_obj->show_in_menu || true === $ptype_obj->show_in_menu ) { continue; } add_submenu_page( $ptype_obj->show_in_menu, $ptype_obj->labels->name, $ptype_obj->labels->all_items, $ptype_obj->cap->edit_posts, "edit.php?post_type=$ptype" ); @@ -1742,7 +1818,7 @@ } /** - * Register support of certain features for a post type. + * Registers support of certain features for a post type. * * All core features are directly associated with a functional area of the edit * screen, such as the editor or a meta box. Features include: 'title', 'editor', @@ -1753,23 +1829,39 @@ * store revisions, 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 + * additional information about supporting that feature. + * + * Example usage: + * + * add_post_type_support( 'my_post_type', 'comments' ); + * add_post_type_support( 'my_post_type', array( + * 'author', 'excerpt', + * ) ); + * add_post_type_support( 'my_post_type', 'my_feature', array( + * 'field' => 'value', + * ) ); + * * @since 3.0.0 + * @since 5.3.0 Formalized the existing and already documented `...$args` parameter + * by adding it to the function signature. * * @global array $_wp_post_type_features * * @param string $post_type The post type for which to add the feature. * @param string|array $feature The feature being added, accepts an array of * feature strings or a single string. - */ -function add_post_type_support( $post_type, $feature ) { + * @param mixed ...$args Optional extra arguments to pass along with certain features. + */ +function add_post_type_support( $post_type, $feature, ...$args ) { global $_wp_post_type_features; $features = (array) $feature; foreach ( $features as $feature ) { - if ( func_num_args() == 2 ) { + if ( $args ) { + $_wp_post_type_features[ $post_type ][ $feature ] = $args; + } else { $_wp_post_type_features[ $post_type ][ $feature ] = true; - } else { - $_wp_post_type_features[ $post_type ][ $feature ] = array_slice( func_get_args(), 2 ); } } } @@ -1839,7 +1931,7 @@ * only one element from the array needs to match; 'and' * means all elements must match; 'not' means no elements may * match. Default 'and'. - * @return array A list of post type names. + * @return string[] A list of post type names. */ function get_post_types_by_support( $feature, $operator = 'and' ) { global $_wp_post_type_features; @@ -1936,34 +2028,34 @@ 'suppress_filters' => true, ); - $r = wp_parse_args( $args, $defaults ); - if ( empty( $r['post_status'] ) ) { - $r['post_status'] = ( 'attachment' == $r['post_type'] ) ? 'inherit' : 'publish'; - } - if ( ! empty( $r['numberposts'] ) && empty( $r['posts_per_page'] ) ) { - $r['posts_per_page'] = $r['numberposts']; - } - if ( ! empty( $r['category'] ) ) { - $r['cat'] = $r['category']; - } - if ( ! empty( $r['include'] ) ) { - $incposts = wp_parse_id_list( $r['include'] ); - $r['posts_per_page'] = count( $incposts ); // only the number of posts included - $r['post__in'] = $incposts; - } elseif ( ! empty( $r['exclude'] ) ) { - $r['post__not_in'] = wp_parse_id_list( $r['exclude'] ); - } - - $r['ignore_sticky_posts'] = true; - $r['no_found_rows'] = true; + $parsed_args = wp_parse_args( $args, $defaults ); + if ( empty( $parsed_args['post_status'] ) ) { + $parsed_args['post_status'] = ( 'attachment' === $parsed_args['post_type'] ) ? 'inherit' : 'publish'; + } + if ( ! empty( $parsed_args['numberposts'] ) && empty( $parsed_args['posts_per_page'] ) ) { + $parsed_args['posts_per_page'] = $parsed_args['numberposts']; + } + if ( ! empty( $parsed_args['category'] ) ) { + $parsed_args['cat'] = $parsed_args['category']; + } + if ( ! empty( $parsed_args['include'] ) ) { + $incposts = wp_parse_id_list( $parsed_args['include'] ); + $parsed_args['posts_per_page'] = count( $incposts ); // Only the number of posts included. + $parsed_args['post__in'] = $incposts; + } elseif ( ! empty( $parsed_args['exclude'] ) ) { + $parsed_args['post__not_in'] = wp_parse_id_list( $parsed_args['exclude'] ); + } + + $parsed_args['ignore_sticky_posts'] = true; + $parsed_args['no_found_rows'] = true; $get_posts = new WP_Query; - return $get_posts->query( $r ); + return $get_posts->query( $parsed_args ); } // -// Post meta functions +// Post meta functions. // /** @@ -2001,8 +2093,9 @@ * * @param int $post_id Post ID. * @param string $meta_key Metadata name. - * @param mixed $meta_value Optional. Metadata value. Must be serializable if - * non-scalar. Default empty. + * @param mixed $meta_value Optional. Metadata value. If provided, + * rows will only be removed that match the value. + * Must be serializable if non-scalar. Default empty. * @return bool True on success, false on failure. */ function delete_post_meta( $post_id, $meta_key, $meta_value = '' ) { @@ -2021,12 +2114,13 @@ * @since 1.5.0 * * @param int $post_id Post ID. - * @param string $key Optional. The meta key to retrieve. By default, returns - * data for all keys. Default empty. - * @param bool $single Optional. If true, returns only the first value for the specified meta key. - * This parameter has no effect if $key is not specified. Default false. - * @return mixed Will be an array if $single is false. Will be value of the meta - * field if $single is true. + * @param string $key Optional. The meta key to retrieve. By default, + * returns data for all keys. Default empty. + * @param bool $single Optional. Whether to return a single value. + * This parameter has no effect if $key is not specified. + * Default false. + * @return mixed An array if $single is false. The value of the meta field + * if $single is true. False for an invalid $post_id. */ function get_post_meta( $post_id, $key = '', $single = false ) { return get_metadata( 'post', $post_id, $key, $single ); @@ -2048,8 +2142,11 @@ * @param string $meta_key Metadata key. * @param mixed $meta_value Metadata value. Must be serializable if non-scalar. * @param mixed $prev_value Optional. Previous value to check before updating. - * @return int|bool The new meta field ID if a field with the given key didn't exist and was - * therefore added, true on successful update, false on failure. + * If specified, only update existing metadata entries with + * this value. Otherwise, update all entries. Default empty. + * @return int|bool Meta ID if the key didn't exist, true on successful update, + * false on failure or if the value passed to the function + * is the same as the one that is already in the database. */ function update_post_meta( $post_id, $meta_key, $meta_value, $prev_value = '' ) { // Make sure meta is added to the post, not a revision. @@ -2143,7 +2240,8 @@ return; } - if ( $keys = array_keys( $custom ) ) { + $keys = array_keys( $custom ); + if ( $keys ) { return $keys; } } @@ -2194,15 +2292,22 @@ $stickies = get_option( 'sticky_posts' ); - if ( ! is_array( $stickies ) ) { - return false; - } - - if ( in_array( $post_id, $stickies ) ) { - return true; - } - - return false; + if ( is_array( $stickies ) ) { + $stickies = array_map( 'intval', $stickies ); + $is_sticky = in_array( $post_id, $stickies, true ); + } else { + $is_sticky = false; + } + + /** + * Filters whether a post is sticky. + * + * @since 5.3.0 + * + * @param bool $is_sticky Whether a post is sticky. + * @param int $post_id Post ID. + */ + return apply_filters( 'is_sticky', $is_sticky, $post_id ); } /** @@ -2252,7 +2357,7 @@ } /** - * Sanitize post field based on context. + * Sanitizes a post field based on context. * * Possible context values are: 'raw', 'edit', 'db', 'display', 'attribute' and * 'js'. The 'display' context is used by default. 'attribute' and 'js' contexts @@ -2264,24 +2369,24 @@ * @param string $field The Post Object field name. * @param mixed $value The Post Object value. * @param int $post_id Post ID. - * @param string $context Optional. How to sanitize post fields. Looks for 'raw', 'edit', + * @param string $context Optional. How to sanitize the field. Possible values are 'raw', 'edit', * 'db', 'display', 'attribute' and 'js'. Default 'display'. * @return mixed Sanitized value. */ function sanitize_post_field( $field, $value, $post_id, $context = 'display' ) { $int_fields = array( 'ID', 'post_parent', 'menu_order' ); - if ( in_array( $field, $int_fields ) ) { + if ( in_array( $field, $int_fields, true ) ) { $value = (int) $value; } // Fields which contain arrays of integers. $array_int_fields = array( 'ancestors' ); - if ( in_array( $field, $array_int_fields ) ) { + if ( in_array( $field, $array_int_fields, true ) ) { $value = array_map( 'absint', $value ); return $value; } - if ( 'raw' == $context ) { + if ( 'raw' === $context ) { return $value; } @@ -2291,7 +2396,7 @@ $field_no_prefix = str_replace( 'post_', '', $field ); } - if ( 'edit' == $context ) { + if ( 'edit' === $context ) { $format_to_edit = array( 'post_content', 'post_excerpt', 'post_title', 'post_password' ); if ( $prefixed ) { @@ -2325,8 +2430,8 @@ $value = apply_filters( "edit_post_{$field}", $value, $post_id ); } - if ( in_array( $field, $format_to_edit ) ) { - if ( 'post_content' == $field ) { + if ( in_array( $field, $format_to_edit, true ) ) { + if ( 'post_content' === $field ) { $value = format_to_edit( $value, user_can_richedit() ); } else { $value = format_to_edit( $value ); @@ -2334,7 +2439,7 @@ } else { $value = esc_attr( $value ); } - } elseif ( 'db' == $context ) { + } elseif ( 'db' === $context ) { if ( $prefixed ) { /** @@ -2391,7 +2496,7 @@ * @param mixed $value Value of the prefixed post field. * @param int $post_id Post ID. * @param string $context Context for how to sanitize the field. Possible - * values include 'raw', 'edit', 'db', 'display', + * values include 'edit', 'display', * 'attribute' and 'js'. */ $value = apply_filters( "{$field}", $value, $post_id, $context ); @@ -2399,9 +2504,9 @@ $value = apply_filters( "post_{$field}", $value, $post_id, $context ); } - if ( 'attribute' == $context ) { + if ( 'attribute' === $context ) { $value = esc_attr( $value ); - } elseif ( 'js' == $context ) { + } elseif ( 'js' === $context ) { $value = esc_js( $value ); } } @@ -2419,13 +2524,16 @@ * @param int $post_id Post ID. */ function stick_post( $post_id ) { + $post_id = (int) $post_id; $stickies = get_option( 'sticky_posts' ); if ( ! is_array( $stickies ) ) { - $stickies = array( $post_id ); - } - - if ( ! in_array( $post_id, $stickies ) ) { + $stickies = array(); + } + + $stickies = array_map( 'intval', $stickies ); + + if ( ! in_array( $post_id, $stickies, true ) ) { $stickies[] = $post_id; } @@ -2453,17 +2561,20 @@ * @param int $post_id Post ID. */ function unstick_post( $post_id ) { + $post_id = (int) $post_id; $stickies = get_option( 'sticky_posts' ); if ( ! is_array( $stickies ) ) { return; } - if ( ! in_array( $post_id, $stickies ) ) { + $stickies = array_map( 'intval', $stickies ); + + if ( ! in_array( $post_id, $stickies, true ) ) { return; } - $offset = array_search( $post_id, $stickies ); + $offset = array_search( $post_id, $stickies, true ); if ( false === $offset ) { return; } @@ -2496,12 +2607,15 @@ */ function _count_posts_cache_key( $type = 'post', $perm = '' ) { $cache_key = 'posts-' . $type; - if ( 'readable' == $perm && is_user_logged_in() ) { + + if ( 'readable' === $perm && is_user_logged_in() ) { $post_type_object = get_post_type_object( $type ); + if ( $post_type_object && ! current_user_can( $post_type_object->cap->read_private_posts ) ) { $cache_key .= '_' . $perm . '_' . get_current_user_id(); } } + return $cache_key; } @@ -2535,12 +2649,20 @@ $counts = wp_cache_get( $cache_key, 'counts' ); if ( false !== $counts ) { + // We may have cached this before every status was registered. + foreach ( get_post_stati() as $status ) { + if ( ! isset( $counts->{$status} ) ) { + $counts->{$status} = 0; + } + } + /** This filter is documented in wp-includes/post.php */ return apply_filters( 'wp_count_posts', $counts, $type, $perm ); } $query = "SELECT post_status, COUNT( * ) AS num_posts FROM {$wpdb->posts} WHERE post_type = %s"; - if ( 'readable' == $perm && is_user_logged_in() ) { + + if ( 'readable' === $perm && is_user_logged_in() ) { $post_type_object = get_post_type_object( $type ); if ( ! current_user_can( $post_type_object->cap->read_private_posts ) ) { $query .= $wpdb->prepare( @@ -2549,6 +2671,7 @@ ); } } + $query .= ' GROUP BY post_status'; $results = (array) $wpdb->get_results( $wpdb->prepare( $query, $type ), ARRAY_A ); @@ -2620,16 +2743,96 @@ * Get default post mime types. * * @since 2.9.0 + * @since 5.3.0 Added the 'Documents', 'Spreadsheets', and 'Archives' mime type groups. * * @return array List of post mime types. */ function get_post_mime_types() { - $post_mime_types = array( // array( adj, noun ) - 'image' => array( __( 'Images' ), __( 'Manage Images' ), _n_noop( 'Image (%s)', 'Images (%s)' ) ), - 'audio' => array( __( 'Audio' ), __( 'Manage Audio' ), _n_noop( 'Audio (%s)', 'Audio (%s)' ) ), - 'video' => array( __( 'Video' ), __( 'Manage Video' ), _n_noop( 'Video (%s)', 'Video (%s)' ) ), + $post_mime_types = array( // array( adj, noun ) + 'image' => array( + __( 'Images' ), + __( 'Manage Images' ), + /* translators: %s: Number of images. */ + _n_noop( + 'Image (%s)', + 'Images (%s)' + ), + ), + 'audio' => array( + __( 'Audio' ), + __( 'Manage Audio' ), + /* translators: %s: Number of audio files. */ + _n_noop( + 'Audio (%s)', + 'Audio (%s)' + ), + ), + 'video' => array( + __( 'Video' ), + __( 'Manage Video' ), + /* translators: %s: Number of video files. */ + _n_noop( + 'Video (%s)', + 'Video (%s)' + ), + ), + 'document' => array( + __( 'Documents' ), + __( 'Manage Documents' ), + /* translators: %s: Number of documents. */ + _n_noop( + 'Document (%s)', + 'Documents (%s)' + ), + ), + 'spreadsheet' => array( + __( 'Spreadsheets' ), + __( 'Manage Spreadsheets' ), + /* translators: %s: Number of spreadsheets. */ + _n_noop( + 'Spreadsheet (%s)', + 'Spreadsheets (%s)' + ), + ), + 'archive' => array( + _x( 'Archives', 'file type group' ), + __( 'Manage Archives' ), + /* translators: %s: Number of archives. */ + _n_noop( + 'Archive (%s)', + 'Archives (%s)' + ), + ), ); + $ext_types = wp_get_ext_types(); + $mime_types = wp_get_mime_types(); + + foreach ( $post_mime_types as $group => $labels ) { + if ( in_array( $group, array( 'image', 'audio', 'video' ), true ) ) { + continue; + } + + if ( ! isset( $ext_types[ $group ] ) ) { + unset( $post_mime_types[ $group ] ); + continue; + } + + $group_mime_types = array(); + foreach ( $ext_types[ $group ] as $extension ) { + foreach ( $mime_types as $exts => $mime ) { + if ( preg_match( '!^(' . $exts . ')$!i', $extension ) ) { + $group_mime_types[] = $mime; + break; + } + } + } + $group_mime_types = implode( ',', array_unique( $group_mime_types ) ); + + $post_mime_types[ $group_mime_types ] = $labels; + unset( $post_mime_types[ $group ] ); + } + /** * Filters the default list of post mime types. * @@ -2669,8 +2872,10 @@ foreach ( (array) $wildcard_mime_types as $type ) { $mimes = array_map( 'trim', explode( ',', $type ) ); foreach ( $mimes as $mime ) { - $regex = str_replace( '__wildcard__', $wild, preg_quote( str_replace( '*', '__wildcard__', $mime ) ) ); + $regex = str_replace( '__wildcard__', $wild, preg_quote( str_replace( '*', '__wildcard__', $mime ) ) ); + $patternses[][ $type ] = "^$regex$"; + if ( false === strpos( $mime, '/' ) ) { $patternses[][ $type ] = "^$regex/"; $patternses[][ $type ] = $regex; @@ -2682,12 +2887,15 @@ foreach ( $patternses as $patterns ) { foreach ( $patterns as $type => $pattern ) { foreach ( (array) $real_mime_types as $real ) { - if ( preg_match( "#$pattern#", $real ) && ( empty( $matches[ $type ] ) || false === array_search( $real, $matches[ $type ] ) ) ) { + if ( preg_match( "#$pattern#", $real ) + && ( empty( $matches[ $type ] ) || false === array_search( $real, $matches[ $type ], true ) ) + ) { $matches[ $type ][] = $real; } } } } + return $matches; } @@ -2732,7 +2940,7 @@ $mime_pattern = preg_replace( '/\*+/', '%', $mime_pattern ); - if ( in_array( $mime_type, $wildcards ) ) { + if ( in_array( $mime_type, $wildcards, true ) ) { return ''; } @@ -2742,9 +2950,11 @@ $wheres[] = empty( $table_alias ) ? "post_mime_type = '$mime_pattern'" : "$table_alias.post_mime_type = '$mime_pattern'"; } } + if ( ! empty( $wheres ) ) { $where = ' AND (' . join( ' OR ', $wheres ) . ') '; } + return $where; } @@ -2755,8 +2965,8 @@ * it is deleted also. This includes comments, post meta fields, and terms * associated with the post. * - * The post or page is moved to trash instead of permanently deleted unless - * trash is disabled, item is already in the trash, or $force_delete is true. + * The post or page is moved to Trash instead of permanently deleted unless + * Trash is disabled, item is already in the Trash, or $force_delete is true. * * @since 1.0.0 * @@ -2765,7 +2975,7 @@ * @see wp_trash_post() * * @param int $postid Optional. Post ID. Default 0. - * @param bool $force_delete Optional. Whether to bypass trash and force deletion. + * @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. */ @@ -2793,9 +3003,9 @@ * * @since 4.4.0 * - * @param bool $delete Whether to go forward with deletion. - * @param WP_Post $post Post object. - * @param bool $force_delete Whether to bypass the trash. + * @param bool|null $delete Whether to go forward with deletion. + * @param WP_Post $post Post object. + * @param bool $force_delete Whether to bypass the Trash. */ $check = apply_filters( 'pre_delete_post', null, $post, $force_delete ); if ( null !== $check ) { @@ -2806,12 +3016,14 @@ * Fires before a post is deleted, at the start of wp_delete_post(). * * @since 3.2.0 + * @since 5.5.0 Added the `$post` parameter. * * @see wp_delete_post() * - * @param int $postid Post ID. + * @param int $postid Post ID. + * @param WP_Post $post Post object. */ - do_action( 'before_delete_post', $postid ); + do_action( 'before_delete_post', $postid, $post ); delete_post_meta( $postid, '_wp_trash_meta_status' ); delete_post_meta( $postid, '_wp_trash_meta_time' ); @@ -2858,10 +3070,13 @@ * 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 int $postid Post ID. + * @param WP_Post $post Post object. */ - do_action( 'delete_post', $postid ); + do_action( 'delete_post', $postid, $post ); + $result = $wpdb->delete( $wpdb->posts, array( 'ID' => $postid ) ); if ( ! $result ) { return false; @@ -2871,10 +3086,12 @@ * 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 int $postid Post ID. + * @param WP_Post $post Post object. */ - do_action( 'deleted_post', $postid ); + do_action( 'deleted_post', $postid, $post ); clean_post_cache( $post ); @@ -2890,12 +3107,14 @@ * Fires after a post is deleted, at the conclusion of wp_delete_post(). * * @since 3.2.0 + * @since 5.5.0 Added the `$post` parameter. * * @see wp_delete_post() * - * @param int $postid Post ID. + * @param int $postid Post ID. + * @param WP_Post $post Post object. */ - do_action( 'after_delete_post', $postid ); + do_action( 'after_delete_post', $postid, $post ); return $post; } @@ -2913,26 +3132,28 @@ */ function _reset_front_page_settings_for_post( $post_id ) { $post = get_post( $post_id ); - if ( 'page' == $post->post_type ) { + + if ( 'page' === $post->post_type ) { /* - * If the page is defined in option page_on_front or post_for_posts, - * adjust the corresponding options. - */ + * If the page is defined in option page_on_front or post_for_posts, + * adjust the corresponding options. + */ if ( get_option( 'page_on_front' ) == $post->ID ) { update_option( 'show_on_front', 'posts' ); update_option( 'page_on_front', 0 ); } if ( get_option( 'page_for_posts' ) == $post->ID ) { - delete_option( 'page_for_posts', 0 ); + update_option( 'page_for_posts', 0 ); } } + unstick_post( $post->ID ); } /** * Move a post or page to the Trash * - * If trash is disabled, the post or page is permanently deleted. + * If Trash is disabled, the post or page is permanently deleted. * * @since 2.9.0 * @@ -2962,8 +3183,8 @@ * * @since 4.9.0 * - * @param bool $trash Whether to go forward with trashing. - * @param WP_Post $post Post object. + * @param bool|null $trash Whether to go forward with trashing. + * @param WP_Post $post Post object. */ $check = apply_filters( 'pre_trash_post', null, $post ); if ( null !== $check ) { @@ -2971,7 +3192,7 @@ } /** - * Fires before a post is sent to the trash. + * Fires before a post is sent to the Trash. * * @since 3.3.0 * @@ -2982,17 +3203,21 @@ add_post_meta( $post_id, '_wp_trash_meta_status', $post->post_status ); add_post_meta( $post_id, '_wp_trash_meta_time', time() ); - wp_update_post( + $post_updated = wp_update_post( array( 'ID' => $post_id, 'post_status' => 'trash', ) ); + if ( ! $post_updated ) { + return false; + } + wp_trash_post_comments( $post_id ); /** - * Fires after a post is sent to the trash. + * Fires after a post is sent to the Trash. * * @since 2.9.0 * @@ -3027,8 +3252,8 @@ * * @since 4.9.0 * - * @param bool $untrash Whether to go forward with untrashing. - * @param WP_Post $post Post object. + * @param bool|null $untrash Whether to go forward with untrashing. + * @param WP_Post $post Post object. */ $check = apply_filters( 'pre_untrash_post', null, $post ); if ( null !== $check ) { @@ -3036,7 +3261,7 @@ } /** - * Fires before a post is restored from the trash. + * Fires before a post is restored from the Trash. * * @since 2.9.0 * @@ -3049,17 +3274,21 @@ delete_post_meta( $post_id, '_wp_trash_meta_status' ); delete_post_meta( $post_id, '_wp_trash_meta_time' ); - wp_update_post( + $post_updated = wp_update_post( array( 'ID' => $post_id, 'post_status' => $post_status, ) ); + if ( ! $post_updated ) { + return false; + } + wp_untrash_post_comments( $post_id ); /** - * Fires after a post is restored from the trash. + * Fires after a post is restored from the Trash. * * @since 2.9.0 * @@ -3071,7 +3300,7 @@ } /** - * Moves comments for a post to the trash. + * Moves comments for a post to the Trash. * * @since 2.9.0 * @@ -3091,7 +3320,7 @@ $post_id = $post->ID; /** - * Fires before comments are sent to the trash. + * Fires before comments are sent to the Trash. * * @since 2.9.0 * @@ -3117,7 +3346,7 @@ clean_comment_cache( array_keys( $statuses ) ); /** - * Fires after comments are sent to the trash. + * Fires after comments are sent to the Trash. * * @since 2.9.0 * @@ -3130,7 +3359,7 @@ } /** - * Restore comments for a post from the trash. + * Restore comments for a post from the Trash. * * @since 2.9.0 * @@ -3156,7 +3385,7 @@ } /** - * Fires before comments are restored for a post from the trash. + * Fires before comments are restored for a post from the Trash. * * @since 2.9.0 * @@ -3172,7 +3401,7 @@ foreach ( $group_by_status as $status => $comments ) { // Sanity check. This shouldn't happen. - if ( 'post-trashed' == $status ) { + if ( 'post-trashed' === $status ) { $status = '0'; } $comments_in = implode( ', ', array_map( 'intval', $comments ) ); @@ -3184,7 +3413,7 @@ delete_post_meta( $post_id, '_wp_trash_meta_comments_status' ); /** - * Fires after comments are restored for a post from the trash. + * Fires after comments are restored for a post from the Trash. * * @since 2.9.0 * @@ -3209,7 +3438,7 @@ * See WP_Term_Query::__construct() for supported arguments. * @return array|WP_Error List of categories. If the `$fields` argument passed via `$args` is 'all' or * 'all_with_object_id', an array of WP_Term objects will be returned. If `$fields` - * is 'ids', an array of category ids. If `$fields` is 'names', an array of category names. + * is 'ids', an array of category IDs. If `$fields` is 'names', an array of category names. * WP_Error object if 'category' taxonomy doesn't exist. */ function wp_get_post_categories( $post_id = 0, $args = array() ) { @@ -3278,10 +3507,11 @@ * @see get_posts() * * @param array $args Optional. Arguments to retrieve posts. Default empty array. - * @param string $output Optional. The required return type. One of OBJECT or ARRAY_A, which correspond to - * a WP_Post object or an associative array, respectively. Default ARRAY_A. - * @return array|false Array of recent posts, where the type of each element is determined by $output parameter. - * Empty array on failure. + * @param string $output Optional. The required return type. One of OBJECT or ARRAY_A, which + * correspond to a WP_Post object or an associative array, respectively. + * Default ARRAY_A. + * @return array|false Array of recent posts, where the type of each element is determined + * by the `$output` parameter. Empty array on failure. */ function wp_get_recent_posts( $args = array(), $output = ARRAY_A ) { @@ -3306,9 +3536,9 @@ 'suppress_filters' => true, ); - $r = wp_parse_args( $args, $defaults ); - - $results = get_posts( $r ); + $parsed_args = wp_parse_args( $args, $defaults ); + + $results = get_posts( $parsed_args ); // Backward compatibility. Prior to 3.1 expected posts to be returned in array. if ( ARRAY_A == $output ) { @@ -3385,6 +3615,9 @@ function wp_insert_post( $postarr, $wp_error = false ) { global $wpdb; + // Capture original pre-sanitized array for passing into filters. + $unsanitized_postarr = $postarr; + $user_id = get_current_user_id(); $defaults = array( @@ -3424,6 +3657,7 @@ // Get the post ID and GUID. $post_ID = $postarr['ID']; $post_before = get_post( $post_ID ); + if ( is_null( $post_before ) ) { if ( $wp_error ) { return new WP_Error( 'invalid_post', __( 'Invalid post ID.' ) ); @@ -3442,6 +3676,7 @@ $post_title = $postarr['post_title']; $post_content = $postarr['post_content']; $post_excerpt = $postarr['post_excerpt']; + if ( isset( $postarr['post_name'] ) ) { $post_name = $postarr['post_name']; } elseif ( $update ) { @@ -3462,8 +3697,8 @@ * 1. The post type supports the title, editor, and excerpt fields * 2. The title, editor, and excerpt fields are all empty * - * Returning a truthy value to the filter will effectively short-circuit - * the new post being inserted, returning 0. If $wp_error is true, a WP_Error + * Returning a truthy value from the filter will effectively short-circuit + * the new post being inserted and return 0. If $wp_error is true, a WP_Error * will be returned instead. * * @since 3.3.0 @@ -3480,6 +3715,7 @@ } $post_status = empty( $postarr['post_status'] ) ? 'draft' : $postarr['post_status']; + if ( 'attachment' === $post_type && ! in_array( $post_status, array( 'inherit', 'private', 'trash', 'auto-draft' ), true ) ) { $post_status = 'inherit'; } @@ -3490,9 +3726,9 @@ } // Make sure we set a valid category. - if ( empty( $post_category ) || 0 == count( $post_category ) || ! is_array( $post_category ) ) { + if ( empty( $post_category ) || 0 === count( $post_category ) || ! is_array( $post_category ) ) { // 'post' requires at least one category. - if ( 'post' == $post_type && 'auto-draft' != $post_status ) { + if ( 'post' === $post_type && 'auto-draft' !== $post_status ) { $post_category = array( get_option( 'default_category' ) ); } else { $post_category = array(); @@ -3517,7 +3753,7 @@ * an empty post name. */ if ( empty( $post_name ) ) { - if ( ! in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) ) { + if ( ! in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ), true ) ) { $post_name = sanitize_title( $post_title ); } else { $post_name = ''; @@ -3525,6 +3761,7 @@ } else { // 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 ) { $post_name = $check_name; } else { // new post, or slug has changed. @@ -3536,8 +3773,8 @@ * If the post date is empty (due to having been new or a draft) and status * is not 'draft' or 'pending', set date to now. */ - if ( empty( $postarr['post_date'] ) || '0000-00-00 00:00:00' == $postarr['post_date'] ) { - if ( empty( $postarr['post_date_gmt'] ) || '0000-00-00 00:00:00' == $postarr['post_date_gmt'] ) { + if ( empty( $postarr['post_date'] ) || '0000-00-00 00:00:00' === $postarr['post_date'] ) { + if ( empty( $postarr['post_date_gmt'] ) || '0000-00-00 00:00:00' === $postarr['post_date_gmt'] ) { $post_date = current_time( 'mysql' ); } else { $post_date = get_date_from_gmt( $postarr['post_date_gmt'] ); @@ -3559,8 +3796,8 @@ } } - if ( empty( $postarr['post_date_gmt'] ) || '0000-00-00 00:00:00' == $postarr['post_date_gmt'] ) { - if ( ! in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) ) { + if ( empty( $postarr['post_date_gmt'] ) || '0000-00-00 00:00:00' === $postarr['post_date_gmt'] ) { + if ( ! in_array( $post_status, get_post_stati( array( 'date_floating' => true ) ), true ) ) { $post_date_gmt = get_gmt_from_date( $post_date ); } else { $post_date_gmt = '0000-00-00 00:00:00'; @@ -3569,7 +3806,7 @@ $post_date_gmt = $postarr['post_date_gmt']; } - if ( $update || '0000-00-00 00:00:00' == $post_date ) { + if ( $update || '0000-00-00 00:00:00' === $post_date ) { $post_modified = current_time( 'mysql' ); $post_modified_gmt = current_time( 'mysql', 1 ); } else { @@ -3578,14 +3815,14 @@ } if ( 'attachment' !== $post_type ) { - if ( 'publish' == $post_status ) { - $now = gmdate( 'Y-m-d H:i:59' ); - if ( mysql2date( 'U', $post_date_gmt, false ) > mysql2date( 'U', $now, false ) ) { + $now = gmdate( 'Y-m-d H:i:s' ); + + if ( 'publish' === $post_status ) { + if ( strtotime( $post_date_gmt ) - strtotime( $now ) >= MINUTE_IN_SECONDS ) { $post_status = 'future'; } - } elseif ( 'future' == $post_status ) { - $now = gmdate( 'Y-m-d H:i:59' ); - if ( mysql2date( 'U', $post_date_gmt, false ) <= mysql2date( 'U', $now, false ) ) { + } elseif ( 'future' === $post_status ) { + if ( strtotime( $post_date_gmt ) - strtotime( $now ) < MINUTE_IN_SECONDS ) { $post_status = 'publish'; } } @@ -3621,7 +3858,7 @@ } $post_password = isset( $postarr['post_password'] ) ? $postarr['post_password'] : ''; - if ( 'private' == $post_status ) { + if ( 'private' === $post_status ) { $post_password = ''; } @@ -3656,6 +3893,7 @@ */ if ( 'trash' === $previous_status && 'trash' !== $post_status ) { $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' ); $post_name = $desired_post_slug; @@ -3664,7 +3902,20 @@ // If a trashed post has the desired slug, change it and let this post have it. if ( 'trash' !== $post_status && $post_name ) { - wp_add_trashed_suffix_to_post_name_for_trashed_posts( $post_name, $post_ID ); + /** + * Filters whether or not to add a `__trashed` suffix to trashed posts that match the name of the updated post. + * + * @since 5.4.0 + * + * @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. + */ + $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 ); + } } // When trashing an existing post, change its slug to allow non-trashed posts to use it. @@ -3685,6 +3936,7 @@ foreach ( $emoji_fields as $emoji_field ) { if ( isset( $data[ $emoji_field ] ) ) { $charset = $wpdb->get_col_charset( $wpdb->posts, $emoji_field ); + if ( 'utf8' === $charset ) { $data[ $emoji_field ] = wp_encode_emoji( $data[ $emoji_field ] ); } @@ -3696,22 +3948,29 @@ * Filters attachment post data before it is updated in or added to the database. * * @since 3.9.0 + * @since 5.4.1 `$unsanitized_postarr` argument added. * - * @param array $data An array of sanitized attachment post data. - * @param array $postarr An array of unsanitized attachment post data. + * @param array $data An array of slashed, sanitized, and processed attachment post data. + * @param array $postarr An array of slashed and sanitized attachment post data, but not processed. + * @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed attachment post data + * as originally passed to wp_insert_post(). */ - $data = apply_filters( 'wp_insert_attachment_data', $data, $postarr ); + $data = apply_filters( 'wp_insert_attachment_data', $data, $postarr, $unsanitized_postarr ); } else { /** * Filters slashed post data just before it is inserted into the database. * * @since 2.7.0 + * @since 5.4.1 `$unsanitized_postarr` argument added. * - * @param array $data An array of slashed post data. - * @param array $postarr An array of sanitized, but otherwise unmodified post data. + * @param array $data An array of slashed, sanitized, and processed post data. + * @param array $postarr An array of sanitized (and slashed) but otherwise unmodified post data. + * @param array $unsanitized_postarr An array of slashed yet *unsanitized* and unprocessed post data as + * originally passed to wp_insert_post(). */ - $data = apply_filters( 'wp_insert_post_data', $data, $postarr ); - } + $data = apply_filters( 'wp_insert_post_data', $data, $postarr, $unsanitized_postarr ); + } + $data = wp_unslash( $data ); $where = array( 'ID' => $post_ID ); @@ -3725,9 +3984,16 @@ * @param array $data Array of unslashed post data. */ do_action( 'pre_post_update', $post_ID, $data ); + if ( false === $wpdb->update( $wpdb->posts, $data, $where ) ) { if ( $wp_error ) { - return new WP_Error( 'db_update_error', __( 'Could not update post in the database' ), $wpdb->last_error ); + if ( 'attachment' === $post_type ) { + $message = __( 'Could not update attachment in the database.' ); + } else { + $message = __( 'Could not update post in the database.' ); + } + + return new WP_Error( 'db_update_error', $message, $wpdb->last_error ); } else { return 0; } @@ -3736,25 +4002,35 @@ // If there is a suggested ID, use it if not already present. if ( ! empty( $import_id ) ) { $import_id = (int) $import_id; + if ( ! $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE ID = %d", $import_id ) ) ) { $data['ID'] = $import_id; } } + if ( false === $wpdb->insert( $wpdb->posts, $data ) ) { if ( $wp_error ) { - return new WP_Error( 'db_insert_error', __( 'Could not insert post into the database' ), $wpdb->last_error ); + if ( 'attachment' === $post_type ) { + $message = __( 'Could not insert attachment into the database.' ); + } else { + $message = __( 'Could not insert post into the database.' ); + } + + return new WP_Error( 'db_insert_error', $message, $wpdb->last_error ); } else { return 0; } } + $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' ) ) ) { + 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 ); + $wpdb->update( $wpdb->posts, array( 'post_name' => $data['post_name'] ), $where ); clean_post_cache( $post_ID ); } @@ -3767,12 +4043,40 @@ wp_set_post_tags( $post_ID, $postarr['tags_input'] ); } + // Add default term for all associated custom taxonomies. + if ( 'auto-draft' !== $post_status ) { + foreach ( get_object_taxonomies( $post_type, 'object' ) as $taxonomy => $tax_object ) { + + if ( ! empty( $tax_object->default_term ) ) { + + // Filter out empty terms. + if ( isset( $postarr['tax_input'] ) && is_array( $postarr['tax_input'][ $taxonomy ] ) ) { + $postarr['tax_input'][ $taxonomy ] = array_filter( $postarr['tax_input'][ $taxonomy ] ); + } + + // Passed custom taxonomy list overwrites the existing list if not empty. + $terms = wp_get_object_terms( $post_ID, $taxonomy, array( 'fields' => 'ids' ) ); + if ( ! empty( $terms ) && empty( $postarr['tax_input'][ $taxonomy ] ) ) { + $postarr['tax_input'][ $taxonomy ] = $terms; + } + + if ( empty( $postarr['tax_input'][ $taxonomy ] ) ) { + $default_term_id = get_option( 'default_term_' . $taxonomy ); + if ( ! empty( $default_term_id ) ) { + $postarr['tax_input'][ $taxonomy ] = array( (int) $default_term_id ); + } + } + } + } + } + // New-style support for all custom taxonomies. if ( ! empty( $postarr['tax_input'] ) ) { foreach ( $postarr['tax_input'] as $taxonomy => $tags ) { $taxonomy_obj = get_taxonomy( $taxonomy ); + if ( ! $taxonomy_obj ) { - /* translators: %s: taxonomy name */ + /* translators: %s: Taxonomy name. */ _doing_it_wrong( __FUNCTION__, sprintf( __( 'Invalid taxonomy: %s.' ), $taxonomy ), '4.4.0' ); continue; } @@ -3781,6 +4085,7 @@ if ( is_array( $tags ) ) { $tags = array_filter( $tags ); } + if ( current_user_can( $taxonomy_obj->cap->assign_terms ) ) { wp_set_post_terms( $post_ID, $tags, $taxonomy ); } @@ -3796,7 +4101,7 @@ $current_guid = get_post_field( 'guid', $post_ID ); // Set GUID. - if ( ! $update && '' == $current_guid ) { + if ( ! $update && '' === $current_guid ) { $wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post_ID ) ), $where ); } @@ -3813,6 +4118,7 @@ // Set or remove featured image. if ( isset( $postarr['_thumbnail_id'] ) ) { $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 ) ) { $thumbnail_support = post_type_supports( 'attachment:audio', 'thumbnail' ) || current_theme_supports( 'post-thumbnails', 'attachment:audio' ); @@ -3838,10 +4144,12 @@ if ( ! empty( $postarr['page_template'] ) ) { $post->page_template = $postarr['page_template']; $page_templates = wp_get_theme()->get_page_templates( $post ); - if ( 'default' != $postarr['page_template'] && ! isset( $page_templates[ $postarr['page_template'] ] ) ) { + + if ( 'default' !== $postarr['page_template'] && ! isset( $page_templates[ $postarr['page_template'] ] ) ) { if ( $wp_error ) { return new WP_Error( 'invalid_page_template', __( 'Invalid page template.' ) ); } + update_post_meta( $post_ID, '_wp_page_template', 'default' ); } else { update_post_meta( $post_ID, '_wp_page_template', $postarr['page_template'] ); @@ -3860,6 +4168,7 @@ * @param int $post_ID Attachment ID. */ do_action( 'edit_attachment', $post_ID ); + $post_after = get_post( $post_ID ); /** @@ -3935,7 +4244,7 @@ * * @param int $post_ID Post ID. * @param WP_Post $post Post object. - * @param bool $update Whether this is an existing post being updated or not. + * @param bool $update Whether this is an existing post being updated. */ do_action( "save_post_{$post->post_type}", $post_ID, $post, $update ); @@ -3946,7 +4255,7 @@ * * @param int $post_ID Post ID. * @param WP_Post $post Post object. - * @param bool $update Whether this is an existing post being updated or not. + * @param bool $update Whether this is an existing post being updated. */ do_action( 'save_post', $post_ID, $post, $update ); @@ -3957,7 +4266,7 @@ * * @param int $post_ID Post ID. * @param WP_Post $post Post object. - * @param bool $update Whether this is an existing post being updated or not. + * @param bool $update Whether this is an existing post being updated. */ do_action( 'wp_insert_post', $post_ID, $post, $update ); @@ -3975,7 +4284,7 @@ * @param array|object $postarr Optional. Post data. Arrays are expected to be escaped, * objects are not. Default array. * @param bool $wp_error Optional. Allow return of WP_Error on failure. Default false. - * @return int|WP_Error The value 0 or WP_Error on failure. The post ID on success. + * @return int|WP_Error The post ID on success. The value 0 or WP_Error on failure. */ function wp_update_post( $postarr = array(), $wp_error = false ) { if ( is_object( $postarr ) ) { @@ -3999,15 +4308,18 @@ // Passed post category list overwrites existing category list if not empty. if ( isset( $postarr['post_category'] ) && is_array( $postarr['post_category'] ) - && 0 != count( $postarr['post_category'] ) ) { + && count( $postarr['post_category'] ) > 0 + ) { $post_cats = $postarr['post_category']; } else { $post_cats = $post['post_category']; } // Drafts shouldn't be assigned a date unless explicitly done so by the user. - if ( isset( $post['post_status'] ) && in_array( $post['post_status'], array( 'draft', 'pending', 'auto-draft' ) ) && empty( $postarr['edit_date'] ) && - ( '0000-00-00 00:00:00' == $post['post_date_gmt'] ) ) { + if ( isset( $post['post_status'] ) + && in_array( $post['post_status'], array( 'draft', 'pending', 'auto-draft' ), true ) + && empty( $postarr['edit_date'] ) && ( '0000-00-00 00:00:00' === $post['post_date_gmt'] ) + ) { $clear_date = true; } else { $clear_date = false; @@ -4021,10 +4333,24 @@ $postarr['post_date_gmt'] = ''; } - if ( $postarr['post_type'] == 'attachment' ) { + if ( 'attachment' === $postarr['post_type'] ) { return wp_insert_attachment( $postarr, false, 0, $wp_error ); } + // Discard 'tags_input' parameter if it's the same as existing post tags. + if ( isset( $postarr['tags_input'] ) && is_object_in_taxonomy( $postarr['post_type'], 'post_tag' ) ) { + $tags = get_the_terms( $postarr['ID'], 'post_tag' ); + $tag_names = array(); + + if ( $tags && ! is_wp_error( $tags ) ) { + $tag_names = wp_list_pluck( $tags, 'name' ); + } + + if ( $postarr['tags_input'] === $tag_names ) { + unset( $postarr['tags_input'] ); + } + } + return wp_insert_post( $postarr, $wp_error ); } @@ -4040,11 +4366,12 @@ function wp_publish_post( $post ) { global $wpdb; - if ( ! $post = get_post( $post ) ) { + $post = get_post( $post ); + if ( ! $post ) { return; } - if ( 'publish' == $post->post_status ) { + if ( 'publish' === $post->post_status ) { return; } @@ -4089,7 +4416,7 @@ return; } - if ( 'future' != $post->post_status ) { + if ( 'future' !== $post->post_status ) { return; } @@ -4097,7 +4424,7 @@ // 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_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; } @@ -4111,8 +4438,8 @@ * * @since 2.8.0 * - * @global wpdb $wpdb WordPress database abstraction object. - * @global WP_Rewrite $wp_rewrite + * @global wpdb $wpdb WordPress database abstraction object. + * @global WP_Rewrite $wp_rewrite WordPress rewrite component. * * @param string $slug The desired slug (post_name). * @param int $post_ID Post ID. @@ -4122,7 +4449,9 @@ * @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 ) { - if ( in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) || ( 'inherit' == $post_status && 'revision' == $post_type ) || 'user_request' === $post_type ) { + if ( in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ), true ) + || ( 'inherit' === $post_status && 'revision' === $post_type ) || 'user_request' === $post_type + ) { return $slug; } @@ -4134,12 +4463,12 @@ * * @since 5.1.0 * - * @param string $override_slug Short-circuit return value. - * @param string $slug The desired slug (post_name). - * @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|null $override_slug Short-circuit return value. + * @param string $slug The desired slug (post_name). + * @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 ); if ( null !== $override_slug ) { @@ -4155,7 +4484,7 @@ $feeds = array(); } - if ( 'attachment' == $post_type ) { + 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 ) ); @@ -4168,7 +4497,12 @@ * @param bool $bad_slug Whether the slug would be bad as an attachment slug. * @param string $slug The post slug. */ - if ( $post_name_check || in_array( $slug, $feeds ) || 'embed' === $slug || apply_filters( 'wp_unique_post_slug_is_bad_attachment_slug', false, $slug ) ) { + $is_bad_attachment_slug = apply_filters( 'wp_unique_post_slug_is_bad_attachment_slug', false, $slug ); + + if ( $post_name_check + || in_array( $slug, $feeds, true ) || 'embed' === $slug + || $is_bad_attachment_slug + ) { $suffix = 2; do { $alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix"; @@ -4178,7 +4512,7 @@ $slug = $alt_post_name; } } elseif ( is_post_type_hierarchical( $post_type ) ) { - if ( 'nav_menu_item' == $post_type ) { + if ( 'nav_menu_item' === $post_type ) { return $slug; } @@ -4199,7 +4533,13 @@ * @param string $post_type Post type. * @param int $post_parent Post parent ID. */ - if ( $post_name_check || in_array( $slug, $feeds ) || 'embed' === $slug || preg_match( "@^($wp_rewrite->pagination_base)?\d+$@", $slug ) || apply_filters( 'wp_unique_post_slug_is_bad_hierarchical_slug', false, $slug, $post_type, $post_parent ) ) { + $is_bad_hierarchical_slug = apply_filters( 'wp_unique_post_slug_is_bad_hierarchical_slug', false, $slug, $post_type, $post_parent ); + + if ( $post_name_check + || in_array( $slug, $feeds, true ) || 'embed' === $slug + || preg_match( "@^($wp_rewrite->pagination_base)?\d+$@", $slug ) + || $is_bad_hierarchical_slug + ) { $suffix = 2; do { $alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix"; @@ -4213,25 +4553,30 @@ $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 ); + // Prevent new post slugs that could result in URLs that conflict with date archives. - $post = get_post( $post_ID ); $conflicts_with_date_archive = false; - if ( 'post' === $post_type && ( ! $post || $post->post_name !== $slug ) && preg_match( '/^[0-9]+$/', $slug ) && $slug_num = intval( $slug ) ) { - $permastructs = array_values( array_filter( explode( '/', get_option( 'permalink_structure' ) ) ) ); - $postname_index = array_search( '%postname%', $permastructs ); - - /* - * Potential date clashes are as follows: - * - * - Any integer in the first permastruct position could be a year. - * - An integer between 1 and 12 that follows 'year' conflicts with 'monthnum'. - * - An integer between 1 and 31 that follows 'monthnum' conflicts with 'day'. - */ - if ( 0 === $postname_index || - ( $postname_index && '%year%' === $permastructs[ $postname_index - 1 ] && 13 > $slug_num ) || - ( $postname_index && '%monthnum%' === $permastructs[ $postname_index - 1 ] && 32 > $slug_num ) - ) { - $conflicts_with_date_archive = true; + if ( 'post' === $post_type && ( ! $post || $post->post_name !== $slug ) && preg_match( '/^[0-9]+$/', $slug ) ) { + $slug_num = intval( $slug ); + + if ( $slug_num ) { + $permastructs = array_values( array_filter( explode( '/', get_option( 'permalink_structure' ) ) ) ); + $postname_index = array_search( '%postname%', $permastructs, true ); + + /* + * Potential date clashes are as follows: + * + * - Any integer in the first permastruct position could be a year. + * - An integer between 1 and 12 that follows 'year' conflicts with 'monthnum'. + * - An integer between 1 and 31 that follows 'monthnum' conflicts with 'day'. + */ + if ( 0 === $postname_index || + ( $postname_index && '%year%' === $permastructs[ $postname_index - 1 ] && 13 > $slug_num ) || + ( $postname_index && '%monthnum%' === $permastructs[ $postname_index - 1 ] && 32 > $slug_num ) + ) { + $conflicts_with_date_archive = true; + } } } @@ -4244,7 +4589,13 @@ * @param string $slug The post slug. * @param string $post_type Post type. */ - if ( $post_name_check || in_array( $slug, $feeds ) || 'embed' === $slug || $conflicts_with_date_archive || apply_filters( 'wp_unique_post_slug_is_bad_flat_slug', false, $slug, $post_type ) ) { + $is_bad_flat_slug = apply_filters( 'wp_unique_post_slug_is_bad_flat_slug', false, $slug, $post_type ); + + if ( $post_name_check + || in_array( $slug, $feeds, true ) || 'embed' === $slug + || $conflicts_with_date_archive + || $is_bad_flat_slug + ) { $suffix = 2; do { $alt_post_name = _truncate_post_slug( $slug, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix"; @@ -4379,8 +4730,7 @@ /** * Set categories for a post. * - * If the post categories parameter is not set, then the default category is - * going used. + * If no categories are provided, the default category is used. * * @since 2.1.0 * @@ -4396,16 +4746,33 @@ $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: + + // If $post_categories isn't already an array, make it one. $post_categories = (array) $post_categories; + if ( empty( $post_categories ) ) { - if ( 'post' == $post_type && 'auto-draft' != $post_status ) { + /** + * Filters post types (in addition to 'post') that require a default category. + * + * @since 5.5.0 + * + * @param string[] $post_types An array of post type names. Default empty array. + */ + $default_category_post_types = apply_filters( 'default_category_post_types', array() ); + + // Regular posts always require a default category. + $default_category_post_types = array_merge( $default_category_post_types, array( 'post' ) ); + + if ( in_array( $post_type, $default_category_post_types, true ) + && is_object_in_taxonomy( $post_type, 'category' ) + && 'auto-draft' !== $post_status + ) { $post_categories = array( get_option( 'default_category' ) ); $append = false; } else { $post_categories = array(); } - } elseif ( 1 == count( $post_categories ) && '' == reset( $post_categories ) ) { + } elseif ( 1 === count( $post_categories ) && '' === reset( $post_categories ) ) { return true; } @@ -4534,7 +4901,7 @@ * @since 1.5.0 * * @param int $post_id Post ID. - * @return array List of enclosures. + * @return string[] Array of enclosures for the given post. */ function get_enclosed( $post_id ) { $custom_fields = get_post_custom( $post_id ); @@ -4544,7 +4911,7 @@ } foreach ( $custom_fields as $key => $val ) { - if ( 'enclosure' != $key || ! is_array( $val ) ) { + if ( 'enclosure' !== $key || ! is_array( $val ) ) { continue; } foreach ( $val as $enc ) { @@ -4558,8 +4925,8 @@ * * @since 2.0.0 * - * @param array $pung Array of enclosures for the given post. - * @param int $post_id Post ID. + * @param string[] $pung Array of enclosures for the given post. + * @param int $post_id Post ID. */ return apply_filters( 'get_enclosed', $pung, $post_id ); } @@ -4600,7 +4967,7 @@ * @since 4.7.0 `$post_id` can be a WP_Post object. * * @param int|WP_Post $post_id Post Object or ID - * @return array + * @return string[]|false List of URLs yet to ping. */ function get_to_ping( $post_id ) { $post = get_post( $post_id ); @@ -4617,7 +4984,7 @@ * * @since 2.0.0 * - * @param array $to_ping List of URLs yet to ping. + * @param string[] $to_ping List of URLs yet to ping. */ return apply_filters( 'get_to_ping', $to_ping ); } @@ -4651,7 +5018,7 @@ } // -// Page functions +// Page functions. // /** @@ -4661,7 +5028,7 @@ * * @global wpdb $wpdb WordPress database abstraction object. * - * @return array List of page IDs. + * @return string[] List of page IDs as strings. */ function get_all_page_ids() { global $wpdb; @@ -4683,12 +5050,13 @@ * @since 1.5.1 * @deprecated 3.5.0 Use get_post() * - * @param mixed $page Page object or page ID. Passed by reference. - * @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 $filter Optional. How the return value should be filtered. Accepts 'raw', - * 'edit', 'db', 'display'. Default 'raw'. - * @return WP_Post|array|null WP_Post (or array) on success, or null on failure. + * @param int|WP_Post $page Page object or page ID. Passed by reference. + * @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 $filter Optional. How the return value should be filtered. Accepts 'raw', + * 'edit', 'db', 'display'. Default 'raw'. + * @return WP_Post|array|null WP_Post or array on success, null on failure. */ function get_page( $page, $output = OBJECT, $filter = 'raw' ) { return get_post( $page, $output, $filter ); @@ -4702,8 +5070,9 @@ * @global wpdb $wpdb WordPress database abstraction object. * * @param string $page_path Page path. - * @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 $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. */ @@ -4762,7 +5131,7 @@ * Loop through the given path parts from right to left, * ensuring each matches the post ancestry. */ - while ( $p->post_parent != 0 && isset( $pages[ $p->post_parent ] ) ) { + while ( 0 != $p->post_parent && isset( $pages[ $p->post_parent ] ) ) { $count++; $parent = $pages[ $p->post_parent ]; if ( ! isset( $revparts[ $count ] ) || $parent->post_name != $revparts[ $count ] ) { @@ -4771,7 +5140,7 @@ $p = $parent; } - if ( $p->post_parent == 0 && $count + 1 == count( $revparts ) && $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; @@ -4791,13 +5160,22 @@ /** * 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 $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. */ @@ -4884,9 +5262,9 @@ * * @since 2.0.0 * - * @param array $pages Posts array (passed by reference). - * @param int $page_id Optional. Parent page ID. Default 0. - * @return array A list arranged by hierarchy. Children immediately follow their parents. + * @param WP_Post[] $pages Posts array (passed by reference). + * @param int $page_id Optional. Parent page ID. Default 0. + * @return string[] Array of post names keyed by ID and arranged by hierarchy. Children immediately follow their parents. */ function get_page_hierarchy( &$pages, $page_id = 0 ) { if ( empty( $pages ) ) { @@ -4915,9 +5293,9 @@ * * @see _page_traverse_name() * - * @param int $page_id Page ID. - * @param array $children Parent-children relations (passed by reference). - * @param array $result Result (passed by reference). + * @param int $page_id Page ID. + * @param array $children Parent-children relations (passed by reference). + * @param string[] $result Array of page names keyed by ID (passed by reference). */ function _page_traverse_name( $page_id, &$children, &$result ) { if ( isset( $children[ $page_id ] ) ) { @@ -4934,7 +5312,7 @@ * Sub pages will be in the "directory" under the parent page post name. * * @since 1.5.0 - * @since 4.6.0 Converted the `$page` parameter to optional. + * @since 4.6.0 The `$page` parameter was made optional. * * @param WP_Post|object|int $page Optional. Page ID or WP_Post object. Default is global $post. * @return string|false Page URI, false on error. @@ -5031,21 +5409,21 @@ 'post_status' => 'publish', ); - $r = wp_parse_args( $args, $defaults ); - - $number = (int) $r['number']; - $offset = (int) $r['offset']; - $child_of = (int) $r['child_of']; - $hierarchical = $r['hierarchical']; - $exclude = $r['exclude']; - $meta_key = $r['meta_key']; - $meta_value = $r['meta_value']; - $parent = $r['parent']; - $post_status = $r['post_status']; + $parsed_args = wp_parse_args( $args, $defaults ); + + $number = (int) $parsed_args['number']; + $offset = (int) $parsed_args['offset']; + $child_of = (int) $parsed_args['child_of']; + $hierarchical = $parsed_args['hierarchical']; + $exclude = $parsed_args['exclude']; + $meta_key = $parsed_args['meta_key']; + $meta_value = $parsed_args['meta_value']; + $parent = $parsed_args['parent']; + $post_status = $parsed_args['post_status']; // Make sure the post type is hierarchical. $hierarchical_post_types = get_post_types( array( 'hierarchical' => true ) ); - if ( ! in_array( $r['post_type'], $hierarchical_post_types ) ) { + if ( ! in_array( $parsed_args['post_type'], $hierarchical_post_types, true ) ) { return false; } @@ -5062,7 +5440,7 @@ } // $args can be whatever, only use the args defined in defaults to compute the key. - $key = md5( serialize( wp_array_slice_assoc( $r, array_keys( $defaults ) ) ) ); + $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"; @@ -5071,19 +5449,19 @@ // 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, $r ); + $pages = apply_filters( 'get_pages', $pages, $parsed_args ); return $pages; } $inclusions = ''; - if ( ! empty( $r['include'] ) ) { - $child_of = 0; //ignore child_of, parent, exclude, meta_key, and meta_value params if using include + 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( $r['include'] ); + $incpages = wp_parse_id_list( $parsed_args['include'] ); if ( ! empty( $incpages ) ) { $inclusions = ' AND ID IN (' . implode( ',', $incpages ) . ')'; } @@ -5098,12 +5476,12 @@ } $author_query = ''; - if ( ! empty( $r['authors'] ) ) { - $post_authors = wp_parse_list( $r['authors'] ); + if ( ! empty( $parsed_args['authors'] ) ) { + $post_authors = wp_parse_list( $parsed_args['authors'] ); if ( ! empty( $post_authors ) ) { foreach ( $post_authors as $post_author ) { - //Do we have an author id or an author login? + // Do we have an author id or an author login? if ( 0 == intval( $post_author ) ) { $post_author = get_user_by( 'login', $post_author ); if ( empty( $post_author ) ) { @@ -5115,13 +5493,13 @@ $post_author = $post_author->ID; } - if ( '' == $author_query ) { + 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 ) { + if ( '' !== $author_query ) { $author_query = " AND ($author_query)"; } } @@ -5132,7 +5510,7 @@ 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 and meta_value might be slashed. $meta_key = wp_unslash( $meta_key ); $meta_value = wp_unslash( $meta_value ); if ( '' !== $meta_key ) { @@ -5152,11 +5530,11 @@ $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', $r['post_type'], reset( $post_status ) ); + 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')", $r['post_type'] ); + $where_post_type = $wpdb->prepare( "post_type = %s AND post_status IN ('$post_status')", $parsed_args['post_type'] ); } $orderby_array = array(); @@ -5181,9 +5559,9 @@ 'comment_count', ); - foreach ( explode( ',', $r['sort_column'] ) as $orderby ) { + foreach ( explode( ',', $parsed_args['sort_column'] ) as $orderby ) { $orderby = trim( $orderby ); - if ( ! in_array( $orderby, $allowed_keys ) ) { + if ( ! in_array( $orderby, $allowed_keys, true ) ) { continue; } @@ -5212,8 +5590,8 @@ } $sort_column = ! empty( $orderby_array ) ? implode( ',', $orderby_array ) : "$wpdb->posts.post_title"; - $sort_order = strtoupper( $r['sort_order'] ); - if ( '' !== $sort_order && ! in_array( $sort_order, array( 'ASC', 'DESC' ) ) ) { + $sort_order = strtoupper( $parsed_args['sort_order'] ); + if ( '' !== $sort_order && ! in_array( $sort_order, array( 'ASC', 'DESC' ), true ) ) { $sort_order = 'ASC'; } @@ -5231,7 +5609,7 @@ wp_cache_set( $cache_key, array(), 'posts' ); /** This filter is documented in wp-includes/post.php */ - $pages = apply_filters( 'get_pages', array(), $r ); + $pages = apply_filters( 'get_pages', array(), $parsed_args ); return $pages; } @@ -5248,8 +5626,8 @@ $pages = get_page_children( $child_of, $pages ); } - if ( ! empty( $r['exclude_tree'] ) ) { - $exclude = wp_parse_id_list( $r['exclude_tree'] ); + if ( ! empty( $parsed_args['exclude_tree'] ) ) { + $exclude = wp_parse_id_list( $parsed_args['exclude_tree'] ); foreach ( $exclude as $id ) { $children = get_page_children( $id, $pages ); foreach ( $children as $child ) { @@ -5259,7 +5637,7 @@ $num_pages = count( $pages ); for ( $i = 0; $i < $num_pages; $i++ ) { - if ( in_array( $pages[ $i ]->ID, $exclude ) ) { + if ( in_array( $pages[ $i ]->ID, $exclude, true ) ) { unset( $pages[ $i ] ); } } @@ -5272,7 +5650,7 @@ wp_cache_set( $cache_key, $page_structure, 'posts' ); - // Convert to WP_Post instances + // Convert to WP_Post instances. $pages = array_map( 'get_post', $pages ); /** @@ -5280,14 +5658,14 @@ * * @since 2.1.0 * - * @param array $pages List of pages to retrieve. - * @param array $r Array of get_pages() arguments. + * @param WP_Post[] $pages Array of page objects. + * @param array $parsed_args Array of get_pages() arguments. */ - return apply_filters( 'get_pages', $pages, $r ); + return apply_filters( 'get_pages', $pages, $parsed_args ); } // -// Attachment functions +// Attachment functions. // /** @@ -5309,9 +5687,11 @@ if ( strpos( $url, home_url( '/?attachment_id=' ) ) !== false ) { return true; } - if ( $id = url_to_postid( $url ) ) { + + $id = url_to_postid( $url ); + if ( $id ) { $post = get_post( $id ); - if ( 'attachment' == $post->post_type ) { + if ( 'attachment' === $post->post_type ) { return true; } } @@ -5367,15 +5747,15 @@ * Deletion removes all post meta fields, taxonomy, comments, etc. associated * with the attachment (except the main post). * - * The attachment is moved to the trash instead of permanently deleted unless trash - * for media is disabled, item is already in the trash, or $force_delete is true. + * The attachment is moved to the Trash instead of permanently deleted unless Trash + * for media is disabled, item is already in the Trash, or $force_delete is true. * * @since 2.0.0 * * @global wpdb $wpdb WordPress database abstraction object. * * @param int $post_id Attachment ID. - * @param bool $force_delete Optional. Whether to bypass trash and force deletion. + * @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. */ @@ -5398,6 +5778,20 @@ return wp_trash_post( $post_id ); } + /** + * Filters whether an attachment deletion should take place. + * + * @since 5.5.0 + * + * @param bool|null $delete Whether to go forward with deletion. + * @param WP_Post $post Post object. + * @param bool $force_delete Whether to bypass the Trash. + */ + $check = apply_filters( 'pre_delete_attachment', null, $post, $force_delete ); + if ( null !== $check ) { + return $check; + } + delete_post_meta( $post_id, '_wp_trash_meta_status' ); delete_post_meta( $post_id, '_wp_trash_meta_time' ); @@ -5413,10 +5807,12 @@ * Fires before an attachment is deleted, at the start of wp_delete_attachment(). * * @since 2.0.0 + * @since 5.5.0 Added the `$post` parameter. * - * @param int $post_id Attachment ID. + * @param int $post_id Attachment ID. + * @param WP_Post $post Post object. */ - do_action( 'delete_attachment', $post_id ); + do_action( 'delete_attachment', $post_id, $post ); wp_delete_object_term_relationships( $post_id, array( 'category', 'post_tag' ) ); wp_delete_object_term_relationships( $post_id, get_object_taxonomies( $post->post_type ) ); @@ -5439,13 +5835,13 @@ } /** This action is documented in wp-includes/post.php */ - do_action( 'delete_post', $post_id ); + do_action( 'delete_post', $post_id, $post ); $result = $wpdb->delete( $wpdb->posts, array( 'ID' => $post_id ) ); if ( ! $result ) { return false; } /** This action is documented in wp-includes/post.php */ - do_action( 'deleted_post', $post_id ); + do_action( 'deleted_post', $post_id, $post ); wp_delete_attachment_files( $post_id, $meta, $backup_sizes, $file ); @@ -5475,6 +5871,7 @@ // Don't delete the thumb if another attachment uses it. if ( ! $wpdb->get_row( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attachment_metadata' AND meta_value LIKE %s AND post_id <> %d", '%' . $wpdb->esc_like( $meta['thumb'] ) . '%', $post_id ) ) ) { $thumbfile = str_replace( wp_basename( $file ), $meta['thumb'], $file ); + if ( ! empty( $thumbfile ) ) { $thumbfile = path_join( $uploadpath['basedir'], $thumbfile ); $thumbdir = path_join( $uploadpath['basedir'], dirname( $file ) ); @@ -5489,8 +5886,10 @@ // Remove intermediate and backup images if there are any. if ( isset( $meta['sizes'] ) && is_array( $meta['sizes'] ) ) { $intermediate_dir = path_join( $uploadpath['basedir'], dirname( $file ) ); + foreach ( $meta['sizes'] as $size => $sizeinfo ) { $intermediate_file = str_replace( wp_basename( $file ), $sizeinfo['file'], $file ); + if ( ! empty( $intermediate_file ) ) { $intermediate_file = path_join( $uploadpath['basedir'], $intermediate_file ); @@ -5501,10 +5900,28 @@ } } + if ( ! empty( $meta['original_image'] ) ) { + if ( empty( $intermediate_dir ) ) { + $intermediate_dir = path_join( $uploadpath['basedir'], dirname( $file ) ); + } + + $original_image = str_replace( wp_basename( $file ), $meta['original_image'], $file ); + + if ( ! empty( $original_image ) ) { + $original_image = path_join( $uploadpath['basedir'], $original_image ); + + if ( ! wp_delete_file_from_directory( $original_image, $intermediate_dir ) ) { + $deleted = false; + } + } + } + if ( is_array( $backup_sizes ) ) { $del_dir = path_join( $uploadpath['basedir'], dirname( $meta['file'] ) ); + foreach ( $backup_sizes as $size ) { $del_file = path_join( dirname( $meta['file'] ), $size['file'] ); + if ( ! empty( $del_file ) ) { $del_file = path_join( $uploadpath['basedir'], $del_file ); @@ -5523,17 +5940,28 @@ } /** - * Retrieve attachment meta field for attachment ID. + * Retrieves attachment metadata for attachment ID. * * @since 2.1.0 * * @param int $attachment_id Attachment post ID. Defaults to global $post. * @param bool $unfiltered Optional. If true, filters are not run. Default false. - * @return mixed Attachment meta field. False on failure. + * @return array|false { + * Attachment metadata. False on failure. + * + * @type int $width The width of the attachment. + * @type int $height The height of the attachment. + * @type string $file The file path relative to `wp-content/uploads`. + * @type array $sizes Keys are size slugs, each value is an array containing + * 'file', 'width', 'height', and 'mime-type'. + * @type array $image_meta Image metadata. + * } */ function wp_get_attachment_metadata( $attachment_id = 0, $unfiltered = false ) { $attachment_id = (int) $attachment_id; - if ( ! $post = get_post( $attachment_id ) ) { + + $post = get_post( $attachment_id ); + if ( ! $post ) { return false; } @@ -5556,7 +5984,7 @@ } /** - * Update metadata for an attachment. + * Updates metadata for an attachment. * * @since 2.1.0 * @@ -5566,7 +5994,9 @@ */ function wp_update_attachment_metadata( $attachment_id, $data ) { $attachment_id = (int) $attachment_id; - if ( ! $post = get_post( $attachment_id ) ) { + + $post = get_post( $attachment_id ); + if ( ! $post ) { return false; } @@ -5578,7 +6008,8 @@ * @param array $data Array of updated attachment meta data. * @param int $attachment_id Attachment post ID. */ - if ( $data = apply_filters( 'wp_update_attachment_metadata', $data, $post->ID ) ) { + $data = apply_filters( 'wp_update_attachment_metadata', $data, $post->ID ); + if ( $data ) { return update_post_meta( $post->ID, '_wp_attachment_metadata', $data ); } else { return delete_post_meta( $post->ID, '_wp_attachment_metadata' ); @@ -5597,25 +6028,29 @@ */ function wp_get_attachment_url( $attachment_id = 0 ) { $attachment_id = (int) $attachment_id; - if ( ! $post = get_post( $attachment_id ) ) { + + $post = get_post( $attachment_id ); + if ( ! $post ) { return false; } - if ( 'attachment' != $post->post_type ) { + if ( 'attachment' !== $post->post_type ) { return false; } $url = ''; // Get attached file. - if ( $file = get_post_meta( $post->ID, '_wp_attached_file', true ) ) { + $file = get_post_meta( $post->ID, '_wp_attached_file', true ); + if ( $file ) { // Get upload directory. - if ( ( $uploads = wp_get_upload_dir() ) && false === $uploads['error'] ) { + $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'] ) ) { // Replace file location with url location. $url = str_replace( $uploads['basedir'], $uploads['baseurl'], $file ); } elseif ( false !== strpos( $file, 'wp-content/uploads' ) ) { - // Get the directory name relative to the basedir (back compat for pre-2.7 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 { // It's a newly-uploaded file, therefore $file is relative to the basedir. @@ -5664,7 +6099,8 @@ */ function wp_get_attachment_caption( $post_id = 0 ) { $post_id = (int) $post_id; - if ( ! $post = get_post( $post_id ) ) { + $post = get_post( $post_id ); + if ( ! $post ) { return false; } @@ -5695,25 +6131,31 @@ */ function wp_get_attachment_thumb_file( $post_id = 0 ) { $post_id = (int) $post_id; - if ( ! $post = get_post( $post_id ) ) { + $post = get_post( $post_id ); + if ( ! $post ) { return false; } - if ( ! is_array( $imagedata = wp_get_attachment_metadata( $post->ID ) ) ) { + + $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 ) ) && 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 ); + 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; } @@ -5728,10 +6170,13 @@ */ function wp_get_attachment_thumb_url( $post_id = 0 ) { $post_id = (int) $post_id; - if ( ! $post = get_post( $post_id ) ) { + $post = get_post( $post_id ); + if ( ! $post ) { return false; } - if ( ! $url = wp_get_attachment_url( $post->ID ) ) { + + $url = wp_get_attachment_url( $post->ID ); + if ( ! $url ) { return false; } @@ -5740,7 +6185,8 @@ return $sized[0]; } - if ( ! $thumb = wp_get_attachment_thumb_file( $post->ID ) ) { + $thumb = wp_get_attachment_thumb_file( $post->ID ); + if ( ! $thumb ) { return false; } @@ -5767,11 +6213,13 @@ * @return bool True if one of the accepted types, false otherwise. */ function wp_attachment_is( $type, $post = null ) { - if ( ! $post = get_post( $post ) ) { + $post = get_post( $post ); + if ( ! $post ) { return false; } - if ( ! $file = get_attached_file( $post->ID ) ) { + $file = get_attached_file( $post->ID ); + if ( ! $file ) { return false; } @@ -5793,13 +6241,13 @@ switch ( $type ) { case 'image': $image_exts = array( 'jpg', 'jpeg', 'jpe', 'gif', 'png' ); - return in_array( $ext, $image_exts ); + return in_array( $ext, $image_exts, true ); case 'audio': - return in_array( $ext, wp_get_audio_extensions() ); + return in_array( $ext, wp_get_audio_extensions(), true ); case 'video': - return in_array( $ext, wp_get_video_extensions() ); + return in_array( $ext, wp_get_video_extensions(), true ); default: return $type === $ext; @@ -5825,7 +6273,7 @@ } /** - * Retrieve the icon for a MIME type. + * Retrieve the icon for a MIME type or attachment. * * @since 2.1.0 * @@ -5842,13 +6290,15 @@ $post_mimes = array(); if ( is_numeric( $mime ) ) { $mime = (int) $mime; - if ( $post = get_post( $mime ) ) { + $post = get_post( $mime ); + if ( $post ) { $post_id = (int) $post->ID; $file = get_attached_file( $post_id ); $ext = preg_replace( '/^.+?\.([^.]+)$/', '$1', $file ); if ( ! empty( $ext ) ) { $post_mimes[] = $ext; - if ( $ext_type = wp_ext2type( $ext ) ) { + $ext_type = wp_ext2type( $ext ); + if ( $ext_type ) { $post_mimes[] = $ext_type; } } @@ -5882,11 +6332,11 @@ $icon_dir_uri = apply_filters( 'icon_dir_uri', includes_url( 'images/media' ) ); /** - * Filters the list of icon directory URIs. + * Filters the array of icon directory URIs. * * @since 2.5.0 * - * @param array $uris List of icon directory URIs. + * @param string[] $uris Array of icon directory URIs keyed by directory absolute path. */ $dirs = apply_filters( 'icon_dirs', array( $icon_dir => $icon_dir_uri ) ); $icon_files = array(); @@ -5894,13 +6344,16 @@ $keys = array_keys( $dirs ); $dir = array_shift( $keys ); $uri = array_shift( $dirs ); - if ( $dh = opendir( $dir ) ) { + $dh = opendir( $dir ); + if ( $dh ) { while ( false !== $file = readdir( $dh ) ) { $file = wp_basename( $file ); - if ( substr( $file, 0, 1 ) == '.' ) { + if ( '.' === substr( $file, 0, 1 ) ) { continue; } - if ( ! in_array( strtolower( substr( $file, -4 ) ), array( '.png', '.gif', '.jpg' ) ) ) { + + $ext = strtolower( substr( $file, -4 ) ); + if ( ! in_array( $ext, array( '.png', '.gif', '.jpg' ), true ) ) { if ( is_dir( "$dir/$file" ) ) { $dirs[ "$dir/$file" ] = "$uri/$file"; } @@ -5990,12 +6443,12 @@ $old_slugs = (array) get_post_meta( $post_id, '_wp_old_slug' ); // If we haven't added this old slug before, add it now. - if ( ! empty( $post_before->post_name ) && ! in_array( $post_before->post_name, $old_slugs ) ) { + if ( ! empty( $post_before->post_name ) && ! in_array( $post_before->post_name, $old_slugs, true ) ) { add_post_meta( $post_id, '_wp_old_slug', $post_before->post_name ); } // If the new slug was used previously, delete it from the list. - if ( in_array( $post->post_name, $old_slugs ) ) { + if ( in_array( $post->post_name, $old_slugs, true ) ) { delete_post_meta( $post_id, '_wp_old_slug', $post->post_name ); } } @@ -6020,23 +6473,28 @@ * @param WP_Post $post_before The Previous Post Object */ function wp_check_for_changed_dates( $post_id, $post, $post_before ) { - $previous_date = date( 'Y-m-d', strtotime( $post_before->post_date ) ); - $new_date = date( 'Y-m-d', strtotime( $post->post_date ) ); + $previous_date = gmdate( 'Y-m-d', strtotime( $post_before->post_date ) ); + $new_date = gmdate( 'Y-m-d', strtotime( $post->post_date ) ); + // Don't bother if it hasn't changed. if ( $new_date == $previous_date ) { return; } + // We're only concerned with published, non-hierarchical objects. if ( ! ( 'publish' === $post->post_status || ( 'attachment' === get_post_type( $post ) && 'inherit' === $post->post_status ) ) || is_post_type_hierarchical( $post->post_type ) ) { return; } + $old_dates = (array) get_post_meta( $post_id, '_wp_old_date' ); + // If we haven't added this old date before, add it now. - if ( ! empty( $previous_date ) && ! in_array( $previous_date, $old_dates ) ) { + if ( ! empty( $previous_date ) && ! in_array( $previous_date, $old_dates, true ) ) { add_post_meta( $post_id, '_wp_old_date', $previous_date ); } + // If the new slug was used previously, delete it from the list. - if ( in_array( $new_date, $old_dates ) ) { + if ( in_array( $new_date, $old_dates, true ) ) { delete_post_meta( $post_id, '_wp_old_date', $new_date ); } } @@ -6068,12 +6526,12 @@ * @see get_private_posts_cap_sql() * @global wpdb $wpdb WordPress database abstraction object. * - * @param array|string $post_type Single post type or an array of post types. - * @param bool $full Optional. Returns a full WHERE statement instead of just - * an 'andalso' term. Default true. - * @param int $post_author Optional. Query posts having a single author ID. Default null. - * @param bool $public_only Optional. Only return public posts. Skips cap checks for - * $current_user. Default false. + * @param string|string[] $post_type Single post type or an array of post types. + * @param bool $full Optional. Returns a full WHERE statement instead of just + * an 'andalso' term. Default true. + * @param int $post_author Optional. Query posts having a single author ID. Default null. + * @param bool $public_only Optional. Only return public posts. Skips cap checks for + * $current_user. Default false. * @return string SQL WHERE code that can be added to a query. */ function get_posts_by_author_sql( $post_type, $full = true, $post_author = null, $public_only = false ) { @@ -6101,7 +6559,8 @@ * * @param string $cap Capability. */ - if ( ! $cap = apply_filters( 'pub_priv_sql_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 ); } @@ -6118,8 +6577,8 @@ $post_status_sql .= " OR post_status = 'private' AND post_author = $id"; } elseif ( $id == (int) $post_author ) { $post_status_sql .= " OR post_status = 'private'"; - } // else none - } // else none + } // Else none. + } // Else none. } $post_type_clauses[] = "( post_type = '" . $post_type . "' AND ( $post_status_sql ) )"; @@ -6143,42 +6602,47 @@ } /** - * Retrieve the date that the last post was published. + * Retrieves the most recent time that a post on the site was published. * * The server timezone is the default and is the difference between GMT and - * server time. The 'blog' value is the date when the last post was posted. The - * 'gmt' is when the last post was posted in GMT formatted date. + * server time. The 'blog' value is the date when the last post was posted. + * The 'gmt' is when the last post was posted in GMT formatted date. * * @since 0.71 * @since 4.4.0 The `$post_type` argument was added. * * @param string $timezone Optional. The timezone for the timestamp. Accepts 'server', 'blog', or 'gmt'. * 'server' uses the server's internal timezone. - * 'blog' uses the `post_modified` field, which proxies to the timezone set for the site. - * 'gmt' uses the `post_modified_gmt` field. + * 'blog' uses the `post_date` field, which proxies to the timezone set for the site. + * 'gmt' uses the `post_date_gmt` field. * Default 'server'. * @param string $post_type Optional. The post type to check. Default 'any'. - * @return string The date of the last post. + * @return string The date of the last post, or false on failure. */ function get_lastpostdate( $timezone = 'server', $post_type = 'any' ) { + $lastpostdate = _get_last_post_time( $timezone, 'date', $post_type ); + /** - * Filters the date the last post was published. + * Filters the most recent time that a post on the site was published. * * @since 2.3.0 + * @since 5.5.0 Added the `$post_type` parameter. * - * @param string $date Date the last post was published. - * @param string $timezone Location to use for getting the post published date. - * See get_lastpostdate() for accepted `$timezone` values. + * @param string|false $lastpostdate The most recent time that a post was published, + * in 'Y-m-d H:i:s' format. False on failure. + * @param string $timezone Location to use for getting the post published date. + * See get_lastpostdate() for accepted `$timezone` values. + * @param string $post_type The post type to check. */ - return apply_filters( 'get_lastpostdate', _get_last_post_time( $timezone, 'date', $post_type ), $timezone ); -} - -/** - * Get the timestamp of the last time any post was modified. + return apply_filters( 'get_lastpostdate', $lastpostdate, $timezone, $post_type ); +} + +/** + * Get 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. The - * 'gmt' is when the last post was modified in GMT time. + * server time. The 'blog' value is just when the last post was modified. + * The 'gmt' is when the last post was modified in GMT time. * * @since 1.2.0 * @since 4.4.0 The `$post_type` argument was added. @@ -6187,7 +6651,7 @@ * for information on accepted values. * Default 'server'. * @param string $post_type Optional. The post type to check. Default 'any'. - * @return string The timestamp. + * @return string The timestamp in 'Y-m-d H:i:s' format, or false on failure. */ function get_lastpostmodified( $timezone = 'server', $post_type = 'any' ) { /** @@ -6195,38 +6659,43 @@ * * @since 4.4.0 * - * @param string $lastpostmodified Date the last post was modified. - * Returning anything other than false will short-circuit the function. - * @param string $timezone Location to use for getting the post modified date. - * See get_lastpostdate() for accepted `$timezone` values. - * @param string $post_type The post type to check. + * @param string|false $lastpostmodified The most recent time that a post was modified, + * in 'Y-m-d H:i:s' format, or false. Returning anything + * other than false will short-circuit the function. + * @param string $timezone Location to use for getting the post modified date. + * See get_lastpostdate() for accepted `$timezone` values. + * @param string $post_type The post type to check. */ $lastpostmodified = apply_filters( 'pre_get_lastpostmodified', false, $timezone, $post_type ); + if ( false !== $lastpostmodified ) { return $lastpostmodified; } $lastpostmodified = _get_last_post_time( $timezone, 'modified', $post_type ); - - $lastpostdate = get_lastpostdate( $timezone ); + $lastpostdate = get_lastpostdate( $timezone, $post_type ); + if ( $lastpostdate > $lastpostmodified ) { $lastpostmodified = $lastpostdate; } /** - * Filters the date the last post was modified. + * Filters the most recent time that a post on the site was modified. * * @since 2.3.0 + * @since 5.5.0 Added the `$post_type` parameter. * - * @param string $lastpostmodified Date the last post was modified. - * @param string $timezone Location to use for getting the post modified date. - * See get_lastpostdate() for accepted `$timezone` values. + * @param string|false $lastpostmodified The most recent time that a post was modified, + * in 'Y-m-d H:i:s' format. False on failure. + * @param string $timezone Location to use for getting the post modified date. + * See get_lastpostdate() for accepted `$timezone` values. + * @param string $post_type The post type to check. */ - return apply_filters( 'get_lastpostmodified', $lastpostmodified, $timezone ); -} - -/** - * Get the timestamp of the last time any post was modified or published. + return apply_filters( 'get_lastpostmodified', $lastpostmodified, $timezone, $post_type ); +} + +/** + * Gets the timestamp of the last time any post was modified or published. * * @since 3.1.0 * @since 4.4.0 The `$post_type` argument was added. @@ -6238,12 +6707,12 @@ * for information on accepted values. * @param string $field Post field to check. Accepts 'date' or 'modified'. * @param string $post_type Optional. The post type to check. Default 'any'. - * @return string|false The timestamp. + * @return string|false The timestamp in 'Y-m-d H:i:s' format, or false on failure. */ function _get_last_post_time( $timezone, $field, $post_type = 'any' ) { global $wpdb; - if ( ! in_array( $field, array( 'date', 'modified' ) ) ) { + if ( ! in_array( $field, array( 'date', 'modified' ), true ) ) { return false; } @@ -6275,7 +6744,7 @@ $date = $wpdb->get_var( "SELECT post_{$field} FROM $wpdb->posts WHERE post_status = 'publish' AND post_type IN ({$post_types}) ORDER BY post_{$field}_gmt DESC LIMIT 1" ); break; case 'server': - $add_seconds_server = date( 'Z' ); + $add_seconds_server = gmdate( 'Z' ); $date = $wpdb->get_var( "SELECT DATE_ADD(post_{$field}_gmt, INTERVAL '$add_seconds_server' SECOND) FROM $wpdb->posts WHERE post_status = 'publish' AND post_type IN ({$post_types}) ORDER BY post_{$field}_gmt DESC LIMIT 1" ); break; } @@ -6294,7 +6763,7 @@ * * @since 1.5.1 * - * @param array $posts Array of post objects (passed by reference). + * @param WP_Post[] $posts Array of post objects (passed by reference). */ function update_post_cache( &$posts ) { if ( ! $posts ) { @@ -6350,7 +6819,7 @@ */ do_action( 'clean_post_cache', $post->ID, $post ); - if ( 'page' == $post->post_type ) { + if ( 'page' === $post->post_type ) { wp_cache_delete( 'all_page_ids', 'posts' ); /** @@ -6371,10 +6840,10 @@ * * @since 1.5.0 * - * @param array $posts Array of Post objects - * @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. + * @param WP_Post[] $posts Array of Post objects + * @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. */ function update_post_caches( &$posts, $post_type = 'post', $update_term_cache = true, $update_meta_cache = true ) { // No point in doing all this work if we didn't match any posts. @@ -6396,7 +6865,7 @@ if ( $update_term_cache ) { if ( is_array( $post_type ) ) { $ptypes = $post_type; - } elseif ( 'any' == $post_type ) { + } elseif ( 'any' === $post_type ) { $ptypes = array(); // Just use the post_types in the supplied posts. foreach ( $posts as $post ) { @@ -6426,9 +6895,8 @@ * * @since 2.1.0 * - * @param array $post_ids List of post IDs. - * @return array|false Returns false if there is nothing to update or an array - * of metadata. + * @param int[] $post_ids Array of post IDs. + * @return array|false An array of metadata on success, false if there is nothing to update. */ function update_postmeta_cache( $post_ids ) { return update_meta_cache( 'post', $post_ids ); @@ -6476,7 +6944,7 @@ } // -// Hooks +// Hooks. // /** @@ -6495,9 +6963,9 @@ function _transition_post_status( $new_status, $old_status, $post ) { global $wpdb; - if ( $old_status != 'publish' && $new_status == 'publish' ) { + if ( 'publish' !== $old_status && 'publish' === $new_status ) { // Reset GUID if transitioning to publish and it is empty. - if ( '' == get_the_guid( $post->ID ) ) { + if ( '' === get_the_guid( $post->ID ) ) { $wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post->ID ) ), array( 'ID' => $post->ID ) ); } @@ -6505,15 +6973,15 @@ * Fires when a post's status is transitioned from private to published. * * @since 1.5.0 - * @deprecated 2.3.0 Use 'private_to_publish' instead. + * @deprecated 2.3.0 Use {@see 'private_to_publish'} instead. * * @param int $post_id Post ID. */ - do_action( 'private_to_published', $post->ID ); + do_action_deprecated( 'private_to_published', array( $post->ID ), '2.3.0', 'private_to_publish' ); } // If published posts changed clear the lastpostmodified cache. - if ( 'publish' == $new_status || 'publish' == $old_status ) { + if ( 'publish' === $new_status || 'publish' === $old_status ) { foreach ( array( 'server', 'gmt', 'blog' ) as $timezone ) { wp_cache_delete( "lastpostmodified:$timezone", 'timeinfo' ); wp_cache_delete( "lastpostdate:$timezone", 'timeinfo' ); @@ -6575,9 +7043,14 @@ } if ( get_option( 'default_pingback_flag' ) ) { - add_post_meta( $post_id, '_pingme', '1' ); - } - add_post_meta( $post_id, '_encloseme', '1' ); + add_post_meta( $post_id, '_pingme', '1', true ); + } + add_post_meta( $post_id, '_encloseme', '1', true ); + + $to_ping = get_to_ping( $post_id ); + if ( ! empty( $to_ping ) ) { + add_post_meta( $post_id, '_trackbackme', '1' ); + } if ( ! wp_next_scheduled( 'do_pings' ) ) { wp_schedule_single_event( time(), 'do_pings' ); @@ -6590,7 +7063,8 @@ * @since 3.1.0 * * @param int|WP_Post $post Post ID or post object. Defaults to global $post. - * @return int|false Post parent ID (which can be 0 if there is no parent), or false if the post does not exist. + * @return int|false Post parent ID (which can be 0 if there is no parent), + * or false if the post does not exist. */ function wp_get_post_parent_id( $post ) { $post = get_post( $post ); @@ -6631,8 +7105,9 @@ } // Now look for larger loops. - if ( ! $loop = wp_find_hierarchy_loop( 'wp_get_post_parent_id', $post_ID, $post_parent ) ) { - return $post_parent; // No loop + $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. @@ -6717,7 +7192,8 @@ * @param array $posts Array of WP_Post objects. */ function wp_queue_posts_for_term_meta_lazyload( $posts ) { - $post_type_taxonomies = $term_ids = array(); + $post_type_taxonomies = array(); + $term_ids = array(); foreach ( $posts as $post ) { if ( ! ( $post instanceof WP_Post ) ) { continue; @@ -6768,7 +7244,7 @@ } /** - * Adds any posts from the given ids to the cache that do not already exist in cache + * Adds any posts from the given IDs to the cache that do not already exist in cache * * @since 3.4.0 * @access private @@ -6861,9 +7337,9 @@ * * @global wpdb $wpdb WordPress database abstraction object. * - * @param array $clauses An array including WHERE, GROUP BY, JOIN, ORDER BY, - * DISTINCT, fields (SELECT), and LIMITS clauses. - * @return array The modified clauses. + * @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; @@ -6908,3 +7384,85 @@ $types = $wpdb->get_col( $wpdb->prepare( "SELECT DISTINCT post_mime_type FROM $wpdb->posts WHERE post_type = %s", $type ) ); return $types; } + +/** + * Retrieves the path to an uploaded image file. + * + * Similar to `get_attached_file()` however some images may have been processed after uploading + * to make them suitable for web use. In this case the attached "full" size file is usually replaced + * with a scaled down version of the original image. This function always returns the path + * to the originally uploaded image file. + * + * @since 5.3.0 + * @since 5.4.0 Added the `$unfiltered` parameter. + * + * @param int $attachment_id Attachment ID. + * @param bool $unfiltered Optional. Passed through to `get_attached_file()`. Default false. + * @return string|false Path to the original image file or false if the attachment is not an image. + */ +function wp_get_original_image_path( $attachment_id, $unfiltered = false ) { + if ( ! wp_attachment_is_image( $attachment_id ) ) { + return false; + } + + $image_meta = wp_get_attachment_metadata( $attachment_id ); + $image_file = get_attached_file( $attachment_id, $unfiltered ); + + if ( empty( $image_meta['original_image'] ) ) { + $original_image = $image_file; + } else { + $original_image = path_join( dirname( $image_file ), $image_meta['original_image'] ); + } + + /** + * Filters the path to the original image. + * + * @since 5.3.0 + * + * @param string $original_image Path to original image file. + * @param int $attachment_id Attachment ID. + */ + return apply_filters( 'wp_get_original_image_path', $original_image, $attachment_id ); +} + +/** + * Retrieve 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 + * to the originally uploaded image file. + * + * @since 5.3.0 + * + * @param int $attachment_id Attachment post ID. + * @return string|false Attachment image URL, false on error or if the attachment is not an image. + */ +function wp_get_original_image_url( $attachment_id ) { + if ( ! wp_attachment_is_image( $attachment_id ) ) { + return false; + } + + $image_url = wp_get_attachment_url( $attachment_id ); + + if ( empty( $image_url ) ) { + return false; + } + + $image_meta = wp_get_attachment_metadata( $attachment_id ); + + if ( empty( $image_meta['original_image'] ) ) { + $original_image_url = $image_url; + } else { + $original_image_url = path_join( dirname( $image_url ), $image_meta['original_image'] ); + } + + /** + * Filters the URL to the original attachment image. + * + * @since 5.3.0 + * + * @param string $original_image_url URL to original image. + * @param int $attachment_id Attachment ID. + */ + return apply_filters( 'wp_get_original_image_url', $original_image_url, $attachment_id ); +}