* Filters the value of a specific post field to edit.
* The dynamic portion of the hook name, `$field`, refers to the post
* @param mixed $value Value of the post field.
* @param int $post_id Post ID.
$value = apply_filters( "edit_{$field}", $value, $post_id );
* Filters the value of a specific post field to edit.
* The dynamic portion of the hook name, `$field_no_prefix`, refers to
* @param mixed $value Value of the post field.
* @param int $post_id Post ID.
$value = apply_filters( "{$field_no_prefix}_edit_pre", $value, $post_id );
$value = apply_filters( "edit_post_{$field}", $value, $post_id );
if ( in_array( $field, $format_to_edit, true ) ) {
if ( 'post_content' === $field ) {
$value = format_to_edit( $value, user_can_richedit() );
$value = format_to_edit( $value );
$value = esc_attr( $value );
} elseif ( 'db' === $context ) {
* Filters the value of a specific post field before saving.
* The dynamic portion of the hook name, `$field`, refers to the post
* @param mixed $value Value of the post field.
$value = apply_filters( "pre_{$field}", $value );
* Filters the value of a specific field before saving.
* The dynamic portion of the hook name, `$field_no_prefix`, refers
* to the post field name.
* @param mixed $value Value of the post field.
$value = apply_filters( "{$field_no_prefix}_save_pre", $value );
$value = apply_filters( "pre_post_{$field}", $value );
* Filters the value of a specific post field before saving.
* The dynamic portion of the hook name, `$field`, refers to the post
* @param mixed $value Value of the post field.
$value = apply_filters( "{$field}_pre", $value );
// Use display filters by default.
* Filters the value of a specific post field for display.
* The dynamic portion of the hook name, `$field`, refers to the post
* @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.
* Accepts 'raw', 'edit', 'db', 'display',
* 'attribute', or 'js'. Default 'display'.
$value = apply_filters( "{$field}", $value, $post_id, $context );
$value = apply_filters( "post_{$field}", $value, $post_id, $context );
if ( 'attribute' === $context ) {
$value = esc_attr( $value );
} elseif ( 'js' === $context ) {
$value = esc_js( $value );
* Sticky posts should be displayed at the top of the front page.
* @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 );
$stickies = array_unique( array_map( 'intval', $stickies ) );
if ( ! in_array( $post_id, $stickies, true ) ) {
$updated = update_option( 'sticky_posts', array_values( $stickies ) );
* Fires once a post has been added to the sticky list.
* @param int $post_id ID of the post that was stuck.
do_action( 'post_stuck', $post_id );
* Sticky posts should be displayed at the top of the front page.
* @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 ) ) {
$stickies = array_values( array_unique( array_map( 'intval', $stickies ) ) );
if ( ! in_array( $post_id, $stickies, true ) ) {
$offset = array_search( $post_id, $stickies, true );
if ( false === $offset ) {
array_splice( $stickies, $offset, 1 );
$updated = update_option( 'sticky_posts', $stickies );
* Fires once a post has been removed from the sticky list.
* @param int $post_id ID of the post that was unstuck.
do_action( 'post_unstuck', $post_id );
* Return the cache key for wp_count_posts() based on the passed arguments.
* @param string $type Optional. Post type to retrieve count Default 'post'.
* @param string $perm Optional. 'readable' or empty. Default empty.
* @return string The cache key.
function _count_posts_cache_key( $type = 'post', $perm = '' ) {
$cache_key = 'posts-' . $type;
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();
* Count number of posts of a post type and if user has permissions to view.
* This function provides an efficient method of finding the amount of post's
* type a blog has. Another method is to count the amount of items in
* get_posts(), but that method has a lot of overhead with doing so. Therefore,
* when developing for 2.5+, use this function instead.
* The $perm parameter checks for 'readable' value and if the user can read
* private posts, it will display that for the user that is signed in.
* @global wpdb $wpdb WordPress database abstraction object.
* @param string $type Optional. Post type to retrieve count. Default 'post'.
* @param string $perm Optional. 'readable' or empty. Default empty.
* @return object Number of posts for each status.
function wp_count_posts( $type = 'post', $perm = '' ) {
if ( ! post_type_exists( $type ) ) {
$cache_key = _count_posts_cache_key( $type, $perm );
$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} ) ) {
/** 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() ) {
$post_type_object = get_post_type_object( $type );
if ( ! current_user_can( $post_type_object->cap->read_private_posts ) ) {
$query .= $wpdb->prepare(
" AND (post_status != 'private' OR ( post_author = %d AND post_status = 'private' ))",
$query .= ' GROUP BY post_status';
$results = (array) $wpdb->get_results( $wpdb->prepare( $query, $type ), ARRAY_A );
$counts = array_fill_keys( get_post_stati(), 0 );
foreach ( $results as $row ) {
$counts[ $row['post_status'] ] = $row['num_posts'];
$counts = (object) $counts;
wp_cache_set( $cache_key, $counts, 'counts' );
* Modify returned post counts by status for the current post type.
* @param object $counts An object containing the current post_type's post
* @param string $type Post type.
* @param string $perm The permission to determine if the posts are 'readable'
return apply_filters( 'wp_count_posts', $counts, $type, $perm );
* Count number of attachments for the mime type(s).
* If you set the optional mime_type parameter, then an array will still be
* returned, but will only have the item you are looking for. It does not give
* you the number of attachments that are children of a post. You can get that
* by counting the number of children that post has.
* @global wpdb $wpdb WordPress database abstraction object.
* @param string|string[] $mime_type Optional. Array or comma-separated list of
* MIME patterns. Default empty.
* @return object An object containing the attachment counts by mime type.
function wp_count_attachments( $mime_type = '' ) {
$and = wp_post_mime_type_where( $mime_type );
$count = $wpdb->get_results( "SELECT post_mime_type, COUNT( * ) AS num_posts FROM $wpdb->posts WHERE post_type = 'attachment' AND post_status != 'trash' $and GROUP BY post_mime_type", ARRAY_A );
foreach ( (array) $count as $row ) {
$counts[ $row['post_mime_type'] ] = $row['num_posts'];
$counts['trash'] = $wpdb->get_var( "SELECT COUNT( * ) FROM $wpdb->posts WHERE post_type = 'attachment' AND post_status = 'trash' $and" );
* Modify returned attachment counts by mime type.
* @param object $counts An object containing the attachment counts by
* @param string|string[] $mime_type Array or comma-separated list of MIME patterns.
return apply_filters( 'wp_count_attachments', (object) $counts, $mime_type );
* Get default post mime types.
* @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 )
/* translators: %s: Number of images. */
'Image <span class="count">(%s)</span>',
'Images <span class="count">(%s)</span>'
/* translators: %s: Number of audio files. */
'Audio <span class="count">(%s)</span>',
'Audio <span class="count">(%s)</span>'
/* translators: %s: Number of video files. */
'Video <span class="count">(%s)</span>',
'Video <span class="count">(%s)</span>'
__( 'Manage Documents' ),
/* translators: %s: Number of documents. */
'Document <span class="count">(%s)</span>',
'Documents <span class="count">(%s)</span>'
__( 'Manage Spreadsheets' ),
/* translators: %s: Number of spreadsheets. */
'Spreadsheet <span class="count">(%s)</span>',
'Spreadsheets <span class="count">(%s)</span>'
_x( 'Archives', 'file type group' ),
/* translators: %s: Number of archives. */
'Archive <span class="count">(%s)</span>',
'Archives <span class="count">(%s)</span>'
$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 ) ) {
if ( ! isset( $ext_types[ $group ] ) ) {
unset( $post_mime_types[ $group ] );
$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;
$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.
* @param array $post_mime_types Default list of post mime types.
return apply_filters( 'post_mime_types', $post_mime_types );
* Check a MIME-Type against a list.
* If the wildcard_mime_types parameter is a string, it must be comma separated
* list. If the real_mime_types is a string, it is also comma separated to
* @param string|string[] $wildcard_mime_types Mime types, e.g. audio/mpeg or image (same as image/*)
* or flash (same as *flash*).
* @param string|string[] $real_mime_types Real post mime type values.
* @return array array(wildcard=>array(real types)).
function wp_match_mime_types( $wildcard_mime_types, $real_mime_types ) {
if ( is_string( $wildcard_mime_types ) ) {
$wildcard_mime_types = array_map( 'trim', explode( ',', $wildcard_mime_types ) );
if ( is_string( $real_mime_types ) ) {
$real_mime_types = array_map( 'trim', explode( ',', $real_mime_types ) );
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 ) ) );
$patternses[][ $type ] = "^$regex$";
if ( false === strpos( $mime, '/' ) ) {
$patternses[][ $type ] = "^$regex/";
$patternses[][ $type ] = $regex;
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 ], true ) )
$matches[ $type ][] = $real;