[ Index ]

PHP Cross Reference of WordPress 3.0 beta 1

[ Index ]     [ Variables ]     [ Functions ]     [ Classes ]     [ Constants ]     [ Statistics ]

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  function create_initial_post_types() {
  18      register_post_type( 'post', array(    'label' => __('Posts'),
  19                                          'singular_label' => __('Post'),
  20                                          'public' => true,
  21                                          'show_ui' => false,
  22                                          '_builtin' => true,
  23                                          '_edit_link' => 'post.php?post=%d',
  24                                          'capability_type' => 'post',
  25                                          'hierarchical' => false,
  26                                          'rewrite' => false,
  27                                          'query_var' => false,
  28                                          'supports' => array('title', 'editor', 'author', 'thumbnail', 'excerpt', 'trackbacks', 'custom-fields', 'comments', 'revisions')
  29                                      ) );
  30  
  31      register_post_type( 'page', array(    'label' => __('Pages'),
  32                                          'singular_label' => __('Page'),
  33                                          'public' => true,
  34                                          'show_ui' => false,
  35                                          '_builtin' => true,
  36                                          '_edit_link' => 'post.php?post=%d',
  37                                          'capability_type' => 'page',
  38                                          'hierarchical' => true,
  39                                          'rewrite' => false,
  40                                          'query_var' => false,
  41                                          'supports' => array('title', 'editor', 'author', 'thumbnail', 'page-attributes', 'custom-fields', 'comments', 'revisions')
  42                                      ) );
  43  
  44      register_post_type( 'attachment', array('label' => __('Media'),
  45                                              'public' => true,
  46                                              'show_ui' => false,
  47                                              '_builtin' => true,
  48                                              '_edit_link' => 'media.php?attachment_id=%d',
  49                                              'capability_type' => 'post',
  50                                              'hierarchical' => false,
  51                                              'rewrite' => false,
  52                                              'query_var' => false,
  53                                          ) );
  54  
  55      register_post_type( 'revision', array(    'label' => __('Revisions'),
  56                                              'singular_label' => __('Revision'),
  57                                              'public' => false,
  58                                              '_builtin' => true,
  59                                              '_edit_link' => 'revision.php?revision=%d',
  60                                              'capability_type' => 'post',
  61                                              'hierarchical' => false,
  62                                              'rewrite' => false,
  63                                              'query_var' => false,
  64                                          ) );
  65  
  66      register_post_type( 'nav_menu_item', array(    'label' => __('Navigation Menu Items'),
  67                                                  'singular_label' => __('Navigation Menu Item'),
  68                                                  'public' => false,
  69                                                  'show_ui' => false,
  70                                                  '_builtin' => true,
  71                                                  'capability_type' => 'post',
  72                                                  'hierarchical' => false,
  73                                                  'rewrite' => false,
  74                                                  'query_var' => false,
  75                                              ) );
  76  
  77      register_post_status( 'publish', array(    'label' => _x('Published', 'post'),
  78                                              'public' => true,
  79                                              '_builtin' => true,
  80                                              'label_count' => _n_noop('Published <span class="count">(%s)</span>', 'Published <span class="count">(%s)</span>')
  81                                          ) );
  82  
  83      register_post_status( 'future', array(    'label' => _x('Scheduled', 'post'),
  84                                              'protected' => true,
  85                                              '_builtin' => true,
  86                                              'label_count' => _n_noop('Scheduled <span class="count">(%s)</span>', 'Scheduled <span class="count">(%s)</span>')
  87                                          ) );
  88  
  89      register_post_status( 'draft', array(    'label' => _x('Draft', 'post'),
  90                                              'protected' => true,
  91                                              '_builtin' => true,
  92                                              'label_count' => _n_noop('Draft <span class="count">(%s)</span>', 'Drafts <span class="count">(%s)</span>')
  93                                          ) );
  94  
  95      register_post_status( 'pending', array(    'label' => _x('Pending', 'post'),
  96                                              'protected' => true,
  97                                              '_builtin' => true,
  98                                              'label_count' => _n_noop('Pending <span class="count">(%s)</span>', 'Pending <span class="count">(%s)</span>')
  99                                          ) );
 100  
 101      register_post_status( 'private', array(    'label' => _x('Private', 'post'),
 102                                              'private' => true,
 103                                              '_builtin' => true,
 104                                              'label_count' => _n_noop('Private <span class="count">(%s)</span>', 'Private <span class="count">(%s)</span>')
 105                                          ) );
 106  
 107      register_post_status( 'trash', array(    'label' => _x('Trash', 'post'),
 108                                              'internal' => true,
 109                                              'show_in_admin_status_list' => true,
 110                                              '_builtin' => true,
 111                                              'label_count' => _n_noop('Trash <span class="count">(%s)</span>', 'Trash <span class="count">(%s)</span>')
 112                                          ) );
 113  
 114      register_post_status( 'auto-draft', array(    'label' => _x('Auto-Draft', 'post'),
 115                                              'internal' => true,
 116                                              '_builtin' => true,
 117                                              'label_count' => _n_noop('Auto-Draft <span class="count">(%s)</span>', 'Auto-Drafts <span class="count">(%s)</span>')
 118                                          ) );
 119  }
 120  add_action( 'init', 'create_initial_post_types', 0 ); // highest priority
 121  
 122  /**
 123   * Retrieve attached file path based on attachment ID.
 124   *
 125   * You can optionally send it through the 'get_attached_file' filter, but by
 126   * default it will just return the file path unfiltered.
 127   *
 128   * The function works by getting the single post meta name, named
 129   * '_wp_attached_file' and returning it. This is a convenience function to
 130   * prevent looking up the meta name and provide a mechanism for sending the
 131   * attached filename through a filter.
 132   *
 133   * @since 2.0.0
 134   * @uses apply_filters() Calls 'get_attached_file' on file path and attachment ID.
 135   *
 136   * @param int $attachment_id Attachment ID.
 137   * @param bool $unfiltered Whether to apply filters.
 138   * @return string The file path to the attached file.
 139   */
 140  function get_attached_file( $attachment_id, $unfiltered = false ) {
 141      $file = get_post_meta( $attachment_id, '_wp_attached_file', true );
 142      // If the file is relative, prepend upload dir
 143      if ( 0 !== strpos($file, '/') && !preg_match('|^.:\\\|', $file) && ( ($uploads = wp_upload_dir()) && false === $uploads['error'] ) )
 144          $file = $uploads['basedir'] . "/$file";
 145      if ( $unfiltered )
 146          return $file;
 147      return apply_filters( 'get_attached_file', $file, $attachment_id );
 148  }
 149  
 150  /**
 151   * Update attachment file path based on attachment ID.
 152   *
 153   * Used to update the file path of the attachment, which uses post meta name
 154   * '_wp_attached_file' to store the path of the attachment.
 155   *
 156   * @since 2.1.0
 157   * @uses apply_filters() Calls 'update_attached_file' on file path and attachment ID.
 158   *
 159   * @param int $attachment_id Attachment ID
 160   * @param string $file File path for the attachment
 161   * @return bool False on failure, true on success.
 162   */
 163  function update_attached_file( $attachment_id, $file ) {
 164      if ( !get_post( $attachment_id ) )
 165          return false;
 166  
 167      $file = apply_filters( 'update_attached_file', $file, $attachment_id );
 168      $file = _wp_relative_upload_path($file);
 169  
 170      return update_post_meta( $attachment_id, '_wp_attached_file', $file );
 171  }
 172  
 173  /**
 174   * Return relative path to an uploaded file.
 175   *
 176   * The path is relative to the current upload dir.
 177   *
 178   * @since 2.9.0
 179   * @uses apply_filters() Calls '_wp_relative_upload_path' on file path.
 180   *
 181   * @param string $path Full path to the file
 182   * @return string relative path on success, unchanged path on failure.
 183   */
 184  function _wp_relative_upload_path( $path ) {
 185      $new_path = $path;
 186  
 187      if ( ($uploads = wp_upload_dir()) && false === $uploads['error'] ) {
 188          if ( 0 === strpos($new_path, $uploads['basedir']) ) {
 189                  $new_path = str_replace($uploads['basedir'], '', $new_path);
 190                  $new_path = ltrim($new_path, '/');
 191          }
 192      }
 193  
 194      return apply_filters( '_wp_relative_upload_path', $new_path, $path );
 195  }
 196  
 197  /**
 198   * Retrieve all children of the post parent ID.
 199   *
 200   * Normally, without any enhancements, the children would apply to pages. In the
 201   * context of the inner workings of WordPress, pages, posts, and attachments
 202   * share the same table, so therefore the functionality could apply to any one
 203   * of them. It is then noted that while this function does not work on posts, it
 204   * does not mean that it won't work on posts. It is recommended that you know
 205   * what context you wish to retrieve the children of.
 206   *
 207   * Attachments may also be made the child of a post, so if that is an accurate
 208   * statement (which needs to be verified), it would then be possible to get
 209   * all of the attachments for a post. Attachments have since changed since
 210   * version 2.5, so this is most likely unaccurate, but serves generally as an
 211   * example of what is possible.
 212   *
 213   * The arguments listed as defaults are for this function and also of the
 214   * {@link get_posts()} function. The arguments are combined with the
 215   * get_children defaults and are then passed to the {@link get_posts()}
 216   * function, which accepts additional arguments. You can replace the defaults in
 217   * this function, listed below and the additional arguments listed in the
 218   * {@link get_posts()} function.
 219   *
 220   * The 'post_parent' is the most important argument and important attention
 221   * needs to be paid to the $args parameter. If you pass either an object or an
 222   * integer (number), then just the 'post_parent' is grabbed and everything else
 223   * is lost. If you don't specify any arguments, then it is assumed that you are
 224   * in The Loop and the post parent will be grabbed for from the current post.
 225   *
 226   * The 'post_parent' argument is the ID to get the children. The 'numberposts'
 227   * is the amount of posts to retrieve that has a default of '-1', which is
 228   * used to get all of the posts. Giving a number higher than 0 will only
 229   * retrieve that amount of posts.
 230   *
 231   * The 'post_type' and 'post_status' arguments can be used to choose what
 232   * criteria of posts to retrieve. The 'post_type' can be anything, but WordPress
 233   * post types are 'post', 'pages', and 'attachments'. The 'post_status'
 234   * argument will accept any post status within the write administration panels.
 235   *
 236   * @see get_posts() Has additional arguments that can be replaced.
 237   * @internal Claims made in the long description might be inaccurate.
 238   *
 239   * @since 2.0.0
 240   *
 241   * @param mixed $args Optional. User defined arguments for replacing the defaults.
 242   * @param string $output Optional. Constant for return type, either OBJECT (default), ARRAY_A, ARRAY_N.
 243   * @return array|bool False on failure and the type will be determined by $output parameter.
 244   */
 245  function &get_children($args = '', $output = OBJECT) {
 246      $kids = array();
 247      if ( empty( $args ) ) {
 248          if ( isset( $GLOBALS['post'] ) ) {
 249              $args = array('post_parent' => (int) $GLOBALS['post']->post_parent );
 250          } else {
 251              return $kids;
 252          }
 253      } elseif ( is_object( $args ) ) {
 254          $args = array('post_parent' => (int) $args->post_parent );
 255      } elseif ( is_numeric( $args ) ) {
 256          $args = array('post_parent' => (int) $args);
 257      }
 258  
 259      $defaults = array(
 260          'numberposts' => -1, 'post_type' => 'any',
 261          'post_status' => 'any', 'post_parent' => 0,
 262      );
 263  
 264      $r = wp_parse_args( $args, $defaults );
 265  
 266      $children = get_posts( $r );
 267  
 268      if ( !$children )
 269          return $kids;
 270  
 271      update_post_cache($children);
 272  
 273      foreach ( $children as $key => $child )
 274          $kids[$child->ID] =& $children[$key];
 275  
 276      if ( $output == OBJECT ) {
 277          return $kids;
 278      } elseif ( $output == ARRAY_A ) {
 279          foreach ( (array) $kids as $kid )
 280              $weeuns[$kid->ID] = get_object_vars($kids[$kid->ID]);
 281          return $weeuns;
 282      } elseif ( $output == ARRAY_N ) {
 283          foreach ( (array) $kids as $kid )
 284              $babes[$kid->ID] = array_values(get_object_vars($kids[$kid->ID]));
 285          return $babes;
 286      } else {
 287          return $kids;
 288      }
 289  }
 290  
 291  /**
 292   * Get extended entry info (<!--more-->).
 293   *
 294   * There should not be any space after the second dash and before the word
 295   * 'more'. There can be text or space(s) after the word 'more', but won't be
 296   * referenced.
 297   *
 298   * The returned array has 'main' and 'extended' keys. Main has the text before
 299   * the <code><!--more--></code>. The 'extended' key has the content after the
 300   * <code><!--more--></code> comment.
 301   *
 302   * @since 1.0.0
 303   *
 304   * @param string $post Post content.
 305   * @return array Post before ('main') and after ('extended').
 306   */
 307  function get_extended($post) {
 308      //Match the new style more links
 309      if ( preg_match('/<!--more(.*?)?-->/', $post, $matches) ) {
 310          list($main, $extended) = explode($matches[0], $post, 2);
 311      } else {
 312          $main = $post;
 313          $extended = '';
 314      }
 315  
 316      // Strip leading and trailing whitespace
 317      $main = preg_replace('/^[\s]*(.*)[\s]*$/', '\\1', $main);
 318      $extended = preg_replace('/^[\s]*(.*)[\s]*$/', '\\1', $extended);
 319  
 320      return array('main' => $main, 'extended' => $extended);
 321  }
 322  
 323  /**
 324   * Retrieves post data given a post ID or post object.
 325   *
 326   * See {@link sanitize_post()} for optional $filter values. Also, the parameter
 327   * $post, must be given as a variable, since it is passed by reference.
 328   *
 329   * @since 1.5.1
 330   * @uses $wpdb
 331   * @link http://codex.wordpress.org/Function_Reference/get_post
 332   *
 333   * @param int|object $post Post ID or post object.
 334   * @param string $output Optional, default is Object. Either OBJECT, ARRAY_A, or ARRAY_N.
 335   * @param string $filter Optional, default is raw.
 336   * @return mixed Post data
 337   */
 338  function &get_post(&$post, $output = OBJECT, $filter = 'raw') {
 339      global $wpdb;
 340      $null = null;
 341  
 342      if ( empty($post) ) {
 343          if ( isset($GLOBALS['post']) )
 344              $_post = & $GLOBALS['post'];
 345          else
 346              return $null;
 347      } elseif ( is_object($post) && empty($post->filter) ) {
 348          _get_post_ancestors($post);
 349          $_post = sanitize_post($post, 'raw');
 350          wp_cache_add($post->ID, $_post, 'posts');
 351      } else {
 352          if ( is_object($post) )
 353              $post = $post->ID;
 354          $post = (int) $post;
 355          if ( ! $_post = wp_cache_get($post, 'posts') ) {
 356              $_post = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d LIMIT 1", $post));
 357              if ( ! $_post )
 358                  return $null;
 359              _get_post_ancestors($_post);
 360              $_post = sanitize_post($_post, 'raw');
 361              wp_cache_add($_post->ID, $_post, 'posts');
 362          }
 363      }
 364  
 365      if ($filter != 'raw')
 366          $_post = sanitize_post($_post, $filter);
 367  
 368      if ( $output == OBJECT ) {
 369          return $_post;
 370      } elseif ( $output == ARRAY_A ) {
 371          $__post = get_object_vars($_post);
 372          return $__post;
 373      } elseif ( $output == ARRAY_N ) {
 374          $__post = array_values(get_object_vars($_post));
 375          return $__post;
 376      } else {
 377          return $_post;
 378      }
 379  }
 380  
 381  /**
 382   * Retrieve ancestors of a post.
 383   *
 384   * @since 2.5.0
 385   *
 386   * @param int|object $post Post ID or post object
 387   * @return array Ancestor IDs or empty array if none are found.
 388   */
 389  function get_post_ancestors($post) {
 390      $post = get_post($post);
 391  
 392      if ( !empty($post->ancestors) )
 393          return $post->ancestors;
 394  
 395      return array();
 396  }
 397  
 398  /**
 399   * Retrieve data from a post field based on Post ID.
 400   *
 401   * Examples of the post field will be, 'post_type', 'post_status', 'content',
 402   * etc and based off of the post object property or key names.
 403   *
 404   * The context values are based off of the taxonomy filter functions and
 405   * supported values are found within those functions.
 406   *
 407   * @since 2.3.0
 408   * @uses sanitize_post_field() See for possible $context values.
 409   *
 410   * @param string $field Post field name
 411   * @param id $post Post ID
 412   * @param string $context Optional. How to filter the field. Default is display.
 413   * @return WP_Error|string Value in post field or WP_Error on failure
 414   */
 415  function get_post_field( $field, $post, $context = 'display' ) {
 416      $post = (int) $post;
 417      $post = get_post( $post );
 418  
 419      if ( is_wp_error($post) )
 420          return $post;
 421  
 422      if ( !is_object($post) )
 423          return '';
 424  
 425      if ( !isset($post->$field) )
 426          return '';
 427  
 428      return sanitize_post_field($field, $post->$field, $post->ID, $context);
 429  }
 430  
 431  /**
 432   * Retrieve the mime type of an attachment based on the ID.
 433   *
 434   * This function can be used with any post type, but it makes more sense with
 435   * attachments.
 436   *
 437   * @since 2.0.0
 438   *
 439   * @param int $ID Optional. Post ID.
 440   * @return bool|string False on failure or returns the mime type
 441   */
 442  function get_post_mime_type($ID = '') {
 443      $post = & get_post($ID);
 444  
 445      if ( is_object($post) )
 446          return $post->post_mime_type;
 447  
 448      return false;
 449  }
 450  
 451  /**
 452   * Retrieve the post status based on the Post ID.
 453   *
 454   * If the post ID is of an attachment, then the parent post status will be given
 455   * instead.
 456   *
 457   * @since 2.0.0
 458   *
 459   * @param int $ID Post ID
 460   * @return string|bool Post status or false on failure.
 461   */
 462  function get_post_status($ID = '') {
 463      $post = get_post($ID);
 464  
 465      if ( is_object($post) ) {
 466          if ( ('attachment' == $post->post_type) && $post->post_parent && ($post->ID != $post->post_parent) )
 467              return get_post_status($post->post_parent);
 468          else
 469              return $post->post_status;
 470      }
 471  
 472      return false;
 473  }
 474  
 475  /**
 476   * Retrieve all of the WordPress supported post statuses.
 477   *
 478   * Posts have a limited set of valid status values, this provides the
 479   * post_status values and descriptions.
 480   *
 481   * @since 2.5.0
 482   *
 483   * @return array List of post statuses.
 484   */
 485  function get_post_statuses( ) {
 486      $status = array(
 487          'draft'            => __('Draft'),
 488          'pending'        => __('Pending Review'),
 489          'private'        => __('Private'),
 490          'publish'        => __('Published')
 491      );
 492  
 493      return $status;
 494  }
 495  
 496  /**
 497   * Retrieve all of the WordPress support page statuses.
 498   *
 499   * Pages have a limited set of valid status values, this provides the
 500   * post_status values and descriptions.
 501   *
 502   * @since 2.5.0
 503   *
 504   * @return array List of page statuses.
 505   */
 506  function get_page_statuses( ) {
 507      $status = array(
 508          'draft'            => __('Draft'),
 509          'private'        => __('Private'),
 510          'publish'        => __('Published')
 511      );
 512  
 513      return $status;
 514  }
 515  
 516  /**
 517   * Register a post type. Do not use before init.
 518   *
 519   * A simple function for creating or modifying a post status based on the
 520   * parameters given. The function will accept an array (second optional
 521   * parameter), along with a string for the post status name.
 522   *
 523   *
 524   * Optional $args contents:
 525   *
 526   * label - A descriptive name for the post status marked for translation. Defaults to $post_status.
 527   * public - Whether posts of this status should be shown in the admin UI. Defaults to true.
 528   * exclude_from_search - Whether to exclude posts with this post status from search results. Defaults to true.
 529   *
 530   * @package WordPress
 531   * @subpackage Post
 532   * @since 3.0.0
 533   * @uses $wp_post_statuses Inserts new post status object into the list
 534   *
 535   * @param string $post_status Name of the post status.
 536   * @param array|string $args See above description.
 537   */
 538  function register_post_status($post_status, $args = array()) {
 539      global $wp_post_statuses;
 540  
 541      if (!is_array($wp_post_statuses))
 542          $wp_post_statuses = array();
 543  
 544      // Args prefixed with an underscore are reserved for internal use.
 545      $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);
 546      $args = wp_parse_args($args, $defaults);
 547      $args = (object) $args;
 548  
 549      $post_status = sanitize_user($post_status, true);
 550      $args->name = $post_status;
 551  
 552      if ( null === $args->public && null === $args->internal && null === $args->protected && null === $args->private )
 553          $args->internal = true;
 554  
 555      if ( null === $args->public  )
 556          $args->public = false;
 557  
 558      if ( null === $args->private  )
 559          $args->private = false;
 560  
 561      if ( null === $args->protected  )
 562          $args->protected = false;
 563  
 564      if ( null === $args->internal  )
 565          $args->internal = false;
 566  
 567      if ( null === $args->publicly_queryable )
 568          $args->publicly_queryable = $args->public;
 569  
 570      if ( null === $args->exclude_from_search )
 571          $args->exclude_from_search = $args->internal;
 572  
 573      if ( null === $args->show_in_admin_all_list )
 574          $args->show_in_admin_all_list = !$args->internal;
 575  
 576      if ( null === $args->show_in_admin_status_list )
 577              $args->show_in_admin_status_list = !$args->internal;
 578  
 579      if ( null === $args->single_view_cap )
 580          $args->single_view_cap = $args->public ? '' : 'edit';
 581  
 582      if ( false === $args->label )
 583          $args->label = $post_status;
 584  
 585      if ( false === $args->label_count )
 586          $args->label_count = $args->label;
 587  
 588      $wp_post_statuses[$post_status] = $args;
 589  
 590      return $args;
 591  }
 592  
 593  /**
 594   * Retrieve a post status object by name
 595   *
 596   * @package WordPress
 597   * @subpackage Post
 598   * @since 3.0.0
 599   * @uses $wp_post_statuses
 600   * @see register_post_status
 601   * @see get_post_statuses
 602   *
 603   * @param string $post_type The name of a registered post status
 604   * @return object A post status object
 605   */
 606  function get_post_status_object( $post_status ) {
 607      global $wp_post_statuses;
 608  
 609      if ( empty($wp_post_statuses[$post_status]) )
 610          return null;
 611  
 612      return $wp_post_statuses[$post_status];
 613  }
 614  
 615  /**
 616   * Get a list of all registered post status objects.
 617   *
 618   * @package WordPress
 619   * @subpackage Post
 620   * @since 3.0.0
 621   * @uses $wp_post_statuses
 622   * @see register_post_status
 623   * @see get_post_status_object
 624   *
 625   * @param array|string $args An array of key => value arguments to match against the post statuses.
 626   *  Only post statuses having attributes that match all arguments are returned.
 627   * @param string $output The type of output to return, either post status 'names' or 'objects'. 'names' is the default.
 628   * @param string $operator Whether the elements in $args should be logicallly 'or'ed or 'and'ed together. 'or' means only one element from the array needs to match. 'and' means all elements must match. The default is 'or'.
 629   * @return array A list of post type names or objects
 630   */
 631  function get_post_stati( $args = array(), $output = 'names', $operator = 'or' ) {
 632      global $wp_post_statuses;
 633  
 634      $do_names = false;
 635      if ( 'names' == $output )
 636          $do_names = true;
 637  
 638      if ( 'and' == $operator )
 639          $arg_count = count($args);
 640      else
 641          $arg_count = 0;
 642  
 643      $post_statuses = array();
 644      foreach ( (array) $wp_post_statuses as $post_status ) {
 645          if ( empty($args) ) {
 646              if ( $do_names )
 647                  $post_statuses[] = $post_status->name;
 648              else
 649                  $post_statuses[] = $post_status;
 650          } elseif ( $intersect = array_intersect_assoc((array) $post_status, $args) ) {
 651              if ( $arg_count && ( $arg_count != count($intersect) ) )
 652                  continue;
 653              if ( $do_names )
 654                  $post_statuses[] = $post_status->name;
 655              else
 656                  $post_statuses[] = $post_status;
 657          }
 658      }
 659  
 660      return $post_statuses;
 661  }
 662  
 663  /**
 664   * Retrieve the post type of the current post or of a given post.
 665   *
 666   * @since 2.1.0
 667   *
 668   * @uses $wpdb
 669   * @uses $posts The Loop post global
 670   *
 671   * @param mixed $post Optional. Post object or post ID.
 672   * @return bool|string post type or false on failure.
 673   */
 674  function get_post_type($post = false) {
 675      global $posts;
 676  
 677      if ( false === $post )
 678          $post = $posts[0];
 679      elseif ( (int) $post )
 680          $post = get_post($post, OBJECT);
 681  
 682      if ( is_object($post) )
 683          return $post->post_type;
 684  
 685      return false;
 686  }
 687  
 688  /**
 689   * Retrieve a post type object by name
 690   *
 691   * @package WordPress
 692   * @subpackage Post
 693   * @since 3.0.0
 694   * @uses $wp_post_types
 695   * @see register_post_type
 696   * @see get_post_types
 697   *
 698   * @param string $post_type The name of a registered post type
 699   * @return object A post type object
 700   */
 701  function get_post_type_object( $post_type ) {
 702      global $wp_post_types;
 703  
 704      if ( empty($wp_post_types[$post_type]) )
 705          return null;
 706  
 707      return $wp_post_types[$post_type];
 708  }
 709  
 710  /**
 711   * Get a list of all registered post type objects.
 712   *
 713   * @package WordPress
 714   * @subpackage Post
 715   * @since 2.9.0
 716   * @uses $wp_post_types
 717   * @see register_post_type
 718   * @see get_post_types
 719   *
 720   * @param array|string $args An array of key => value arguments to match against the post types.
 721   *  Only post types having attributes that match all arguments are returned.
 722   * @param string $output The type of output to return, either post type 'names' or 'objects'. 'names' is the default.
 723   * @return array A list of post type names or objects
 724   */
 725  function get_post_types( $args = array(), $output = 'names' ) {
 726      global $wp_post_types;
 727  
 728      $do_names = false;
 729      if ( 'names' == $output )
 730          $do_names = true;
 731  
 732      $post_types = array();
 733      foreach ( (array) $wp_post_types as $post_type ) {
 734          if ( empty($args) ) {
 735              if ( $do_names )
 736                  $post_types[] = $post_type->name;
 737              else
 738                  $post_types[] = $post_type;
 739          } elseif ( array_intersect_assoc((array) $post_type, $args) ) {
 740              if ( $do_names )
 741                  $post_types[] = $post_type->name;
 742              else
 743                  $post_types[] = $post_type;
 744          }
 745      }
 746  
 747      return $post_types;
 748  }
 749  
 750  /**
 751   * Register a post type. Do not use before init.
 752   *
 753   * A simple function for creating or modifying a post type based on the
 754   * parameters given. The function will accept an array (second optional
 755   * parameter), along with a string for the post type name.
 756   *
 757   *
 758   * Optional $args contents:
 759   *
 760   * label - A (plural) descriptive name for the post type marked for translation. Defaults to $post_type.
 761   * singular_label - A (singular) descriptive name for the post type marked for translation. Defaults to $label.
 762   * description - A short descriptive summary of what the post type is. Defaults to blank.
 763   * public - Whether posts of this type should be shown in the admin UI. Defaults to false.
 764   * exclude_from_search - Whether to exclude posts with this post type from search results. Defaults to true if the type is not public, false if the type is public.
 765   * publicly_queryable - Whether post_type queries can be performed from the front page.  Defaults to whatever public is set as.
 766   * show_ui - Whether to generate a default UI for managing this post type. Defaults to true if the type is public, false if the type is not public.
 767   * inherit_type - The post type from which to inherit the edit link and capability type. Defaults to none.
 768   * capability_type - The post type to use for checking read, edit, and delete capabilities. Defaults to "post".
 769   * edit_cap - The capability that controls editing a particular object of this post type. Defaults to "edit_$capability_type" (edit_post).
 770   * edit_type_cap - The capability that controls editing objects of this post type as a class. Defaults to "edit_ . $capability_type . s" (edit_posts).
 771   * edit_others_cap - The capability that controls editing objects of this post type that are owned by other users. Defaults to "edit_others_ . $capability_type . s" (edit_others_posts).
 772   * publish_others_cap - The capability that controls publishing objects of this post type. Defaults to "publish_ . $capability_type . s" (publish_posts).
 773   * read_cap - The capability that controls reading a particular object of this post type. Defaults to "read_$capability_type" (read_post).
 774   * delete_cap - The capability that controls deleting a particular object of this post type. Defaults to "delete_$capability_type" (delete_post).
 775   * hierarchical - Whether the post type is hierarchical. Defaults to false.
 776   * supports - An alias for calling add_post_type_support() directly. See add_post_type_support() for Documentation. Defaults to none.
 777   * register_meta_box_cb - Provide a callback function that will be called when setting up the meta boxes for the edit form.  Do remove_meta_box() and add_meta_box() calls in the callback.
 778   * taxonomies - An array of taxonomy identifiers that will be registered for the post type.  Default is no taxonomies. Taxonomies can be registered later with register_taxonomy() or register_taxonomy_for_object_type().
 779   *
 780   * @package WordPress
 781   * @subpackage Post
 782   * @since 2.9.0
 783   * @uses $wp_post_types Inserts new post type object into the list
 784   *
 785   * @param string $post_type Name of the post type.
 786   * @param array|string $args See above description.
 787   */
 788  function register_post_type($post_type, $args = array()) {
 789      global $wp_post_types, $wp_rewrite, $wp;
 790  
 791      if ( !is_array($wp_post_types) )
 792          $wp_post_types = array();
 793  
 794      // Args prefixed with an underscore are reserved for internal use.
 795      $defaults = array('label' => false, 'singular_label' => false, 'description' => '', 'publicly_queryable' => null, 'exclude_from_search' => null, '_builtin' => false, '_edit_link' => 'post.php?post=%d', 'capability_type' => 'post', 'hierarchical' => false, 'public' => false, 'rewrite' => true, 'query_var' => true, 'supports' => array(), 'register_meta_box_cb' => null, 'taxonomies' => array(), 'show_ui' => null, 'permalink_epmask' => EP_NONE );
 796      $args = wp_parse_args($args, $defaults);
 797      $args = (object) $args;
 798  
 799      $post_type = sanitize_user($post_type, true);
 800      $args->name = $post_type;
 801  
 802      // If not set, default to the setting for public.
 803      if ( null === $args->publicly_queryable )
 804          $args->publicly_queryable = $args->public;
 805  
 806      // If not set, default to the setting for public.
 807      if ( null === $args->show_ui )
 808          $args->show_ui = $args->public;
 809  
 810      // If not set, default to true if not public, false if public.
 811      if ( null === $args->exclude_from_search )
 812          $args->exclude_from_search = !$args->public;
 813  
 814      if ( false === $args->label )
 815          $args->label = $post_type;
 816  
 817      if ( false === $args->singular_label )
 818          $args->singular_label = $args->label;
 819  
 820      if ( empty($args->capability_type) )
 821          $args->capability_type = 'post';
 822      if ( empty($args->edit_cap) )
 823          $args->edit_cap = 'edit_' . $args->capability_type;
 824      if ( empty($args->edit_type_cap) )
 825          $args->edit_type_cap = 'edit_' . $args->capability_type . 's';
 826      if ( empty($args->edit_others_cap) )
 827          $args->edit_others_cap = 'edit_others_' . $args->capability_type . 's';
 828      if ( empty($args->publish_cap) )
 829          $args->publish_cap = 'publish_' . $args->capability_type . 's';
 830      if ( empty($args->read_cap) )
 831          $args->read_cap = 'read_' . $args->capability_type;
 832      if ( empty($args->read_private_cap) )
 833          $args->read_private_cap = 'read_private_' . $args->capability_type . 's';
 834      if ( empty($args->delete_cap) )
 835          $args->delete_cap = 'delete_' . $args->capability_type;
 836  
 837      if ( ! empty($args->supports) ) {
 838          add_post_type_support($post_type, $args->supports);
 839          unset($args->supports);
 840      } else {
 841          // Add default features
 842          add_post_type_support($post_type, array('title', 'editor'));
 843      }
 844  
 845      if ( false !== $args->query_var && !empty($wp) ) {
 846          if ( true === $args->query_var )
 847              $args->query_var = $post_type;
 848          $args->query_var = sanitize_title_with_dashes($args->query_var);
 849          $wp->add_query_var($args->query_var);
 850      }
 851  
 852      if ( false !== $args->rewrite && '' != get_option('permalink_structure') ) {
 853          if ( !is_array($args->rewrite) )
 854              $args->rewrite = array();
 855          if ( !isset($args->rewrite['slug']) )
 856              $args->rewrite['slug'] = $post_type;
 857          if ( !isset($args->rewrite['with_front']) )
 858              $args->rewrite['with_front'] = true;
 859          if ( $args->hierarchical )
 860              $wp_rewrite->add_rewrite_tag("%$post_type%", '(.+?)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&name=");
 861          else
 862              $wp_rewrite->add_rewrite_tag("%$post_type%", '([^/]+)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&name=");
 863          $wp_rewrite->add_permastruct($post_type, "{$args->rewrite['slug']}/%$post_type%", $args->rewrite['with_front'], $args->permalink_epmask);
 864      }
 865  
 866      if ( $args->register_meta_box_cb )
 867          add_action('add_meta_boxes_' . $post_type, $args->register_meta_box_cb, 10, 1);
 868  
 869      $wp_post_types[$post_type] = $args;
 870  
 871      foreach ( $args->taxonomies as $taxonomy ) {
 872          register_taxonomy_for_object_type( $taxonomy, $post_type );
 873      }
 874  
 875      return $args;
 876  }
 877  
 878  /**
 879   * Register support of certain features for a post type.
 880   *
 881   * @since 3.0.0
 882   * @param string $post_type The post type for which to add the feature
 883   * @param string|array $feature the feature being added, can be an array of feature strings or a single string
 884   */
 885  function add_post_type_support( $post_type, $feature ) {
 886      global $_wp_post_type_features;
 887  
 888      $features = (array) $feature;
 889      foreach ($features as $feature) {
 890          if ( func_num_args() == 2 )
 891              $_wp_post_type_features[$post_type][$feature] = true;
 892          else
 893              $_wp_post_type_features[$post_type][$feature] = array_slice( func_get_args(), 2 );
 894      }
 895  }
 896  
 897  /**
 898   * Remove support for a feature from a post type.
 899   *
 900   * @since 3.0.0
 901   * @param string $post_type The post type for which to remove the feature
 902   * @param string $feature The feature being removed
 903   */
 904  function remove_post_type_support( $post_type, $feature ) {
 905      global $_wp_post_type_features;
 906  
 907      if ( !isset($_wp_post_type_features[$post_type]) )
 908          return;
 909  
 910      if ( isset($_wp_post_type_features[$post_type][$feature]) )
 911          unset($_wp_post_type_features[$post_type][$feature]);
 912  }
 913  
 914  /**
 915   * Checks a post type's support for a given feature
 916   *
 917   * @since 3.0.0
 918   * @param string $post_type The post type being checked
 919   * @param string $feature the feature being checked
 920   * @return boolean
 921   */
 922  
 923  function post_type_supports( $post_type, $feature ) {
 924      global $_wp_post_type_features;
 925  
 926      if ( !isset( $_wp_post_type_features[$post_type][$feature] ) )
 927          return false;
 928  
 929      // If no args passed then no extra checks need be performed
 930      if ( func_num_args() <= 2 )
 931          return true;
 932  
 933      // @todo Allow pluggable arg checking
 934      //$args = array_slice( func_get_args(), 2 );
 935  
 936      return true;
 937  }
 938  
 939  /**
 940   * Updates the post type for the post ID.
 941   *
 942   * The page or post cache will be cleaned for the post ID.
 943   *
 944   * @since 2.5.0
 945   *
 946   * @uses $wpdb
 947   *
 948   * @param int $post_id Post ID to change post type. Not actually optional.
 949   * @param string $post_type Optional, default is post. Supported values are 'post' or 'page' to
 950   *  name a few.
 951   * @return int Amount of rows changed. Should be 1 for success and 0 for failure.
 952   */
 953  function set_post_type( $post_id = 0, $post_type = 'post' ) {
 954      global $wpdb;
 955  
 956      $post_type = sanitize_post_field('post_type', $post_type, $post_id, 'db');
 957      $return = $wpdb->update($wpdb->posts, array('post_type' => $post_type), array('ID' => $post_id) );
 958  
 959      if ( 'page' == $post_type )
 960          clean_page_cache($post_id);
 961      else
 962          clean_post_cache($post_id);
 963  
 964      return $return;
 965  }
 966  
 967  /**
 968   * Retrieve list of latest posts or posts matching criteria.
 969   *
 970   * The defaults are as follows:
 971   *     'numberposts' - Default is 5. Total number of posts to retrieve.
 972   *     'offset' - Default is 0. See {@link WP_Query::query()} for more.
 973   *     'category' - What category to pull the posts from.
 974   *     'orderby' - Default is 'post_date'. How to order the posts.
 975   *     'order' - Default is 'DESC'. The order to retrieve the posts.
 976   *     'include' - See {@link WP_Query::query()} for more.
 977   *     'exclude' - See {@link WP_Query::query()} for more.
 978   *     'meta_key' - See {@link WP_Query::query()} for more.
 979   *     'meta_value' - See {@link WP_Query::query()} for more.
 980   *     'post_type' - Default is 'post'. Can be 'page', or 'attachment' to name a few.
 981   *     'post_parent' - The parent of the post or post type.
 982   *     'post_status' - Default is 'published'. Post status to retrieve.
 983   *
 984   * @since 1.2.0
 985   * @uses $wpdb
 986   * @uses WP_Query::query() See for more default arguments and information.
 987   * @link http://codex.wordpress.org/Template_Tags/get_posts
 988   *
 989   * @param array $args Optional. Overrides defaults.
 990   * @return array List of posts.
 991   */
 992  function get_posts($args = null) {
 993      $defaults = array(
 994          'numberposts' => 5, 'offset' => 0,
 995          'category' => 0, 'orderby' => 'post_date',
 996          'order' => 'DESC', 'include' => '',
 997          'exclude' => '', 'meta_key' => '',
 998          'meta_value' =>'', 'post_type' => 'post',
 999          'suppress_filters' => true
1000      );
1001  
1002      $r = wp_parse_args( $args, $defaults );
1003      if ( empty( $r['post_status'] ) )
1004          $r['post_status'] = ( 'attachment' == $r['post_type'] ) ? 'inherit' : 'publish';
1005      if ( ! empty($r['numberposts']) )
1006          $r['posts_per_page'] = $r['numberposts'];
1007      if ( ! empty($r['category']) )
1008          $r['cat'] = $r['category'];
1009      if ( ! empty($r['include']) ) {
1010          $incposts = preg_split('/[\s,]+/',$r['include']);
1011          $r['posts_per_page'] = count($incposts);  // only the number of posts included
1012          $r['post__in'] = $incposts;
1013      } elseif ( ! empty($r['exclude']) )
1014          $r['post__not_in'] = preg_split('/[\s,]+/',$r['exclude']);
1015  
1016      $r['caller_get_posts'] = true;
1017  
1018      $get_posts = new WP_Query;
1019      return $get_posts->query($r);
1020  
1021  }
1022  
1023  //
1024  // Post meta functions
1025  //
1026  
1027  /**
1028   * Add meta data field to a post.
1029   *
1030   * Post meta data is called "Custom Fields" on the Administration Panels.
1031   *
1032   * @since 1.5.0
1033   * @uses $wpdb
1034   * @link http://codex.wordpress.org/Function_Reference/add_post_meta
1035   *
1036   * @param int $post_id Post ID.
1037   * @param string $key Metadata name.
1038   * @param mixed $value Metadata value.
1039   * @param bool $unique Optional, default is false. Whether the same key should not be added.
1040   * @return bool False for failure. True for success.
1041   */
1042  function add_post_meta($post_id, $meta_key, $meta_value, $unique = false) {
1043      // make sure meta is added to the post, not a revision
1044      if ( $the_post = wp_is_post_revision($post_id) )
1045          $post_id = $the_post;
1046  
1047      return add_metadata('post', $post_id, $meta_key, $meta_value, $unique);
1048  }
1049  
1050  /**
1051   * Remove metadata matching criteria from a post.
1052   *
1053   * You can match based on the key, or key and value. Removing based on key and
1054   * value, will keep from removing duplicate metadata with the same key. It also
1055   * allows removing all metadata matching key, if needed.
1056   *
1057   * @since 1.5.0
1058   * @uses $wpdb
1059   * @link http://codex.wordpress.org/Function_Reference/delete_post_meta
1060   *
1061   * @param int $post_id post ID
1062   * @param string $meta_key Metadata name.
1063   * @param mixed $meta_value Optional. Metadata value.
1064   * @return bool False for failure. True for success.
1065   */
1066  function delete_post_meta($post_id, $meta_key, $meta_value = '') {
1067      // make sure meta is added to the post, not a revision
1068      if ( $the_post = wp_is_post_revision($post_id) )
1069          $post_id = $the_post;
1070  
1071      return delete_metadata('post', $post_id, $meta_key, $meta_value);
1072  }
1073  
1074  /**
1075   * Retrieve post meta field for a post.
1076   *
1077   * @since 1.5.0
1078   * @uses $wpdb
1079   * @link http://codex.wordpress.org/Function_Reference/get_post_meta
1080   *
1081   * @param int $post_id Post ID.
1082   * @param string $key The meta key to retrieve.
1083   * @param bool $single Whether to return a single value.
1084   * @return mixed Will be an array if $single is false. Will be value of meta data field if $single
1085   *  is true.
1086   */
1087  function get_post_meta($post_id, $key, $single = false) {
1088      return get_metadata('post', $post_id, $key, $single);
1089  }
1090  
1091  /**
1092   * Update post meta field based on post ID.
1093   *
1094   * Use the $prev_value parameter to differentiate between meta fields with the
1095   * same key and post ID.
1096   *
1097   * If the meta field for the post does not exist, it will be added.
1098   *
1099   * @since 1.5
1100   * @uses $wpdb
1101   * @link http://codex.wordpress.org/Function_Reference/update_post_meta
1102   *
1103   * @param int $post_id Post ID.
1104   * @param string $key Metadata key.
1105   * @param mixed $value Metadata value.
1106   * @param mixed $prev_value Optional. Previous value to check before removing.
1107   * @return bool False on failure, true if success.
1108   */
1109  function update_post_meta($post_id, $meta_key, $meta_value, $prev_value = '') {
1110      // make sure meta is added to the post, not a revision
1111      if ( $the_post = wp_is_post_revision($post_id) )
1112          $post_id = $the_post;
1113  
1114      return update_metadata('post', $post_id, $meta_key, $meta_value, $prev_value);
1115  }
1116  
1117  /**
1118   * Delete everything from post meta matching meta key.
1119   *
1120   * @since 2.3.0
1121   * @uses $wpdb
1122   *
1123   * @param string $post_meta_key Key to search for when deleting.
1124   * @return bool Whether the post meta key was deleted from the database
1125   */
1126  function delete_post_meta_by_key($post_meta_key) {
1127      if ( !$post_meta_key )
1128          return false;
1129  
1130      global $wpdb;
1131      $post_ids = $wpdb->get_col($wpdb->prepare("SELECT DISTINCT post_id FROM $wpdb->postmeta WHERE meta_key = %s", $post_meta_key));
1132      if ( $post_ids ) {
1133          $postmetaids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = %s", $post_meta_key ) );
1134          $in = implode( ',', array_fill(1, count($postmetaids), '%d'));
1135          do_action( 'delete_postmeta', $postmetaids );
1136          $wpdb->query( $wpdb->prepare("DELETE FROM $wpdb->postmeta WHERE meta_id IN($in)", $postmetaids ));
1137          do_action( 'deleted_postmeta', $postmetaids );
1138          foreach ( $post_ids as $post_id )
1139              wp_cache_delete($post_id, 'post_meta');
1140          return true;
1141      }
1142      return false;
1143  }
1144  
1145  /**
1146   * Retrieve post meta fields, based on post ID.
1147   *
1148   * The post meta fields are retrieved from the cache, so the function is
1149   * optimized to be called more than once. It also applies to the functions, that
1150   * use this function.
1151   *
1152   * @since 1.2.0
1153   * @link http://codex.wordpress.org/Function_Reference/get_post_custom
1154   *
1155   * @uses $id Current Loop Post ID
1156   *
1157   * @param int $post_id post ID
1158   * @return array
1159   */
1160  function get_post_custom($post_id = 0) {
1161      global $id;
1162  
1163      if ( !$post_id )
1164          $post_id = (int) $id;
1165  
1166      $post_id = (int) $post_id;
1167  
1168      if ( ! wp_cache_get($post_id, 'post_meta') )
1169          update_postmeta_cache($post_id);
1170  
1171      return wp_cache_get($post_id, 'post_meta');
1172  }
1173  
1174  /**
1175   * Retrieve meta field names for a post.
1176   *
1177   * If there are no meta fields, then nothing (null) will be returned.
1178   *
1179   * @since 1.2.0
1180   * @link http://codex.wordpress.org/Function_Reference/get_post_custom_keys
1181   *
1182   * @param int $post_id post ID
1183   * @return array|null Either array of the keys, or null if keys could not be retrieved.
1184   */
1185  function get_post_custom_keys( $post_id = 0 ) {
1186      $custom = get_post_custom( $post_id );
1187  
1188      if ( !is_array($custom) )
1189          return;
1190  
1191      if ( $keys = array_keys($custom) )
1192          return $keys;
1193  }
1194  
1195  /**
1196   * Retrieve values for a custom post field.
1197   *
1198   * The parameters must not be considered optional. All of the post meta fields
1199   * will be retrieved and only the meta field key values returned.
1200   *
1201   * @since 1.2.0
1202   * @link http://codex.wordpress.org/Function_Reference/get_post_custom_values
1203   *
1204   * @param string $key Meta field key.
1205   * @param int $post_id Post ID
1206   * @return array Meta field values.
1207   */
1208  function get_post_custom_values( $key = '', $post_id = 0 ) {
1209      if ( !$key )
1210          return null;
1211  
1212      $custom = get_post_custom($post_id);
1213  
1214      return isset($custom[$key]) ? $custom[$key] : null;
1215  }
1216  
1217  /**
1218   * Check if post is sticky.
1219   *
1220   * Sticky posts should remain at the top of The Loop. If the post ID is not
1221   * given, then The Loop ID for the current post will be used.
1222   *
1223   * @since 2.7.0
1224   *
1225   * @param int $post_id Optional. Post ID.
1226   * @return bool Whether post is sticky.
1227   */
1228  function is_sticky($post_id = null) {
1229      global $id;
1230  
1231      $post_id = absint($post_id);
1232  
1233      if ( !$post_id )
1234          $post_id = absint($id);
1235  
1236      $stickies = get_option('sticky_posts');
1237  
1238      if ( !is_array($stickies) )
1239          return false;
1240  
1241      if ( in_array($post_id, $stickies) )
1242          return true;
1243  
1244      return false;
1245  }
1246  
1247  /**
1248   * Sanitize every post field.
1249   *
1250   * If the context is 'raw', then the post object or array will get minimal santization of the int fields.
1251   *
1252   * @since 2.3.0
1253   * @uses sanitize_post_field() Used to sanitize the fields.
1254   *
1255   * @param object|array $post The Post Object or Array
1256   * @param string $context Optional, default is 'display'. How to sanitize post fields.
1257   * @return object|array The now sanitized Post Object or Array (will be the same type as $post)
1258   */
1259  function sanitize_post($post, $context = 'display') {
1260      if ( is_object($post) ) {
1261          // Check if post already filtered for this context
1262          if ( isset($post->filter) && $context == $post->filter )
1263              return $post;
1264          if ( !isset($post->ID) )
1265              $post->ID = 0;
1266          foreach ( array_keys(get_object_vars($post)) as $field )
1267              $post->$field = sanitize_post_field($field, $post->$field, $post->ID, $context);
1268          $post->filter = $context;
1269      } else {
1270          // Check if post already filtered for this context
1271          if ( isset($post['filter']) && $context == $post['filter'] )
1272              return $post;
1273          if ( !isset($post['ID']) )
1274              $post['ID'] = 0;
1275          foreach ( array_keys($post) as $field )
1276              $post[$field] = sanitize_post_field($field, $post[$field], $post['ID'], $context);
1277          $post['filter'] = $context;
1278      }
1279      return $post;
1280  }
1281  
1282  /**
1283   * Sanitize post field based on context.
1284   *
1285   * Possible context values are:  'raw', 'edit', 'db', 'display', 'attribute' and 'js'. The
1286   * 'display' context is used by default. 'attribute' and 'js' contexts are treated like 'display'
1287   * when calling filters.
1288   *
1289   * @since 2.3.0
1290   * @uses apply_filters() Calls 'edit_$field' and '${field_no_prefix}_edit_pre' passing $value and
1291   *  $post_id if $context == 'edit' and field name prefix == 'post_'.
1292   *
1293   * @uses apply_filters() Calls 'edit_post_$field' passing $value and $post_id if $context == 'db'.
1294   * @uses apply_filters() Calls 'pre_$field' passing $value if $context == 'db' and field name prefix == 'post_'.
1295   * @uses apply_filters() Calls '${field}_pre' passing $value if $context == 'db' and field name prefix != 'post_'.
1296   *
1297   * @uses apply_filters() Calls '$field' passing $value, $post_id and $context if $context == anything
1298   *  other than 'raw', 'edit' and 'db' and field name prefix == 'post_'.
1299   * @uses apply_filters() Calls 'post_$field' passing $value if $context == anything other than 'raw',
1300   *  'edit' and 'db' and field name prefix != 'post_'.
1301   *
1302   * @param string $field The Post Object field name.
1303   * @param mixed $value The Post Object value.
1304   * @param int $post_id Post ID.
1305   * @param string $context How to sanitize post fields. Looks for 'raw', 'edit', 'db', 'display',
1306   *               'attribute' and 'js'.
1307   * @return mixed Sanitized value.
1308   */
1309  function sanitize_post_field($field, $value, $post_id, $context) {
1310      $int_fields = array('ID', 'post_parent', 'menu_order');
1311      if ( in_array($field, $int_fields) )
1312          $value = (int) $value;
1313  
1314      // Fields which contain arrays of ints.
1315      $array_int_fields = array( 'ancestors' );
1316      if ( in_array($field, $array_int_fields) ) {
1317          $value = array_map( 'absint', $value);
1318          return $value;
1319      }
1320  
1321      if ( 'raw' == $context )
1322          return $value;
1323  
1324      $prefixed = false;
1325      if ( false !== strpos($field, 'post_') ) {
1326          $prefixed = true;
1327          $field_no_prefix = str_replace('post_', '', $field);
1328      }
1329  
1330      if ( 'edit' == $context ) {
1331          $format_to_edit = array('post_content', 'post_excerpt', 'post_title', 'post_password');
1332  
1333          if ( $prefixed ) {
1334              $value = apply_filters("edit_$field", $value, $post_id);
1335              // Old school
1336              $value = apply_filters("$field_no_prefix}_edit_pre", $value, $post_id);
1337          } else {
1338              $value = apply_filters("edit_post_$field", $value, $post_id);
1339          }
1340  
1341          if ( in_array($field, $format_to_edit) ) {
1342              if ( 'post_content' == $field )
1343                  $value = format_to_edit($value, user_can_richedit());
1344              else
1345                  $value = format_to_edit($value);
1346          } else {
1347              $value = esc_attr($value);
1348          }
1349      } else if ( 'db' == $context ) {
1350          if ( $prefixed ) {
1351              $value = apply_filters("pre_$field", $value);
1352              $value = apply_filters("$field_no_prefix}_save_pre", $value);
1353          } else {
1354              $value = apply_filters("pre_post_$field", $value);
1355              $value = apply_filters("$field}_pre", $value);
1356          }
1357      } else {
1358          // Use display filters by default.
1359          if ( $prefixed )
1360              $value = apply_filters($field, $value, $post_id, $context);
1361          else
1362              $value = apply_filters("post_$field", $value, $post_id, $context);
1363      }
1364  
1365      if ( 'attribute' == $context )
1366          $value = esc_attr($value);
1367      else if ( 'js' == $context )
1368          $value = esc_js($value);
1369  
1370      return $value;
1371  }
1372  
1373  /**
1374   * Make a post sticky.
1375   *
1376   * Sticky posts should be displayed at the top of the front page.
1377   *
1378   * @since 2.7.0
1379   *
1380   * @param int $post_id Post ID.
1381   */
1382  function stick_post($post_id) {
1383      $stickies = get_option('sticky_posts');
1384  
1385      if ( !is_array($stickies) )
1386          $stickies = array($post_id);
1387  
1388      if ( ! in_array($post_id, $stickies) )
1389          $stickies[] = $post_id;
1390  
1391      update_option('sticky_posts', $stickies);
1392  }
1393  
1394  /**
1395   * Unstick a post.
1396   *
1397   * Sticky posts should be displayed at the top of the front page.
1398   *
1399   * @since 2.7.0
1400   *
1401   * @param int $post_id Post ID.
1402   */
1403  function unstick_post($post_id) {
1404      $stickies = get_option('sticky_posts');
1405  
1406      if ( !is_array($stickies) )
1407          return;
1408  
1409      if ( ! in_array($post_id, $stickies) )
1410          return;
1411  
1412      $offset = array_search($post_id, $stickies);
1413      if ( false === $offset )
1414          return;
1415  
1416      array_splice($stickies, $offset, 1);
1417  
1418      update_option('sticky_posts', $stickies);
1419  }
1420  
1421  /**
1422   * Count number of posts of a post type and is user has permissions to view.
1423   *
1424   * This function provides an efficient method of finding the amount of post's
1425   * type a blog has. Another method is to count the amount of items in
1426   * get_posts(), but that method has a lot of overhead with doing so. Therefore,
1427   * when developing for 2.5+, use this function instead.
1428   *
1429   * The $perm parameter checks for 'readable' value and if the user can read
1430   * private posts, it will display that for the user that is signed in.
1431   *
1432   * @since 2.5.0
1433   * @link http://codex.wordpress.org/Template_Tags/wp_count_posts
1434   *
1435   * @param string $type Optional. Post type to retrieve count
1436   * @param string $perm Optional. 'readable' or empty.
1437   * @return object Number of posts for each status
1438   */
1439  function wp_count_posts( $type = 'post', $perm = '' ) {
1440      global $wpdb;
1441  
1442      $user = wp_get_current_user();
1443  
1444      $cache_key = $type;
1445  
1446      $query = "SELECT post_status, COUNT( * ) AS num_posts FROM {$wpdb->posts} WHERE post_type = %s";
1447      if ( 'readable' == $perm && is_user_logged_in() ) {
1448          $post_type_object = get_post_type_object($type);
1449          if ( !current_user_can("read_private_{$post_type_object->capability_type}s") ) {
1450              $cache_key .= '_' . $perm . '_' . $user->ID;
1451              $query .= " AND (post_status != 'private' OR ( post_author = '$user->ID' AND post_status = 'private' ))";
1452          }
1453      }
1454      $query .= ' GROUP BY post_status';
1455  
1456      $count = wp_cache_get($cache_key, 'counts');
1457      if ( false !== $count )
1458          return $count;
1459  
1460      $count = $wpdb->get_results( $wpdb->prepare( $query, $type ), ARRAY_A );
1461  
1462      $stats = array();
1463      foreach ( get_post_stati() as $state )
1464          $stats[$state] = 0;
1465  
1466      foreach ( (array) $count as $row )
1467          $stats[$row['post_status']] = $row['num_posts'];
1468  
1469      $stats = (object) $stats;
1470      wp_cache_set($cache_key, $stats, 'counts');
1471  
1472      return $stats;
1473  }
1474  
1475  
1476  /**
1477   * Count number of attachments for the mime type(s).
1478   *
1479   * If you set the optional mime_type parameter, then an array will still be
1480   * returned, but will only have the item you are looking for. It does not give
1481   * you the number of attachments that are children of a post. You can get that
1482   * by counting the number of children that post has.
1483   *
1484   * @since 2.5.0
1485   *
1486   * @param string|array $mime_type Optional. Array or comma-separated list of MIME patterns.
1487   * @return array Number of posts for each mime type.
1488   */
1489  function wp_count_attachments( $mime_type = '' ) {
1490      global $wpdb;
1491  
1492      $and = wp_post_mime_type_where( $mime_type );
1493      $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 );
1494  
1495      $stats = array( );
1496      foreach( (array) $count as $row ) {
1497          $stats[$row['post_mime_type']] = $row['num_posts'];
1498      }
1499      $stats['trash'] = $wpdb->get_var( "SELECT COUNT( * ) FROM $wpdb->posts WHERE post_type = 'attachment' AND post_status = 'trash' $and");
1500  
1501      return (object) $stats;
1502  }
1503  
1504  /**
1505   * Check a MIME-Type against a list.
1506   *
1507   * If the wildcard_mime_types parameter is a string, it must be comma separated
1508   * list. If the real_mime_types is a string, it is also comma separated to
1509   * create the list.
1510   *
1511   * @since 2.5.0
1512   *
1513   * @param string|array $wildcard_mime_types e.g. audio/mpeg or image (same as image/*) or
1514   *  flash (same as *flash*).
1515   * @param string|array $real_mime_types post_mime_type values
1516   * @return array array(wildcard=>array(real types))
1517   */
1518  function wp_match_mime_types($wildcard_mime_types, $real_mime_types) {
1519      $matches = array();
1520      if ( is_string($wildcard_mime_types) )
1521          $wildcard_mime_types = array_map('trim', explode(',', $wildcard_mime_types));
1522      if ( is_string($real_mime_types) )
1523          $real_mime_types = array_map('trim', explode(',', $real_mime_types));
1524      $wild = '[-._a-z0-9]*';
1525      foreach ( (array) $wildcard_mime_types as $type ) {
1526          $type = str_replace('*', $wild, $type);
1527          $patternses[1][$type] = "^$type$";
1528          if ( false === strpos($type, '/') ) {
1529              $patternses[2][$type] = "^$type/";
1530              $patternses[3][$type] = $type;
1531          }
1532      }
1533      asort($patternses);
1534      foreach ( $patternses as $patterns )
1535          foreach ( $patterns as $type => $pattern )
1536              foreach ( (array) $real_mime_types as $real )
1537                  if ( preg_match("#$pattern#", $real) && ( empty($matches[$type]) || false === array_search($real, $matches[$type]) ) )
1538                      $matches[$type][] = $real;
1539      return $matches;
1540  }
1541  
1542  /**
1543   * Convert MIME types into SQL.
1544   *
1545   * @since 2.5.0
1546   *
1547   * @param string|array $mime_types List of mime types or comma separated string of mime types.
1548   * @return string The SQL AND clause for mime searching.
1549   */
1550  function wp_post_mime_type_where($post_mime_types) {
1551      $where = '';
1552      $wildcards = array('', '%', '%/%');
1553      if ( is_string($post_mime_types) )
1554          $post_mime_types = array_map('trim', explode(',', $post_mime_types));
1555      foreach ( (array) $post_mime_types as $mime_type ) {
1556          $mime_type = preg_replace('/\s/', '', $mime_type);
1557          $slashpos = strpos($mime_type, '/');
1558          if ( false !== $slashpos ) {
1559              $mime_group = preg_replace('/[^-*.a-zA-Z0-9]/', '', substr($mime_type, 0, $slashpos));
1560              $mime_subgroup = preg_replace('/[^-*.+a-zA-Z0-9]/', '', substr($mime_type, $slashpos + 1));
1561              if ( empty($mime_subgroup) )
1562                  $mime_subgroup = '*';
1563              else
1564                  $mime_subgroup = str_replace('/', '', $mime_subgroup);
1565              $mime_pattern = "$mime_group/$mime_subgroup";
1566          } else {
1567              $mime_pattern = preg_replace('/[^-*.a-zA-Z0-9]/', '', $mime_type);
1568              if ( false === strpos($mime_pattern, '*') )
1569                  $mime_pattern .= '/*';
1570          }
1571  
1572          $mime_pattern = preg_replace('/\*+/', '%', $mime_pattern);
1573  
1574          if ( in_array( $mime_type, $wildcards ) )
1575              return '';
1576  
1577          if ( false !== strpos($mime_pattern, '%') )
1578              $wheres[] = "post_mime_type LIKE '$mime_pattern'";
1579          else
1580              $wheres[] = "post_mime_type = '$mime_pattern'";
1581      }
1582      if ( !empty($wheres) )
1583          $where = ' AND (' . join(' OR ', $wheres) . ') ';
1584      return $where;
1585  }
1586  
1587  /**
1588   * Removes a post, attachment, or page.
1589   *
1590   * When the post and page goes, everything that is tied to it is deleted also.
1591   * This includes comments, post meta fields, and terms associated with the post.
1592   *
1593   * @since 1.0.0
1594   * @uses do_action() on 'delete_post' before deletion unless post type is 'attachment'.
1595   * @uses do_action() on 'deleted_post' after deletion unless post type is 'attachment'.
1596   * @uses wp_delete_attachment() if post type is 'attachment'.
1597   *
1598   * @param int $postid Post ID.
1599   * @param bool $force_delete Whether to bypass trash and force deletion
1600   * @return mixed False on failure
1601   */
1602  function wp_delete_post( $postid = 0, $force_delete = false ) {
1603      global $wpdb, $wp_rewrite;
1604  
1605      if ( !$post = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d", $postid)) )
1606          return $post;
1607  
1608      if ( !$force_delete && ( $post->post_type == 'post' || $post->post_type == 'page') && get_post_status( $postid ) != 'trash' && EMPTY_TRASH_DAYS > 0 )
1609              return wp_trash_post($postid);
1610  
1611      if ( $post->post_type == 'attachment' )
1612          return wp_delete_attachment( $postid, $force_delete );
1613  
1614      do_action('delete_post', $postid);
1615  
1616      delete_post_meta($postid,'_wp_trash_meta_status');
1617      delete_post_meta($postid,'_wp_trash_meta_time');
1618  
1619      wp_delete_object_term_relationships($postid, get_object_taxonomies($post->post_type));
1620  
1621      $parent_data = array( 'post_parent' => $post->post_parent );
1622      $parent_where = array( 'post_parent' => $postid );
1623  
1624      if ( 'page' == $post->post_type) {
1625           // if the page is defined in option page_on_front or post_for_posts,
1626          // adjust the corresponding options
1627          if ( get_option('page_on_front') == $postid ) {
1628              update_option('show_on_front', 'posts');
1629              delete_option('page_on_front');
1630          }
1631          if ( get_option('page_for_posts') == $postid ) {
1632              delete_option('page_for_posts');
1633          }
1634  
1635          // Point children of this page to its parent, also clean the cache of affected children
1636          $children_query = $wpdb->prepare("SELECT * FROM $wpdb->posts WHERE post_parent = %d AND post_type='page'", $postid);
1637          $children = $wpdb->get_results($children_query);
1638  
1639          $wpdb->update( $wpdb->posts, $parent_data, $parent_where + array( 'post_type' => 'page' ) );
1640      } else {
1641          unstick_post($postid);
1642      }
1643  
1644      // Do raw query.  wp_get_post_revisions() is filtered
1645      $revision_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'revision'", $postid ) );
1646      // Use wp_delete_post (via wp_delete_post_revision) again.  Ensures any meta/misplaced data gets cleaned up.
1647      foreach ( $revision_ids as $revision_id )
1648          wp_delete_post_revision( $revision_id );
1649  
1650      // Point all attachments to this post up one level
1651      $wpdb->update( $wpdb->posts, $parent_data, $parent_where + array( 'post_type' => 'attachment' ) );
1652  
1653      $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d", $postid ));
1654      if ( ! empty($comment_ids) ) {
1655          do_action( 'delete_comment', $comment_ids );
1656          $in_comment_ids = "'" . implode("', '", $comment_ids) . "'";
1657          $wpdb->query( "DELETE FROM $wpdb->comments WHERE comment_ID IN($in_comment_ids)" );
1658          do_action( 'deleted_comment', $comment_ids );
1659      }
1660  
1661      $post_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d ", $postid ));
1662      if ( !empty($post_meta_ids) ) {
1663          do_action( 'delete_postmeta', $post_meta_ids );
1664          $in_post_meta_ids = "'" . implode("', '", $post_meta_ids) . "'";
1665          $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_id IN($in_post_meta_ids)" );
1666          do_action( 'deleted_postmeta', $post_meta_ids );
1667      }
1668  
1669      do_action( 'delete_post', $postid );
1670      $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->posts WHERE ID = %d", $postid ));
1671      do_action( 'deleted_post', $postid );
1672  
1673      if ( 'page' == $post->post_type ) {
1674          clean_page_cache($postid);
1675  
1676          foreach ( (array) $children as $child )
1677              clean_page_cache($child->ID);
1678  
1679          $wp_rewrite->flush_rules(false);
1680      } else {
1681          clean_post_cache($postid);
1682      }
1683  
1684      wp_clear_scheduled_hook('publish_future_post', array( $postid ) );
1685  
1686      do_action('deleted_post', $postid);
1687  
1688      return $post;
1689  }
1690  
1691  /**
1692   * Moves a post or page to the Trash
1693   *
1694   * @since 2.9.0
1695   * @uses do_action() on 'trash_post' before trashing
1696   * @uses do_action() on 'trashed_post' after trashing
1697   *
1698   * @param int $postid Post ID.
1699   * @return mixed False on failure
1700   */
1701  function wp_trash_post($post_id = 0) {
1702      if ( EMPTY_TRASH_DAYS == 0 )
1703          return wp_delete_post($post_id);
1704  
1705      if ( !$post = wp_get_single_post($post_id, ARRAY_A) )
1706          return $post;
1707  
1708      if ( $post['post_status'] == 'trash' )
1709          return false;
1710  
1711      do_action('trash_post', $post_id);
1712  
1713      add_post_meta($post_id,'_wp_trash_meta_status', $post['post_status']);
1714      add_post_meta($post_id,'_wp_trash_meta_time', time());
1715  
1716      $post['post_status'] = 'trash';
1717      wp_insert_post($post);
1718  
1719      wp_trash_post_comments($post_id);
1720  
1721      do_action('trashed_post', $post_id);
1722  
1723      return $post;
1724  }
1725  
1726  /**
1727   * Restores a post or page from the Trash
1728   *
1729   * @since 2.9.0
1730   * @uses do_action() on 'untrash_post' before undeletion
1731   * @uses do_action() on 'untrashed_post' after undeletion
1732   *
1733   * @param int $postid Post ID.
1734   * @return mixed False on failure
1735   */
1736  function wp_untrash_post($post_id = 0) {
1737      if ( !$post = wp_get_single_post($post_id, ARRAY_A) )
1738          return $post;
1739  
1740      if ( $post['post_status'] != 'trash' )
1741          return false;
1742  
1743      do_action('untrash_post', $post_id);
1744  
1745      $post_status = get_post_meta($post_id, '_wp_trash_meta_status', true);
1746  
1747      $post['post_status'] = $post_status;
1748  
1749      delete_post_meta($post_id, '_wp_trash_meta_status');
1750      delete_post_meta($post_id, '_wp_trash_meta_time');
1751  
1752      wp_insert_post($post);
1753  
1754      wp_untrash_post_comments($post_id);
1755  
1756      do_action('untrashed_post', $post_id);
1757  
1758      return $post;
1759  }
1760  
1761  /**
1762   * Moves comments for a post to the trash
1763   *
1764   * @since 2.9.0
1765   * @uses do_action() on 'trash_post_comments' before trashing
1766   * @uses do_action() on 'trashed_post_comments' after trashing
1767   *
1768   * @param int $post Post ID or object.
1769   * @return mixed False on failure
1770   */
1771  function wp_trash_post_comments($post = null) {
1772      global $wpdb;
1773  
1774      $post = get_post($post);
1775      if ( empty($post) )
1776          return;
1777  
1778      $post_id = $post->ID;
1779  
1780      do_action('trash_post_comments', $post_id);
1781  
1782      $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_ID, comment_approved FROM $wpdb->comments WHERE comment_post_ID = %d", $post_id) );
1783      if ( empty($comments) )
1784          return;
1785  
1786      // Cache current status for each comment
1787      $statuses = array();
1788      foreach ( $comments as $comment )
1789          $statuses[$comment->comment_ID] = $comment->comment_approved;
1790      add_post_meta($post_id, '_wp_trash_meta_comments_status', $statuses);
1791  
1792      // Set status for all comments to post-trashed
1793      $result = $wpdb->update($wpdb->comments, array('comment_approved' => 'post-trashed'), array('comment_post_ID' => $post_id));
1794  
1795      clean_comment_cache( array_keys($statuses) );
1796  
1797      do_action('trashed_post_comments', $post_id, $statuses);
1798  
1799      return $result;
1800  }
1801  
1802  /**
1803   * Restore comments for a post from the trash
1804   *
1805   * @since 2.9.0
1806   * @uses do_action() on 'untrash_post_comments' before trashing
1807   * @uses do_action() on 'untrashed_post_comments' after trashing
1808   *
1809   * @param int $post Post ID or object.
1810   * @return mixed False on failure
1811   */
1812  function wp_untrash_post_comments($post = null) {
1813      global $wpdb;
1814  
1815      $post = get_post($post);
1816      if ( empty($post) )
1817          return;
1818  
1819      $post_id = $post->ID;
1820  
1821      $statuses = get_post_meta($post_id, '_wp_trash_meta_comments_status', true);
1822  
1823      if ( empty($statuses) )
1824          return true;
1825  
1826      do_action('untrash_post_comments', $post_id);
1827  
1828      // Restore each comment to its original status
1829      $group_by_status = array();
1830      foreach ( $statuses as $comment_id => $comment_status )
1831          $group_by_status[$comment_status][] = $comment_id;
1832  
1833      foreach ( $group_by_status as $status => $comments ) {
1834          // Sanity check. This shouldn't happen.
1835          if ( 'post-trashed' == $status )
1836              $status = '0';
1837          $comments_in = implode( "', '", $comments );
1838          $wpdb->query( "UPDATE $wpdb->comments SET comment_approved = '$status' WHERE comment_ID IN ('" . $comments_in . "')" );
1839      }
1840  
1841      clean_comment_cache( array_keys($statuses) );
1842  
1843      delete_post_meta($post_id, '_wp_trash_meta_comments_status');
1844  
1845      do_action('untrashed_post_comments', $post_id);
1846  }
1847  
1848  /**
1849   * Retrieve the list of categories for a post.
1850   *
1851   * Compatibility layer for themes and plugins. Also an easy layer of abstraction
1852   * away from the complexity of the taxonomy layer.
1853   *
1854   * @since 2.1.0
1855   *
1856   * @uses wp_get_object_terms() Retrieves the categories. Args details can be found here.
1857   *
1858   * @param int $post_id Optional. The Post ID.
1859   * @param array $args Optional. Overwrite the defaults.
1860   * @return array
1861   */
1862  function wp_get_post_categories( $post_id = 0, $args = array() ) {
1863      $post_id = (int) $post_id;
1864  
1865      $defaults = array('fields' => 'ids');
1866      $args = wp_parse_args( $args, $defaults );
1867  
1868      $cats = wp_get_object_terms($post_id, 'category', $args);
1869      return $cats;
1870  }
1871  
1872  /**
1873   * Retrieve the tags for a post.
1874   *
1875   * There is only one default for this function, called 'fields' and by default
1876   * is set to 'all'. There are other defaults that can be overridden in
1877   * {@link wp_get_object_terms()}.
1878   *
1879   * @package WordPress
1880   * @subpackage Post
1881   * @since 2.3.0
1882   *
1883   * @uses wp_get_object_terms() Gets the tags for returning. Args can be found here
1884   *
1885   * @param int $post_id Optional. The Post ID
1886   * @param array $args Optional. Overwrite the defaults
1887   * @return array List of post tags.
1888   */
1889  function wp_get_post_tags( $post_id = 0, $args = array() ) {
1890      return wp_get_post_terms( $post_id, 'post_tag', $args);
1891  }
1892  
1893  /**
1894   * Retrieve the terms for a post.
1895   *
1896   * There is only one default for this function, called 'fields' and by default
1897   * is set to 'all'. There are other defaults that can be overridden in
1898   * {@link wp_get_object_terms()}.
1899   *
1900   * @package WordPress
1901   * @subpackage Post
1902   * @since 2.8.0
1903   *
1904   * @uses wp_get_object_terms() Gets the tags for returning. Args can be found here
1905   *
1906   * @param int $post_id Optional. The Post ID
1907   * @param string $taxonomy The taxonomy for which to retrieve terms. Defaults to post_tag.
1908   * @param array $args Optional. Overwrite the defaults
1909   * @return array List of post tags.
1910   */
1911  function wp_get_post_terms( $post_id = 0, $taxonomy = 'post_tag', $args = array() ) {
1912      $post_id = (int) $post_id;
1913  
1914      $defaults = array('fields' => 'all');
1915      $args = wp_parse_args( $args, $defaults );
1916  
1917      $tags = wp_get_object_terms($post_id, $taxonomy, $args);
1918  
1919      return $tags;
1920  }
1921  
1922  /**
1923   * Retrieve number of recent posts.
1924   *
1925   * @since 1.0.0
1926   * @uses $wpdb
1927   *
1928   * @param int $num Optional, default is 10. Number of posts to get.
1929   * @return array List of posts.
1930   */
1931  function wp_get_recent_posts($num = 10) {
1932      global $wpdb;
1933  
1934      // Set the limit clause, if we got a limit
1935      $num = (int) $num;
1936      if ( $num ) {
1937          $limit = "LIMIT $num";
1938      }
1939  
1940      $sql = "SELECT * FROM $wpdb->posts WHERE post_type = 'post' AND post_status IN ( 'draft', 'publish', 'future', 'pending', 'private' ) ORDER BY post_date DESC $limit";
1941      $result = $wpdb->get_results($sql, ARRAY_A);
1942  
1943      return $result ? $result : array();
1944  }
1945  
1946  /**
1947   * Retrieve a single post, based on post ID.
1948   *
1949   * Has categories in 'post_category' property or key. Has tags in 'tags_input'
1950   * property or key.
1951   *
1952   * @since 1.0.0
1953   *
1954   * @param int $postid Post ID.
1955   * @param string $mode How to return result, either OBJECT, ARRAY_N, or ARRAY_A.
1956   * @return object|array Post object or array holding post contents and information
1957   */
1958  function wp_get_single_post($postid = 0, $mode = OBJECT) {
1959      $postid = (int) $postid;
1960  
1961      $post = get_post($postid, $mode);
1962  
1963      // Set categories and tags
1964      if($mode == OBJECT) {
1965          $post->post_category = wp_get_post_categories($postid);
1966          $post->tags_input = wp_get_post_tags($postid, array('fields' => 'names'));
1967      }
1968      else {
1969          $post['post_category'] = wp_get_post_categories($postid);
1970          $post['tags_input'] = wp_get_post_tags($postid, array('fields' => 'names'));
1971      }
1972  
1973      return $post;
1974  }
1975  
1976  /**
1977   * Insert a post.
1978   *
1979   * If the $postarr parameter has 'ID' set to a value, then post will be updated.
1980   *
1981   * You can set the post date manually, but setting the values for 'post_date'
1982   * and 'post_date_gmt' keys. You can close the comments or open the comments by
1983   * setting the value for 'comment_status' key.
1984   *
1985   * The defaults for the parameter $postarr are:
1986   *     'post_status'   - Default is 'draft'.
1987   *     'post_type'     - Default is 'post'.
1988   *     'post_author'   - Default is current user ID ($user_ID). The ID of the user who added the post.
1989   *     'ping_status'   - Default is the value in 'default_ping_status' option.
1990   *                       Whether the attachment can accept pings.
1991   *     'post_parent'   - Default is 0. Set this for the post it belongs to, if any.
1992   *     'menu_order'    - Default is 0. The order it is displayed.
1993   *     'to_ping'       - Whether to ping.
1994   *     'pinged'        - Default is empty string.
1995   *     'post_password' - Default is empty string. The password to access the attachment.
1996   *     'guid'          - Global Unique ID for referencing the attachment.
1997   *     'post_content_filtered' - Post content filtered.
1998   *     'post_excerpt'  - Post excerpt.
1999   *
2000   * @since 1.0.0
2001   * @link http://core.trac.wordpress.org/ticket/9084 Bug report on 'wp_insert_post_data' filter.
2002   * @uses $wpdb
2003   * @uses $wp_rewrite
2004   * @uses $user_ID
2005   *
2006   * @uses do_action() Calls 'pre_post_update' on post ID if this is an update.
2007   * @uses do_action() Calls 'edit_post' action on post ID and post data if this is an update.
2008   * @uses do_action() Calls 'save_post' and 'wp_insert_post' on post id and post data just before
2009   *                   returning.
2010   *
2011   * @uses apply_filters() Calls 'wp_insert_post_data' passing $data, $postarr prior to database
2012   *                       update or insert.
2013   * @uses wp_transition_post_status()
2014   *
2015   * @param array $postarr Optional. Overrides defaults.
2016   * @param bool $wp_error Optional. Allow return of WP_Error on failure.
2017   * @return int|WP_Error The value 0 or WP_Error on failure. The post ID on success.
2018   */
2019  function wp_insert_post($postarr = array(), $wp_error = false) {
2020      global $wpdb, $wp_rewrite, $user_ID;
2021  
2022      $defaults = array('post_status' => 'draft', 'post_type' => 'post', 'post_author' => $user_ID,
2023          'ping_status' => get_option('default_ping_status'), 'post_parent' => 0,
2024          'menu_order' => 0, 'to_ping' =>  '', 'pinged' => '', 'post_password' => '',
2025          'guid' => '', 'post_content_filtered' => '', 'post_excerpt' => '', 'import_id' => 0,
2026          'post_content' => '', 'post_title' => '');
2027  
2028      $postarr = wp_parse_args($postarr, $defaults);
2029      $postarr = sanitize_post($postarr, 'db');
2030  
2031      // export array as variables
2032      extract($postarr, EXTR_SKIP);
2033  
2034      // Are we updating or creating?
2035      $update = false;
2036      if ( !empty($ID) ) {
2037          $update = true;
2038          $previous_status = get_post_field('post_status', $ID);
2039      } else {
2040          $previous_status = 'new';
2041      }
2042  
2043      if ( ('' == $post_content) && ('' == $post_title) && ('' == $post_excerpt) && ('attachment' != $post_type) ) {
2044          if ( $wp_error )
2045              return new WP_Error('empty_content', __('Content, title, and excerpt are empty.'));
2046          else
2047              return 0;
2048      }
2049  
2050      if ( empty($post_type) )
2051          $post_type = 'post';
2052  
2053      // Make sure we set a valid category.
2054      if ( empty($post_category) || 0 == count($post_category) || !is_array($post_category) ) {
2055          // 'post' requires at least one category.
2056          if ( 'post' == $post_type )
2057              $post_category = array( get_option('default_category') );
2058          else
2059              $post_category = array();
2060      }
2061  
2062      // Set the default tag list
2063      if ( !isset($tags_input) )
2064          $tags_input = array();
2065  
2066      if ( empty($post_author) )
2067          $post_author = $user_ID;
2068  
2069      if ( empty($post_status) )
2070          $post_status = 'draft';
2071  
2072      $post_ID = 0;
2073  
2074      // Get the post ID and GUID
2075      if ( $update ) {
2076          $post_ID = (int) $ID;
2077          $guid = get_post_field( 'guid', $post_ID );
2078      }
2079  
2080      // Don't allow contributors to set to set the post slug for pending review posts
2081      if ( 'pending' == $post_status && !current_user_can( 'publish_posts' ) )
2082          $post_name = '';
2083  
2084      // Create a valid post name.  Drafts and pending posts are allowed to have an empty
2085      // post name.
2086      if ( empty($post_name) ) {
2087          if ( !in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) )
2088              $post_name = sanitize_title($post_title);
2089          else
2090              $post_name = '';
2091      } else {
2092          $post_name = sanitize_title($post_name);
2093      }
2094  
2095      // If the post date is empty (due to having been new or a draft) and status is not 'draft' or 'pending', set date to now
2096      if ( empty($post_date) || '0000-00-00 00:00:00' == $post_date )
2097          $post_date = current_time('mysql');
2098  
2099      if ( empty($post_date_gmt) || '0000-00-00 00:00:00' == $post_date_gmt ) {
2100          if ( !in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) )
2101              $post_date_gmt = get_gmt_from_date($post_date);
2102          else
2103              $post_date_gmt = '0000-00-00 00:00:00';
2104      }
2105  
2106      if ( $update || '0000-00-00 00:00:00' == $post_date ) {
2107          $post_modified     = current_time( 'mysql' );
2108          $post_modified_gmt = current_time( 'mysql', 1 );
2109      } else {
2110          $post_modified     = $post_date;
2111          $post_modified_gmt = $post_date_gmt;
2112      }
2113  
2114      if ( 'publish' == $post_status ) {
2115          $now = gmdate('Y-m-d H:i:59');
2116          if ( mysql2date('U', $post_date_gmt, false) > mysql2date('U', $now, false) )
2117              $post_status = 'future';
2118      }
2119  
2120      if ( empty($comment_status) ) {
2121          if ( $update )
2122              $comment_status = 'closed';
2123          else
2124              $comment_status = get_option('default_comment_status');
2125      }
2126      if ( empty($ping_status) )
2127          $ping_status = get_option('default_ping_status');
2128  
2129      if ( isset($to_ping) )
2130          $to_ping = preg_replace('|\s+|', "\n", $to_ping);
2131      else
2132          $to_ping = '';
2133  
2134      if ( ! isset($pinged) )
2135          $pinged = '';
2136  
2137      if ( isset($post_parent) )
2138          $post_parent = (int) $post_parent;
2139      else
2140          $post_parent = 0;
2141  
2142      if ( !empty($post_ID) ) {
2143          if ( $post_parent == $post_ID ) {
2144              // Post can't be its own parent
2145              $post_parent = 0;
2146          } elseif ( !empty($post_parent) ) {
2147              $parent_post = get_post($post_parent);
2148              // Check for circular dependency
2149              if ( $parent_post->post_parent == $post_ID )
2150                  $post_parent = 0;
2151          }
2152      }
2153  
2154      if ( isset($menu_order) )
2155          $menu_order = (int) $menu_order;
2156      else
2157          $menu_order = 0;
2158  
2159      if ( !isset($post_password) || 'private' == $post_status )
2160          $post_password = '';
2161  
2162      $post_name = wp_unique_post_slug($post_name, $post_ID, $post_status, $post_type, $post_parent);
2163  
2164      // expected_slashed (everything!)
2165      $data = compact( array( 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_content_filtered', 'post_title', 'post_excerpt', 'post_status', 'post_type', 'comment_status', 'ping_status', 'post_password', 'post_name', 'to_ping', 'pinged', 'post_modified', 'post_modified_gmt', 'post_parent', 'menu_order', 'guid' ) );
2166      $data = apply_filters('wp_insert_post_data', $data, $postarr);
2167      $data = stripslashes_deep( $data );
2168      $where = array( 'ID' => $post_ID );
2169  
2170      if ($update) {
2171          do_action( 'pre_post_update', $post_ID );
2172          if ( false === $wpdb->update( $wpdb->posts, $data, $where ) ) {
2173              if ( $wp_error )
2174                  return new WP_Error('db_update_error', __('Could not update post in the database'), $wpdb->last_error);
2175              else
2176                  return 0;
2177          }
2178      } else {
2179          if ( isset($post_mime_type) )
2180              $data['post_mime_type'] = stripslashes( $post_mime_type ); // This isn't in the update
2181          // If there is a suggested ID, use it if not already present
2182          if ( !empty($import_id) ) {
2183              $import_id = (int) $import_id;
2184              if ( ! $wpdb->get_var( $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE ID = %d", $import_id) ) ) {
2185                  $data['ID'] = $import_id;
2186              }
2187          }
2188          if ( false === $wpdb->insert( $wpdb->posts, $data ) ) {
2189              if ( $wp_error )
2190                  return new WP_Error('db_insert_error', __('Could not insert post into the database'), $wpdb->last_error);
2191              else
2192                  return 0;
2193          }
2194          $post_ID = (int) $wpdb->insert_id;
2195  
2196          // use the newly generated $post_ID
2197          $where = array( 'ID' => $post_ID );
2198      }
2199  
2200      if ( empty($data['post_name']) && !in_array( $data['post_status'], array( 'draft', 'pending', 'auto-draft' ) ) ) {
2201          $data['post_name'] = sanitize_title($data['post_title'], $post_ID);
2202          $wpdb->update( $wpdb->posts, array( 'post_name' => $data['post_name'] ), $where );
2203      }
2204  
2205      wp_set_post_categories( $post_ID, $post_category );
2206      // old-style tags_input
2207      if ( !empty($tags_input) )
2208          wp_set_post_tags( $post_ID, $tags_input );
2209      // new-style support for all tag-like taxonomies
2210      if ( !empty($tax_input) ) {
2211          foreach ( $tax_input as $taxonomy => $tags ) {
2212              $taxonomy_obj = get_taxonomy($taxonomy);
2213              if ( current_user_can($taxonomy_obj->assign_cap) )
2214                  wp_set_post_terms( $post_ID, $tags, $taxonomy );
2215          }
2216      }
2217  
2218      $current_guid = get_post_field( 'guid', $post_ID );
2219  
2220      if ( 'page' == $data['post_type'] )
2221          clean_page_cache($post_ID);
2222      else
2223          clean_post_cache($post_ID);
2224  
2225      // Set GUID
2226      if ( !$update && '' == $current_guid )
2227          $wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post_ID ) ), $where );
2228  
2229      $post = get_post($post_ID);
2230  
2231      if ( !empty($page_template) && 'page' == $data['post_type'] ) {
2232          $post->page_template = $page_template;
2233          $page_templates = get_page_templates();
2234          if ( 'default' != $page_template && !in_array($page_template, $page_templates) ) {
2235              if ( $wp_error )
2236                  return new WP_Error('invalid_page_template', __('The page template is invalid.'));
2237              else
2238                  return 0;
2239          }
2240          update_post_meta($post_ID, '_wp_page_template',  $page_template);
2241      }
2242  
2243      wp_transition_post_status($data['post_status'], $previous_status, $post);
2244  
2245      if ( $update)
2246          do_action('edit_post', $post_ID, $post);
2247  
2248      do_action('save_post', $post_ID, $post);
2249      do_action('wp_insert_post', $post_ID, $post);
2250  
2251      return $post_ID;
2252  }
2253  
2254  /**
2255   * Update a post with new post data.
2256   *
2257   * The date does not have to be set for drafts. You can set the date and it will
2258   * not be overridden.
2259   *
2260   * @since 1.0.0
2261   *
2262   * @param array|object $postarr Post data. Arrays are expected to be escaped, objects are not.
2263   * @return int 0 on failure, Post ID on success.
2264   */
2265  function wp_update_post($postarr = array()) {
2266      if ( is_object($postarr) ) {
2267          // non-escaped post was passed
2268          $postarr = get_object_vars($postarr);
2269          $postarr = add_magic_quotes($postarr);
2270      }
2271  
2272      // First, get all of the original fields
2273      $post = wp_get_single_post($postarr['ID'], ARRAY_A);
2274  
2275      // Escape data pulled from DB.
2276      $post = add_magic_quotes($post);
2277  
2278      // Passed post category list overwrites existing category list if not empty.
2279      if ( isset($postarr['post_category']) && is_array($postarr['post_category'])
2280               && 0 != count($postarr['post_category']) )
2281          $post_cats = $postarr['post_category'];
2282      else
2283          $post_cats = $post['post_category'];
2284  
2285      // Drafts shouldn't be assigned a date unless explicitly done so by the user
2286      if ( in_array($post['post_status'], array('draft', 'pending', 'auto-draft')) && empty($postarr['edit_date']) &&
2287               ('0000-00-00 00:00:00' == $post['post_date_gmt']) )
2288          $clear_date = true;
2289      else
2290          $clear_date = false;
2291  
2292      // Merge old and new fields with new fields overwriting old ones.
2293      $postarr = array_merge($post, $postarr);
2294      $postarr['post_category'] = $post_cats;
2295      if ( $clear_date ) {
2296          $postarr['post_date'] = current_time('mysql');
2297          $postarr['post_date_gmt'] = '';
2298      }
2299  
2300      if ($postarr['post_type'] == 'attachment')
2301          return wp_insert_attachment($postarr);
2302  
2303      return wp_insert_post($postarr);
2304  }
2305  
2306  /**
2307   * Publish a post by transitioning the post status.
2308   *
2309   * @since 2.1.0
2310   * @uses $wpdb
2311   * @uses do_action() Calls 'edit_post', 'save_post', and 'wp_insert_post' on post_id and post data.
2312   *
2313   * @param int $post_id Post ID.
2314   * @return null
2315   */
2316  function wp_publish_post($post_id) {
2317      global $wpdb;
2318  
2319      $post = get_post($post_id);
2320  
2321      if ( empty($post) )
2322          return;
2323  
2324      if ( 'publish' == $post->post_status )
2325          return;
2326  
2327      $wpdb->update( $wpdb->posts, array( 'post_status' => 'publish' ), array( 'ID' => $post_id ) );
2328  
2329      $old_status = $post->post_status;
2330      $post->post_status = 'publish';
2331      wp_transition_post_status('publish', $old_status, $post);
2332  
2333      // Update counts for the post's terms.
2334      foreach ( (array) get_object_taxonomies('post') as $taxonomy ) {
2335          $tt_ids = wp_get_object_terms($post_id, $taxonomy, array('fields' => 'tt_ids'));
2336          wp_update_term_count($tt_ids, $taxonomy);
2337      }
2338  
2339      do_action('edit_post', $post_id, $post);
2340      do_action('save_post', $post_id, $post);
2341      do_action('wp_insert_post', $post_id, $post);
2342  }
2343  
2344  /**
2345   * Publish future post and make sure post ID has future post status.
2346   *
2347   * Invoked by cron 'publish_future_post' event. This safeguard prevents cron
2348   * from publishing drafts, etc.
2349   *
2350   * @since 2.5.0
2351   *
2352   * @param int $post_id Post ID.
2353   * @return null Nothing is returned. Which can mean that no action is required or post was published.
2354   */
2355  function check_and_publish_future_post($post_id) {
2356  
2357      $post = get_post($post_id);
2358  
2359      if ( empty($post) )
2360          return;
2361  
2362      if ( 'future' != $post->post_status )
2363          return;
2364  
2365      $time = strtotime( $post->post_date_gmt . ' GMT' );
2366  
2367      if ( $time > time() ) { // Uh oh, someone jumped the gun!
2368          wp_clear_scheduled_hook( 'publish_future_post', array( $post_id ) ); // clear anything else in the system
2369          wp_schedule_single_event( $time, 'publish_future_post', array( $post_id ) );
2370          return;
2371      }
2372  
2373      return wp_publish_post($post_id);
2374  }
2375  
2376  
2377  /**
2378   * Computes a unique slug for the post, when given the desired slug and some post details.
2379   *
2380   * @global wpdb $wpdb
2381   * @global WP_Rewrite $wp_rewrite
2382   * @param string $slug the desired slug (post_name)
2383   * @param integer $post_ID
2384   * @param string $post_status no uniqueness checks are made if the post is still draft or pending
2385   * @param string $post_type
2386   * @param integer $post_parent
2387   * @return string unique slug for the post, based on $post_name (with a -1, -2, etc. suffix)
2388   */
2389  function wp_unique_post_slug( $slug, $post_ID, $post_status, $post_type, $post_parent ) {
2390      if ( in_array( $post_status, array( 'draft', 'pending', 'auto-draft' ) ) )
2391          return $slug;
2392  
2393      global $wpdb, $wp_rewrite;
2394  
2395      $feeds = $wp_rewrite->feeds;
2396      if ( ! is_array( $feeds ) )
2397          $feeds = array();
2398  
2399      $hierarchical_post_types = apply_filters( 'hierarchical_post_types', array( 'page' ) );
2400      if ( 'attachment' == $post_type ) {
2401          // Attachment slugs must be unique across all types.
2402          $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND ID != %d LIMIT 1";
2403          $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_ID ) );
2404  
2405          if ( $post_name_check || in_array( $slug, $feeds ) ) {
2406              $suffix = 2;
2407              do {
2408                  $alt_post_name = substr ($slug, 0, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
2409                  $post_name_check = $wpdb->get_var( $wpdb->prepare($check_sql, $alt_post_name, $post_ID ) );
2410                  $suffix++;
2411              } while ( $post_name_check );
2412              $slug = $alt_post_name;
2413          }
2414      } elseif ( in_array( $post_type, $hierarchical_post_types ) ) {
2415          // Page slugs must be unique within their own trees. Pages are in a separate
2416          // namespace than posts so page slugs are allowed to overlap post slugs.
2417          $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type IN ( '" . implode( "', '", esc_sql( $hierarchical_post_types ) ) . "' ) AND ID != %d AND post_parent = %d LIMIT 1";
2418          $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_ID, $post_parent ) );
2419  
2420          if ( $post_name_check || in_array( $slug, $feeds ) || preg_match( '@^(page)?\d+$@', $slug ) ) {
2421              $suffix = 2;
2422              do {
2423                  $alt_post_name = substr( $slug, 0, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
2424                  $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_ID, $post_parent ) );
2425                  $suffix++;
2426              } while ( $post_name_check );
2427              $slug = $alt_post_name;
2428          }
2429      } else {
2430          // Post slugs must be unique across all posts.
2431          $check_sql = "SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d LIMIT 1";
2432          $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $slug, $post_type, $post_ID ) );
2433  
2434          if ( $post_name_check || in_array( $slug, $feeds ) ) {
2435              $suffix = 2;
2436              do {
2437                  $alt_post_name = substr( $slug, 0, 200 - ( strlen( $suffix ) + 1 ) ) . "-$suffix";
2438                  $post_name_check = $wpdb->get_var( $wpdb->prepare( $check_sql, $alt_post_name, $post_type, $post_ID ) );
2439                  $suffix++;
2440              } while ( $post_name_check );
2441              $slug = $alt_post_name;
2442          }
2443      }
2444  
2445      return $slug;
2446  }
2447  
2448  /**
2449   * Adds tags to a post.
2450   *
2451   * @uses wp_set_post_tags() Same first two parameters, but the last parameter is always set to true.
2452   *
2453   * @package WordPress
2454   * @subpackage Post
2455   * @since 2.3.0
2456   *
2457   * @param int $post_id Post ID
2458   * @param string $tags The tags to set for the post, separated by commas.
2459   * @return bool|null Will return false if $post_id is not an integer or is 0. Will return null otherwise
2460   */
2461  function wp_add_post_tags($post_id = 0, $tags = '') {
2462      return wp_set_post_tags($post_id, $tags, true);
2463  }
2464  
2465  
2466  /**
2467   * Set the tags for a post.
2468   *
2469   * @since 2.3.0
2470   * @uses wp_set_object_terms() Sets the tags for the post.
2471   *
2472   * @param int $post_id Post ID.
2473   * @param string $tags The tags to set for the post, separated by commas.
2474   * @param bool $append If true, don't delete existing tags, just add on. If false, replace the tags with the new tags.
2475   * @return bool|null Will return false if $post_id is not an integer or is 0. Will return null otherwise
2476   */
2477  function wp_set_post_tags( $post_id = 0, $tags = '', $append = false ) {
2478      return wp_set_post_terms( $post_id, $tags, 'post_tag', $append);
2479  }
2480  
2481  /**
2482   * Set the terms for a post.
2483   *
2484   * @since 2.8.0
2485   * @uses wp_set_object_terms() Sets the tags for the post.
2486   *
2487   * @param int $post_id Post ID.
2488   * @param string $tags The tags to set for the post, separated by commas.
2489   * @param bool $append If true, don't delete existing tags, just add on. If false, replace the tags with the new tags.
2490   * @return bool|null Will return false if $post_id is not an integer or is 0. Will return null otherwise
2491   */
2492  function wp_set_post_terms( $post_id = 0, $tags = '', $taxonomy = 'post_tag', $append = false ) {
2493      $post_id = (int) $post_id;
2494  
2495      if ( !$post_id )
2496          return false;
2497  
2498      if ( empty($tags) )
2499          $tags = array();
2500  
2501      $tags = is_array($tags) ? $tags : explode( ',', trim($tags, " \n\t\r\0\x0B,") );
2502  
2503      // Hierarchical taxonomies must always pass IDs rather than names so that children with the same
2504      // names but different parents aren't confused.
2505      $taxonomy_obj = get_taxonomy( $taxonomy );
2506      if ( $taxonomy_obj->hierarchical ) {
2507          $tags = array_map( 'intval', $tags );
2508          $tags = array_unique( $tags );
2509      }
2510  
2511      wp_set_object_terms($post_id, $tags, $taxonomy, $append);
2512  }
2513  
2514  /**
2515   * Set categories for a post.
2516   *
2517   * If the post categories parameter is not set, then the default category is
2518   * going used.
2519   *
2520   * @since 2.1.0
2521   *
2522   * @param int $post_ID Post ID.
2523   * @param array $post_categories Optional. List of categories.
2524   * @return bool|mixed
2525   */
2526  function wp_set_post_categories($post_ID = 0, $post_categories = array()) {
2527      $post_ID = (int) $post_ID;
2528      $post_type = get_post_type( $post_ID );
2529      // If $post_categories isn't already an array, make it one:
2530      if ( !is_array($post_categories) || 0 == count($post_categories) || empty($post_categories) ) {
2531          if ( 'post' == $post_type )
2532              $post_categories = array( get_option('default_category') );
2533          else
2534              $post_categories = array();
2535      } else if ( 1 == count($post_categories) && '' == $post_categories[0] ) {
2536          return true;
2537      }
2538  
2539      if ( !empty($post_categories) ) {
2540          $post_categories = array_map('intval', $post_categories);
2541          $post_categories = array_unique($post_categories);
2542      }
2543  
2544      return wp_set_object_terms($post_ID, $post_categories, 'category');
2545  }
2546  
2547  /**
2548   * Transition the post status of a post.
2549   *
2550   * Calls hooks to transition post status.
2551   *
2552   * The first is 'transition_post_status' with new status, old status, and post data.
2553   *
2554   * The next action called is 'OLDSTATUS_to_NEWSTATUS' the 'NEWSTATUS' is the
2555   * $new_status parameter and the 'OLDSTATUS' is $old_status parameter; it has the
2556   * post data.
2557   *
2558   * The final action is named 'NEWSTATUS_POSTTYPE', 'NEWSTATUS' is from the $new_status
2559   * parameter and POSTTYPE is post_type post data.
2560   *
2561   * @since 2.3.0
2562   * @link http://codex.wordpress.org/Post_Status_Transitions
2563   *
2564   * @uses do_action() Calls 'transition_post_status' on $new_status, $old_status and
2565   *  $post if there is a status change.
2566   * @uses do_action() Calls '${old_status}_to_$new_status' on $post if there is a status change.
2567   * @uses do_action() Calls '${new_status}_$post->post_type' on post ID and $post.
2568   *
2569   * @param string $new_status Transition to this post status.
2570   * @param string $old_status Previous post status.
2571   * @param object $post Post data.
2572   */
2573  function wp_transition_post_status($new_status, $old_status, $post) {
2574      do_action('transition_post_status', $new_status, $old_status, $post);
2575      do_action("$old_status}_to_$new_status", $post);
2576      do_action("$new_status}_$post->post_type", $post->ID, $post);
2577  }
2578  
2579  //
2580  // Trackback and ping functions
2581  //
2582  
2583  /**
2584   * Add a URL to those already pung.
2585   *
2586   * @since 1.5.0
2587   * @uses $wpdb
2588   *
2589   * @param int $post_id Post ID.
2590   * @param string $uri Ping URI.
2591   * @return int How many rows were updated.
2592   */
2593  function add_ping($post_id, $uri) {
2594      global $wpdb;
2595      $pung = $wpdb->get_var( $wpdb->prepare( "SELECT pinged FROM $wpdb->posts WHERE ID = %d", $post_id ));
2596      $pung = trim($pung);
2597      $pung = preg_split('/\s/', $pung);
2598      $pung[] = $uri;
2599      $new = implode("\n", $pung);
2600      $new = apply_filters('add_ping', $new);
2601      // expected_slashed ($new)
2602      $new = stripslashes($new);
2603      return $wpdb->update( $wpdb->posts, array( 'pinged' => $new ), array( 'ID' => $post_id ) );
2604  }
2605  
2606  /**
2607   * Retrieve enclosures already enclosed for a post.
2608   *
2609   * @since 1.5.0
2610   * @uses $wpdb
2611   *
2612   * @param int $post_id Post ID.
2613   * @return array List of enclosures
2614   */
2615  function get_enclosed($post_id) {
2616      $custom_fields = get_post_custom( $post_id );
2617      $pung = array();
2618      if ( !is_array( $custom_fields ) )
2619          return $pung;
2620  
2621      foreach ( $custom_fields as $key => $val ) {
2622          if ( 'enclosure' != $key || !is_array( $val ) )
2623              continue;
2624          foreach( $val as $enc ) {
2625              $enclosure = split( "\n", $enc );
2626              $pung[] = trim( $enclosure[ 0 ] );
2627          }
2628      }
2629      $pung = apply_filters('get_enclosed', $pung);
2630      return $pung;
2631  }
2632  
2633  /**
2634   * Retrieve URLs already pinged for a post.
2635   *
2636   * @since 1.5.0
2637   * @uses $wpdb
2638   *
2639   * @param int $post_id Post ID.
2640   * @return array
2641   */
2642  function get_pung($post_id) {
2643      global $wpdb;
2644      $pung = $wpdb->get_var( $wpdb->prepare( "SELECT pinged FROM $wpdb->posts WHERE ID = %d", $post_id ));
2645      $pung = trim($pung);
2646      $pung = preg_split('/\s/', $pung);
2647      $pung = apply_filters('get_pung', $pung);
2648      return $pung;
2649  }
2650  
2651  /**
2652   * Retrieve URLs that need to be pinged.
2653   *
2654   * @since 1.5.0
2655   * @uses $wpdb
2656   *
2657   * @param int $post_id Post ID
2658   * @return array
2659   */
2660  function get_to_ping($post_id) {
2661      global $wpdb;
2662      $to_ping = $wpdb->get_var( $wpdb->prepare( "SELECT to_ping FROM $wpdb->posts WHERE ID = %d", $post_id ));
2663      $to_ping = trim($to_ping);
2664      $to_ping = preg_split('/\s/', $to_ping, -1, PREG_SPLIT_NO_EMPTY);
2665      $to_ping = apply_filters('get_to_ping',  $to_ping);
2666      return $to_ping;
2667  }
2668  
2669  /**
2670   * Do trackbacks for a list of URLs.
2671   *
2672   * @since 1.0.0
2673   *
2674   * @param string $tb_list Comma separated list of URLs
2675   * @param int $post_id Post ID
2676   */
2677  function trackback_url_list($tb_list, $post_id) {
2678      if ( ! empty( $tb_list ) ) {
2679          // get post data
2680          $postdata = wp_get_single_post($post_id, ARRAY_A);
2681  
2682          // import postdata as variables
2683          extract($postdata, EXTR_SKIP);
2684  
2685          // form an excerpt
2686          $excerpt = strip_tags($post_excerpt ? $post_excerpt : $post_content);
2687  
2688          if (strlen($excerpt) > 255) {
2689              $excerpt = substr($excerpt,0,252) . '...';
2690          }
2691  
2692          $trackback_urls = explode(',', $tb_list);
2693          foreach( (array) $trackback_urls as $tb_url) {
2694              $tb_url = trim($tb_url);
2695              trackback($tb_url, stripslashes($post_title), $excerpt, $post_id);
2696          }
2697      }
2698  }
2699  
2700  //
2701  // Page functions
2702  //
2703  
2704  /**
2705   * Get a list of page IDs.
2706   *
2707   * @since 2.0.0
2708   * @uses $wpdb
2709   *
2710   * @return array List of page IDs.
2711   */
2712  function get_all_page_ids() {
2713      global $wpdb;
2714  
2715      if ( ! $page_ids = wp_cache_get('all_page_ids', 'posts') ) {
2716          $page_ids = $wpdb->get_col("SELECT ID FROM $wpdb->posts WHERE post_type = 'page'");
2717          wp_cache_add('all_page_ids', $page_ids, 'posts');
2718      }
2719  
2720      return $page_ids;
2721  }
2722  
2723  /**
2724   * Retrieves page data given a page ID or page object.
2725   *
2726   * @since 1.5.1
2727   *
2728   * @param mixed $page Page object or page ID. Passed by reference.
2729   * @param string $output What to output. OBJECT, ARRAY_A, or ARRAY_N.
2730   * @param string $filter How the return value should be filtered.
2731   * @return mixed Page data.
2732   */
2733  function &get_page(&$page, $output = OBJECT, $filter = 'raw') {
2734      if ( empty($page) ) {
2735          if ( isset( $GLOBALS['post'] ) && isset( $GLOBALS['post']->ID ) ) {
2736              return get_post($GLOBALS['post'], $output, $filter);
2737          } else {
2738              $page = null;
2739              return $page;
2740          }
2741      }
2742  
2743      $the_page = get_post($page, $output, $filter);
2744      return $the_page;
2745  }
2746  
2747  /**
2748   * Retrieves a page given its path.
2749   *
2750   * @since 2.1.0
2751   * @uses $wpdb
2752   *
2753   * @param string $page_path Page path
2754   * @param string $output Optional. Output type. OBJECT, ARRAY_N, or ARRAY_A. Default OBJECT.
2755   * @param string $post_type Optional. Post type. Default page.
2756   * @return mixed Null when complete.
2757   */
2758  function get_page_by_path($page_path, $output = OBJECT, $post_type = 'page') {
2759      global $wpdb;
2760      $page_path = rawurlencode(urldecode($page_path));
2761      $page_path = str_replace('%2F', '/', $page_path);
2762      $page_path = str_replace('%20', ' ', $page_path);
2763      $page_paths = '/' . trim($page_path, '/');
2764      $leaf_path  = sanitize_title(basename($page_paths));
2765      $page_paths = explode('/', $page_paths);
2766      $full_path = '';
2767      foreach( (array) $page_paths as $pathdir)
2768          $full_path .= ($pathdir!=''?'/':'') . sanitize_title($pathdir);
2769  
2770      $pages = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_name, post_parent FROM $wpdb->posts WHERE post_name = %s AND (post_type = %s OR post_type = 'attachment')", $leaf_path, $post_type ));
2771  
2772      if ( empty($pages) )
2773          return null;
2774  
2775      foreach ( $pages as $page ) {
2776          $path = '/' . $leaf_path;
2777          $curpage = $page;
2778          while ( $curpage->post_parent != 0 ) {
2779              $curpage = $wpdb->get_row( $wpdb->prepare( "SELECT ID, post_name, post_parent FROM $wpdb->posts WHERE ID = %d and post_type = %s", $curpage->post_parent, $post_type ));
2780              $path = '/' . $curpage->post_name . $path;
2781          }
2782  
2783          if ( $path == $full_path )
2784              return get_page($page->ID, $output, $post_type);
2785      }
2786  
2787      return null;
2788  }
2789  
2790  /**
2791   * Retrieve a page given its title.
2792   *
2793   * @since 2.1.0
2794   * @uses $wpdb
2795   *
2796   * @param string $page_title Page title
2797   * @param string $output Optional. Output type. OBJECT, ARRAY_N, or ARRAY_A. Default OBJECT.
2798   * @param string $post_type Optional. Post type. Default page.
2799   * @return mixed
2800   */
2801  function get_page_by_title($page_title, $output = OBJECT, $post_type = 'page' ) {
2802      global $wpdb;
2803      $page = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_title = %s AND post_type= %s", $page_title, $post_type ) );
2804      if ( $page )
2805          return get_page($page, $output);
2806  
2807      return null;
2808  }
2809  
2810  /**
2811   * Retrieve child pages from list of pages matching page ID.
2812   *
2813   * Matches against the pages parameter against the page ID. Also matches all
2814   * children for the same to retrieve all children of a page. Does not make any
2815   * SQL queries to get the children.
2816   *
2817   * @since 1.5.1
2818   *
2819   * @param int $page_id Page ID.
2820   * @param array $pages List of pages' objects.
2821   * @return array
2822   */
2823  function &get_page_children($page_id, $pages) {
2824      $page_list = array();
2825      foreach ( (array) $pages as $page ) {
2826          if ( $page->post_parent == $page_id ) {
2827              $page_list[] = $page;
2828              if ( $children = get_page_children($page->ID, $pages) )
2829                  $page_list = array_merge($page_list, $children);
2830          }
2831      }
2832      return $page_list;
2833  }
2834  
2835  /**
2836   * Order the pages with children under parents in a flat list.
2837   *
2838   * It uses auxiliary structure to hold parent-children relationships and
2839   * runs in O(N) complexity
2840   *
2841   * @since 2.0.0
2842   *
2843   * @param array $posts Posts array.
2844   * @param int $parent Parent page ID.
2845   * @return array A list arranged by hierarchy. Children immediately follow their parents.
2846   */
2847  function &get_page_hierarchy( &$pages, $page_id = 0 ) {
2848  
2849      if ( empty( $pages ) ) {
2850          $result = array();
2851          return $result;
2852      }
2853  
2854      $children = array();
2855      foreach ( (array) $pages as $p ) {
2856  
2857          $parent_id = intval( $p->post_parent );
2858          $children[ $parent_id ][] = $p;
2859       }
2860  
2861       $result = array();
2862       _page_traverse_name( $page_id, $children, $result );
2863  
2864      return $result;
2865  }
2866  
2867  /**
2868   * function to traverse and return all the nested children post names of a root page.
2869   * $children contains parent-chilren relations
2870   *
2871   */
2872  function _page_traverse_name( $page_id, &$children, &$result ){
2873  
2874      if ( isset( $children[ $page_id ] ) ){
2875  
2876          foreach( (array)$children[ $page_id ] as $child ) {
2877  
2878              $result[ $child->ID ] = $child->post_name;
2879              _page_traverse_name( $child->ID, $children, $result );
2880          }
2881      }
2882  }
2883  
2884  /**
2885   * Builds URI for a page.
2886   *
2887   * Sub pages will be in the "directory" under the parent page post name.
2888   *
2889   * @since 1.5.0
2890   *
2891   * @param int $page_id Page ID.
2892   * @return string Page URI.
2893   */
2894  function get_page_uri($page_id) {
2895      $page = get_page($page_id);
2896      $uri = $page->post_name;
2897  
2898      // A page cannot be it's own parent.
2899      if ( $page->post_parent == $page->ID )
2900          return $uri;
2901  
2902      while ($page->post_parent != 0) {
2903          $page = get_page($page->post_parent);
2904          $uri = $page->post_name . "/" . $uri;
2905      }
2906  
2907      return $uri;
2908  }
2909  
2910  /**
2911   * Retrieve a list of pages.
2912   *
2913   * The defaults that can be overridden are the following: 'child_of',
2914   * 'sort_order', 'sort_column', 'post_title', 'hierarchical', 'exclude',
2915   * 'include', 'meta_key', 'meta_value','authors', 'number', and 'offset'.
2916   *
2917   * @since 1.5.0
2918   * @uses $wpdb
2919   *
2920   * @param mixed $args Optional. Array or string of options that overrides defaults.
2921   * @return array List of pages matching defaults or $args
2922   */
2923  function &get_pages($args = '') {
2924      global $wpdb;
2925  
2926      $defaults = array(
2927          'child_of' => 0, 'sort_order' => 'ASC',
2928          'sort_column' => 'post_title', 'hierarchical' => 1,
2929          'exclude' => '', 'include' => '',
2930          'meta_key' => '', 'meta_value' => '',
2931          'authors' => '', 'parent' => -1, 'exclude_tree' => '',
2932          'number' => '', 'offset' => 0,
2933          'post_type' => 'page', 'post_status' => 'publish',
2934      );
2935  
2936      $r = wp_parse_args( $args, $defaults );
2937      extract( $r, EXTR_SKIP );
2938      $number = (int) $number;
2939      $offset = (int) $offset;
2940  
2941      // Make sure the post type is hierarchical
2942      $hierarchical_post_types = get_post_types( array( 'hierarchical' => true ) );
2943      if ( !in_array( $post_type, $hierarchical_post_types ) )
2944          return false;
2945  
2946      // Make sure we have a valid post status
2947      if ( !in_array($post_status, get_post_stati()) )
2948          return false;
2949  
2950      $cache = array();
2951      $key = md5( serialize( compact(array_keys($defaults)) ) );
2952      if ( $cache = wp_cache_get( 'get_pages', 'posts' ) ) {
2953          if ( is_array($cache) && isset( $cache[ $key ] ) ) {
2954              $pages = apply_filters('get_pages', $cache[ $key ], $r );
2955              return $pages;
2956          }
2957      }
2958  
2959      if ( !is_array($cache) )
2960          $cache = array();
2961  
2962      $inclusions = '';
2963      if ( !empty($include) ) {
2964          $child_of = 0; //ignore child_of, parent, exclude, meta_key, and meta_value params if using include
2965          $parent = -1;
2966          $exclude = '';
2967          $meta_key = '';
2968          $meta_value = '';
2969          $hierarchical = false;
2970          $incpages = preg_split('/[\s,]+/',$include);
2971          if ( count($incpages) ) {
2972              foreach ( $incpages as $incpage ) {
2973                  if (empty($inclusions))
2974                      $inclusions = $wpdb->prepare(' AND ( ID = %d ', $incpage);
2975                  else
2976                      $inclusions .= $wpdb->prepare(' OR ID = %d ', $incpage);
2977              }
2978          }
2979      }
2980      if (!empty($inclusions))
2981          $inclusions .= ')';
2982  
2983      $exclusions = '';
2984      if ( !empty($exclude) ) {
2985          $expages = preg_split('/[\s,]+/',$exclude);
2986          if ( count($expages) ) {
2987              foreach ( $expages as $expage ) {
2988                  if (empty($exclusions))
2989                      $exclusions = $wpdb->prepare(' AND ( ID <> %d ', $expage);
2990                  else
2991                      $exclusions .= $wpdb->prepare(' AND ID <> %d ', $expage);
2992              }
2993          }
2994      }
2995      if (!empty($exclusions))
2996          $exclusions .= ')';
2997  
2998      $author_query = '';
2999      if (!empty($authors)) {
3000          $post_authors = preg_split('/[\s,]+/',$authors);
3001  
3002          if ( count($post_authors) ) {
3003              foreach ( $post_authors as $post_author ) {
3004                  //Do we have an author id or an author login?
3005                  if ( 0 == intval($post_author) ) {
3006                      $post_author = get_userdatabylogin($post_author);
3007                      if ( empty($post_author) )
3008                          continue;
3009                      if ( empty($post_author->ID) )
3010                          continue;
3011                      $post_author = $post_author->ID;
3012                  }
3013  
3014                  if ( '' == $author_query )
3015                      $author_query = $wpdb->prepare(' post_author = %d ', $post_author);
3016                  else
3017                      $author_query .= $wpdb->prepare(' OR post_author = %d ', $post_author);
3018              }
3019              if ( '' != $author_query )
3020                  $author_query = " AND ($author_query)";
3021          }
3022      }
3023  
3024      $join = '';
3025      $where = "$exclusions $inclusions ";
3026      if ( ! empty( $meta_key ) || ! empty( $meta_value ) ) {
3027          $join = " LEFT JOIN $wpdb->postmeta ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id )";
3028  
3029          // meta_key and meta_value might be slashed
3030          $meta_key = stripslashes($meta_key);
3031          $meta_value = stripslashes($meta_value);
3032          if ( ! empty( $meta_key ) )
3033              $where .= $wpdb->prepare(" AND $wpdb->postmeta.meta_key = %s", $meta_key);
3034          if ( ! empty( $meta_value ) )
3035              $where .= $wpdb->prepare(" AND $wpdb->postmeta.meta_value = %s", $meta_value);
3036  
3037      }
3038  
3039      if ( $parent >= 0 )
3040          $where .= $wpdb->prepare(' AND post_parent = %d ', $parent);
3041  
3042      $where_post_type = $wpdb->prepare( "post_type = '%s' AND post_status = '%s'", $post_type, $post_status );
3043  
3044      $query = "SELECT * FROM $wpdb->posts $join WHERE ($where_post_type) $where ";
3045      $query .= $author_query;
3046      $query .= " ORDER BY " . $sort_column . " " . $sort_order ;
3047  
3048      if ( !empty($number) )
3049          $query .= ' LIMIT ' . $offset . ',' . $number;
3050  
3051      $pages = $wpdb->get_results($query);
3052  
3053      if ( empty($pages) ) {
3054          $pages = apply_filters('get_pages', array(), $r);
3055          return $pages;
3056      }
3057  
3058      // Sanitize before caching so it'll only get done once
3059      $num_pages = count($pages);
3060      for ($i = 0; $i < $num_pages; $i++) {
3061          $pages[$i] = sanitize_post($pages[$i], 'raw');
3062      }
3063  
3064      // Update cache.
3065      update_page_cache($pages);
3066  
3067      if ( $child_of || $hierarchical )
3068          $pages = & get_page_children($child_of, $pages);
3069  
3070      if ( !empty($exclude_tree) ) {
3071          $exclude = (int) $exclude_tree;
3072          $children = get_page_children($exclude, $pages);
3073          $excludes = array();
3074          foreach ( $children as $child )
3075              $excludes[] = $child->ID;
3076          $excludes[] = $exclude;
3077          $num_pages = count($pages);
3078          for ( $i = 0; $i < $num_pages; $i++ ) {
3079              if ( in_array($pages[$i]->ID, $excludes) )
3080                  unset($pages[$i]);
3081          }
3082      }
3083  
3084      $cache[ $key ] = $pages;
3085      wp_cache_set( 'get_pages', $cache, 'posts' );
3086  
3087      $pages = apply_filters('get_pages', $pages, $r);
3088  
3089      return $pages;
3090  }
3091  
3092  //
3093  // Attachment functions
3094  //
3095  
3096  /**
3097   * Check if the attachment URI is local one and is really an attachment.
3098   *
3099   * @since 2.0.0
3100   *
3101   * @param string $url URL to check
3102   * @return bool True on success, false on failure.
3103   */
3104  function is_local_attachment($url) {
3105      if (strpos($url, get_bloginfo('url')) === false)
3106          return false;
3107      if (strpos($url, get_bloginfo('url') . '/?attachment_id=') !== false)
3108          return true;
3109      if ( $id = url_to_postid($url) ) {
3110          $post = & get_post($id);
3111          if ( 'attachment' == $post->post_type )
3112              return true;
3113      }
3114      return false;
3115  }
3116  
3117  /**
3118   * Insert an attachment.
3119   *
3120   * If you set the 'ID' in the $object parameter, it will mean that you are
3121   * updating and attempt to update the attachment. You can also set the
3122   * attachment name or title by setting the key 'post_name' or 'post_title'.
3123   *
3124   * You can set the dates for the attachment manually by setting the 'post_date'
3125   * and 'post_date_gmt' keys' values.
3126   *
3127   * By default, the comments will use the default settings for whether the
3128   * comments are allowed. You can close them manually or keep them open by
3129   * setting the value for the 'comment_status' key.
3130   *
3131   * The $object parameter can have the following:
3132   *     'post_status'   - Default is 'draft'. Can not be overridden, set the same as parent post.
3133   *     'post_type'     - Default is 'post', will be set to attachment. Can not override.
3134   *     'post_author'   - Default is current user ID. The ID of the user, who added the attachment.
3135   *     'ping_status'   - Default is the value in default ping status option. Whether the attachment
3136   *                       can accept pings.
3137   *     'post_parent'   - Default is 0. Can use $parent parameter or set this for the post it belongs
3138   *                       to, if any.
3139   *     'menu_order'    - Default is 0. The order it is displayed.
3140   *     'to_ping'       - Whether to ping.
3141   *     'pinged'        - Default is empty string.
3142   *     'post_password' - Default is empty string. The password to access the attachment.
3143   *     'guid'          - Global Unique ID for referencing the attachment.
3144   *     'post_content_filtered' - Attachment post content filtered.
3145   *     'post_excerpt'  - Attachment excerpt.
3146   *
3147   * @since 2.0.0
3148   * @uses $wpdb
3149   * @uses $user_ID
3150   * @uses do_action() Calls 'edit_attachment' on $post_ID if this is an update.
3151   * @uses do_action() Calls 'add_attachment' on $post_ID if this is not an update.
3152   *
3153   * @param string|array $object Arguments to override defaults.
3154   * @param string $file Optional filename.
3155   * @param int $post_parent Parent post ID.
3156   * @return int Attachment ID.
3157   */
3158  function wp_insert_attachment($object, $file = false, $parent = 0) {
3159      global $wpdb, $user_ID;
3160  
3161      $defaults = array('post_status' => 'draft', 'post_type' => 'post', 'post_author' => $user_ID,
3162          'ping_status' => get_option('default_ping_status'), 'post_parent' => 0,
3163          'menu_order' => 0, 'to_ping' =>  '', 'pinged' => '', 'post_password' => '',
3164          'guid' => '', 'post_content_filtered' => '', 'post_excerpt' => '', 'import_id' => 0);
3165  
3166      $object = wp_parse_args($object, $defaults);
3167      if ( !empty($parent) )
3168          $object['post_parent'] = $parent;
3169  
3170      $object = sanitize_post($object, 'db');
3171  
3172      // export array as variables
3173      extract($object, EXTR_SKIP);
3174  
3175      if ( empty($post_author) )
3176          $post_author = $user_ID;
3177  
3178      $post_type = 'attachment';
3179      $post_status = 'inherit';
3180  
3181      // Make sure we set a valid category.
3182      if ( !isset($post_category) || 0 == count($post_category) || !is_array($post_category) ) {
3183          // 'post' requires at least one category.
3184          if ( 'post' == $post_type )
3185              $post_category = array( get_option('default_category') );
3186          else
3187              $post_category = array();
3188      }
3189  
3190      // Are we updating or creating?
3191      if ( !empty($ID) ) {
3192          $update = true;
3193          $post_ID = (int) $ID;
3194      } else {
3195          $update = false;
3196          $post_ID = 0;
3197      }
3198  
3199      // Create a valid post name.
3200      if ( empty($post_name) )
3201          $post_name = sanitize_title($post_title);
3202      else
3203          $post_name = sanitize_title($post_name);
3204  
3205      // expected_slashed ($post_name)
3206      $post_name = wp_unique_post_slug($post_name, $post_ID, $post_status, $post_type, $post_parent);
3207  
3208      if ( empty($post_date) )
3209          $post_date = current_time('mysql');
3210      if ( empty($post_date_gmt) )
3211          $post_date_gmt = current_time('mysql', 1);
3212  
3213      if ( empty($post_modified) )
3214          $post_modified = $post_date;
3215      if ( empty($post_modified_gmt) )
3216          $post_modified_gmt = $post_date_gmt;
3217  
3218      if ( empty($comment_status) ) {
3219          if ( $update )
3220              $comment_status = 'closed';
3221          else
3222              $comment_status = get_option('default_comment_status');
3223      }
3224      if ( empty($ping_status) )
3225          $ping_status = get_option('default_ping_status');
3226  
3227      if ( isset($to_ping) )
3228          $to_ping = preg_replace('|\s+|', "\n", $to_ping);
3229      else
3230          $to_ping = '';
3231  
3232      if ( isset($post_parent) )
3233          $post_parent = (int) $post_parent;
3234      else
3235          $post_parent = 0;
3236  
3237      if ( isset($menu_order) )
3238          $menu_order = (int) $menu_order;
3239      else
3240          $menu_order = 0;
3241  
3242      if ( !isset($post_password) )
3243          $post_password = '';
3244  
3245      if ( ! isset($pinged) )
3246          $pinged = '';
3247  
3248      // expected_slashed (everything!)
3249      $data = compact( array( 'post_author', 'post_date', 'post_date_gmt', 'post_content', 'post_content_filtered', 'post_title', 'post_excerpt', 'post_status', 'post_type', 'comment_status', 'ping_status', 'post_password', 'post_name', 'to_ping', 'pinged', 'post_modified', 'post_modified_gmt', 'post_parent', 'menu_order', 'post_mime_type', 'guid' ) );
3250      $data = stripslashes_deep( $data );
3251  
3252      if ( $update ) {
3253          $wpdb->update( $wpdb->posts, $data, array( 'ID' => $post_ID ) );
3254      } else {
3255          // If there is a suggested ID, use it if not already present
3256          if ( !empty($import_id) ) {
3257              $import_id = (int) $import_id;
3258              if ( ! $wpdb->get_var( $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE ID = %d", $import_id) ) ) {
3259                  $data['ID'] = $import_id;
3260              }
3261          }
3262  
3263          $wpdb->insert( $wpdb->posts, $data );
3264          $post_ID = (int) $wpdb->insert_id;
3265      }
3266  
3267      if ( empty($post_name) ) {
3268          $post_name = sanitize_title($post_title, $post_ID);
3269          $wpdb->update( $wpdb->posts, compact("post_name"), array( 'ID' => $post_ID ) );
3270      }
3271  
3272      wp_set_post_categories($post_ID, $post_category);
3273  
3274      if ( $file )
3275          update_attached_file( $post_ID, $file );
3276  
3277      clean_post_cache($post_ID);
3278  
3279      if ( isset($post_parent) && $post_parent < 0 )
3280          add_post_meta($post_ID, '_wp_attachment_temp_parent', $post_parent, true);
3281  
3282      if ( $update) {
3283          do_action('edit_attachment', $post_ID);
3284      } else {
3285          do_action('add_attachment', $post_ID);
3286      }
3287  
3288      return $post_ID;
3289  }
3290  
3291  /**
3292   * Delete an attachment.
3293   *
3294   * Will remove the file also, when the attachment is removed. Removes all post
3295   * meta fields, taxonomy, comments, etc associated with the attachment (except
3296   * the main post).
3297   *
3298   * @since 2.0.0
3299   * @uses $wpdb
3300   * @uses do_action() Calls 'delete_attachment' hook on Attachment ID.
3301   *
3302   * @param int $postid Attachment ID.
3303   * @param bool $force_delete Whether to bypass trash and force deletion
3304   * @return mixed False on failure. Post data on success.
3305   */
3306  function wp_delete_attachment( $post_id, $force_delete = false ) {
3307      global $wpdb;
3308  
3309      if ( !$post = $wpdb->get_row( $wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d", $post_id) ) )
3310          return $post;
3311  
3312      if ( 'attachment' != $post->post_type )
3313          return false;
3314  
3315      if ( !$force_delete && EMPTY_TRASH_DAYS && MEDIA_TRASH && 'trash' != $post->post_status )
3316          return wp_trash_post( $post_id );
3317  
3318      delete_post_meta($post_id, '_wp_trash_meta_status');
3319      delete_post_meta($post_id, '_wp_trash_meta_time');
3320  
3321      $meta = wp_get_attachment_metadata( $post_id );
3322      $backup_sizes = get_post_meta( $post->ID, '_wp_attachment_backup_sizes', true );
3323      $file = get_attached_file( $post_id );
3324  
3325      if ( is_multisite() )
3326          delete_transient( 'dirsize_cache' );
3327  
3328      do_action('delete_attachment', $post_id);
3329  
3330      wp_delete_object_term_relationships($post_id, array('category', 'post_tag'));
3331      wp_delete_object_term_relationships($post_id, get_object_taxonomies($post->post_type));
3332  
3333      $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE meta_key = '_thumbnail_id' AND meta_value = %d", $post_id ));
3334  
3335      $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d", $post_id ));
3336      if ( ! empty($comment_ids) ) {
3337          do_action( 'delete_comment', $comment_ids );
3338          $in_comment_ids = "'" . implode("', '", $comment_ids) . "'";
3339          $wpdb->query( "DELETE FROM $wpdb->comments WHERE comment_ID IN($in_comment_ids)" );
3340          do_action( 'deleted_comment', $comment_ids );
3341      }
3342  
3343      $post_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d ", $post_id ));
3344      if ( !empty($post_meta_ids) ) {
3345          do_action( 'delete_postmeta', $post_meta_ids );
3346          $in_post_meta_ids = "'" . implode("', '", $post_meta_ids) . "'";
3347          $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_id IN($in_post_meta_ids)" );
3348          do_action( 'deleted_postmeta', $post_meta_ids );
3349      }
3350  
3351      do_action( 'delete_post', $post_id );
3352      $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->posts WHERE ID = %d", $post_id ));
3353      do_action( 'deleted_post', $post_id );
3354  
3355      $uploadpath = wp_upload_dir();
3356  
3357      if ( ! empty($meta['thumb']) ) {
3358          // Don't delete the thumb if another attachment uses it
3359          if (! $wpdb->get_row( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attachment_metadata' AND meta_value LIKE %s AND post_id <> %d", '%' . $meta['thumb'] . '%', $post_id)) ) {
3360              $thumbfile = str_replace(basename($file), $meta['thumb'], $file);
3361              $thumbfile = apply_filters('wp_delete_file', $thumbfile);
3362              @ unlink( path_join($uploadpath['basedir'], $thumbfile) );
3363          }
3364      }
3365  
3366      // remove intermediate and backup images if there are any
3367      foreach ( get_intermediate_image_sizes() as $size ) {
3368          if ( $intermediate = image_get_intermediate_size($post_id, $size) ) {
3369              $intermediate_file = apply_filters('wp_delete_file', $intermediate['path']);
3370              @ unlink( path_join($uploadpath['basedir'], $intermediate_file) );
3371          }
3372      }
3373  
3374      if ( is_array($backup_sizes) ) {
3375          foreach ( $backup_sizes as $size ) {
3376              $del_file = path_join( dirname($meta['file']), $size['file'] );
3377              $del_file = apply_filters('wp_delete_file', $del_file);
3378              @ unlink( path_join($uploadpath['basedir'], $del_file) );
3379          }
3380      }
3381  
3382      $file = apply_filters('wp_delete_file', $file);
3383  
3384      if ( ! empty($file) )
3385          @ unlink($file);
3386  
3387      clean_post_cache($post_id);
3388  
3389      return $post;
3390  }
3391  
3392  /**
3393   * Retrieve attachment meta field for attachment ID.
3394   *
3395   * @since 2.1.0
3396   *
3397   * @param int $post_id Attachment ID
3398   * @param bool $unfiltered Optional, default is false. If true, filters are not run.
3399   * @return string|bool Attachment meta field. False on failure.
3400   */
3401  function wp_get_attachment_metadata( $post_id, $unfiltered = false ) {
3402      $post_id = (int) $post_id;
3403      if ( !$post =& get_post( $post_id ) )
3404          return false;
3405  
3406      $data = get_post_meta( $post->ID, '_wp_attachment_metadata', true );
3407  
3408      if ( $unfiltered )
3409          return $data;
3410  
3411      return apply_filters( 'wp_get_attachment_metadata', $data, $post->ID );
3412  }
3413  
3414  /**
3415   * Update metadata for an attachment.
3416   *
3417   * @since 2.1.0
3418   *
3419   * @param int $post_id Attachment ID.
3420   * @param array $data Attachment data.
3421   * @return int
3422   */
3423  function wp_update_attachment_metadata( $post_id, $data ) {
3424      $post_id = (int) $post_id;
3425      if ( !$post =& get_post( $post_id ) )
3426          return false;
3427  
3428      $data = apply_filters( 'wp_update_attachment_metadata', $data, $post->ID );
3429  
3430      return update_post_meta( $post->ID, '_wp_attachment_metadata', $data);
3431  }
3432  
3433  /**
3434   * Retrieve the URL for an attachment.
3435   *
3436   * @since 2.1.0
3437   *
3438   * @param int $post_id Attachment ID.
3439   * @return string
3440   */
3441  function wp_get_attachment_url( $post_id = 0 ) {
3442      $post_id = (int) $post_id;
3443      if ( !$post =& get_post( $post_id ) )
3444          return false;
3445  
3446      $url = '';
3447      if ( $file = get_post_meta( $post->ID, '_wp_attached_file', true) ) { //Get attached file
3448          if ( ($uploads = wp_upload_dir()) && false === $uploads['error'] ) { //Get upload directory
3449              if ( 0 === strpos($file, $uploads['basedir']) ) //Check that the upload base exists in the file location
3450                  $url = str_replace($uploads['basedir'], $uploads['baseurl'], $file); //replace file location with url location
3451              elseif ( false !== strpos($file, 'wp-content/uploads') )
3452                  $url = $uploads['baseurl'] . substr( $file, strpos($file, 'wp-content/uploads') + 18 );
3453              else
3454                  $url = $uploads['baseurl'] . "/$file"; //Its a newly uploaded file, therefor $file is relative to the basedir.
3455          }
3456      }
3457  
3458      if ( empty($url) ) //If any of the above options failed, Fallback on the GUID as used pre-2.7, not recomended to rely upon this.
3459          $url = get_the_guid( $post->ID );
3460  
3461      if ( 'attachment' != $post->post_type || empty($url) )
3462          return false;
3463  
3464      return apply_filters( 'wp_get_attachment_url', $url, $post->ID );
3465  }
3466  
3467  /**
3468   * Retrieve thumbnail for an attachment.
3469   *
3470   * @since 2.1.0
3471   *
3472   * @param int $post_id Attachment ID.
3473   * @return mixed False on failure. Thumbnail file path on success.
3474   */
3475  function wp_get_attachment_thumb_file( $post_id = 0 ) {
3476      $post_id = (int) $post_id;
3477      if ( !$post =& get_post( $post_id ) )
3478          return false;
3479      if ( !is_array( $imagedata = wp_get_attachment_metadata( $post->ID ) ) )
3480          return false;
3481  
3482      $file = get_attached_file( $post->ID );
3483  
3484      if ( !empty($imagedata['thumb']) && ($thumbfile = str_replace(basename($file), $imagedata['thumb'], $file)) && file_exists($thumbfile) )
3485          return apply_filters( 'wp_get_attachment_thumb_file', $thumbfile, $post->ID );
3486      return false;
3487  }
3488  
3489  /**
3490   * Retrieve URL for an attachment thumbnail.
3491   *
3492   * @since 2.1.0
3493   *
3494   * @param int $post_id Attachment ID
3495   * @return string|bool False on failure. Thumbnail URL on success.
3496   */
3497  function wp_get_attachment_thumb_url( $post_id = 0 ) {
3498      $post_id = (int) $post_id;
3499      if ( !$post =& get_post( $post_id ) )
3500          return false;
3501      if ( !$url = wp_get_attachment_url( $post->ID ) )
3502          return false;
3503  
3504      $sized = image_downsize( $post_id, 'thumbnail' );
3505      if ( $sized )
3506          return $sized[0];
3507  
3508      if ( !$thumb = wp_get_attachment_thumb_file( $post->ID ) )
3509          return false;
3510  
3511      $url = str_replace(basename($url), basename($thumb), $url);
3512  
3513      return apply_filters( 'wp_get_attachment_thumb_url', $url, $post->ID );
3514  }
3515  
3516  /**
3517   * Check if the attachment is an image.
3518   *
3519   * @since 2.1.0
3520   *
3521   * @param int $post_id Attachment ID
3522   * @return bool
3523   */
3524  function wp_attachment_is_image( $post_id = 0 ) {
3525      $post_id = (int) $post_id;
3526      if ( !$post =& get_post( $post_id ) )
3527          return false;
3528  
3529      if ( !$file = get_attached_file( $post->ID ) )
3530          return false;
3531  
3532      $ext = preg_match('/\.([^.]+)$/', $file, $matches) ? strtolower($matches[1]) : false;
3533  
3534      $image_exts = array('jpg', 'jpeg', 'gif', 'png');
3535  
3536      if ( 'image/' == substr($post->post_mime_type, 0, 6) || $ext && 'import' == $post->post_mime_type && in_array($ext, $image_exts) )
3537          return true;
3538      return false;
3539  }
3540  
3541  /**
3542   * Retrieve the icon for a MIME type.
3543   *
3544   * @since 2.1.0
3545   *
3546   * @param string $mime MIME type
3547   * @return string|bool
3548   */
3549  function wp_mime_type_icon( $mime = 0 ) {
3550      if ( !is_numeric($mime) )
3551          $icon = wp_cache_get("mime_type_icon_$mime");
3552      if ( empty($icon) ) {
3553          $post_id = 0;
3554          $post_mimes = array();
3555          if ( is_numeric($mime) ) {
3556              $mime = (int) $mime;
3557              if ( $post =& get_post( $mime ) ) {
3558                  $post_id = (int) $post->ID;
3559                  $ext = preg_replace('/^.+?\.([^.]+)$/', '$1', $post->guid);
3560                  if ( !empty($ext) ) {
3561                      $post_mimes[] = $ext;
3562                      if ( $ext_type = wp_ext2type( $ext ) )
3563                          $post_mimes[] = $ext_type;
3564                  }
3565                  $mime = $post->post_mime_type;
3566              } else {
3567                  $mime = 0;
3568              }
3569          } else {
3570              $post_mimes[] = $mime;
3571          }
3572  
3573          $icon_files = wp_cache_get('icon_files');
3574  
3575          if ( !is_array($icon_files) ) {
3576              $icon_dir = apply_filters( 'icon_dir', ABSPATH . WPINC . '/images/crystal' );
3577              $icon_dir_uri = apply_filters( 'icon_dir_uri', includes_url('images/crystal') );
3578              $dirs = apply_filters( 'icon_dirs', array($icon_dir => $icon_dir_uri) );
3579              $icon_files = array();
3580              while ( $dirs ) {
3581                  $dir = array_shift($keys = array_keys($dirs));
3582                  $uri = array_shift($dirs);
3583                  if ( $dh = opendir($dir) ) {
3584                      while ( false !== $file = readdir($dh) ) {
3585                          $file = basename($file);
3586                          if ( substr($file, 0, 1) == '.' )
3587                              continue;
3588                          if ( !in_array(strtolower(substr($file, -4)), array('.png', '.gif', '.jpg') ) ) {
3589                              if ( is_dir("$dir/$file") )
3590                                  $dirs["$dir/$file"] = "$uri/$file";
3591                              continue;
3592                          }
3593                          $icon_files["$dir/$file"] = "$uri/$file";
3594                      }
3595                      closedir($dh);
3596                  }
3597              }
3598              wp_cache_set('icon_files', $icon_files, 600);
3599          }
3600  
3601          // Icon basename - extension = MIME wildcard
3602          foreach ( $icon_files as $file => $uri )
3603              $types[ preg_replace('/^([^.]*).*$/', '$1', basename($file)) ] =& $icon_files[$file];
3604  
3605          if ( ! empty($mime) ) {
3606              $post_mimes[] = substr($mime, 0, strpos($mime, '/'));
3607              $post_mimes[] = substr($mime, strpos($mime, '/') + 1);
3608              $post_mimes[] = str_replace('/', '_', $mime);
3609          }
3610  
3611          $matches = wp_match_mime_types(array_keys($types), $post_mimes);
3612          $matches['default'] = array('default');
3613  
3614          foreach ( $matches as $match => $wilds ) {
3615              if ( isset($types[$wilds[0]])) {
3616                  $icon = $types[$wilds[0]];
3617                  if ( !is_numeric($mime) )
3618                      wp_cache_set("mime_type_icon_$mime", $icon);
3619                  break;
3620              }
3621          }
3622      }
3623  
3624      return apply_filters( 'wp_mime_type_icon', $icon, $mime, $post_id ); // Last arg is 0 if function pass mime type.
3625  }
3626  
3627  /**
3628   * Checked for changed slugs for published posts and save old slug.
3629   *
3630   * The function is used along with form POST data. It checks for the wp-old-slug
3631   * POST field. Will only be concerned with published posts and the slug actually
3632   * changing.
3633   *
3634   * If the slug was changed and not already part of the old slugs then it will be
3635   * added to the post meta field ('_wp_old_slug') for storing old slugs for that
3636   * post.
3637   *
3638   * The most logically usage of this function is redirecting changed posts, so
3639   * that those that linked to an changed post will be redirected to the new post.
3640   *
3641   * @since 2.1.0
3642   *
3643   * @param int $post_id Post ID.
3644   * @return int Same as $post_id
3645   */
3646  function wp_check_for_changed_slugs($post_id) {
3647      if ( !isset($_POST['wp-old-slug']) || !strlen($_POST['wp-old-slug']) )
3648          return $post_id;
3649  
3650      $post = &get_post($post_id);
3651  
3652      // we're only concerned with published posts
3653      if ( $post->post_status != 'publish' || $post->post_type != 'post' )
3654          return $post_id;
3655  
3656      // only bother if the slug has changed
3657      if ( $post->post_name == $_POST['wp-old-slug'] )
3658          return $post_id;
3659  
3660      $old_slugs = (array) get_post_meta($post_id, '_wp_old_slug');
3661  
3662      // if we haven't added this old slug before, add it now
3663      if ( !count($old_slugs) || !in_array($_POST['wp-old-slug'], $old_slugs) )
3664          add_post_meta($post_id, '_wp_old_slug', $_POST['wp-old-slug']);
3665  
3666      // if the new slug was used previously, delete it from the list
3667      if ( in_array($post->post_name, $old_slugs) )
3668          delete_post_meta($post_id, '_wp_old_slug', $post->post_name);
3669  
3670      return $post_id;
3671  }
3672  
3673  /**
3674   * Retrieve the private post SQL based on capability.
3675   *
3676   * This function provides a standardized way to appropriately select on the
3677   * post_status of posts/pages. The function will return a piece of SQL code that
3678   * can be added to a WHERE clause; this SQL is constructed to allow all
3679   * published posts, and all private posts to which the user has access.
3680   *
3681   * It also allows plugins that define their own post type to control the cap by
3682   * using the hook 'pub_priv_sql_capability'. The plugin is expected to return
3683   * the capability the user must have to read the private post type.
3684   *
3685   * @since 2.2.0
3686   *
3687   * @uses $user_ID
3688   * @uses apply_filters() Call 'pub_priv_sql_capability' filter for plugins with different post types.
3689   *
3690   * @param string $post_type currently only supports 'post' or 'page'.
3691   * @return string SQL code that can be added to a where clause.
3692   */
3693  function get_private_posts_cap_sql($post_type) {
3694      return get_posts_by_author_sql($post_type, FALSE);
3695  }
3696  
3697  /**
3698   * Retrieve the post SQL based on capability, author, and type.
3699   *
3700   * See above for full description.
3701   *
3702   * @since 3.0.0
3703   * @param string $post_type currently only supports 'post' or 'page'.
3704   * @param bool $full Optional.  Returns a full WHERE statement instead of just an 'andalso' term.
3705   * @param int $post_author Optional.  Query posts having a single author ID.
3706   * @return string SQL WHERE code that can be added to a query.
3707   */
3708  function get_posts_by_author_sql($post_type, $full = TRUE, $post_author = NULL) {
3709      global $user_ID, $wpdb;
3710  
3711      // Private posts
3712      if ($post_type == 'post') {
3713          $cap = 'read_private_posts';
3714      // Private pages
3715      } elseif ($post_type == 'page') {
3716          $cap = 'read_private_pages';
3717      // Dunno what it is, maybe plugins have their own post type?
3718      } else {
3719          $cap = '';
3720          $cap = apply_filters('pub_priv_sql_capability', $cap);
3721  
3722          if (empty($cap)) {
3723              // We don't know what it is, filters don't change anything,
3724              // so set the SQL up to return nothing.
3725              return ' 1 = 0 ';
3726          }
3727      }
3728  
3729      if ($full) {
3730          if (is_null($post_author)) {
3731              $sql = $wpdb->prepare('WHERE post_type = %s AND ', $post_type);
3732          } else {
3733              $sql = $wpdb->prepare('WHERE post_author = %d AND post_type = %s AND ', $post_author, $post_type);
3734          }
3735      } else {
3736          $sql = '';
3737      }
3738  
3739      $sql .= "(post_status = 'publish'";
3740  
3741      if (current_user_can($cap)) {
3742          // Does the user have the capability to view private posts? Guess so.
3743          $sql .= " OR post_status = 'private'";
3744      } elseif (is_user_logged_in()) {
3745          // Users can view their own private posts.
3746          $id = (int) $user_ID;
3747          if (is_null($post_author) || !$full) {
3748              $sql .= " OR post_status = 'private' AND post_author = $id";
3749          } elseif ($id == (int)$post_author) {
3750              $sql .= " OR post_status = 'private'";
3751          } // else none
3752      } // else none
3753  
3754      $sql .= ')';
3755  
3756      return $sql;
3757  }
3758  
3759  /**
3760   * Retrieve the date that the last post was published.
3761   *
3762   * The server timezone is the default and is the difference between GMT and
3763   * server time. The 'blog' value is the date when the last post was posted. The
3764   * 'gmt' is when the last post was posted in GMT formatted date.
3765   *
3766   * @since 0.71
3767   *
3768   * @uses $wpdb
3769   * @uses $blog_id
3770   * @uses apply_filters() Calls 'get_lastpostdate' filter
3771   *
3772   * @global mixed $cache_lastpostdate Stores the last post date
3773   * @global mixed $pagenow The current page being viewed
3774   *
3775   * @param string $timezone The location to get the time. Can be 'gmt', 'blog', or 'server'.
3776   * @return string The date of the last post.
3777   */
3778  function get_lastpostdate($timezone = 'server') {
3779      global $cache_lastpostdate, $wpdb, $blog_id;
3780      $add_seconds_server = date('Z');
3781      if ( !isset($cache_lastpostdate[$blog_id][$timezone]) ) {
3782          switch(strtolower($timezone)) {
3783              case 'gmt':
3784                  $lastpostdate = $wpdb->get_var("SELECT post_date_gmt FROM $wpdb->posts WHERE post_status = 'publish' AND post_type = 'post' ORDER BY post_date_gmt DESC LIMIT 1");
3785                  break;
3786              case 'blog':
3787                  $lastpostdate = $wpdb->get_var("SELECT post_date FROM $wpdb->posts WHERE post_status = 'publish' AND post_type = 'post' ORDER BY post_date_gmt DESC LIMIT 1");
3788                  break;
3789              case 'server':
3790                  $lastpostdate = $wpdb->get_var("SELECT DATE_ADD(post_date_gmt, INTERVAL '$add_seconds_server' SECOND) FROM $wpdb->posts WHERE post_status = 'publish' AND post_type = 'post' ORDER BY post_date_gmt DESC LIMIT 1");
3791                  break;
3792          }
3793          $cache_lastpostdate[$blog_id][$timezone] = $lastpostdate;
3794      } else {
3795          $lastpostdate = $cache_lastpostdate[$blog_id][$timezone];
3796      }
3797      return apply_filters( 'get_lastpostdate', $lastpostdate, $timezone );
3798  }
3799  
3800  /**
3801   * Retrieve last post modified date depending on timezone.
3802   *
3803   * The server timezone is the default and is the difference between GMT and
3804   * server time. The 'blog' value is just when the last post was modified. The
3805   * 'gmt' is when the last post was modified in GMT time.
3806   *
3807   * @since 1.2.0
3808   * @uses $wpdb
3809   * @uses $blog_id
3810   * @uses apply_filters() Calls 'get_lastpostmodified' filter
3811   *
3812   * @param string $timezone The location to get the time. Can be 'gmt', 'blog', or 'server'.
3813   * @return string The date the post was last modified.
3814   */
3815  function get_lastpostmodified($timezone = 'server') {
3816      global $wpdb;
3817  
3818      $add_seconds_server = date('Z');
3819      $timezone = strtolower( $timezone );
3820  
3821      $lastpostmodified = wp_cache_get( "lastpostmodified:$timezone", 'timeinfo' );
3822      if ( $lastpostmodified )
3823          return apply_filters( 'get_lastpostmodified', $lastpostmodified, $timezone );
3824  
3825      switch ( strtolower($timezone) ) {
3826          case 'gmt':
3827              $lastpostmodified = $wpdb->get_var("SELECT post_modified_gmt FROM $wpdb->posts WHERE post_status = 'publish' AND post_type = 'post' ORDER BY post_modified_gmt DESC LIMIT 1");
3828              break;
3829          case 'blog':
3830              $lastpostmodified = $wpdb->get_var("SELECT post_modified FROM $wpdb->posts WHERE post_status = 'publish' AND post_type = 'post' ORDER BY post_modified_gmt DESC LIMIT 1");
3831              break;
3832          case 'server':
3833              $lastpostmodified = $wpdb->get_var("SELECT DATE_ADD(post_modified_gmt, INTERVAL '$add_seconds_server' SECOND) FROM $wpdb->posts WHERE post_status = 'publish' AND post_type = 'post' ORDER BY post_modified_gmt DESC LIMIT 1");
3834              break;
3835      }
3836  
3837      $lastpostdate = get_lastpostdate($timezone);
3838      if ( $lastpostdate > $lastpostmodified )
3839          $lastpostmodified = $lastpostdate;
3840  
3841      if ( $lastpostmodified )
3842          wp_cache_set( "lastpostmodified:$timezone", $lastpostmodified, 'timeinfo' );
3843  
3844      return apply_filters( 'get_lastpostmodified', $lastpostmodified, $timezone );
3845  }
3846  
3847  /**
3848   * Updates posts in cache.
3849   *
3850   * @usedby update_page_cache() Aliased by this function.
3851   *
3852   * @package WordPress
3853   * @subpackage Cache
3854   * @since 1.5.1
3855   *
3856   * @param array $posts Array of post objects
3857   */
3858  function update_post_cache(&$posts) {
3859      if ( !$posts )
3860          return;
3861  
3862      foreach ( $posts as $post )
3863          wp_cache_add($post->ID, $post, 'posts');
3864  }
3865  
3866  /**
3867   * Will clean the post in the cache.
3868   *
3869   * Cleaning means delete from the cache of the post. Will call to clean the term
3870   * object cache associated with the post ID.
3871   *
3872   * clean_post_cache() will call itself recursively for each child post.
3873   *
3874   * This function not run if $_wp_suspend_cache_invalidation is not empty. See
3875   * wp_suspend_cache_invalidation().
3876   *
3877   * @package WordPress
3878   * @subpackage Cache
3879   * @since 2.0.0
3880   *
3881   * @uses do_action() Calls 'clean_post_cache' on $id before adding children (if any).
3882   *
3883   * @param int $id The Post ID in the cache to clean
3884   */
3885  function clean_post_cache($id) {
3886      global $_wp_suspend_cache_invalidation, $wpdb;
3887  
3888      if ( !empty($_wp_suspend_cache_invalidation) )
3889          return;
3890  
3891      $id = (int) $id;
3892  
3893      wp_cache_delete($id, 'posts');
3894      wp_cache_delete($id, 'post_meta');
3895  
3896      clean_object_term_cache($id, 'post');
3897  
3898      wp_cache_delete( 'wp_get_archives', 'general' );
3899  
3900      do_action('clean_post_cache', $id);
3901  
3902      if ( $children = $wpdb->get_col( $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_parent = %d", $id) ) ) {
3903          foreach( $children as $cid )
3904              clean_post_cache( $cid );
3905      }
3906  
3907      if ( is_multisite() )
3908          wp_cache_delete( $wpdb->blogid . '-' . $id, 'global-posts' );
3909  }
3910  
3911  /**
3912   * Alias of update_post_cache().
3913   *
3914   * @see update_post_cache() Posts and pages are the same, alias is intentional
3915   *
3916   * @package WordPress
3917   * @subpackage Cache
3918   * @since 1.5.1
3919   *
3920   * @param array $pages list of page objects
3921   */
3922  function update_page_cache(&$pages) {
3923      update_post_cache($pages);
3924  }
3925  
3926  /**
3927   * Will clean the page in the cache.
3928   *
3929   * Clean (read: delete) page from cache that matches $id. Will also clean cache
3930   * associated with 'all_page_ids' and 'get_pages'.
3931   *
3932   * @package WordPress
3933   * @subpackage Cache
3934   * @since 2.0.0
3935   *
3936   * @uses do_action() Will call the 'clean_page_cache' hook action.
3937   *
3938   * @param int $id Page ID to clean
3939   */
3940  function clean_page_cache($id) {
3941      clean_post_cache($id);
3942  
3943      wp_cache_delete( 'all_page_ids', 'posts' );
3944      wp_cache_delete( 'get_pages', 'posts' );
3945  
3946      do_action('clean_page_cache', $id);
3947  }
3948  
3949  /**
3950   * Call major cache updating functions for list of Post objects.
3951   *
3952   * @package WordPress
3953   * @subpackage Cache
3954   * @since 1.5.0
3955   *
3956   * @uses $wpdb
3957   * @uses update_post_cache()
3958   * @uses update_object_term_cache()
3959   * @uses update_postmeta_cache()
3960   *
3961   * @param array $posts Array of Post objects
3962   */
3963  function update_post_caches(&$posts) {
3964      // No point in doing all this work if we didn't match any posts.
3965      if ( !$posts )
3966          return;
3967  
3968      update_post_cache($posts);
3969  
3970      $post_ids = array();
3971  
3972      for ($i = 0; $i < count($posts); $i++)
3973          $post_ids[] = $posts[$i]->ID;
3974  
3975      update_object_term_cache($post_ids, 'post');
3976  
3977      update_postmeta_cache($post_ids);
3978  }
3979  
3980  /**
3981   * Updates metadata cache for list of post IDs.
3982   *
3983   * Performs SQL query to retrieve the metadata for the post IDs and updates the
3984   * metadata cache for the posts. Therefore, the functions, which call this
3985   * function, do not need to perform SQL queries on their own.
3986   *
3987   * @package WordPress
3988   * @subpackage Cache
3989   * @since 2.1.0
3990   *
3991   * @uses $wpdb
3992   *
3993   * @param array $post_ids List of post IDs.
3994   * @return bool|array Returns false if there is nothing to update or an array of metadata.
3995   */
3996  function update_postmeta_cache($post_ids) {
3997      return update_meta_cache('post', $post_ids);
3998  }
3999  
4000  /**
4001   * Will clean the attachment in the cache.
4002   *
4003   * Cleaning means delete from the cache. Optionaly will clean the term
4004   * object cache associated with the attachment ID.
4005   *
4006   * This function will not run if $_wp_suspend_cache_invalidation is not empty. See
4007   * wp_suspend_cache_invalidation().
4008   *
4009   * @package WordPress
4010   * @subpackage Cache
4011   * @since 3.0.0
4012   *
4013   * @uses do_action() Calls 'clean_attachment_cache' on $id.
4014   *
4015   * @param int $id The attachment ID in the cache to clean
4016   * @param bool $clean_terms optional. Whether to clean terms cache
4017   */
4018  function clean_attachment_cache($id, $clean_terms = false) {
4019      global $_wp_suspend_cache_invalidation;
4020  
4021      if ( !empty($_wp_suspend_cache_invalidation) )
4022          return;
4023  
4024      $id = (int) $id;
4025  
4026      wp_cache_delete($id, 'posts');
4027      wp_cache_delete($id, 'post_meta');
4028  
4029      if ( $clean_terms )
4030          clean_object_term_cache($id, 'attachment');
4031  
4032      do_action('clean_attachment_cache', $id);
4033  }
4034  
4035  //
4036  // Hooks
4037  //
4038  
4039  /**
4040   * Hook for managing future post transitions to published.
4041   *
4042   * @since 2.3.0
4043   * @access private
4044   * @uses $wpdb
4045   * @uses do_action() Calls 'private_to_published' on post ID if this is a 'private_to_published' call.
4046   * @uses wp_clear_scheduled_hook() with 'publish_future_post' and post ID.
4047   *
4048   * @param string $new_status New post status
4049   * @param string $old_status Previous post status
4050   * @param object $post Object type containing the post information
4051   */
4052  function _transition_post_status($new_status, $old_status, $post) {
4053      global $wpdb;
4054  
4055      if ( $old_status != 'publish' && $new_status == 'publish' ) {
4056          // Reset GUID if transitioning to publish and it is empty
4057          if ( '' == get_the_guid($post->ID) )
4058              $wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post->ID ) ), array( 'ID' => $post->ID ) );
4059          do_action('private_to_published', $post->ID);  // Deprecated, use private_to_publish
4060      }
4061  
4062      // If published posts changed clear the lastpostmodified cache
4063      if ( 'publish' == $new_status || 'publish' == $old_status) {
4064          wp_cache_delete( 'lastpostmodified:server', 'timeinfo' );
4065          wp_cache_delete( 'lastpostmodified:gmt',    'timeinfo' );
4066          wp_cache_delete( 'lastpostmodified:blog',   'timeinfo' );
4067      }
4068  
4069      // Always clears the hook in case the post status bounced from future to draft.
4070      wp_clear_scheduled_hook('publish_future_post', array( $post->ID ) );
4071  }
4072  
4073  /**
4074   * Hook used to schedule publication for a post marked for the future.
4075   *
4076   * The $post properties used and must exist are 'ID' and 'post_date_gmt'.
4077   *
4078   * @since 2.3.0
4079   * @access private
4080   *
4081   * @param int $deprecated Not used. Can be set to null. Never implemented.
4082   *   Not marked as deprecated with _deprecated_argument() as it conflicts with
4083   *   wp_transition_post_status() and the default filter for _future_post_hook().
4084   * @param object $post Object type containing the post information
4085   */
4086  function _future_post_hook( $deprecated = '', $post ) {
4087      wp_clear_scheduled_hook( 'publish_future_post', array( $post->ID ) );
4088      wp_schedule_single_event( strtotime( $post->post_date_gmt. ' GMT' ), 'publish_future_post', array( $post->ID ) );
4089  }
4090  
4091  /**
4092   * Hook to schedule pings and enclosures when a post is published.
4093   *
4094   * @since 2.3.0
4095   * @access private
4096   * @uses $wpdb
4097   * @uses XMLRPC_REQUEST and APP_REQUEST constants.
4098   * @uses do_action() Calls 'xmlprc_publish_post' on post ID if XMLRPC_REQUEST is defined.
4099   * @uses do_action() Calls 'app_publish_post' on post ID if APP_REQUEST is defined.
4100   *
4101   * @param int $post_id The ID in the database table of the post being published
4102   */
4103  function _publish_post_hook($post_id) {
4104      global $wpdb;
4105  
4106      if ( defined('XMLRPC_REQUEST') )
4107          do_action('xmlrpc_publish_post', $post_id);
4108      if ( defined('APP_REQUEST') )
4109          do_action('app_publish_post', $post_id);
4110  
4111      if ( defined('WP_IMPORTING') )
4112          return;
4113  
4114      $data = array( 'post_id' => $post_id, 'meta_value' => '1' );
4115      if ( get_option('default_pingback_flag') ) {
4116          $wpdb->insert( $wpdb->postmeta, $data + array( 'meta_key' => '_pingme' ) );
4117          do_action( 'added_postmeta', $wpdb->insert_id, $post_id, '_pingme', 1 );
4118      }
4119      $wpdb->insert( $wpdb->postmeta, $data + array( 'meta_key' => '_encloseme' ) );
4120      do_action( 'added_postmeta', $wpdb->insert_id, $post_id, '_encloseme', 1 );
4121  
4122      wp_schedule_single_event(time(), 'do_pings');
4123  }
4124  
4125  /**
4126   * Hook used to prevent page/post cache and rewrite rules from staying dirty.
4127   *
4128   * Does two things. If the post is a page and has a template then it will
4129   * update/add that template to the meta. For both pages and posts, it will clean
4130   * the post cache to make sure that the cache updates to the changes done
4131   * recently. For pages, the rewrite rules of WordPress are flushed to allow for
4132   * any changes.
4133   *
4134   * The $post parameter, only uses 'post_type' property and 'page_template'
4135   * property.
4136   *
4137   * @since 2.3.0
4138   * @access private
4139   * @uses $wp_rewrite Flushes Rewrite Rules.
4140   *
4141   * @param int $post_id The ID in the database table for the $post
4142   * @param object $post Object type containing the post information
4143   */
4144  function _save_post_hook($post_id, $post) {
4145      if ( $post->post_type == 'page' ) {
4146          clean_page_cache($post_id);
4147          // Avoid flushing rules for every post during import.
4148          if ( !defined('WP_IMPORTING') ) {
4149              global $wp_rewrite;
4150              $wp_rewrite->flush_rules(false);
4151          }
4152      } else {
4153          clean_post_cache($post_id);
4154      }
4155  }
4156  
4157  /**
4158   * Retrieve post ancestors and append to post ancestors property.
4159   *
4160   * Will only retrieve ancestors once, if property is already set, then nothing
4161   * will be done. If there is not a parent post, or post ID and post parent ID
4162   * are the same then nothing will be done.
4163   *
4164   * The parameter is passed by reference, so nothing needs to be returned. The
4165   * property will be updated and can be referenced after the function is
4166   * complete. The post parent will be an ancestor and the parent of the post
4167   * parent will be an ancestor. There will only be two ancestors at the most.
4168   *
4169   * @since unknown
4170   * @access private
4171   * @uses $wpdb
4172   *
4173   * @param object $_post Post data.
4174   * @return null When nothing needs to be done.
4175   */
4176  function _get_post_ancestors(&$_post) {
4177      global $wpdb;
4178  
4179      if ( isset($_post->ancestors) )
4180          return;
4181  
4182      $_post->ancestors = array();
4183  
4184      if ( empty($_post->post_parent) || $_post->ID == $_post->post_parent )
4185          return;
4186  
4187      $id = $_post->ancestors[] = $_post->post_parent;
4188      while ( $ancestor = $wpdb->get_var( $wpdb->prepare("SELECT `post_parent` FROM $wpdb->posts WHERE ID = %d LIMIT 1", $id) ) ) {
4189          if ( $id == $ancestor )
4190              break;
4191          $id = $_post->ancestors[] = $ancestor;
4192      }
4193  }
4194  
4195  /**
4196   * Determines which fields of posts are to be saved in revisions.
4197   *
4198   * Does two things. If passed a post *array*, it will return a post array ready
4199   * to be insterted into the posts table as a post revision. Otherwise, returns
4200   * an array whose keys are the post fields to be saved for post revisions.
4201   *
4202   * @package WordPress
4203   * @subpackage Post_Revisions
4204   * @since 2.6.0
4205   * @access private
4206   * @uses apply_filters() Calls '_wp_post_revision_fields' on 'title', 'content' and 'excerpt' fields.
4207   *
4208   * @param array $post Optional a post array to be processed for insertion as a post revision.
4209   * @param bool $autosave optional Is the revision an autosave?
4210   * @return array Post array ready to be inserted as a post revision or array of fields that can be versioned.
4211   */
4212  function _wp_post_revision_fields( $post = null, $autosave = false ) {
4213      static $fields = false;
4214  
4215      if ( !$fields ) {
4216          // Allow these to be versioned
4217          $fields = array(
4218              'post_title' => __( 'Title' ),
4219              'post_content' => __( 'Content' ),
4220              'post_excerpt' => __( 'Excerpt' ),
4221          );
4222  
4223          // Runs only once
4224          $fields = apply_filters( '_wp_post_revision_fields', $fields );
4225  
4226          // WP uses these internally either in versioning or elsewhere - they cannot be versioned
4227          foreach ( array( 'ID', 'post_name', 'post_parent', 'post_date', 'post_date_gmt', 'post_status', 'post_type', 'comment_count', 'post_author' ) as $protect )
4228              unset( $fields[$protect] );
4229      }
4230  
4231      if ( !is_array($post) )
4232          return $fields;
4233  
4234      $return = array();
4235      foreach ( array_intersect( array_keys( $post ), array_keys( $fields ) ) as $field )
4236          $return[$field] = $post[$field];
4237  
4238      $return['post_parent']   = $post['ID'];
4239      $return['post_status']   = 'inherit';
4240      $return['post_type']     = 'revision';
4241      $return['post_name']     = $autosave ? "$post[ID]-autosave" : "$post[ID]-revision";
4242      $return['post_date']     = isset($post['post_modified']) ? $post['post_modified'] : '';
4243      $return['post_date_gmt'] = isset($post['post_modified_gmt']) ? $post['post_modified_gmt'] : '';
4244  
4245      return $return;
4246  }
4247  
4248  /**
4249   * Saves an already existing post as a post revision.
4250   *
4251   * Typically used immediately prior to post updates.
4252   *
4253   * @package WordPress
4254   * @subpackage Post_Revisions
4255   * @since 2.6.0
4256   *
4257   * @uses _wp_put_post_revision()
4258   *
4259   * @param int $post_id The ID of the post to save as a revision.
4260   * @return mixed Null or 0 if error, new revision ID, if success.
4261   */
4262  function wp_save_post_revision( $post_id ) {
4263      // We do autosaves manually with wp_create_post_autosave()
4264      if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
4265          return;
4266  
4267      // WP_POST_REVISIONS = 0, false
4268      if ( ! WP_POST_REVISIONS )
4269          return;
4270  
4271      if ( !$post = get_post( $post_id, ARRAY_A ) )
4272          return;
4273  
4274      if ( !post_type_supports($post['post_type'], 'revisions') )
4275          return;
4276  
4277      $return = _wp_put_post_revision( $post );
4278  
4279      // WP_POST_REVISIONS = true (default), -1
4280      if ( !is_numeric( WP_POST_REVISIONS ) || WP_POST_REVISIONS < 0 )
4281          return $return;
4282  
4283      // all revisions and (possibly) one autosave
4284      $revisions = wp_get_post_revisions( $post_id, array( 'order' => 'ASC' ) );
4285  
4286      // WP_POST_REVISIONS = (int) (# of autosaves to save)
4287      $delete = count($revisions) - WP_POST_REVISIONS;
4288  
4289      if ( $delete < 1 )
4290          return $return;
4291  
4292      $revisions = array_slice( $revisions, 0, $delete );
4293  
4294      for ( $i = 0; isset($revisions[$i]); $i++ ) {
4295          if ( false !== strpos( $revisions[$i]->post_name, 'autosave' ) )
4296              continue;
4297          wp_delete_post_revision( $revisions[$i]->ID );
4298      }
4299  
4300      return $return;
4301  }
4302  
4303  /**
4304   * Retrieve the autosaved data of the specified post.
4305   *
4306   * Returns a post object containing the information that was autosaved for the
4307   * specified post.
4308   *
4309   * @package WordPress
4310   * @subpackage Post_Revisions
4311   * @since 2.6.0
4312   *
4313   * @param int $post_id The post ID.
4314   * @return object|bool The autosaved data or false on failure or when no autosave exists.
4315   */
4316  function wp_get_post_autosave( $post_id ) {
4317  
4318      if ( !$post = get_post( $post_id ) )
4319          return false;
4320  
4321      $q = array(
4322          'name' => "{$post->ID}-autosave",
4323          'post_parent' => $post->ID,
4324          'post_type' => 'revision',
4325          'post_status' => 'inherit'
4326      );
4327  
4328      // Use WP_Query so that the result gets cached
4329      $autosave_query = new WP_Query;
4330  
4331      add_action( 'parse_query', '_wp_get_post_autosave_hack' );
4332      $autosave = $autosave_query->query( $q );
4333      remove_action( 'parse_query', '_wp_get_post_autosave_hack' );
4334  
4335      if ( $autosave && is_array($autosave) && is_object($autosave[0]) )
4336          return $autosave[0];
4337  
4338      return false;
4339  }
4340  
4341  /**
4342   * Internally used to hack WP_Query into submission.
4343   *
4344   * @package WordPress
4345   * @subpackage Post_Revisions
4346   * @since 2.6.0
4347   *
4348   * @param object $query WP_Query object
4349   */
4350  function _wp_get_post_autosave_hack( $query ) {
4351      $query->is_single = false;
4352  }
4353  
4354  /**
4355   * Determines if the specified post is a revision.
4356   *
4357   * @package WordPress
4358   * @subpackage Post_Revisions
4359   * @since 2.6.0
4360   *
4361   * @param int|object $post Post ID or post object.
4362   * @return bool|int False if not a revision, ID of revision's parent otherwise.
4363   */
4364  function wp_is_post_revision( $post ) {
4365      if ( !$post = wp_get_post_revision( $post ) )
4366          return false;
4367      return (int) $post->post_parent;
4368  }
4369  
4370  /**
4371   * Determines if the specified post is an autosave.
4372   *
4373   * @package WordPress
4374   * @subpackage Post_Revisions
4375   * @since 2.6.0
4376   *
4377   * @param int|object $post Post ID or post object.
4378   * @return bool|int False if not a revision, ID of autosave's parent otherwise
4379   */
4380  function wp_is_post_autosave( $post ) {
4381      if ( !$post = wp_get_post_revision( $post ) )
4382          return false;
4383      if ( "{$post->post_parent}-autosave" !== $post->post_name )
4384          return false;
4385      return (int) $post->post_parent;
4386  }
4387  
4388  /**
4389   * Inserts post data into the posts table as a post revision.
4390   *
4391   * @package WordPress
4392   * @subpackage Post_Revisions
4393   * @since 2.6.0
4394   *
4395   * @uses wp_insert_post()
4396   *
4397   * @param int|object|array $post Post ID, post object OR post array.
4398   * @param bool $autosave Optional. Is the revision an autosave?
4399   * @return mixed Null or 0 if error, new revision ID if success.
4400   */
4401  function _wp_put_post_revision( $post = null, $autosave = false ) {
4402      if ( is_object($post) )
4403          $post = get_object_vars( $post );
4404      elseif ( !is_array($post) )
4405          $post = get_post($post, ARRAY_A);
4406      if ( !$post || empty($post['ID']) )
4407          return;
4408  
4409      if ( isset($post['post_type']) && 'revision' == $post['post_type'] )
4410          return new WP_Error( 'post_type', __( 'Cannot create a revision of a revision' ) );
4411  
4412      $post = _wp_post_revision_fields( $post, $autosave );
4413      $post = add_magic_quotes($post); //since data is from db
4414  
4415      $revision_id = wp_insert_post( $post );
4416      if ( is_wp_error($revision_id) )
4417          return $revision_id;
4418  
4419      if ( $revision_id )
4420          do_action( '_wp_put_post_revision', $revision_id );
4421      return $revision_id;
4422  }
4423  
4424  /**
4425   * Gets a post revision.
4426   *
4427   * @package WordPress
4428   * @subpackage Post_Revisions
4429   * @since 2.6.0
4430   *
4431   * @uses get_post()
4432   *
4433   * @param int|object $post Post ID or post object
4434   * @param string $output Optional. OBJECT, ARRAY_A, or ARRAY_N.
4435   * @param string $filter Optional sanitation filter.  @see sanitize_post()
4436   * @return mixed Null if error or post object if success
4437   */
4438  function &wp_get_post_revision(&$post, $output = OBJECT, $filter = 'raw') {
4439      $null = null;
4440      if ( !$revision = get_post( $post, OBJECT, $filter ) )
4441          return $revision;
4442      if ( 'revision' !== $revision->post_type )
4443          return $null;
4444  
4445      if ( $output == OBJECT ) {
4446          return $revision;
4447      } elseif ( $output == ARRAY_A ) {
4448          $_revision = get_object_vars($revision);
4449          return $_revision;
4450      } elseif ( $output == ARRAY_N ) {
4451          $_revision = array_values(get_object_vars($revision));
4452          return $_revision;
4453      }
4454  
4455      return $revision;
4456  }
4457  
4458  /**
4459   * Restores a post to the specified revision.
4460   *
4461   * Can restore a past revision using all fields of the post revision, or only selected fields.
4462   *
4463   * @package WordPress
4464   * @subpackage Post_Revisions
4465   * @since 2.6.0
4466   *
4467   * @uses wp_get_post_revision()
4468   * @uses wp_update_post()
4469   * @uses do_action() Calls 'wp_restore_post_revision' on post ID and revision ID if wp_update_post()
4470   *  is successful.
4471   *
4472   * @param int|object $revision_id Revision ID or revision object.
4473   * @param array $fields Optional. What fields to restore from. Defaults to all.
4474   * @return mixed Null if error, false if no fields to restore, (int) post ID if success.
4475   */
4476  function wp_restore_post_revision( $revision_id, $fields = null ) {
4477      if ( !$revision = wp_get_post_revision( $revision_id, ARRAY_A ) )
4478          return $revision;
4479  
4480      if ( !is_array( $fields ) )
4481          $fields = array_keys( _wp_post_revision_fields() );
4482  
4483      $update = array();
4484      foreach( array_intersect( array_keys( $revision ), $fields ) as $field )
4485          $update[$field] = $revision[$field];
4486  
4487      if ( !$update )
4488          return false;
4489  
4490      $update['ID'] = $revision['post_parent'];
4491  
4492      $update = add_magic_quotes( $update ); //since data is from db
4493  
4494      $post_id = wp_update_post( $update );
4495      if ( is_wp_error( $post_id ) )
4496          return $post_id;
4497  
4498      if ( $post_id )
4499          do_action( 'wp_restore_post_revision', $post_id, $revision['ID'] );
4500  
4501      return $post_id;
4502  }
4503  
4504  /**
4505   * Deletes a revision.
4506   *
4507   * Deletes the row from the posts table corresponding to the specified revision.
4508   *
4509   * @package WordPress
4510   * @subpackage Post_Revisions
4511   * @since 2.6.0
4512   *
4513   * @uses wp_get_post_revision()
4514   * @uses wp_delete_post()
4515   *
4516   * @param int|object $revision_id Revision ID or revision object.
4517   * @param array $fields Optional. What fields to restore from.  Defaults to all.
4518   * @return mixed Null if error, false if no fields to restore, (int) post ID if success.
4519   */
4520  function wp_delete_post_revision( $revision_id ) {
4521      if ( !$revision = wp_get_post_revision( $revision_id ) )
4522          return $revision;
4523  
4524      $delete = wp_delete_post( $revision->ID );
4525      if ( is_wp_error( $delete ) )
4526          return $delete;
4527  
4528      if ( $delete )
4529          do_action( 'wp_delete_post_revision', $revision->ID, $revision );
4530  
4531      return $delete;
4532  }
4533  
4534  /**
4535   * Returns all revisions of specified post.
4536   *
4537   * @package WordPress
4538   * @subpackage Post_Revisions
4539   * @since 2.6.0
4540   *
4541   * @uses get_children()
4542   *
4543   * @param int|object $post_id Post ID or post object
4544   * @return array empty if no revisions
4545   */
4546  function wp_get_post_revisions( $post_id = 0, $args = null ) {
4547      if ( ! WP_POST_REVISIONS )
4548          return array();
4549      if ( ( !$post = get_post( $post_id ) ) || empty( $post->ID ) )
4550          return array();
4551  
4552      $defaults = array( 'order' => 'DESC', 'orderby' => 'date' );
4553      $args = wp_parse_args( $args, $defaults );
4554      $args = array_merge( $args, array( 'post_parent' => $post->ID, 'post_type' => 'revision', 'post_status' => 'inherit' ) );
4555  
4556      if ( !$revisions = get_children( $args ) )
4557          return array();
4558      return $revisions;
4559  }
4560  
4561  function _set_preview($post) {
4562  
4563      if ( ! is_object($post) )
4564          return $post;
4565  
4566      $preview = wp_get_post_autosave($post->ID);
4567  
4568      if ( ! is_object($preview) )
4569          return $post;
4570  
4571      $preview = sanitize_post($preview);
4572  
4573      $post->post_content = $preview->post_content;
4574      $post->post_title = $preview->post_title;
4575      $post->post_excerpt = $preview->post_excerpt;
4576  
4577      return $post;
4578  }
4579  
4580  function _show_post_preview() {
4581  
4582      if ( isset($_GET['preview_id']) && isset($_GET['preview_nonce']) ) {
4583          $id = (int) $_GET['preview_id'];
4584  
4585          if ( false == wp_verify_nonce( $_GET['preview_nonce'], 'post_preview_' . $id ) )
4586              wp_die( __('You do not have permission to preview drafts.') );
4587  
4588          add_filter('the_preview', '_set_preview');
4589      }
4590  }


Generated: Mon Apr 5 14:26:09 2010 Cross-referenced by PHPXref 0.7