[ Index ]

PHP Cross Reference of WordPress

title

Body

[close]

/wp-includes/ -> post.php (source)

   1  <?php
   2  /**
   3   * Post functions and post utility function.
   4   *
   5   * @package WordPress
   6   * @subpackage Post
   7   * @since 1.5.0
   8   */
   9  
  10  //
  11  // Post Type Registration
  12  //
  13  
  14  /**
  15   * Creates the initial post types when 'init' action is fired.
  16   *
  17   * @since 2.9.0
  18   */
  19  function create_initial_post_types() {
  20      register_post_type( 'post', array(
  21          'labels' => array(
  22              'name_admin_bar' => _x( 'Post', 'add new on admin bar' ),
  23          ),
  24          'public'  => true,
  25          '_builtin' => true, /* internal use only. don't use this when registering your own post type. */
  26          '_edit_link' => 'post.php?post=%d', /* internal use only. don't use this when registering your own post type. */
  27          'capability_type' => 'post',
  28          'map_meta_cap' => true,
  29          'hierarchical' => false,
  30          'rewrite' => false,
  31          'query_var' => false,
  32          'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'trackbacks', 'custom-fields', 'comments', 'revisions', 'post-formats' ),
  33      ) );
  34  
  35      register_post_type( 'page', array(
  36          'labels' => array(
  37              'name_admin_bar' => _x( 'Page', 'add new on admin bar' ),
  38          ),
  39          'public' => true,
  40          'publicly_queryable' => false,
  41          '_builtin' => true, /* internal use only. don't use this when registering your own post type. */
  42          '_edit_link' => 'post.php?post=%d', /* internal use only. don't use this when registering your own post type. */
  43          'capability_type' => 'page',
  44          'map_meta_cap' => true,
  45          'hierarchical' => true,
  46          'rewrite' => false,
  47          'query_var' => false,
  48          'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'page-attributes', 'custom-fields', 'comments', 'revisions' ),
  49      ) );
  50  
  51      register_post_type( 'attachment', array(
  52          'labels' => array(
  53              'name' => __( 'Media' ),
  54              'edit_item' => __( 'Edit Media' ),
  55          ),
  56          'public' => true,
  57          'show_ui' => false,
  58          '_builtin' => true, /* internal use only. don't use this when registering your own post type. */
  59          '_edit_link' => 'media.php?attachment_id=%d', /* internal use only. don't use this when registering your own post type. */
  60          'capability_type' => 'post',
  61          'map_meta_cap' => true,
  62          'hierarchical' => false,
  63          'rewrite' => false,
  64          'query_var' => false,
  65          'show_in_nav_menus' => false,
  66          'supports' => array( 'comments' ),
  67      ) );
  68  
  69      register_post_type( 'revision', array(
  70          'labels' => array(
  71              'name' => __( 'Revisions' ),
  72              'singular_name' => __( 'Revision' ),
  73          ),
  74          'public' => false,
  75          '_builtin' => true, /* internal use only. don't use this when registering your own post type. */
  76          '_edit_link' => 'revision.php?revision=%d', /* internal use only. don't use this when registering your own post type. */
  77          'capability_type' => 'post',
  78          'map_meta_cap' => true,
  79          'hierarchical' => false,
  80          'rewrite' => false,
  81          'query_var' => false,
  82          'can_export' => false,
  83      ) );
  84  
  85      register_post_type( 'nav_menu_item', array(
  86          'labels' => array(
  87              'name' => __( 'Navigation Menu Items' ),
  88              'singular_name' => __( 'Navigation Menu Item' ),
  89          ),
  90          'public' => false,
  91          '_builtin' => true, /* internal use only. don't use this when registering your own post type. */
  92          'hierarchical' => false,
  93          'rewrite' => false,
  94          'query_var' => false,
  95      ) );
  96  
  97      register_post_status( 'publish', array(
  98          'label'       => _x( 'Published', 'post' ),
  99          'public'      => true,
 100          '_builtin'    => true, /* internal use only. */
 101          'label_count' => _n_noop( 'Published <span class="count">(%s)</span>', 'Published <span class="count">(%s)</span>' ),
 102      ) );
 103  
 104      register_post_status( 'future', array(
 105          'label'       => _x( 'Scheduled', 'post' ),
 106          'protected'   => true,
 107          '_builtin'    => true, /* internal use only. */
 108          'label_count' => _n_noop('Scheduled <span class="count">(%s)</span>', 'Scheduled <span class="count">(%s)</span>' ),
 109      ) );
 110  
 111      register_post_status( 'draft', array(
 112          'label'       => _x( 'Draft', 'post' ),
 113          'protected'   => true,
 114          '_builtin'    => true, /* internal use only. */
 115          'label_count' => _n_noop( 'Draft <span class="count">(%s)</span>', 'Drafts <span class="count">(%s)</span>' ),
 116      ) );
 117  
 118      register_post_status( 'pending', array(
 119          'label'       => _x( 'Pending', 'post' ),
 120          'protected'   => true,
 121          '_builtin'    => true, /* internal use only. */
 122          'label_count' => _n_noop( 'Pending <span class="count">(%s)</span>', 'Pending <span class="count">(%s)</span>' ),
 123      ) );
 124  
 125      register_post_status( 'private', array(
 126          'label'       => _x( 'Private', 'post' ),
 127          'private'     => true,
 128          '_builtin'    => true, /* internal use only. */
 129          'label_count' => _n_noop( 'Private <span class="count">(%s)</span>', 'Private <span class="count">(%s)</span>' ),
 130      ) );
 131  
 132      register_post_status( 'trash', array(
 133          'label'       => _x( 'Trash', 'post' ),
 134          'internal'    => true,
 135          '_builtin'    => true, /* internal use only. */
 136          'label_count' => _n_noop( 'Trash <span class="count">(%s)</span>', 'Trash <span class="count">(%s)</span>' ),
 137          'show_in_admin_status_list' => true,
 138      ) );
 139  
 140      register_post_status( 'auto-draft', array(
 141          'label'    => 'auto-draft',
 142          'internal' => true,
 143          '_builtin' => true, /* internal use only. */
 144      ) );
 145  
 146      register_post_status( 'inherit', array(
 147          'label'    => 'inherit',
 148          'internal' => true,
 149          '_builtin' => true, /* internal use only. */
 150          'exclude_from_search' => false,
 151      ) );
 152  }
 153  add_action( 'init', 'create_initial_post_types', 0 ); // highest priority
 154  
 155  /**
 156   * Retrieve attached file path based on attachment ID.
 157   *
 158   * You can optionally send it through the 'get_attached_file' filter, but by
 159   * default it will just return the file path unfiltered.
 160   *
 161   * The function works by getting the single post meta name, named
 162   * '_wp_attached_file' and returning it. This is a convenience function to
 163   * prevent looking up the meta name and provide a mechanism for sending the
 164   * attached filename through a filter.
 165   *
 166   * @since 2.0.0
 167   * @uses apply_filters() Calls 'get_attached_file' on file path and attachment ID.
 168   *
 169   * @param int $attachment_id Attachment ID.
 170   * @param bool $unfiltered Whether to apply filters.
 171   * @return string The file path to the attached file.
 172   */
 173  function get_attached_file( $attachment_id, $unfiltered = false ) {
 174      $file = get_post_meta( $attachment_id, '_wp_attached_file', true );
 175      // If the file is relative, prepend upload dir
 176      if ( 0 !== strpos($file, '/') && !preg_match('|^.:\\\|', $file) && ( ($uploads = wp_upload_dir()) && false === $uploads['error'] ) )
 177          $file = $uploads['basedir'] . "/$file";
 178      if ( $unfiltered )
 179          return $file;
 180      return apply_filters( 'get_attached_file', $file, $attachment_id );
 181  }
 182  
 183  /**
 184   * Update attachment file path based on attachment ID.
 185   *
 186   * Used to update the file path of the attachment, which uses post meta name
 187   * '_wp_attached_file' to store the path of the attachment.
 188   *
 189   * @since 2.1.0
 190   * @uses apply_filters() Calls 'update_attached_file' on file path and attachment ID.
 191   *
 192   * @param int $attachment_id Attachment ID
 193   * @param string $file File path for the attachment
 194   * @return bool False on failure, true on success.
 195   */
 196  function update_attached_file( $attachment_id, $file ) {
 197      if ( !get_post( $attachment_id ) )
 198          return false;
 199  
 200      $file = apply_filters( 'update_attached_file', $file, $attachment_id );
 201      $file = _wp_relative_upload_path($file);
 202  
 203      return update_post_meta( $attachment_id, '_wp_attached_file', $file );
 204  }
 205  
 206  /**
 207   * Return relative path to an uploaded file.
 208   *
 209   * The path is relative to the current upload dir.
 210   *
 211   * @since 2.9.0
 212   * @uses apply_filters() Calls '_wp_relative_upload_path' on file path.
 213   *
 214   * @param string $path Full path to the file
 215   * @return string relative path on success, unchanged path on failure.
 216   */
 217  function _wp_relative_upload_path( $path ) {
 218      $new_path = $path;
 219  
 220      if ( ($uploads = wp_upload_dir()) && false === $uploads['error'] ) {
 221          if ( 0 === strpos($new_path, $uploads['basedir']) ) {
 222                  $new_path = str_replace($uploads['basedir'], '', $new_path);
 223                  $new_path = ltrim($new_path, '/');
 224          }
 225      }
 226  
 227      return apply_filters( '_wp_relative_upload_path', $new_path, $path );
 228  }
 229  
 230  /**
 231   * Retrieve all children of the post parent ID.
 232   *
 233   * Normally, without any enhancements, the children would apply to pages. In the
 234   * context of the inner workings of WordPress, pages, posts, and attachments
 235   * share the same table, so therefore the functionality could apply to any one
 236   * of them. It is then noted that while this function does not work on posts, it
 237   * does not mean that it won't work on posts. It is recommended that you know
 238   * what context you wish to retrieve the children of.
 239   *
 240   * Attachments may also be made the child of a post, so if that is an accurate
 241   * statement (which needs to be verified), it would then be possible to get
 242   * all of the attachments for a post. Attachments have since changed since
 243   * version 2.5, so this is most likely unaccurate, but serves generally as an
 244   * example of what is possible.
 245   *
 246   * The arguments listed as defaults are for this function and also of the
 247   * {@link get_posts()} function. The arguments are combined with the
 248   * get_children defaults and are then passed to the {@link get_posts()}
 249   * function, which accepts additional arguments. You can replace the defaults in
 250   * this function, listed below and the additional arguments listed in the
 251   * {@link get_posts()} function.
 252   *
 253   * The 'post_parent' is the most important argument and important attention
 254   * needs to be paid to the $args parameter. If you pass either an object or an
 255   * integer (number), then just the 'post_parent' is grabbed and everything else
 256   * is lost. If you don't specify any arguments, then it is assumed that you are
 257   * in The Loop and the post parent will be grabbed for from the current post.
 258   *
 259   * The 'post_parent' argument is the ID to get the children. The 'numberposts'
 260   * is the amount of posts to retrieve that has a default of '-1', which is
 261   * used to get all of the posts. Giving a number higher than 0 will only
 262   * retrieve that amount of posts.
 263   *
 264   * The 'post_type' and 'post_status' arguments can be used to choose what
 265   * criteria of posts to retrieve. The 'post_type' can be anything, but WordPress
 266   * post types are 'post', 'pages', and 'attachments'. The 'post_status'
 267   * argument will accept any post status within the write administration panels.
 268   *
 269   * @see get_posts() Has additional arguments that can be replaced.
 270   * @internal Claims made in the long description might be inaccurate.
 271   *
 272   * @since 2.0.0
 273   *
 274   * @param mixed $args Optional. User defined arguments for replacing the defaults.
 275   * @param string $output Optional. Constant for return type, either OBJECT (default), ARRAY_A, ARRAY_N.
 276   * @return array|bool False on failure and the type will be determined by $output parameter.
 277   */
 278  function get_children($args = '', $output = OBJECT) {
 279      $kids = array();
 280      if ( empty( $args ) ) {
 281          if ( isset( $GLOBALS['post'] ) ) {
 282              $args = array('post_parent' => (int) $GLOBALS['post']->post_parent );
 283          } else {
 284              return $kids;
 285          }
 286      } elseif ( is_object( $args ) ) {
 287          $args = array('post_parent' => (int) $args->post_parent );
 288      } elseif ( is_numeric( $args ) ) {
 289          $args = array('post_parent' => (int) $args);
 290      }
 291  
 292      $defaults = array(
 293          'numberposts' => -1, 'post_type' => 'any',
 294          'post_status' => 'any', 'post_parent' => 0,
 295      );
 296  
 297      $r = wp_parse_args( $args, $defaults );
 298  
 299      $children = get_posts( $r );
 300  
 301      if ( !$children )
 302          return $kids;
 303  
 304      update_post_cache($children);
 305  
 306      foreach ( $children as $key => $child )
 307          $kids[$child->ID] = $children[$key];
 308  
 309      if ( $output == OBJECT ) {
 310          return $kids;
 311      } elseif ( $output == ARRAY_A ) {
 312          foreach ( (array) $kids as $kid )
 313              $weeuns[$kid->ID] = get_object_vars($kids[$kid->ID]);
 314          return $weeuns;
 315      } elseif ( $output == ARRAY_N ) {
 316          foreach ( (array) $kids as $kid )
 317              $babes[$kid->ID] = array_values(get_object_vars($kids[$kid->ID]));
 318          return $babes;
 319      } else {
 320          return $kids;
 321      }
 322  }
 323  
 324  /**
 325   * Get extended entry info (<!--more-->).
 326   *
 327   * There should not be any space after the second dash and before the word
 328   * 'more'. There can be text or space(s) after the word 'more', but won't be
 329   * referenced.
 330   *
 331   * The returned array has 'main' and 'extended' keys. Main has the text before
 332   * the <code><!--more--></code>. The 'extended' key has the content after the
 333   * <code><!--more--></code> comment.
 334   *
 335   * @since 1.0.0
 336   *
 337   * @param string $post Post content.
 338   * @return array Post before ('main') and after ('extended').
 339   */
 340  function get_extended($post) {
 341      //Match the new style more links
 342      if ( preg_match('/<!--more(.*?)?-->/', $post, $matches) ) {
 343          list($main, $extended) = explode($matches[0], $post, 2);
 344      } else {
 345          $main = $post;
 346          $extended = '';
 347      }
 348  
 349      // Strip leading and trailing whitespace
 350      $main = preg_replace('/^[\s]*(.*)[\s]*$/', '\\1', $main);
 351      $extended = preg_replace('/^[\s]*(.*)[\s]*$/', '\\1', $extended);
 352  
 353      return array('main' => $main, 'extended' => $extended);
 354  }
 355  
 356  /**
 357   * Retrieves post data given a post ID or post object.
 358   *
 359   * See {@link sanitize_post()} for optional $filter values. Also, the parameter
 360   * $post, must be given as a variable, since it is passed by reference.
 361   *
 362   * @since 1.5.1
 363   * @uses $wpdb
 364   * @link http://codex.wordpress.org/Function_Reference/get_post
 365   *
 366   * @param int|object $post Post ID or post object.
 367   * @param string $output Optional, default is Object. Either OBJECT, ARRAY_A, or ARRAY_N.
 368   * @param string $filter Optional, default is raw.
 369   * @return mixed Post data
 370   */
 371  function &get_post(&$post, $output = OBJECT, $filter = 'raw') {
 372      global $wpdb;
 373      $null = null;
 374  
 375      if ( empty($post) ) {
 376          if ( isset($GLOBALS['post']) )
 377              $_post = & $GLOBALS['post'];
 378          else
 379              return $null;
 380      } elseif ( is_object($post) && empty($post->filter) ) {
 381          _get_post_ancestors($post);
 382          $_post = sanitize_post($post, 'raw');
 383          wp_cache_add($post->ID, $_post, 'posts');
 384      } elseif ( is_object($post) && 'raw' == $post->filter ) {
 385          $_post = $post;
 386      } else {
 387          if ( is_object($post) )
 388              $post_id = $post->ID;
 389          else
 390              $post_id = $post;
 391  
 392          $post_id = (int) $post_id;
 393          if ( ! $_post = wp_cache_get($post_id, 'posts') ) {
 394              $_post = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d LIMIT 1", $post_id));
 395              if ( ! $_post )
 396                  return $null;
 397              _get_post_ancestors($_post);
 398              $_post = sanitize_post($_post, 'raw');
 399              wp_cache_add($_post->ID, $_post, 'posts');
 400          }
 401      }
 402  
 403      if ($filter != 'raw')
 404          $_post = sanitize_post($_post, $filter);
 405  
 406      if ( $output == OBJECT ) {
 407          return $_post;
 408      } elseif ( $output == ARRAY_A ) {
 409          $__post = get_object_vars($_post);
 410          return $__post;
 411      } elseif ( $output == ARRAY_N ) {
 412          $__post = array_values(get_object_vars($_post));
 413          return $__post;
 414      } else {
 415          return $_post;
 416      }
 417  }
 418  
 419  /**
 420   * Retrieve ancestors of a post.
 421   *
 422   * @since 2.5.0
 423   *
 424   * @param int|object $post Post ID or post object
 425   * @return array Ancestor IDs or empty array if none are found.
 426   */
 427  function get_post_ancestors($post) {
 428      $post = get_post($post);
 429  
 430      if ( !empty($post->ancestors) )
 431          return $post->ancestors;
 432  
 433      return array();
 434  }
 435  
 436  /**
 437   * Retrieve data from a post field based on Post ID.
 438   *
 439   * Examples of the post field will be, 'post_type', 'post_status', 'post_content',
 440   * etc and based off of the post object property or key names.
 441   *
 442   * The context values are based off of the taxonomy filter functions and
 443   * supported values are found within those functions.
 444   *
 445   * @since 2.3.0
 446   * @uses sanitize_post_field() See for possible $context values.
 447   *
 448   * @param string $field Post field name
 449   * @param id $post Post ID
 450   * @param string $context Optional. How to filter the field. Default is display.
 451   * @return WP_Error|string Value in post field or WP_Error on failure
 452   */
 453  function get_post_field( $field, $post, $context = 'display' ) {
 454      $post = (int) $post;
 455      $post = get_post( $post );
 456  
 457      if ( is_wp_error($post) )
 458          return $post;
 459  
 460      if ( !is_object($post) )
 461          return '';
 462  
 463      if ( !isset($post->$field) )
 464          return '';
 465  
 466      return sanitize_post_field($field, $post->$field, $post->ID, $context);
 467  }
 468  
 469  /**
 470   * Retrieve the mime type of an attachment based on the ID.
 471   *
 472   * This function can be used with any post type, but it makes more sense with
 473   * attachments.
 474   *
 475   * @since 2.0.0
 476   *
 477   * @param int $ID Optional. Post ID.
 478   * @return bool|string False on failure or returns the mime type
 479   */
 480  function get_post_mime_type($ID = '') {
 481      $post = & get_post($ID);
 482  
 483      if ( is_object($post) )
 484          return $post->post_mime_type;
 485  
 486      return false;
 487  }
 488  
 489  /**
 490   * Retrieve the format slug for a post
 491   *
 492   * @since 3.1.0
 493   *
 494   * @param int|object $post A post
 495   *
 496   * @return mixed The format if successful. False if no format is set. WP_Error if errors.
 497   */
 498  function get_post_format( $post = null ) {
 499      $post = get_post($post);
 500  
 501      if ( ! post_type_supports( $post->post_type, 'post-formats' ) )
 502          return false;
 503  
 504      $_format = get_the_terms( $post->ID, 'post_format' );
 505  
 506      if ( empty( $_format ) )
 507          return false;
 508  
 509      $format = array_shift( $_format );
 510  
 511      return ( str_replace('post-format-', '', $format->slug ) );
 512  }
 513  
 514  /**
 515   * Check if a post has a particular format
 516   *
 517   * @since 3.1.0
 518   * @uses has_term()
 519   *
 520   * @param string $format The format to check for
 521   * @param object|id $post The post to check. If not supplied, defaults to the current post if used in the loop.
 522   * @return bool True if the post has the format, false otherwise.
 523   */
 524  function has_post_format( $format, $post = null ) {
 525      return has_term('post-format-' . sanitize_key($format), 'post_format', $post);
 526  }
 527  
 528  /**
 529   * Assign a format to a post
 530   *
 531   * @since 3.1.0
 532   *
 533   * @param int|object $post The post for which to assign a format
 534   * @param string $format  A format to assign. Use an empty string or array to remove all formats from the post.
 535   * @return mixed WP_Error on error. Array of affected term IDs on success.
 536   */
 537  function set_post_format( $post, $format ) {
 538      $post = get_post($post);
 539  
 540      if ( empty($post) )
 541          return new WP_Error('invalid_post', __('Invalid post'));
 542  
 543      if ( !empty($format) ) {
 544          $format = sanitize_key($format);
 545          if ( 'standard' == $format || !in_array( $format, array_keys( get_post_format_slugs() ) ) )
 546              $format = '';
 547          else
 548              $format = 'post-format-' . $format;
 549      }
 550  
 551      return wp_set_post_terms($post->ID, $format, 'post_format');
 552  }
 553  
 554  /**
 555   * Retrieve the post status based on the Post ID.
 556   *
 557   * If the post ID is of an attachment, then the parent post status will be given
 558   * instead.
 559   *
 560   * @since 2.0.0
 561   *
 562   * @param int $ID Post ID
 563   * @return string|bool Post status or false on failure.
 564   */
 565  function get_post_status($ID = '') {
 566      $post = get_post($ID);
 567  
 568      if ( !is_object($post) )
 569          return false;
 570  
 571      if ( 'attachment' == $post->post_type ) {
 572          if ( 'private' == $post->post_status )
 573              return 'private';
 574  
 575          // Unattached attachments are assumed to be published
 576          if ( ( 'inherit' == $post->post_status ) && ( 0 == $post->post_parent) )
 577              return 'publish';
 578  
 579          // Inherit status from the parent
 580          if ( $post->post_parent && ( $post->ID != $post->post_parent ) )
 581              return get_post_status($post->post_parent);
 582      }
 583  
 584      return $post->post_status;
 585  }
 586  
 587  /**
 588   * Retrieve all of the WordPress supported post statuses.
 589   *
 590   * Posts have a limited set of valid status values, this provides the
 591   * post_status values and descriptions.
 592   *
 593   * @since 2.5.0
 594   *
 595   * @return array List of post statuses.
 596   */
 597  function get_post_statuses( ) {
 598      $status = array(
 599          'draft'            => __('Draft'),
 600          'pending'        => __('Pending Review'),
 601          'private'        => __('Private'),
 602          'publish'        => __('Published')
 603      );
 604  
 605      return $status;
 606  }
 607  
 608  /**
 609   * Retrieve all of the WordPress support page statuses.
 610   *
 611   * Pages have a limited set of valid status values, this provides the
 612   * post_status values and descriptions.
 613   *
 614   * @since 2.5.0
 615   *
 616   * @return array List of page statuses.
 617   */
 618  function get_page_statuses( ) {
 619      $status = array(
 620          'draft'            => __('Draft'),
 621          'private'        => __('Private'),
 622          'publish'        => __('Published')
 623      );
 624  
 625      return $status;
 626  }
 627  
 628  /**
 629   * Register a post type. Do not use before init.
 630   *
 631   * A simple function for creating or modifying a post status based on the
 632   * parameters given. The function will accept an array (second optional
 633   * parameter), along with a string for the post status name.
 634   *
 635   *
 636   * Optional $args contents:
 637   *
 638   * label - A descriptive name for the post status marked for translation. Defaults to $post_status.
 639   * public - Whether posts of this status should be shown in the front end of the site. Defaults to true.
 640   * exclude_from_search - Whether to exclude posts with this post status from search results. Defaults to false.
 641   * show_in_admin_all_list - Whether to include posts in the edit listing for their post type
 642   * show_in_admin_status_list - Show in the list of statuses with post counts at the top of the edit
 643   *                             listings, e.g. All (12) | Published (9) | My Custom Status (2) ...
 644   *
 645   * Arguments prefixed with an _underscore shouldn't be used by plugins and themes.
 646   *
 647   * @package WordPress
 648   * @subpackage Post
 649   * @since 3.0.0
 650   * @uses $wp_post_statuses Inserts new post status object into the list
 651   *
 652   * @param string $post_status Name of the post status.
 653   * @param array|string $args See above description.
 654   */
 655  function register_post_status($post_status, $args = array()) {
 656      global $wp_post_statuses;
 657  
 658      if (!is_array($wp_post_statuses))
 659          $wp_post_statuses = array();
 660  
 661      // Args prefixed with an underscore are reserved for internal use.
 662      $defaults = array('label' => false, 'label_count' => false, 'exclude_from_search' => null, '_builtin' => false, '_edit_link' => 'post.php?post=%d', 'capability_type' => 'post', 'hierarchical' => false, 'public' => null, 'internal' => null, 'protected' => null, 'private' => null, 'show_in_admin_all' => null, 'publicly_queryable' => null, 'show_in_admin_status_list' => null, 'show_in_admin_all_list' => null, 'single_view_cap' => null);
 663      $args = wp_parse_args($args, $defaults);
 664      $args = (object) $args;
 665  
 666      $post_status = sanitize_key($post_status);
 667      $args->name = $post_status;
 668  
 669      if ( null === $args->public && null === $args->internal && null === $args->protected && null === $args->private )
 670          $args->internal = true;
 671  
 672      if ( null === $args->public  )
 673          $args->public = false;
 674  
 675      if ( null === $args->private  )
 676          $args->private = false;
 677  
 678      if ( null === $args->protected  )
 679          $args->protected = false;
 680  
 681      if ( null === $args->internal  )
 682          $args->internal = false;
 683  
 684      if ( null === $args->publicly_queryable )
 685          $args->publicly_queryable = $args->public;
 686  
 687      if ( null === $args->exclude_from_search )
 688          $args->exclude_from_search = $args->internal;
 689  
 690      if ( null === $args->show_in_admin_all_list )
 691          $args->show_in_admin_all_list = !$args->internal;
 692  
 693      if ( null === $args->show_in_admin_status_list )
 694              $args->show_in_admin_status_list = !$args->internal;
 695  
 696      if ( null === $args->single_view_cap )
 697          $args->single_view_cap = $args->public ? '' : 'edit';
 698  
 699      if ( false === $args->label )
 700          $args->label = $post_status;
 701  
 702      if ( false === $args->label_count )
 703          $args->label_count = array( $args->label, $args->label );
 704  
 705      $wp_post_statuses[$post_status] = $args;
 706  
 707      return $args;
 708  }
 709  
 710  /**
 711   * Retrieve a post status object by name
 712   *
 713   * @package WordPress
 714   * @subpackage Post
 715   * @since 3.0.0
 716   * @uses $wp_post_statuses
 717   * @see register_post_status
 718   * @see get_post_statuses
 719   *
 720   * @param string $post_status The name of a registered post status
 721   * @return object A post status object
 722   */
 723  function get_post_status_object( $post_status ) {
 724      global $wp_post_statuses;
 725  
 726      if ( empty($wp_post_statuses[$post_status]) )
 727          return null;
 728  
 729      return $wp_post_statuses[$post_status];
 730  }
 731  
 732  /**
 733   * Get a list of all registered post status objects.
 734   *
 735   * @package WordPress
 736   * @subpackage Post
 737   * @since 3.0.0
 738   * @uses $wp_post_statuses
 739   * @see register_post_status
 740   * @see get_post_status_object
 741   *
 742   * @param array|string $args An array of key => value arguments to match against the post status objects.
 743   * @param string $output The type of output to return, either post status 'names' or 'objects'. 'names' is the default.
 744   * @param string $operator The logical operation to perform. 'or' means only one element
 745   *  from the array needs to match; 'and' means all elements must match. The default is 'and'.
 746   * @return array A list of post type names or objects
 747   */
 748  function get_post_stati( $args = array(), $output = 'names', $operator = 'and' ) {
 749      global $wp_post_statuses;
 750  
 751      $field = ('names' == $output) ? 'name' : false;
 752  
 753      return wp_filter_object_list($wp_post_statuses, $args, $operator, $field);
 754  }
 755  
 756  /**
 757   * Whether the post type is hierarchical.
 758   *
 759   * A false return value might also mean that the post type does not exist.
 760   *
 761   * @since 3.0.0
 762   * @see get_post_type_object
 763   *
 764   * @param string $post_type Post type name
 765   * @return bool Whether post type is hierarchical.
 766   */
 767  function is_post_type_hierarchical( $post_type ) {
 768      if ( ! post_type_exists( $post_type ) )
 769          return false;
 770  
 771      $post_type = get_post_type_object( $post_type );
 772      return $post_type->hierarchical;
 773  }
 774  
 775  /**
 776   * Checks if a post type is registered.
 777   *
 778   * @since 3.0.0
 779   * @uses get_post_type_object()
 780   *
 781   * @param string $post_type Post type name
 782   * @return bool Whether post type is registered.
 783   */
 784  function post_type_exists( $post_type ) {
 785      return (bool) get_post_type_object( $post_type );
 786  }
 787  
 788  /**
 789   * Retrieve the post type of the current post or of a given post.
 790   *
 791   * @since 2.1.0
 792   *
 793   * @uses $post The Loop current post global
 794   *
 795   * @param mixed $the_post Optional. Post object or post ID.
 796   * @return bool|string post type or false on failure.
 797   */
 798  function get_post_type( $the_post = false ) {
 799      global $post;
 800  
 801      if ( false === $the_post )
 802          $the_post = $post;
 803      elseif ( is_numeric($the_post) )
 804          $the_post = get_post($the_post);
 805  
 806      if ( is_object($the_post) )
 807          return $the_post->post_type;
 808  
 809      return false;
 810  }
 811  
 812  /**
 813   * Retrieve a post type object by name
 814   *
 815   * @package WordPress
 816   * @subpackage Post
 817   * @since 3.0.0
 818   * @uses $wp_post_types
 819   * @see register_post_type
 820   * @see get_post_types
 821   *
 822   * @param string $post_type The name of a registered post type
 823   * @return object A post type object
 824   */
 825  function get_post_type_object( $post_type ) {
 826      global $wp_post_types;
 827  
 828      if ( empty($wp_post_types[$post_type]) )
 829          return null;
 830  
 831      return $wp_post_types[$post_type];
 832  }
 833  
 834  /**
 835   * Get a list of all registered post type objects.
 836   *
 837   * @package WordPress
 838   * @subpackage Post
 839   * @since 2.9.0
 840   * @uses $wp_post_types
 841   * @see register_post_type
 842   *
 843   * @param array|string $args An array of key => value arguments to match against the post type objects.
 844   * @param string $output The type of output to return, either post type 'names' or 'objects'. 'names' is the default.
 845   * @param string $operator The logical operation to perform. 'or' means only one element
 846   *  from the array needs to match; 'and' means all elements must match. The default is 'and'.
 847   * @return array A list of post type names or objects
 848   */
 849  function get_post_types( $args = array(), $output = 'names', $operator = 'and' ) {
 850      global $wp_post_types;
 851  
 852      $field = ('names' == $output) ? 'name' : false;
 853  
 854      return wp_filter_object_list($wp_post_types, $args, $operator, $field);
 855  }
 856  
 857  /**
 858   * Register a post type. Do not use before init.
 859   *
 860   * A function for creating or modifying a post type based on the
 861   * parameters given. The function will accept an array (second optional
 862   * parameter), along with a string for the post type name.
 863   *
 864   * Optional $args contents:
 865   *
 866   * - label - Name of the post type shown in the menu. Usually plural. If not set, labels['name'] will be used.
 867   * - description - A short descriptive summary of what the post type is. Defaults to blank.
 868   * - public - Whether posts of this type should be shown in the admin UI. Defaults to false.
 869   * - exclude_from_search - Whether to exclude posts with this post type from search results.
 870   *     Defaults to true if the type is not public, false if the type is public.
 871   * - publicly_queryable - Whether post_type queries can be performed from the front page.
 872   *     Defaults to whatever public is set as.
 873   * - show_ui - Whether to generate a default UI for managing this post type. Defaults to true
 874   *     if the type is public, false if the type is not public.
 875   * - show_in_menu - Where to show the post type in the admin menu. True for a top level menu,
 876   *     false for no menu, or can be a top level page like 'tools.php' or 'edit.php?post_type=page'.
 877   *     show_ui must be true.
 878   * - menu_position - The position in the menu order the post type should appear. Defaults to the bottom.
 879   * - menu_icon - The url to the icon to be used for this menu. Defaults to use the posts icon.
 880   * - capability_type - The string to use to build the read, edit, and delete capabilities. Defaults to 'post'.
 881   *   May be passed as an array to allow for alternative plurals when using this argument as a base to construct the
 882   *   capabilities, e.g. array('story', 'stories').
 883   * - capabilities - Array of capabilities for this post type. By default the capability_type is used
 884   *      as a base to construct capabilities. You can see accepted values in {@link get_post_type_capabilities()}.
 885   * - map_meta_cap - Whether to use the internal default meta capability handling. Defaults to false.
 886   * - hierarchical - Whether the post type is hierarchical. Defaults to false.
 887   * - supports - An alias for calling add_post_type_support() directly. See {@link add_post_type_support()}
 888   *     for documentation. Defaults to none.
 889   * - register_meta_box_cb - Provide a callback function that will be called when setting up the
 890   *     meta boxes for the edit form. Do remove_meta_box() and add_meta_box() calls in the callback.
 891   * - taxonomies - An array of taxonomy identifiers that will be registered for the post type.
 892   *     Default is no taxonomies. Taxonomies can be registered later with register_taxonomy() or
 893   *     register_taxonomy_for_object_type().
 894   * - labels - An array of labels for this post type. By default post labels are used for non-hierarchical
 895   *     types and page labels for hierarchical ones. You can see accepted values in {@link get_post_type_labels()}.
 896   * - has_archive - True to enable post type archives. Will generate the proper rewrite rules if rewrite is enabled.
 897   * - rewrite - false to prevent rewrite. Defaults to true. Use array('slug'=>$slug) to customize permastruct;
 898   *     default will use $post_type as slug. Other options include 'with_front', 'feeds', 'pages', and 'ep_mask'.
 899   * - query_var - false to prevent queries, or string to value of the query var to use for this post type
 900   * - can_export - true allows this post type to be exported.
 901   * - show_in_nav_menus - true makes this post type available for selection in navigation menus.
 902   * - _builtin - true if this post type is a native or "built-in" post_type. THIS IS FOR INTERNAL USE ONLY!
 903   * - _edit_link - URL segement to use for edit link of this post type. THIS IS FOR INTERNAL USE ONLY!
 904   *
 905   * @since 2.9.0
 906   * @uses $wp_post_types Inserts new post type object into the list
 907   *
 908   * @param string $post_type Name of the post type.
 909   * @param array|string $args See above description.
 910   * @return object|WP_Error the registered post type object, or an error object
 911   */
 912  function register_post_type($post_type, $args = array()) {
 913      global $wp_post_types, $wp_rewrite, $wp;
 914  
 915      if ( !is_array($wp_post_types) )
 916          $wp_post_types = array();
 917  
 918      // Args prefixed with an underscore are reserved for internal use.
 919      $defaults = array(
 920          'labels' => array(), 'description' => '', 'publicly_queryable' => null, 'exclude_from_search' => null,
 921          'capability_type' => 'post', 'capabilities' => array(), 'map_meta_cap' => null,
 922          '_builtin' => false, '_edit_link' => 'post.php?post=%d', 'hierarchical' => false,
 923          'public' => false, 'rewrite' => true, 'has_archive' => false, 'query_var' => true,
 924          'supports' => array(), 'register_meta_box_cb' => null,
 925          'taxonomies' => array(), 'show_ui' => null, 'menu_position' => null, 'menu_icon' => null,
 926          'can_export' => true,
 927          'show_in_nav_menus' => null, 'show_in_menu' => null, 'show_in_admin_bar' => null,
 928      );
 929      $args = wp_parse_args($args, $defaults);
 930      $args = (object) $args;
 931  
 932      $post_type = sanitize_key($post_type);
 933      $args->name = $post_type;
 934  
 935      if ( strlen( $post_type ) > 20 )
 936              return new WP_Error( 'post_type_too_long', __( 'Post types cannot exceed 20 characters in length' ) );
 937  
 938      // If not set, default to the setting for public.
 939      if ( null === $args->publicly_queryable )
 940          $args->publicly_queryable = $args->public;
 941  
 942      // If not set, default to the setting for public.
 943      if ( null === $args->show_ui )
 944          $args->show_ui = $args->public;
 945  
 946      // If not set, default to the setting for show_ui.
 947      if ( null === $args->show_in_menu || ! $args->show_ui )
 948          $args->show_in_menu = $args->show_ui;
 949  
 950      // If not set, default to the whether the full UI is shown.
 951      if ( null === $args->show_in_admin_bar )
 952          $args->show_in_admin_bar = true === $args->show_in_menu;
 953  
 954      // Whether to show this type in nav-menus.php. Defaults to the setting for public.
 955      if ( null === $args->show_in_nav_menus )
 956          $args->show_in_nav_menus = $args->public;
 957  
 958      // If not set, default to true if not public, false if public.
 959      if ( null === $args->exclude_from_search )
 960          $args->exclude_from_search = !$args->public;
 961  
 962      // Back compat with quirky handling in version 3.0. #14122
 963      if ( empty( $args->capabilities ) && null === $args->map_meta_cap && in_array( $args->capability_type, array( 'post', 'page' ) ) )
 964          $args->map_meta_cap = true;
 965  
 966      if ( null === $args->map_meta_cap )
 967          $args->map_meta_cap = false;
 968  
 969      $args->cap = get_post_type_capabilities( $args );
 970      unset($args->capabilities);
 971  
 972      if ( is_array( $args->capability_type ) )
 973          $args->capability_type = $args->capability_type[0];
 974  
 975      if ( ! empty($args->supports) ) {
 976          add_post_type_support($post_type, $args->supports);
 977          unset($args->supports);
 978      } else {
 979          // Add default features
 980          add_post_type_support($post_type, array('title', 'editor'));
 981      }
 982  
 983      if ( false !== $args->query_var && !empty($wp) ) {
 984          if ( true === $args->query_var )
 985              $args->query_var = $post_type;
 986          $args->query_var = sanitize_title_with_dashes($args->query_var);
 987          $wp->add_query_var($args->query_var);
 988      }
 989  
 990      if ( false !== $args->rewrite && ( is_admin() || '' != get_option('permalink_structure') ) ) {
 991          if ( ! is_array( $args->rewrite ) )
 992              $args->rewrite = array();
 993          if ( empty( $args->rewrite['slug'] ) )
 994              $args->rewrite['slug'] = $post_type;
 995          if ( ! isset( $args->rewrite['with_front'] ) )
 996              $args->rewrite['with_front'] = true;
 997          if ( ! isset( $args->rewrite['pages'] ) )
 998              $args->rewrite['pages'] = true;
 999          if ( ! isset( $args->rewrite['feeds'] ) || ! $args->has_archive )
1000              $args->rewrite['feeds'] = (bool) $args->has_archive;
1001          if ( ! isset( $args->rewrite['ep_mask'] ) ) {
1002              if ( isset( $args->permalink_epmask ) )
1003                  $args->rewrite['ep_mask'] = $args->permalink_epmask;
1004              else
1005                  $args->rewrite['ep_mask'] = EP_PERMALINK;
1006          }
1007  
1008          if ( $args->hierarchical )
1009              $wp_rewrite->add_rewrite_tag("%$post_type%", '(.+?)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&name=");
1010          else
1011              $wp_rewrite->add_rewrite_tag("%$post_type%", '([^/]+)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&name=");
1012  
1013          if ( $args->has_archive ) {
1014              $archive_slug = $args->has_archive === true ? $args->rewrite['slug'] : $args->has_archive;
1015              if ( $args->rewrite['with_front'] )
1016                  $archive_slug = substr( $wp_rewrite->front, 1 ) . $archive_slug;
1017              else
1018                  $archive_slug = $wp_rewrite->root . $archive_slug;
1019  
1020              $wp_rewrite->add_rule( "{$archive_slug}/?$", "index.php?post_type=$post_type", 'top' );
1021              if ( $args->rewrite['feeds'] && $wp_rewrite->feeds ) {
1022                  $feeds = '(' . trim( implode( '|', $wp_rewrite->feeds ) ) . ')';
1023                  $wp_rewrite->add_rule( "{$archive_slug}/feed/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' );
1024                  $wp_rewrite->add_rule( "{$archive_slug}/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' );
1025              }
1026              if ( $args->rewrite['pages'] )
1027                  $wp_rewrite->add_rule( "{$archive_slug}/{$wp_rewrite->pagination_base}/([0-9]{1,})/?$", "index.php?post_type=$post_type" . '&paged=$matches[1]', 'top' );
1028          }
1029  
1030          $wp_rewrite->add_permastruct( $post_type, "{$args->rewrite['slug']}/%$post_type%", $args->rewrite );
1031      }
1032  
1033      if ( $args->register_meta_box_cb )
1034          add_action('add_meta_boxes_' . $post_type, $args->register_meta_box_cb, 10, 1);
1035  
1036      $args->labels = get_post_type_labels( $args );
1037      $args->label = $args->labels->name;
1038  
1039      $wp_post_types[$post_type] = $args;
1040  
1041      add_action( 'future_' . $post_type, '_future_post_hook', 5, 2 );
1042  
1043      foreach ( $args->taxonomies as $taxonomy ) {
1044          register_taxonomy_for_object_type( $taxonomy, $post_type );
1045      }
1046  
1047      do_action( 'registered_post_type', $post_type, $args );
1048  
1049      return $args;
1050  }
1051  
1052  /**
1053   * Builds an object with all post type capabilities out of a post type object
1054   *
1055   * Post type capabilities use the 'capability_type' argument as a base, if the
1056   * capability is not set in the 'capabilities' argument array or if the
1057   * 'capabilities' argument is not supplied.
1058   *
1059   * The capability_type argument can optionally be registered as an array, with
1060   * the first value being singular and the second plural, e.g. array('story, 'stories')
1061   * Otherwise, an 's' will be added to the value for the plural form. After
1062   * registration, capability_type will always be a string of the singular value.
1063   *
1064   * By default, seven keys are accepted as part of the capabilities array:
1065   *
1066   * - edit_post, read_post, and delete_post are meta capabilities, which are then
1067   *   generally mapped to corresponding primitive capabilities depending on the
1068   *   context, which would be the post being edited/read/deleted and the user or
1069   *   role being checked. Thus these capabilities would generally not be granted
1070   *   directly to users or roles.
1071   *
1072   * - edit_posts - Controls whether objects of this post type can be edited.
1073   * - edit_others_posts - Controls whether objects of this type owned by other users
1074   *   can be edited. If the post type does not support an author, then this will
1075   *   behave like edit_posts.
1076   * - publish_posts - Controls publishing objects of this post type.
1077   * - read_private_posts - Controls whether private objects can be read.
1078  
1079   * These four primitive capabilities are checked in core in various locations.
1080   * There are also seven other primitive capabilities which are not referenced
1081   * directly in core, except in map_meta_cap(), which takes the three aforementioned
1082   * meta capabilities and translates them into one or more primitive capabilities
1083   * that must then be checked against the user or role, depending on the context.
1084   *
1085   * - read - Controls whether objects of this post type can be read.
1086   * - delete_posts - Controls whether objects of this post type can be deleted.
1087   * - delete_private_posts - Controls whether private objects can be deleted.
1088   * - delete_published_posts - Controls whether published objects can be deleted.
1089   * - delete_others_posts - Controls whether objects owned by other users can be
1090   *   can be deleted. If the post type does not support an author, then this will
1091   *   behave like delete_posts.
1092   * - edit_private_posts - Controls whether private objects can be edited.
1093   * - edit_published_posts - Controls whether published objects can be edited.
1094   *
1095   * These additional capabilities are only used in map_meta_cap(). Thus, they are
1096   * only assigned by default if the post type is registered with the 'map_meta_cap'
1097   * argument set to true (default is false).
1098   *
1099   * @see map_meta_cap()
1100   * @since 3.0.0
1101   *
1102   * @param object $args Post type registration arguments
1103   * @return object object with all the capabilities as member variables
1104   */
1105  function get_post_type_capabilities( $args ) {
1106      if ( ! is_array( $args->capability_type ) )
1107          $args->capability_type = array( $args->capability_type, $args->capability_type . 's' );
1108  
1109      // Singular base for meta capabilities, plural base for primitive capabilities.
1110      list( $singular_base, $plural_base ) = $args->capability_type;
1111  
1112      $default_capabilities = array(
1113          // Meta capabilities
1114          'edit_post'          => 'edit_'         . $singular_base,
1115          'read_post'          => 'read_'         . $singular_base,
1116          'delete_post'        => 'delete_'       . $singular_base,
1117          // Primitive capabilities used outside of map_meta_cap():
1118          'edit_posts'         => 'edit_'         . $plural_base,
1119          'edit_others_posts'  => 'edit_others_'  . $plural_base,
1120          'publish_posts'      => 'publish_'      . $plural_base,
1121          'read_private_posts' => 'read_private_' . $plural_base,
1122      );
1123  
1124      // Primitive capabilities used within map_meta_cap():
1125      if ( $args->map_meta_cap ) {
1126          $default_capabilities_for_mapping = array(
1127              'read'                   => 'read',
1128              'delete_posts'           => 'delete_'           . $plural_base,
1129              'delete_private_posts'   => 'delete_private_'   . $plural_base,
1130              'delete_published_posts' => 'delete_published_' . $plural_base,
1131              'delete_others_posts'    => 'delete_others_'    . $plural_base,
1132              'edit_private_posts'     => 'edit_private_'     . $plural_base,
1133              'edit_published_posts'   => 'edit_published_'   . $plural_base,
1134          );
1135          $default_capabilities = array_merge( $default_capabilities, $default_capabilities_for_mapping );
1136      }
1137  
1138      $capabilities = array_merge( $default_capabilities, $args->capabilities );
1139  
1140      // Remember meta capabilities for future reference.
1141      if ( $args->map_meta_cap )
1142          _post_type_meta_capabilities( $capabilities );
1143  
1144      return (object) $capabilities;
1145  }
1146  
1147  /**
1148   * Stores or returns a list of post type meta caps for map_meta_cap().
1149   *
1150   * @since 3.1.0
1151   * @access private
1152   */
1153  function _post_type_meta_capabilities( $capabilities = null ) {
1154      static $meta_caps = array();
1155      if ( null === $capabilities )
1156          return $meta_caps;
1157      foreach ( $capabilities as $core => $custom ) {
1158          if ( in_array( $core, array( 'read_post', 'delete_post', 'edit_post' ) ) )
1159              $meta_caps[ $custom ] = $core;
1160      }
1161  }
1162  
1163  /**
1164   * Builds an object with all post type labels out of a post type object
1165   *
1166   * Accepted keys of the label array in the post type object:
1167   * - name - general name for the post type, usually plural. The same and overridden by $post_type_object->label. Default is Posts/Pages
1168   * - singular_name - name for one object of this post type. Default is Post/Page
1169   * - add_new - Default is Add New for both hierarchical and non-hierarchical types. When internationalizing this string, please use a {@link http://codex.wordpress.org/I18n_for_WordPress_Developers#Disambiguation_by_context gettext context} matching your post type. Example: <code>_x('Add New', 'product');</code>
1170   * - add_new_item - Default is Add New Post/Add New Page
1171   * - edit_item - Default is Edit Post/Edit Page
1172   * - new_item - Default is New Post/New Page
1173   * - view_item - Default is View Post/View Page
1174   * - search_items - Default is Search Posts/Search Pages
1175   * - not_found - Default is No posts found/No pages found
1176   * - not_found_in_trash - Default is No posts found in Trash/No pages found in Trash
1177   * - parent_item_colon - This string isn't used on non-hierarchical types. In hierarchical ones the default is Parent Page:
1178   * - all_items - String for the submenu. Default is All Posts/All Pages
1179   * - menu_name - Default is the same as <code>name</code>
1180   *
1181   * Above, the first default value is for non-hierarchical post types (like posts) and the second one is for hierarchical post types (like pages).
1182   *
1183   * @since 3.0.0
1184   * @param object $post_type_object
1185   * @return object object with all the labels as member variables
1186   */
1187  function get_post_type_labels( $post_type_object ) {
1188      $nohier_vs_hier_defaults = array(
1189          'name' => array( _x('Posts', 'post type general name'), _x('Pages', 'post type general name') ),
1190          'singular_name' => array( _x('Post', 'post type singular name'), _x('Page', 'post type singular name') ),
1191          'add_new' => array( _x('Add New', 'post'), _x('Add New', 'page') ),
1192          'add_new_item' => array( __('Add New Post'), __('Add New Page') ),
1193          'edit_item' => array( __('Edit Post'), __('Edit Page') ),
1194          'new_item' => array( __('New Post'), __('New Page') ),
1195          'view_item' => array( __('View Post'), __('View Page') ),
1196          'search_items' => array( __('Search Posts'), __('Search Pages') ),
1197          'not_found' => array( __('No posts found.'), __('No pages found.') ),
1198          'not_found_in_trash' => array( __('No posts found in Trash.'), __('No pages found in Trash.') ),
1199          'parent_item_colon' => array( null, __('Parent Page:') ),
1200          'all_items' => array( __( 'All Posts' ), __( 'All Pages' ) )
1201      );
1202      $nohier_vs_hier_defaults['menu_name'] = $nohier_vs_hier_defaults['name'];
1203      return _get_custom_object_labels( $post_type_object, $nohier_vs_hier_defaults );
1204  }
1205  
1206  /**
1207   * Builds an object with custom-something object (post type, taxonomy) labels out of a custom-something object
1208   *
1209   * @access private
1210   * @since 3.0.0
1211   */
1212  function _get_custom_object_labels( $object, $nohier_vs_hier_defaults ) {
1213  
1214      if ( isset( $object->label ) && empty( $object->labels['name'] ) )
1215          $object->labels['name'] = $object->label;
1216  
1217      if ( !isset( $object->labels['singular_name'] ) && isset( $object->labels['name'] ) )
1218          $object->labels['singular_name'] = $object->labels['name'];
1219  
1220      if ( ! isset( $object->labels['name_admin_bar'] ) )
1221          $object->labels['name_admin_bar'] = isset( $object->labels['singular_name'] ) ? $object->labels['singular_name'] : $object->name;
1222  
1223      if ( !isset( $object->labels['menu_name'] ) && isset( $object->labels['name'] ) )
1224          $object->labels['menu_name'] = $object->labels['name'];
1225  
1226      if ( !isset( $object->labels['all_items'] ) && isset( $object->labels['menu_name'] ) )
1227          $object->labels['all_items'] = $object->labels['menu_name'];
1228  
1229      foreach ( $nohier_vs_hier_defaults as $key => $value )
1230              $defaults[$key] = $object->hierarchical ? $value[1] : $value[0];
1231  
1232      $labels = array_merge( $defaults, $object->labels );
1233      return (object)$labels;
1234  }
1235  
1236  /**
1237   * Adds submenus for post types.
1238   *
1239   * @access private
1240   * @since 3.1.0
1241   */
1242  function _add_post_type_submenus() {
1243      foreach ( get_post_types( array( 'show_ui' => true ) ) as $ptype ) {
1244          $ptype_obj = get_post_type_object( $ptype );
1245          // Submenus only.
1246          if ( ! $ptype_obj->show_in_menu || $ptype_obj->show_in_menu === true )
1247              continue;
1248          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" );
1249      }
1250  }
1251  add_action( 'admin_menu', '_add_post_type_submenus' );
1252  
1253  /**
1254   * Register support of certain features for a post type.
1255   *
1256   * All features are directly associated with a functional area of the edit screen, such as the
1257   * editor or a meta box: 'title', 'editor', 'comments', 'revisions', 'trackbacks', 'author',
1258   * 'excerpt', 'page-attributes', 'thumbnail', and 'custom-fields'.
1259   *
1260   * Additionally, the 'revisions' feature dictates whether the post type will store revisions,
1261   * and the 'comments' feature dictates whether the comments count will show on the edit screen.
1262   *
1263   * @since 3.0.0
1264   * @param string $post_type The post type for which to add the feature
1265   * @param string|array $feature the feature being added, can be an array of feature strings or a single string
1266   */
1267  function add_post_type_support( $post_type, $feature ) {
1268      global $_wp_post_type_features;
1269  
1270      $features = (array) $feature;
1271      foreach ($features as $feature) {
1272          if ( func_num_args() == 2 )
1273              $_wp_post_type_features[$post_type][$feature] = true;
1274          else
1275              $_wp_post_type_features[$post_type][$feature] = array_slice( func_get_args(), 2 );
1276      }
1277  }
1278  
1279  /**
1280   * Remove support for a feature from a post type.
1281   *
1282   * @since 3.0.0
1283   * @param string $post_type The post type for which to remove the feature
1284   * @param string $feature The feature being removed
1285   */
1286  function remove_post_type_support( $post_type, $feature ) {
1287      global $_wp_post_type_features;
1288  
1289      if ( !isset($_wp_post_type_features[$post_type]) )
1290          return;
1291  
1292      if ( isset($_wp_post_type_features[$post_type][$feature]) )
1293          unset($_wp_post_type_features[$post_type][$feature]);
1294  }
1295  
1296  /**
1297   * Checks a post type's support for a given feature
1298   *
1299   * @since 3.0.0
1300   * @param string $post_type The post type being checked
1301   * @param string $feature the feature being checked
1302   * @return boolean
1303   */
1304  
1305  function post_type_supports( $post_type, $feature ) {
1306      global $_wp_post_type_features;
1307  
1308      if ( !isset( $_wp_post_type_features[$post_type][$feature] ) )
1309          return false;
1310  
1311      // If no args passed then no extra checks need be performed
1312      if ( func_num_args() <= 2 )
1313          return true;
1314  
1315      // @todo Allow pluggable arg checking
1316      //$args = array_slice( func_get_args(), 2 );
1317  
1318      return true;
1319  }
1320  
1321  /**
1322   * Updates the post type for the post ID.
1323   *
1324   * The page or post cache will be cleaned for the post ID.
1325   *
1326   * @since 2.5.0
1327   *
1328   * @uses $wpdb
1329   *
1330   * @param int $post_id Post ID to change post type. Not actually optional.
1331   * @param string $post_type Optional, default is post. Supported values are 'post' or 'page' to
1332   *  name a few.
1333   * @return int Amount of rows changed. Should be 1 for success and 0 for failure.
1334   */
1335  function set_post_type( $post_id = 0, $post_type = 'post' ) {
1336      global $wpdb;
1337  
1338      $post_type = sanitize_post_field('post_type', $post_type, $post_id, 'db');
1339      $return = $wpdb->update($wpdb->posts, array('post_type' => $post_type), array('ID' => $post_id) );
1340  
1341      if ( 'page' == $post_type )
1342          clean_page_cache($post_id);
1343      else
1344          clean_post_cache($post_id);
1345  
1346      return $return;
1347  }
1348  
1349  /**
1350   * Retrieve list of latest posts or posts matching criteria.
1351   *
1352   * The defaults are as follows:
1353   *     'numberposts' - Default is 5. Total number of posts to retrieve.
1354   *     'offset' - Default is 0. See {@link WP_Query::query()} for more.
1355   *     'category' - What category to pull the posts from.
1356   *     'orderby' - Default is 'post_date'. How to order the posts.
1357   *     'order' - Default is 'DESC'. The order to retrieve the posts.
1358   *     'include' - See {@link WP_Query::query()} for more.
1359   *     'exclude' - See {@link WP_Query::query()} for more.
1360   *     'meta_key' - See {@link WP_Query::query()} for more.
1361   *     'meta_value' - See {@link WP_Query::query()} for more.
1362   *     'post_type' - Default is 'post'. Can be 'page', or 'attachment' to name a few.
1363   *     'post_parent' - The parent of the post or post type.
1364   *     'post_status' - Default is 'publish'. Post status to retrieve.
1365   *
1366   * @since 1.2.0
1367   * @uses $wpdb
1368   * @uses WP_Query::query() See for more default arguments and information.
1369   * @link http://codex.wordpress.org/Template_Tags/get_posts
1370   *
1371   * @param array $args Optional. Overrides defaults.
1372   * @return array List of posts.
1373   */
1374  function get_posts($args = null) {
1375      $defaults = array(
1376          'numberposts' => 5, 'offset' => 0,
1377          'category' => 0, 'orderby' => 'post_date',
1378          'order' => 'DESC', 'include' => array(),
1379          'exclude' => array(), 'meta_key' => '',
1380          'meta_value' =>'', 'post_type' => 'post',
1381          'suppress_filters' => true
1382      );
1383  
1384      $r = wp_parse_args( $args, $defaults );
1385      if ( empty( $r['post_status'] ) )
1386          $r['post_status'] = ( 'attachment' == $r['post_type'] ) ? 'inherit' : 'publish';
1387      if ( ! empty($r['numberposts']) && empty($r['posts_per_page']) )
1388          $r['posts_per_page'] = $r['numberposts'];
1389      if ( ! empty($r['category']) )
1390          $r['cat'] = $r['category'];
1391      if ( ! empty($r['include']) ) {
1392          $incposts = wp_parse_id_list( $r['include'] );
1393          $r['posts_per_page'] = count($incposts);  // only the number of posts included
1394          $r['post__in'] = $incposts;
1395      } elseif ( ! empty($r['exclude']) )
1396          $r['post__not_in'] = wp_parse_id_list( $r['exclude'] );
1397  
1398      $r['ignore_sticky_posts'] = true;
1399      $r['no_found_rows'] = true;
1400  
1401      $get_posts = new WP_Query;
1402      return $get_posts->query($r);
1403  
1404  }
1405  
1406  //
1407  // Post meta functions
1408  //
1409  
1410  /**
1411   * Add meta data field to a post.
1412   *
1413   * Post meta data is called "Custom Fields" on the Administration Screen.
1414   *
1415   * @since 1.5.0
1416   * @uses $wpdb
1417   * @link http://codex.wordpress.org/Function_Reference/add_post_meta
1418   *
1419   * @param int $post_id Post ID.
1420   * @param string $meta_key Metadata name.
1421   * @param mixed $meta_value Metadata value.
1422   * @param bool $unique Optional, default is false. Whether the same key should not be added.
1423   * @return bool False for failure. True for success.
1424   */
1425  function add_post_meta($post_id, $meta_key, $meta_value, $unique = false) {
1426      // make sure meta is added to the post, not a revision
1427      if ( $the_post = wp_is_post_revision($post_id) )
1428          $post_id = $the_post;
1429  
1430      return add_metadata('post', $post_id, $meta_key, $meta_value, $unique);
1431  }
1432  
1433  /**
1434   * Remove metadata matching criteria from a post.
1435   *
1436   * You can match based on the key, or key and value. Removing based on key and
1437   * value, will keep from removing duplicate metadata with the same key. It also
1438   * allows removing all metadata matching key, if needed.
1439   *
1440   * @since 1.5.0
1441   * @uses $wpdb
1442   * @link http://codex.wordpress.org/Function_Reference/delete_post_meta
1443   *
1444   * @param int $post_id post ID
1445   * @param string $meta_key Metadata name.
1446   * @param mixed $meta_value Optional. Metadata value.
1447   * @return bool False for failure. True for success.
1448   */
1449  function delete_post_meta($post_id, $meta_key, $meta_value = '') {
1450      // make sure meta is added to the post, not a revision
1451      if ( $the_post = wp_is_post_revision($post_id) )
1452          $post_id = $the_post;
1453  
1454      return delete_metadata('post', $post_id, $meta_key, $meta_value);
1455  }
1456  
1457  /**
1458   * Retrieve post meta field for a post.
1459   *
1460   * @since 1.5.0
1461   * @uses $wpdb
1462   * @link http://codex.wordpress.org/Function_Reference/get_post_meta
1463   *
1464   * @param int $post_id Post ID.
1465   * @param string $key The meta key to retrieve.
1466   * @param bool $single Whether to return a single value.
1467   * @return mixed Will be an array if $single is false. Will be value of meta data field if $single
1468   *  is true.
1469   */
1470  function get_post_meta($post_id, $key, $single = false) {
1471      return get_metadata('post', $post_id, $key, $single);
1472  }
1473  
1474  /**
1475   * Update post meta field based on post ID.
1476   *
1477   * Use the $prev_value parameter to differentiate between meta fields with the
1478   * same key and post ID.
1479   *
1480   * If the meta field for the post does not exist, it will be added.
1481   *
1482   * @since 1.5.0
1483   * @uses $wpdb
1484   * @link http://codex.wordpress.org/Function_Reference/update_post_meta
1485   *
1486   * @param int $post_id Post ID.
1487   * @param string $meta_key Metadata key.
1488   * @param mixed $meta_value Metadata value.
1489   * @param mixed $prev_value Optional. Previous value to check before removing.
1490   * @return bool False on failure, true if success.
1491   */
1492  function update_post_meta($post_id, $meta_key, $meta_value, $prev_value = '') {
1493      // make sure meta is added to the post, not a revision
1494      if ( $the_post = wp_is_post_revision($post_id) )
1495          $post_id = $the_post;
1496  
1497      return update_metadata('post', $post_id, $meta_key, $meta_value, $prev_value);
1498  }
1499  
1500  /**
1501   * Delete everything from post meta matching meta key.
1502   *
1503   * @since 2.3.0
1504   * @uses $wpdb
1505   *
1506   * @param string $post_meta_key Key to search for when deleting.
1507   * @return bool Whether the post meta key was deleted from the database
1508   */
1509  function delete_post_meta_by_key($post_meta_key) {
1510      return delete_metadata( 'post', null, $post_meta_key, '', true );
1511  }
1512  
1513  /**
1514   * Retrieve post meta fields, based on post ID.
1515   *
1516   * The post meta fields are retrieved from the cache, so the function is
1517   * optimized to be called more than once. It also applies to the functions, that
1518   * use this function.
1519   *
1520   * @since 1.2.0
1521   * @link http://codex.wordpress.org/Function_Reference/get_post_custom
1522   *
1523   * @uses $id Current Loop Post ID
1524   *
1525   * @param int $post_id post ID
1526   * @return array
1527   */
1528  function get_post_custom( $post_id = 0 ) {
1529      $post_id = absint( $post_id );
1530  
1531      if ( ! $post_id )
1532          $post_id = get_the_ID();
1533  
1534      if ( ! wp_cache_get( $post_id, 'post_meta' ) )
1535          update_postmeta_cache( $post_id );
1536  
1537      return wp_cache_get( $post_id, 'post_meta' );
1538  }
1539  
1540  /**
1541   * Retrieve meta field names for a post.
1542   *
1543   * If there are no meta fields, then nothing (null) will be returned.
1544   *
1545   * @since 1.2.0
1546   * @link http://codex.wordpress.org/Function_Reference/get_post_custom_keys
1547   *
1548   * @param int $post_id post ID
1549   * @return array|null Either array of the keys, or null if keys could not be retrieved.
1550   */
1551  function get_post_custom_keys( $post_id = 0 ) {
1552      $custom = get_post_custom( $post_id );
1553  
1554      if ( !is_array($custom) )
1555          return;
1556  
1557      if ( $keys = array_keys($custom) )
1558          return $keys;
1559  }
1560  
1561  /**
1562   * Retrieve values for a custom post field.
1563   *
1564   * The parameters must not be considered optional. All of the post meta fields
1565   * will be retrieved and only the meta field key values returned.
1566   *
1567   * @since 1.2.0
1568   * @link http://codex.wordpress.org/Function_Reference/get_post_custom_values
1569   *
1570   * @param string $key Meta field key.
1571   * @param int $post_id Post ID
1572   * @return array Meta field values.
1573   */
1574  function get_post_custom_values( $key = '', $post_id = 0 ) {
1575      if ( !$key )
1576          return null;
1577  
1578      $custom = get_post_custom($post_id);
1579  
1580      return isset($custom[$key]) ? $custom[$key] : null;
1581  }
1582  
1583  /**
1584   * Check if post is sticky.
1585   *
1586   * Sticky posts should remain at the top of The Loop. If the post ID is not
1587   * given, then The Loop ID for the current post will be used.
1588   *
1589   * @since 2.7.0
1590   *
1591   * @param int $post_id Optional. Post ID.
1592   * @return bool Whether post is sticky.
1593   */
1594  function is_sticky( $post_id = 0 ) {
1595      $post_id = absint( $post_id );
1596  
1597      if ( ! $post_id )
1598          $post_id = get_the_ID();
1599  
1600      $stickies = get_option( 'sticky_posts' );
1601  
1602      if ( ! is_array( $stickies ) )
1603          return false;
1604  
1605      if ( in_array( $post_id, $stickies ) )
1606          return true;
1607  
1608      return false;
1609  }
1610  
1611  /**
1612   * Sanitize every post field.
1613   *
1614   * If the context is 'raw', then the post object or array will get minimal santization of the int fields.
1615   *
1616   * @since 2.3.0
1617   * @uses sanitize_post_field() Used to sanitize the fields.
1618   *
1619   * @param object|array $post The Post Object or Array
1620   * @param string $context Optional, default is 'display'. How to sanitize post fields.
1621   * @return object|array The now sanitized Post Object or Array (will be the same type as $post)
1622   */
1623  function sanitize_post($post, $context = 'display') {
1624      if ( is_object($post) ) {
1625          // Check if post already filtered for this context
1626          if ( isset($post->filter) && $context == $post->filter )
1627              return $post;
1628          if ( !isset($post->ID) )
1629              $post->ID = 0;
1630          foreach ( array_keys(get_object_vars($post)) as $field )
1631              $post->$field = sanitize_post_field($field, $post->$field, $post->ID, $context);
1632          $post->filter = $context;
1633      } else {
1634          // Check if post already filtered for this context
1635          if ( isset($post['filter']) && $context == $post['filter'] )
1636              return $post;
1637          if ( !isset($post['ID']) )
1638              $post['ID'] = 0;
1639          foreach ( array_keys($post) as $field )
1640              $post[$field] = sanitize_post_field($field, $post[$field], $post['ID'], $context);
1641          $post['filter'] = $context;
1642      }
1643      return $post;
1644  }
1645  
1646  /**
1647   * Sanitize post field based on context.
1648   *
1649   * Possible context values are:  'raw', 'edit', 'db', 'display', 'attribute' and 'js'. The
1650   * 'display' context is used by default. 'attribute' and 'js' contexts are treated like 'display'
1651   * when calling filters.
1652   *
1653   * @since 2.3.0
1654   * @uses apply_filters() Calls 'edit_$field' and '{$field_no_prefix}_edit_pre' passing $value and
1655   *  $post_id if $context == 'edit' and field name prefix == 'post_'.
1656   *
1657   * @uses apply_filters() Calls 'edit_post_$field' passing $value and $post_id if $context == 'db'.
1658   * @uses apply_filters() Calls 'pre_$field' passing $value if $context == 'db' and field name prefix == 'post_'.
1659   * @uses apply_filters() Calls '{$field}_pre' passing $value if $context == 'db' and field name prefix != 'post_'.
1660   *
1661   * @uses apply_filters() Calls '$field' passing $value, $post_id and $context if $context == anything
1662   *  other than 'raw', 'edit' and 'db' and field name prefix == 'post_'.
1663   * @uses apply_filters() Calls 'post_$field' passing $value if $context == anything other than 'raw',
1664   *  'edit' and 'db' and field name prefix != 'post_'.
1665   *
1666   * @param string $field The Post Object field name.
1667   * @param mixed $value The Post Object value.
1668   * @param int $post_id Post ID.
1669   * @param string $context How to sanitize post fields. Looks for 'raw', 'edit', 'db', 'display',
1670   *               'attribute' and 'js'.
1671   * @return mixed Sanitized value.
1672   */
1673  function sanitize_post_field($field, $value, $post_id, $context) {
1674      $int_fields = array('ID', 'post_parent', 'menu_order');
1675      if ( in_array($field, $int_fields) )
1676          $value = (int) $value;
1677  
1678      // Fields which contain arrays of ints.
1679      $array_int_fields = array( 'ancestors' );
1680      if ( in_array($field, $array_int_fields) ) {
1681          $value = array_map( 'absint', $value);
1682          return $value;
1683      }
1684  
1685      if ( 'raw' == $context )
1686          return $value;
1687  
1688      $prefixed = false;
1689      if ( false !== strpos($field, 'post_') ) {
1690          $prefixed = true;
1691          $field_no_prefix = str_replace('post_', '', $field);
1692      }
1693  
1694      if ( 'edit' == $context ) {
1695          $format_to_edit = array('post_content', 'post_excerpt', 'post_title', 'post_password');
1696  
1697          if ( $prefixed ) {
1698              $value = apply_filters("edit_{$field}", $value, $post_id);
1699              // Old school
1700              $value = apply_filters("{$field_no_prefix}_edit_pre", $value, $post_id);
1701          } else {
1702              $value = apply_filters("edit_post_{$field}", $value, $post_id);
1703          }
1704  
1705          if ( in_array($field, $format_to_edit) ) {
1706              if ( 'post_content' == $field )
1707                  $value = format_to_edit($value, user_can_richedit());
1708              else
1709                  $value = format_to_edit($value);
1710          } else {
1711              $value = esc_attr($value);
1712          }
1713      } else if ( 'db' == $context ) {
1714          if ( $prefixed ) {
1715              $value = apply_filters("pre_{$field}", $value);
1716              $value = apply_filters("{$field_no_prefix}_save_pre", $value);
1717          } else {
1718              $value = apply_filters("pre_post_{$field}", $value);
1719              $value = apply_filters("{$field}_pre", $value);
1720          }
1721      } else {
1722          // Use display filters by default.
1723          if ( $prefixed )
1724              $value = apply_filters($field, $value, $post_id, $context);
1725          else
1726              $value = apply_filters("post_{$field}", $value, $post_id, $context);
1727      }
1728  
1729      if ( 'attribute' == $context )
1730          $value = esc_attr($value);
1731      else if ( 'js' == $context )
1732          $value = esc_js($value);
1733  
1734      return $value;
1735  }
1736  
1737  /**
1738   * Make a post sticky.
1739   *
1740   * Sticky posts should be displayed at the top of the front page.
1741   *
1742   * @since 2.7.0
1743   *
1744   * @param int $post_id Post ID.
1745   */
1746  function stick_post($post_id) {
1747      $stickies = get_option('sticky_posts');
1748  
1749      if ( !is_array($stickies) )
1750          $stickies = array($post_id);
1751  
1752      if ( ! in_array($post_id, $stickies) )
1753          $stickies[] = $post_id;
1754  
1755      update_option('sticky_posts', $stickies);
1756  }
1757  
1758  /**
1759   * Unstick a post.
1760   *
1761   * Sticky posts should be displayed at the top of the front page.
1762   *
1763   * @since 2.7.0
1764   *
1765   * @param int $post_id Post ID.
1766   */
1767  function unstick_post($post_id) {
1768      $stickies = get_option('sticky_posts');
1769  
1770      if ( !is_array($stickies) )
1771          return;
1772  
1773      if ( ! in_array($post_id, $stickies) )
1774          return;
1775  
1776      $offset = array_search($post_id, $stickies);
1777      if ( false === $offset )
1778          return;
1779  
1780      array_splice($stickies, $offset, 1);
1781  
1782      update_option('sticky_posts', $stickies);
1783  }
1784  
1785  /**
1786   * Count number of posts of a post type and is user has permissions to view.
1787   *
1788   * This function provides an efficient method of finding the amount of post's
1789   * type a blog has. Another method is to count the amount of items in
1790   * get_posts(), but that method has a lot of overhead with doing so. Therefore,
1791   * when developing for 2.5+, use this function instead.
1792   *
1793   * The $perm parameter checks for 'readable' value and if the user can read
1794   * private posts, it will display that for the user that is signed in.
1795   *
1796   * @since 2.5.0
1797   * @link http://codex.wordpress.org/Template_Tags/wp_count_posts
1798   *
1799   * @param string $type Optional. Post type to retrieve count
1800   * @param string $perm Optional. 'readable' or empty.
1801   * @return object Number of posts for each status
1802   */
1803  function wp_count_posts( $type = 'post', $perm = '' ) {
1804      global $wpdb;
1805  
1806      $user = wp_get_current_user();
1807  
1808      $cache_key = $type;
1809  
1810      $query = "SELECT post_status, COUNT( * ) AS num_posts FROM {$wpdb->posts} WHERE post_type = %s";
1811      if ( 'readable' == $perm && is_user_logged_in() ) {
1812          $post_type_object = get_post_type_object($type);
1813          if ( !current_user_can( $post_type_object->cap->read_private_posts ) ) {
1814              $cache_key .= '_' . $perm . '_' . $user->ID;
1815              $query .= " AND (post_status != 'private' OR ( post_author = '$user->ID' AND post_status = 'private' ))";
1816          }
1817      }
1818      $query .= ' GROUP BY post_status';
1819  
1820      $count = wp_cache_get($cache_key, 'counts');
1821      if ( false !== $count )
1822          return $count;
1823  
1824      $count = $wpdb->get_results( $wpdb->prepare( $query, $type ), ARRAY_A );
1825  
1826      $stats = array();
1827      foreach ( get_post_stati() as $state )
1828          $stats[$state] = 0;
1829  
1830      foreach ( (array) $count as $row )
1831          $stats[$row['post_status']] = $row['num_posts'];
1832  
1833      $stats = (object) $stats;
1834      wp_cache_set($cache_key, $stats, 'counts');
1835  
1836      return $stats;
1837  }
1838  
1839  /**
1840   * Count number of attachments for the mime type(s).
1841   *
1842   * If you set the optional mime_type parameter, then an array will still be
1843   * returned, but will only have the item you are looking for. It does not give
1844   * you the number of attachments that are children of a post. You can get that
1845   * by counting the number of children that post has.
1846   *
1847   * @since 2.5.0
1848   *
1849   * @param string|array $mime_type Optional. Array or comma-separated list of MIME patterns.
1850   * @return array Number of posts for each mime type.
1851   */
1852  function wp_count_attachments( $mime_type = '' ) {
1853      global $wpdb;
1854  
1855      $and = wp_post_mime_type_where( $mime_type );
1856      $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 );
1857  
1858      $stats = array( );
1859      foreach( (array) $count as $row ) {
1860          $stats[$row['post_mime_type']] = $row['num_posts'];
1861      }
1862      $stats['trash'] = $wpdb->get_var( "SELECT COUNT( * ) FROM $wpdb->posts WHERE post_type = 'attachment' AND post_status = 'trash' $and");
1863  
1864      return (object) $stats;
1865  }
1866  
1867  /**
1868   * Check a MIME-Type against a list.
1869   *
1870   * If the wildcard_mime_types parameter is a string, it must be comma separated
1871   * list. If the real_mime_types is a string, it is also comma separated to
1872   * create the list.
1873   *
1874   * @since 2.5.0
1875   *
1876   * @param string|array $wildcard_mime_types e.g. audio/mpeg or image (same as image/*) or
1877   *  flash (same as *flash*).
1878   * @param string|array $real_mime_types post_mime_type values
1879   * @return array array(wildcard=>array(real types))
1880   */
1881  function wp_match_mime_types($wildcard_mime_types, $real_mime_types) {
1882      $matches = array();
1883      if ( is_string($wildcard_mime_types) )
1884          $wildcard_mime_types = array_map('trim', explode(',', $wildcard_mime_types));
1885      if ( is_string($real_mime_types) )
1886          $real_mime_types = array_map('trim', explode(',', $real_mime_types));
1887      $wild = '[-._a-z0-9]*';
1888      foreach ( (array) $wildcard_mime_types as $type ) {
1889          $type = str_replace('*', $wild, $type);
1890          $patternses[1][$type] = "^$type$";
1891          if ( false === strpos($type, '/') ) {
1892              $patternses[2][$type] = "^$type/";
1893              $patternses[3][$type] = $type;
1894          }
1895      }
1896      asort($patternses);
1897      foreach ( $patternses as $patterns )
1898          foreach ( $patterns as $type => $pattern )
1899              foreach ( (array) $real_mime_types as $real )
1900                  if ( preg_match("#$pattern#", $real) && ( empty($matches[$type]) || false === array_search($real, $matches[$type]) ) )
1901                      $matches[$type][] = $real;
1902      return $matches;
1903  }
1904  
1905  /**
1906   * Convert MIME types into SQL.
1907   *
1908   * @since 2.5.0
1909   *
1910   * @param string|array $post_mime_types List of mime types or comma separated string of mime types.
1911   * @param string $table_alias Optional. Specify a table alias, if needed.
1912   * @return string The SQL AND clause for mime searching.
1913   */
1914  function wp_post_mime_type_where($post_mime_types, $table_alias = '') {
1915      $where = '';
1916      $wildcards = array('', '%', '%/%');
1917      if ( is_string($post_mime_types) )
1918          $post_mime_types = array_map('trim', explode(',', $post_mime_types));
1919      foreach ( (array) $post_mime_types as $mime_type ) {
1920          $mime_type = preg_replace('/\s/', '', $mime_type);
1921          $slashpos = strpos($mime_type, '/');
1922          if ( false !== $slashpos ) {
1923              $mime_group = preg_replace('/[^-*.a-zA-Z0-9]/', '', substr($mime_type, 0, $slashpos));
1924              $mime_subgroup = preg_replace('/[^-*.+a-zA-Z0-9]/', '', substr($mime_type, $slashpos + 1));
1925              if ( empty($mime_subgroup) )
1926                  $mime_subgroup = '*';
1927              else
1928                  $mime_subgroup = str_replace('/', '', $mime_subgroup);
1929              $mime_pattern = "$mime_group/$mime_subgroup";
1930          } else {
1931              $mime_pattern = preg_replace('/[^-*.a-zA-Z0-9]/', '', $mime_type);
1932              if ( false === strpos($mime_pattern, '*') )
1933                  $mime_pattern .= '/*';
1934          }
1935  
1936          $mime_pattern = preg_replace('/\*+/', '%', $mime_pattern);
1937  
1938          if ( in_array( $mime_type, $wildcards ) )
1939              return '';
1940  
1941          if ( false !== strpos($mime_pattern, '%') )
1942              $wheres[] = empty($table_alias) ? "post_mime_type LIKE '$mime_pattern'" : "$table_alias.post_mime_type LIKE '$mime_pattern'";
1943          else
1944              $wheres[] = empty($table_alias) ? "post_mime_type = '$mime_pattern'" : "$table_alias.post_mime_type = '$mime_pattern'";
1945      }
1946      if ( !empty($wheres) )
1947          $where = ' AND (' . join(' OR ', $wheres) . ') ';
1948      return $where;
1949  }
1950  
1951  /**
1952   * Trashes or deletes a post or page.
1953   *
1954   * When the post and page is permanently deleted, everything that is tied to it is deleted also.
1955   * This includes comments, post meta fields, and terms associated with the post.
1956   *
1957   * The post or page is moved to trash instead of permanently deleted unless trash is
1958   * disabled, item is already in the trash, or $force_delete is true.
1959   *
1960   * @since 1.0.0
1961   * @uses do_action() on 'delete_post' before deletion unless post type is 'attachment'.
1962   * @uses do_action() on 'deleted_post' after deletion unless post type is 'attachment'.
1963   * @uses wp_delete_attachment() if post type is 'attachment'.
1964   * @uses wp_trash_post() if item should be trashed.
1965   *
1966   * @param int $postid Post ID.
1967   * @param bool $force_delete Whether to bypass trash and force deletion. Defaults to false.
1968   * @return mixed False on failure
1969   */
1970  function wp_delete_post( $postid = 0, $force_delete = false ) {
1971      global $wpdb, $wp_rewrite;
1972  
1973      if ( !$post = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d", $postid)) )
1974          return $post;
1975  
1976      if ( !$force_delete && ( $post->post_type == 'post' || $post->post_type == 'page') && get_post_status( $postid ) != 'trash' && EMPTY_TRASH_DAYS )
1977              return wp_trash_post($postid);
1978  
1979      if ( $post->post_type == 'attachment' )
1980          return wp_delete_attachment( $postid, $force_delete );
1981  
1982      do_action('before_delete_post', $postid);
1983  
1984      delete_post_meta($postid,'_wp_trash_meta_status');
1985      delete_post_meta($postid,'_wp_trash_meta_time');
1986  
1987      wp_delete_object_term_relationships($postid, get_object_taxonomies($post->post_type));
1988  
1989      $parent_data = array( 'post_parent' => $post->post_parent );
1990      $parent_where = array( 'post_parent' => $postid );
1991  
1992      if ( is_post_type_hierarchical( $post->post_type ) ) {
1993          // Point children of this page to its parent, also clean the cache of affected children
1994          $children_query = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_parent = %d AND post_type = %s", $postid, $post->post_type );
1995          $children = $wpdb->get_results( $children_query );
1996  
1997          $wpdb->update( $wpdb->posts, $parent_data, $parent_where + array( 'post_type' => $post->post_type ) );
1998      }
1999  
2000      if ( 'page' == $post->post_type) {
2001           // if the page is defined in option page_on_front or post_for_posts,
2002          // adjust the corresponding options
2003          if ( get_option('page_on_front') == $postid ) {
2004              update_option('show_on_front', 'posts');
2005              delete_option('page_on_front');
2006          }
2007          if ( get_option('page_for_posts') == $postid ) {
2008              delete_option('page_for_posts');
2009          }
2010      } else {
2011          unstick_post($postid);
2012      }
2013  
2014      // Do raw query. wp_get_post_revisions() is filtered
2015      $revision_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'revision'", $postid ) );
2016      // Use wp_delete_post (via wp_delete_post_revision) again. Ensures any meta/misplaced data gets cleaned up.
2017      foreach ( $revision_ids as $revision_id )
2018          wp_delete_post_revision( $revision_id );
2019  
2020      // Point all attachments to this post up one level
2021      $wpdb->update( $wpdb->posts, $parent_data, $parent_where + array( 'post_type' => 'attachment' ) );
2022  
2023      $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d", $postid ));
2024      if ( ! empty($comment_ids) ) {
2025          do_action( 'delete_comment', $comment_ids );
2026          foreach ( $comment_ids as $comment_id )
2027              wp_delete_comment( $comment_id, true );
2028          do_action( 'deleted_comment', $comment_ids );
2029      }
2030  
2031      $post_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d ", $postid ));
2032      if ( !empty($post_meta_ids) ) {
2033          do_action( 'delete_postmeta', $post_meta_ids );
2034          $in_post_meta_ids = "'" . implode("', '", $post_meta_ids) . "'";
2035          $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_id IN($in_post_meta_ids)" );
2036          do_action( 'deleted_postmeta', $post_meta_ids );
2037      }
2038  
2039      do_action( 'delete_post', $postid );
2040      $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->posts WHERE ID = %d", $postid ));
2041      do_action( 'deleted_post', $postid );
2042  
2043      if ( 'page' == $post->post_type ) {
2044          clean_page_cache($postid);
2045      } else {
2046          clean_post_cache($postid);
2047      }
2048  
2049      if ( is_post_type_hierarchical( $post->post_type ) ) {
2050          foreach ( (array) $children as $child )
2051              clean_post_cache( $child->ID );
2052      }
2053  
2054      wp_clear_scheduled_hook('publish_future_post', array( $postid ) );
2055  
2056      do_action('after_delete_post', $postid);
2057  
2058      return $post;
2059  }
2060  
2061  /**
2062   * Moves a post or page to the Trash
2063   *
2064   * If trash is disabled, the post or page is permanently deleted.
2065   *
2066   * @since 2.9.0
2067   * @uses do_action() on 'trash_post' before trashing
2068   * @uses do_action() on 'trashed_post' after trashing
2069   * @uses wp_delete_post() if trash is disabled
2070   *
2071   * @param int $post_id Post ID.
2072   * @return mixed False on failure
2073   */
2074  function wp_trash_post($post_id = 0) {
2075      if ( !EMPTY_TRASH_DAYS )
2076          return wp_delete_post($post_id, true);
2077  
2078      if ( !$post = wp_get_single_post($post_id, ARRAY_A) )
2079          return $post;
2080  
2081      if ( $post['post_status'] == 'trash' )
2082          return false;
2083  
2084      do_action('wp_trash_post', $post_id);
2085  
2086      add_post_meta($post_id,'_wp_trash_meta_status', $post['post_status']);
2087      add_post_meta($post_id,'_wp_trash_meta_time', time());
2088  
2089      $post['post_status'] = 'trash';
2090      wp_insert_post($post);
2091  
2092      wp_trash_post_comments($post_id);
2093  
2094      do_action('trashed_post', $post_id);
2095  
2096      return $post;
2097  }
2098  
2099  /**
2100   * Restores a post or page from the Trash
2101   *
2102   * @since 2.9.0
2103   * @uses do_action() on 'untrash_post' before undeletion
2104   * @uses do_action() on 'untrashed_post' after undeletion
2105   *
2106   * @param int $post_id Post ID.
2107   * @return mixed False on failure
2108   */
2109  function wp_untrash_post($post_id = 0) {
2110      if ( !$post = wp_get_single_post($post_id, ARRAY_A) )
2111          return $post;
2112  
2113      if ( $post['post_status'] != 'trash' )
2114          return false;
2115  
2116      do_action('untrash_post', $post_id);
2117  
2118      $post_status = get_post_meta($post_id, '_wp_trash_meta_status', true);
2119  
2120      $post['post_status'] = $post_status;
2121  
2122      delete_post_meta($post_id, '_wp_trash_meta_status');
2123      delete_post_meta($post_id, '_wp_trash_meta_time');
2124  
2125      wp_insert_post($post);
2126  
2127      wp_untrash_post_comments($post_id);
2128  
2129      do_action('untrashed_post', $post_id);
2130  
2131      return $post;
2132  }
2133  
2134  /**
2135   * Moves comments for a post to the trash
2136   *
2137   * @since 2.9.0
2138   * @uses do_action() on 'trash_post_comments' before trashing
2139   * @uses do_action() on 'trashed_post_comments' after trashing
2140   *
2141   * @param int $post Post ID or object.
2142   * @return mixed False on failure
2143   */
2144  function wp_trash_post_comments($post = null) {
2145      global $wpdb;
2146  
2147      $post = get_post($post);
2148      if ( empty($post) )
2149          return;
2150  
2151      $post_id = $post->ID;
2152  
2153      do_action('trash_post_comments', $post_id);
2154  
2155      $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_ID, comment_approved FROM $wpdb->comments WHERE comment_post_ID = %d", $post_id) );
2156      if ( empty($comments) )
2157          return;
2158  
2159      // Cache current status for each comment
2160      $statuses = array();
2161      foreach ( $comments as $comment )
2162          $statuses[$comment->comment_ID] = $comment->comment_approved;
2163      add_post_meta($post_id, '_wp_trash_meta_comments_status', $statuses);
2164  
2165      // Set status for all comments to post-trashed
2166      $result = $wpdb->update($wpdb->comments, array('comment_approved' => 'post-trashed'), array('comment_post_ID' => $post_id));
2167  
2168      clean_comment_cache( array_keys($statuses) );
2169  
2170      do_action('trashed_post_comments', $post_id, $statuses);
2171  
2172      return $result;
2173  }
2174  
2175  /**
2176   * Restore comments for a post from the trash
2177   *
2178   * @since 2.9.0
2179   * @uses do_action() on 'untrash_post_comments' before trashing
2180   * @uses do_action() on 'untrashed_post_comments' after trashing
2181   *
2182   * @param int $post Post ID or object.
2183   * @return mixed False on failure
2184   */
2185  function wp_untrash_post_comments($post = null) {
2186      global $wpdb;
2187  
2188      $post = get_post($post);
2189      if ( empty($post) )
2190          return;
2191  
2192      $post_id = $post->ID;
2193  
2194      $statuses = get_post_meta($post_id, '_wp_trash_meta_comments_status', true);
2195  
2196      if ( empty($statuses) )
2197          return true;
2198  
2199      do_action('untrash_post_comments', $post_id);
2200  
2201      // Restore each comment to its original status
2202      $group_by_status = array();
2203      foreach ( $statuses as $comment_id => $comment_status )
2204          $group_by_status[$comment_status][] = $comment_id;
2205  
2206      foreach ( $group_by_status as $status => $comments ) {
2207          // Sanity check. This shouldn't happen.
2208          if ( 'post-trashed' == $status )
2209              $status = '0';
2210          $comments_in = implode( "', '", $comments );
2211          $wpdb->query( "UPDATE $wpdb->comments SET comment_approved = '$status' WHERE comment_ID IN ('" . $comments_in . "')" );
2212      }
2213  
2214      clean_comment_cache( array_keys($statuses) );
2215  
2216      delete_post_meta($post_id, '_wp_trash_meta_comments_status');
2217  
2218      do_action('untrashed_post_comments', $post_id);
2219  }
2220  
2221  /**
2222   * Retrieve the list of categories for a post.
2223   *
2224   * Compatibility layer for themes and plugins. Also an easy layer of abstraction
2225   * away from the complexity of the taxonomy layer.
2226   *
2227   * @since 2.1.0
2228   *
2229   * @uses wp_get_object_terms() Retrieves the categories. Args details can be found here.
2230   *
2231   * @param int $post_id Optional. The Post ID.
2232   * @param array $args Optional. Overwrite the defaults.
2233   * @return array
2234   */
2235  function wp_get_post_categories( $post_id = 0, $args = array() ) {
2236      $post_id = (int) $post_id;
2237  
2238      $defaults = array('fields' => 'ids');
2239      $args = wp_parse_args( $args, $defaults );
2240  
2241      $cats = wp_get_object_terms($post_id, 'category', $args);
2242      return $cats;
2243  }
2244  
2245  /**
2246   * Retrieve the tags for a post.
2247   *
2248   * There is only one default for this function, called 'fields' and by default
2249   * is set to 'all'. There are other defaults that can be overridden in
2250   * {@link wp_get_object_terms()}.
2251   *
2252   * @package WordPress
2253   * @subpackage Post
2254   * @since 2.3.0
2255   *
2256   * @uses wp_get_object_terms() Gets the tags for returning. Args can be found here
2257   *
2258   * @param int $post_id Optional. The Post ID
2259   * @param array $args Optional. Overwrite the defaults
2260   * @return array List of post tags.
2261   */
2262  function wp_get_post_tags( $post_id = 0, $args = array() ) {
2263      return wp_get_post_terms( $post_id, 'post_tag', $args);
2264  }
2265  
2266  /**
2267   * Retrieve the terms for a post.
2268   *
2269   * There is only one default for this function, called 'fields' and by default
2270   * is set to 'all'. There are other defaults that can be overridden in
2271   * {@link wp_get_object_terms()}.
2272   *
2273   * @package WordPress
2274   * @subpackage Post
2275   * @since 2.8.0
2276   *
2277   * @uses wp_get_object_terms() Gets the tags for returning. Args can be found here
2278   *
2279   * @param int $post_id Optional. The Post ID
2280   * @param string $taxonomy The taxonomy for which to retrieve terms. Defaults to post_tag.
2281   * @param array $args Optional. Overwrite the defaults
2282   * @return array List of post tags.
2283   */
2284  function wp_get_post_terms( $post_id = 0, $taxonomy = 'post_tag', $args = array() ) {
2285      $post_id = (int) $post_id;
2286  
2287      $defaults = array('fields' => 'all');
2288      $args = wp_parse_args( $args, $defaults );
2289  
2290      $tags = wp_get_object_terms($post_id, $taxonomy, $args);
2291  
2292      return $tags;
2293  }
2294  
2295  /**
2296   * Retrieve number of recent posts.
2297   *
2298   * @since 1.0.0
2299   * @uses wp_parse_args()
2300   * @uses get_posts()
2301   *
2302   * @param string $deprecated Deprecated.
2303   * @param array $args Optional. Overrides defaults.
2304   * @param string $output Optional.
2305   * @return unknown.
2306   */
2307  function wp_get_recent_posts( $args = array(), $output = ARRAY_A ) {
2308  
2309      if ( is_numeric( $args ) ) {
2310          _deprecated_argument( __FUNCTION__, '3.1', __( 'Passing an integer number of posts is deprecated. Pass an array of arguments instead.' ) );
2311          $args = array( 'numberposts' => absint( $args ) );
2312      }
2313  
2314      // Set default arguments
2315      $defaults = array(
2316          'numberposts' => 10, 'offset' => 0,
2317          'category' => 0, 'orderby' => 'post_date',
2318          'order' => 'DESC', 'include' => '',
2319          'exclude' => '', 'meta_key' => '',
2320          'meta_value' =>'', 'post_type' => 'post', 'post_status' => 'draft, publish, future, pending, private',
2321          'suppress_filters' => true
2322      );
2323  
2324