]> scripts.mit.edu Git - autoinstalls/wordpress.git/blob - wp-includes/post.php
Wordpress 2.6.2
[autoinstalls/wordpress.git] / wp-includes / post.php
1 <?php
2 /**
3  * Post functions and post utility function.
4  *
5  * Warning: The inline documentation for the functions contained
6  * in this file might be inaccurate, so the documentation is not
7  * authoritative at the moment.
8  *
9  * @package WordPress
10  * @subpackage Post
11  * @since 1.5
12  */
13
14 /**
15  * Retrieve attached file path based on attachment ID.
16  *
17  * You can optionally send it through the 'get_attached_file' filter, but by
18  * default it will just return the file path unfiltered.
19  *
20  * The function works by getting the single post meta name, named
21  * '_wp_attached_file' and returning it. This is a convenience function to
22  * prevent looking up the meta name and provide a mechanism for sending the
23  * attached filename through a filter.
24  *
25  * @package WordPress
26  * @subpackage Post
27  * @since 2.0
28  * @uses apply_filters() Calls 'get_attached_file' on file path and attachment ID
29  *
30  * @param int $attachment_id Attachment ID
31  * @param bool $unfiltered Whether to apply filters or not
32  * @return string The file path to the attached file.
33  */
34 function get_attached_file( $attachment_id, $unfiltered = false ) {
35         $file = get_post_meta( $attachment_id, '_wp_attached_file', true );
36         if ( $unfiltered )
37                 return $file;
38         return apply_filters( 'get_attached_file', $file, $attachment_id );
39 }
40
41 /**
42  * Update attachment file path based on attachment ID.
43  *
44  * Used to update the file path of the attachment, which uses post meta name
45  * '_wp_attached_file' to store the path of the attachment.
46  *
47  * @package WordPress
48  * @subpackage Post
49  * @since 2.1
50  * @uses apply_filters() Calls 'update_attached_file' on file path and attachment ID
51  *
52  * @param int $attachment_id Attachment ID
53  * @param string $file File path for the attachment
54  * @return bool False on failure, true on success.
55  */
56 function update_attached_file( $attachment_id, $file ) {
57         if ( !get_post( $attachment_id ) )
58                 return false;
59
60         $file = apply_filters( 'update_attached_file', $file, $attachment_id );
61
62         return update_post_meta( $attachment_id, '_wp_attached_file', $file );
63 }
64
65 /**
66  * Retrieve all children of the post parent ID.
67  *
68  * Normally, without any enhancements, the children would apply to pages. In the
69  * context of the inner workings of WordPress, pages, posts, and attachments
70  * share the same table, so therefore the functionality could apply to any one
71  * of them. It is then noted that while this function does not work on posts, it
72  * does not mean that it won't work on posts. It is recommended that you know
73  * what context you wish to retrieve the children of.
74  *
75  * Attachments may also be made the child of a post, so if that is an accurate
76  * statement (which needs to be verified), it would then be possible to get
77  * all of the attachments for a post. Attachments have since changed since
78  * version 2.5, so this is most likely unaccurate, but serves generally as an
79  * example of what is possible.
80  *
81  * The arguments listed as defaults are for this function and also of the
82  * get_posts() function. The arguments are combined with the get_children
83  * defaults and are then passed to the get_posts() function, which accepts
84  * additional arguments. You can replace the defaults in this function, listed
85  * below and the additional arguments listed in the get_posts() function.
86  *
87  * The 'post_parent' is the most important argument and important attention
88  * needs to be paid to the $args parameter. If you pass either an object or an
89  * integer (number), then just the 'post_parent' is grabbed and everything else
90  * is lost. If you don't specify any arguments, then it is assumed that you are
91  * in The Loop and the post parent will be grabbed for from the current post.
92  *
93  * The 'post_parent' argument is the ID to get the children. The 'numberposts'
94  * is the amount of posts to retrieve that has a default of '-1', which is
95  * used to get all of the posts. Giving a number higher than 0 will only
96  * retrieve that amount of posts.
97  *
98  * The 'post_type' and 'post_status' arguments can be used to choose what
99  * criteria of posts to retrieve. The 'post_type' can be anything, but WordPress
100  * post types are 'post', 'pages', and 'attachments'. The 'post_status'
101  * argument will accept any post status within the write administration panels.
102  *
103  * @see get_posts() Has additional arguments that can be replaced.
104  * @internal Claims made in the long description might be inaccurate.
105  *
106  * @package WordPress
107  * @subpackage Post
108  * @since 2.0
109  *
110  * @param mixed $args Optional. User defined arguments for replacing the defaults.
111  * @param string $output Optional. Constant for return type, either OBJECT (default), ARRAY_A, ARRAY_N.
112  * @return array|bool False on failure and the type will be determined by $output parameter.
113  */
114 function &get_children($args = '', $output = OBJECT) {
115         if ( empty( $args ) ) {
116                 if ( isset( $GLOBALS['post'] ) ) {
117                         $args = array('post_parent' => (int) $GLOBALS['post']->post_parent );
118                 } else {
119                         return false;
120                 }
121         } elseif ( is_object( $args ) ) {
122                 $args = array('post_parent' => (int) $args->post_parent );
123         } elseif ( is_numeric( $args ) ) {
124                 $args = array('post_parent' => (int) $args);
125         }
126
127         $defaults = array(
128                 'numberposts' => -1, 'post_type' => '',
129                 'post_status' => '', 'post_parent' => 0
130         );
131
132         $r = wp_parse_args( $args, $defaults );
133
134         $children = get_posts( $r );
135         if ( !$children )
136                 return false;
137
138         update_post_cache($children);
139
140         foreach ( $children as $key => $child )
141                 $kids[$child->ID] =& $children[$key];
142
143         if ( $output == OBJECT ) {
144                 return $kids;
145         } elseif ( $output == ARRAY_A ) {
146                 foreach ( $kids as $kid )
147                         $weeuns[$kid->ID] = get_object_vars($kids[$kid->ID]);
148                 return $weeuns;
149         } elseif ( $output == ARRAY_N ) {
150                 foreach ( $kids as $kid )
151                         $babes[$kid->ID] = array_values(get_object_vars($kids[$kid->ID]));
152                 return $babes;
153         } else {
154                 return $kids;
155         }
156 }
157
158 /**
159  * get_extended() - Get extended entry info (<!--more-->)
160  *
161  * {@internal Missing Long Description}}
162  *
163  * @package WordPress
164  * @subpackage Post
165  * @since 1.0.0
166  *
167  * @param string $post {@internal Missing Description}}
168  * @return array {@internal Missing Description}}
169  */
170 function get_extended($post) {
171         //Match the new style more links
172         if ( preg_match('/<!--more(.*?)?-->/', $post, $matches) ) {
173                 list($main, $extended) = explode($matches[0], $post, 2);
174         } else {
175                 $main = $post;
176                 $extended = '';
177         }
178
179         // Strip leading and trailing whitespace
180         $main = preg_replace('/^[\s]*(.*)[\s]*$/', '\\1', $main);
181         $extended = preg_replace('/^[\s]*(.*)[\s]*$/', '\\1', $extended);
182
183         return array('main' => $main, 'extended' => $extended);
184 }
185
186 /**
187  * get_post() - Retrieves post data given a post ID or post object.
188  *
189  * {@internal Missing Long Description}}
190  *
191  * @package WordPress
192  * @subpackage Post
193  * @since 1.5.1
194  * @uses $wpdb
195  * @link http://codex.wordpress.org/Function_Reference/get_post
196  *
197  * @param int|object &$post post ID or post object
198  * @param string $output {@internal Missing Description}}
199  * @param string $filter {@internal Missing Description}}
200  * @return mixed {@internal Missing Description}}
201  */
202 function &get_post(&$post, $output = OBJECT, $filter = 'raw') {
203         global $wpdb;
204         $null = null;
205
206         if ( empty($post) ) {
207                 if ( isset($GLOBALS['post']) )
208                         $_post = & $GLOBALS['post'];
209                 else
210                         return $null;
211         } elseif ( is_object($post) ) {
212                 _get_post_ancestors($post);
213                 wp_cache_add($post->ID, $post, 'posts');
214                 $_post = &$post;
215         } else {
216                 $post = (int) $post;
217                 if ( ! $_post = wp_cache_get($post, 'posts') ) {
218                         $_post = & $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d LIMIT 1", $post));
219                         if ( ! $_post )
220                                 return $null;
221                         _get_post_ancestors($_post);
222                         wp_cache_add($_post->ID, $_post, 'posts');
223                 }
224         }
225
226         $_post = sanitize_post($_post, $filter);
227
228         if ( $output == OBJECT ) {
229                 return $_post;
230         } elseif ( $output == ARRAY_A ) {
231                 $__post = get_object_vars($_post);
232                 return $__post;
233         } elseif ( $output == ARRAY_N ) {
234                 $__post = array_values(get_object_vars($_post));
235                 return $__post;
236         } else {
237                 return $_post;
238         }
239 }
240
241 /**
242  * Retrieve ancestors of a post.
243  *
244  * @package WordPress
245  * @subpackage Post
246  * @since 2.5
247  *
248  * @param int|object $post Post ID or post object
249  * @return array Ancestor IDs or empty array if none are found.
250  */
251 function get_post_ancestors($post) {
252         $post = get_post($post);
253
254         if ( !empty($post->ancestors) )
255                 return $post->ancestors;
256
257         return array();
258 }
259
260 /**
261  * Retrieve data from a post field based on Post ID.
262  *
263  * Examples of the post field will be, 'post_type', 'post_status', 'content',
264  * etc and based off of the post object property or key names.
265  *
266  * The context values are based off of the taxonomy filter functions and
267  * supported values are found within those functions.
268  *
269  * @package WordPress
270  * @subpackage Post
271  * @since 2.3
272  * @uses sanitize_post_field() See for possible $context values.
273  *
274  * @param string $field Post field name
275  * @param id $post Post ID
276  * @param string $context Optional. How to filter the field. Default is display.
277  * @return WP_Error|string Value in post field or WP_Error on failure
278  */
279 function get_post_field( $field, $post, $context = 'display' ) {
280         $post = (int) $post;
281         $post = get_post( $post );
282
283         if ( is_wp_error($post) )
284                 return $post;
285
286         if ( !is_object($post) )
287                 return '';
288
289         if ( !isset($post->$field) )
290                 return '';
291
292         return sanitize_post_field($field, $post->$field, $post->ID, $context);
293 }
294
295 /**
296  * Retrieve the mime type of an attachment based on the ID.
297  *
298  * This function can be used with any post type, but it makes more sense with
299  * attachments.
300  *
301  * @package WordPress
302  * @subpackage Post
303  * @since 2.0
304  *
305  * @param int $ID Optional. Post ID.
306  * @return bool|string False on failure or returns the mime type
307  */
308 function get_post_mime_type($ID = '') {
309         $post = & get_post($ID);
310
311         if ( is_object($post) )
312                 return $post->post_mime_type;
313
314         return false;
315 }
316
317 /**
318  * Retrieve the post status based on the Post ID.
319  *
320  * If the post ID is of an attachment, then the parent post status will be given
321  * instead.
322  *
323  * @package WordPress
324  * @subpackage Post
325  * @since 2.0
326  *
327  * @param int $ID Post ID
328  * @return string|bool Post status or false on failure.
329  */
330 function get_post_status($ID = '') {
331         $post = get_post($ID);
332
333         if ( is_object($post) ) {
334                 if ( ('attachment' == $post->post_type) && $post->post_parent && ($post->ID != $post->post_parent) )
335                         return get_post_status($post->post_parent);
336                 else
337                         return $post->post_status;
338         }
339
340         return false;
341 }
342
343 /**
344  * Retrieve all of the WordPress supported post statuses.
345  *
346  * Posts have a limited set of valid status values, this provides the
347  * post_status values and descriptions.
348  *
349  * @package WordPress
350  * @subpackage Post
351  * @since 2.5
352  *
353  * @return array List of post statuses.
354  */
355 function get_post_statuses( ) {
356         $status = array(
357                 'draft'                 => __('Draft'),
358                 'pending'               => __('Pending Review'),
359                 'private'               => __('Private'),
360                 'publish'               => __('Published')
361         );
362
363         return $status;
364 }
365
366 /**
367  * Retrieve all of the WordPress support page statuses.
368  *
369  * Pages have a limited set of valid status values, this provides the
370  * post_status values and descriptions.
371  *
372  * @package WordPress
373  * @subpackage Page
374  * @since 2.5
375  *
376  * @return array List of page statuses.
377  */
378 function get_page_statuses( ) {
379         $status = array(
380                 'draft'                 => __('Draft'),
381                 'private'               => __('Private'),
382                 'publish'               => __('Published')
383         );
384
385         return $status;
386 }
387
388 /**
389  * get_post_type() - Returns post type
390  *
391  * {@internal Missing Long Description}}
392  *
393  * @package WordPress
394  * @subpackage Post
395  * @since 2.1
396  *
397  * @uses $wpdb
398  * @uses $posts {@internal Missing Description}}
399  *
400  * @param mixed $post post object or post ID
401  * @return mixed post type or false
402  */
403 function get_post_type($post = false) {
404         global $posts;
405
406         if ( false === $post )
407                 $post = $posts[0];
408         elseif ( (int) $post )
409                 $post = get_post($post, OBJECT);
410
411         if ( is_object($post) )
412                 return $post->post_type;
413
414         return false;
415 }
416
417 /**
418  * set_post_type() - Set post type
419  *
420  * {@internal Missing Long Description}}
421  *
422  * @package WordPress
423  * @subpackage Post
424  * @since 2.5
425  *
426  * @uses $wpdb
427  * @uses $posts {@internal Missing Description}}
428  *
429  * @param mixed $post_id post ID
430  * @param mixed post type
431  * @return bool {@internal Missing Description}}
432  */
433 function set_post_type( $post_id = 0, $post_type = 'post' ) {
434         global $wpdb;
435
436         $post_type = sanitize_post_field('post_type', $post_type, $post_id, 'db');
437         $return = $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET post_type = %s WHERE ID = %d", $post_type, $post_id) );
438
439         if ( 'page' == $post_type )
440                 clean_page_cache($post_id);
441         else
442                 clean_post_cache($post_id);
443
444         return $return;
445 }
446
447 /**
448  * get_posts() - Returns a number of posts
449  *
450  * {@internal Missing Long Description}}
451  *
452  * @package WordPress
453  * @subpackage Post
454  * @since 1.2
455  * @uses $wpdb
456  * @link http://codex.wordpress.org/Template_Tags/get_posts
457  *
458  * @param array $args {@internal Missing Description}}
459  * @return array {@internal Missing Description}}
460  */
461 function get_posts($args = null) {
462         $defaults = array(
463                 'numberposts' => 5, 'offset' => 0,
464                 'category' => 0, 'orderby' => 'post_date',
465                 'order' => 'DESC', 'include' => '',
466                 'exclude' => '', 'meta_key' => '',
467                 'meta_value' =>'', 'post_type' => 'post',
468                 'post_parent' => 0, 'suppress_filters' => true
469         );
470
471         $r = wp_parse_args( $args, $defaults );
472         if ( empty( $r['post_status'] ) )
473                 $r['post_status'] = ( 'attachment' == $r['post_type'] ) ? 'inherit' : 'publish';
474         if ( ! empty($r['numberposts']) )
475                 $r['posts_per_page'] = $r['numberposts'];
476         if ( ! empty($r['category']) )
477                 $r['cat'] = $r['category'];
478         if ( ! empty($r['include']) ) {
479                 $incposts = preg_split('/[\s,]+/',$r['include']);
480                 $r['posts_per_page'] = count($incposts);  // only the number of posts included
481                 $r['post__in'] = $incposts;
482         } elseif ( ! empty($r['exclude']) )
483                 $r['post__not_in'] = preg_split('/[\s,]+/',$r['exclude']);
484
485         $get_posts = new WP_Query;
486         return $get_posts->query($r);
487
488 }
489
490 //
491 // Post meta functions
492 //
493
494 /**
495  * add_post_meta() - adds metadata for post
496  *
497  * {@internal Missing Long Description}}
498  *
499  * @package WordPress
500  * @subpackage Post
501  * @since 1.5
502  * @uses $wpdb
503  * @link http://codex.wordpress.org/Function_Reference/add_post_meta
504  *
505  * @param int $post_id post ID
506  * @param string $key {@internal Missing Description}}
507  * @param mixed $value {@internal Missing Description}}
508  * @param bool $unique whether to check for a value with the same key
509  * @return bool {@internal Missing Description}}
510  */
511 function add_post_meta($post_id, $meta_key, $meta_value, $unique = false) {
512         global $wpdb;
513
514         // make sure meta is added to the post, not a revision
515         if ( $the_post = wp_is_post_revision($post_id) )
516                 $post_id = $the_post;
517
518         // expected_slashed ($meta_key)
519         $meta_key = stripslashes($meta_key);
520
521         if ( $unique && $wpdb->get_var( $wpdb->prepare( "SELECT meta_key FROM $wpdb->postmeta WHERE meta_key = %s AND post_id = %d", $meta_key, $post_id ) ) )
522                 return false;
523
524         $meta_value = maybe_serialize($meta_value);
525
526         $wpdb->insert( $wpdb->postmeta, compact( 'post_id', 'meta_key', 'meta_value' ) );
527
528         wp_cache_delete($post_id, 'post_meta');
529
530         return true;
531 }
532
533 /**
534  * delete_post_meta() - delete post metadata
535  *
536  * {@internal Missing Long Description}}
537  *
538  * @package WordPress
539  * @subpackage Post
540  * @since 1.5
541  * @uses $wpdb
542  * @link http://codex.wordpress.org/Function_Reference/delete_post_meta
543  *
544  * @param int $post_id post ID
545  * @param string $key {@internal Missing Description}}
546  * @param mixed $value {@internal Missing Description}}
547  * @return bool {@internal Missing Description}}
548  */
549 function delete_post_meta($post_id, $key, $value = '') {
550         global $wpdb;
551
552         $post_id = absint( $post_id );
553
554         // expected_slashed ($key, $value)
555         $key = stripslashes( $key );
556         $value = stripslashes( $value );
557
558         if ( empty( $value ) )
559                 $meta_id = $wpdb->get_var( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s", $post_id, $key ) );
560         else
561                 $meta_id = $wpdb->get_var( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s AND meta_value = %s", $post_id, $key, $value ) );
562
563         if ( !$meta_id )
564                 return false;
565
566         if ( empty( $value ) )
567                 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s", $post_id, $key ) );
568         else
569                 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s AND meta_value = %s", $post_id, $key, $value ) );
570
571         wp_cache_delete($post_id, 'post_meta');
572
573         return true;
574 }
575
576 /**
577  * get_post_meta() - Get a post meta field
578  *
579  * {@internal Missing Long Description}}
580  *
581  * @package WordPress
582  * @subpackage Post
583  * @since 1.5
584  * @uses $wpdb
585  * @link http://codex.wordpress.org/Function_Reference/get_post_meta
586  *
587  * @param int $post_id post ID
588  * @param string $key The meta key to retrieve
589  * @param bool $single Whether to return a single value
590  * @return mixed {@internal Missing Description}}
591  */
592 function get_post_meta($post_id, $key, $single = false) {
593         $post_id = (int) $post_id;
594
595         $meta_cache = wp_cache_get($post_id, 'post_meta');
596
597         if ( !$meta_cache ) {
598                 update_postmeta_cache($post_id);
599                 $meta_cache = wp_cache_get($post_id, 'post_meta');
600         }
601
602         if ( isset($meta_cache[$key]) ) {
603                 if ( $single ) {
604                         return maybe_unserialize( $meta_cache[$key][0] );
605                 } else {
606                         return array_map('maybe_unserialize', $meta_cache[$key]);
607                 }
608         }
609
610         return '';
611 }
612
613 /**
614  * update_post_meta() - Update a post meta field
615  *
616  * {@internal Missing Long Description}}
617  *
618  * @package WordPress
619  * @subpackage Post
620  * @since 1.5
621  * @uses $wpdb
622  * @link http://codex.wordpress.org/Function_Reference/update_post_meta
623  *
624  * @param int $post_id post ID
625  * @param string $key {@internal Missing Description}}
626  * @param mixed $value {@internal Missing Description}}
627  * @param mixed $prev_value previous value (for differentiating between meta fields with the same key and post ID)
628  * @return bool {@internal Missing Description}}
629  */
630 function update_post_meta($post_id, $meta_key, $meta_value, $prev_value = '') {
631         global $wpdb;
632
633         // expected_slashed ($meta_key)
634         $meta_key = stripslashes($meta_key);
635
636         if ( ! $wpdb->get_var( $wpdb->prepare( "SELECT meta_key FROM $wpdb->postmeta WHERE meta_key = %s AND post_id = %d", $meta_key, $post_id ) ) ) {
637                 return add_post_meta($post_id, $meta_key, $meta_value);
638         }
639
640         $meta_value = maybe_serialize($meta_value);
641
642         $data  = compact( 'meta_value' );
643         $where = compact( 'meta_key', 'post_id' );
644
645         if ( !empty( $prev_value ) ) {
646                 $prev_value = maybe_serialize($prev_value);
647                 $where['meta_value'] = $prev_value;
648         }
649
650         $wpdb->update( $wpdb->postmeta, $data, $where );
651         wp_cache_delete($post_id, 'post_meta');
652         return true;
653 }
654
655 /**
656  * delete_post_meta_by_key() - Delete everything from post meta matching $post_meta_key
657  *
658  * @package WordPress
659  * @subpackage Post
660  * @since 2.3
661  * @uses $wpdb
662  *
663  * @param string $post_meta_key What to search for when deleting
664  * @return bool Whether the post meta key was deleted from the database
665  */
666 function delete_post_meta_by_key($post_meta_key) {
667         global $wpdb;
668         if ( $wpdb->query($wpdb->prepare("DELETE FROM $wpdb->postmeta WHERE meta_key = %s", $post_meta_key)) ) {
669                 /** @todo Get post_ids and delete cache */
670                 // wp_cache_delete($post_id, 'post_meta');
671                 return true;
672         }
673         return false;
674 }
675
676 /**
677  * get_post_custom() - Retrieve post custom fields
678  *
679  * {@internal Missing Long Description}}
680  *
681  * @package WordPress
682  * @subpackage Post
683  * @since 1.2
684  * @link http://codex.wordpress.org/Function_Reference/get_post_custom
685  *
686  * @uses $id
687  * @uses $wpdb
688  *
689  * @param int $post_id post ID
690  * @return array {@internal Missing Description}}
691  */
692 function get_post_custom($post_id = 0) {
693         global $id;
694
695         if ( !$post_id )
696                 $post_id = (int) $id;
697
698         $post_id = (int) $post_id;
699
700         if ( ! wp_cache_get($post_id, 'post_meta') )
701                 update_postmeta_cache($post_id);
702
703         return wp_cache_get($post_id, 'post_meta');
704 }
705
706 /**
707  * get_post_custom_keys() - Retrieve post custom field names
708  *
709  * @package WordPress
710  * @subpackage Post
711  * @since 1.2
712  * @link http://codex.wordpress.org/Function_Reference/get_post_custom_keys
713  *
714  * @param int $post_id post ID
715  * @return array|null Either array of the keys, or null if keys would not be retrieved
716  */
717 function get_post_custom_keys( $post_id = 0 ) {
718         $custom = get_post_custom( $post_id );
719
720         if ( !is_array($custom) )
721                 return;
722
723         if ( $keys = array_keys($custom) )
724                 return $keys;
725 }
726
727 /**
728  * get_post_custom_values() - Retrieve values for a custom post field
729  *
730  * @package WordPress
731  * @subpackage Post
732  * @since 1.2
733  * @link http://codex.wordpress.org/Function_Reference/get_post_custom_values
734  *
735  * @param string $key field name
736  * @param int $post_id post ID
737  * @return mixed {@internal Missing Description}}
738  */
739 function get_post_custom_values( $key = '', $post_id = 0 ) {
740         $custom = get_post_custom($post_id);
741
742         return $custom[$key];
743 }
744
745 /**
746  * sanitize_post() - Sanitize every post field
747  *
748  * {@internal Missing Long Description}}
749  *
750  * @package WordPress
751  * @subpackage Post
752  * @since 2.3
753  *
754  * @param object|array $post The Post Object or Array
755  * @param string $context How to sanitize post fields
756  * @return object|array The now sanitized Post Object or Array (will be the same type as $post)
757  */
758 function sanitize_post($post, $context = 'display') {
759         if ( 'raw' == $context )
760                 return $post;
761         if ( is_object($post) ) {
762                 foreach ( array_keys(get_object_vars($post)) as $field )
763                         $post->$field = sanitize_post_field($field, $post->$field, $post->ID, $context);
764         } else {
765                 foreach ( array_keys($post) as $field )
766                         $post[$field] = sanitize_post_field($field, $post[$field], $post['ID'], $context);
767         }
768         return $post;
769 }
770
771 /**
772  * sanitize_post_field() - Sanitize post field based on context
773  *
774  * {@internal Missing Long Description}}
775  *
776  * @package WordPress
777  * @subpackage Post
778  * @since 2.3
779  *
780  * @param string $field The Post Object field name
781  * @param string $value The Post Object value
782  * @param int $postid Post ID
783  * @param string $context How to sanitize post fields
784  * @return string Sanitized value
785  */
786 function sanitize_post_field($field, $value, $post_id, $context) {
787         $int_fields = array('ID', 'post_parent', 'menu_order');
788         if ( in_array($field, $int_fields) )
789                 $value = (int) $value;
790
791         if ( 'raw' == $context )
792                 return $value;
793
794         $prefixed = false;
795         if ( false !== strpos($field, 'post_') ) {
796                 $prefixed = true;
797                 $field_no_prefix = str_replace('post_', '', $field);
798         }
799
800         if ( 'edit' == $context ) {
801                 $format_to_edit = array('post_content', 'post_excerpt', 'post_title', 'post_password');
802
803                 if ( $prefixed ) {
804                         $value = apply_filters("edit_$field", $value, $post_id);
805                         // Old school
806                         $value = apply_filters("${field_no_prefix}_edit_pre", $value, $post_id);
807                 } else {
808                         $value = apply_filters("edit_post_$field", $value, $post_id);
809                 }
810
811                 if ( in_array($field, $format_to_edit) ) {
812                         if ( 'post_content' == $field )
813                                 $value = format_to_edit($value, user_can_richedit());
814                         else
815                                 $value = format_to_edit($value);
816                 } else {
817                         $value = attribute_escape($value);
818                 }
819         } else if ( 'db' == $context ) {
820                 if ( $prefixed ) {
821                         $value = apply_filters("pre_$field", $value);
822                         $value = apply_filters("${field_no_prefix}_save_pre", $value);
823                 } else {
824                         $value = apply_filters("pre_post_$field", $value);
825                         $value = apply_filters("${field}_pre", $value);
826                 }
827         } else {
828                 // Use display filters by default.
829                 if ( $prefixed )
830                         $value = apply_filters($field, $value, $post_id, $context);
831                 else
832                         $value = apply_filters("post_$field", $value, $post_id, $context);
833         }
834
835         if ( 'attribute' == $context )
836                 $value = attribute_escape($value);
837         else if ( 'js' == $context )
838                 $value = js_escape($value);
839
840         return $value;
841 }
842
843 /**
844  * Count number of posts of a post type and is user has permissions to view.
845  *
846  * This function provides an efficient method of finding the amount of post's
847  * type a blog has. Another method is to count the amount of items in
848  * get_posts(), but that method has a lot of overhead with doing so. Therefore,
849  * when developing for 2.5+, use this function instead.
850  *
851  * The $perm parameter checks for 'readable' value and if the user can read
852  * private posts, it will display that for the user that is signed in.
853  *
854  * @package WordPress
855  * @subpackage Post
856  * @since 2.5
857  * @link http://codex.wordpress.org/Template_Tags/wp_count_posts
858  *
859  * @param string $type Optional. Post type to retrieve count
860  * @param string $perm Optional. 'readable' or empty.
861  * @return object Number of posts for each status
862  */
863 function wp_count_posts( $type = 'post', $perm = '' ) {
864         global $wpdb;
865
866         $user = wp_get_current_user();
867
868         $cache_key = $type;
869
870         $query = "SELECT post_status, COUNT( * ) AS num_posts FROM {$wpdb->posts} WHERE post_type = %s";
871         if ( 'readable' == $perm && is_user_logged_in() ) {
872                 if ( !current_user_can("read_private_{$type}s") ) {
873                         $cache_key .= '_' . $perm . '_' . $user->ID;
874                         $query .= " AND (post_status != 'private' OR ( post_author = '$user->ID' AND post_status = 'private' ))";
875                 }
876         }
877         $query .= ' GROUP BY post_status';
878
879         $count = wp_cache_get($cache_key, 'counts');
880         if ( false !== $count )
881                 return $count;
882
883         $count = $wpdb->get_results( $wpdb->prepare( $query, $type ), ARRAY_A );
884
885         $stats = array( );
886         foreach( (array) $count as $row_num => $row ) {
887                 $stats[$row['post_status']] = $row['num_posts'];
888         }
889
890         $stats = (object) $stats;
891         wp_cache_set($cache_key, $stats, 'counts');
892
893         return $stats;
894 }
895
896
897 /**
898  * wp_count_attachments() - Count number of attachments
899  *
900  * {@internal Missing Long Description}}
901  *
902  * @package WordPress
903  * @subpackage Post
904  * @since 2.5
905  *
906  * @param string|array $post_mime_type Array or comma-separated list of MIME patterns
907  * @return array Number of posts for each post_mime_type
908  */
909
910 function wp_count_attachments( $mime_type = '' ) {
911         global $wpdb;
912
913         $and = wp_post_mime_type_where( $mime_type );
914         $count = $wpdb->get_results( "SELECT post_mime_type, COUNT( * ) AS num_posts FROM $wpdb->posts WHERE post_type = 'attachment' $and GROUP BY post_mime_type", ARRAY_A );
915
916         $stats = array( );
917         foreach( (array) $count as $row ) {
918                 $stats[$row['post_mime_type']] = $row['num_posts'];
919         }
920
921         return (object) $stats;
922 }
923
924 /**
925  * wp_match_mime_type() - Check a MIME-Type against a list
926  *
927  * {@internal Missing Long Description}}
928  *
929  * @package WordPress
930  * @subpackage Post
931  * @since 2.5
932  *
933  * @param string|array $wildcard_mime_types e.g. audio/mpeg or image (same as image/*) or flash (same as *flash*)
934  * @param string|array $real_mime_types post_mime_type values
935  * @return array array(wildcard=>array(real types))
936  */
937 function wp_match_mime_types($wildcard_mime_types, $real_mime_types) {
938         $matches = array();
939         if ( is_string($wildcard_mime_types) )
940                 $wildcard_mime_types = array_map('trim', explode(',', $wildcard_mime_types));
941         if ( is_string($real_mime_types) )
942                 $real_mime_types = array_map('trim', explode(',', $real_mime_types));
943         $wild = '[-._a-z0-9]*';
944         foreach ( (array) $wildcard_mime_types as $type ) {
945                 $type = str_replace('*', $wild, $type);
946                 $patternses[1][$type] = "^$type$";
947                 if ( false === strpos($type, '/') ) {
948                         $patternses[2][$type] = "^$type/";
949                         $patternses[3][$type] = $type;
950                 }
951         }
952         asort($patternses);
953         foreach ( $patternses as $patterns )
954                 foreach ( $patterns as $type => $pattern )
955                         foreach ( (array) $real_mime_types as $real )
956                                 if ( preg_match("#$pattern#", $real) && ( empty($matches[$type]) || false === array_search($real, $matches[$type]) ) )
957                                         $matches[$type][] = $real;
958         return $matches;
959 }
960
961 /**
962  * wp_get_post_mime_type_where() - Convert MIME types into SQL
963  *
964  * @package WordPress
965  * @subpackage Post
966  * @since 2.5
967  *
968  * @param string|array $mime_types MIME types
969  * @return string SQL AND clause
970  */
971 function wp_post_mime_type_where($post_mime_types) {
972         $where = '';
973         $wildcards = array('', '%', '%/%');
974         if ( is_string($post_mime_types) )
975                 $post_mime_types = array_map('trim', explode(',', $post_mime_types));
976         foreach ( (array) $post_mime_types as $mime_type ) {
977                 $mime_type = preg_replace('/\s/', '', $mime_type);
978                 $slashpos = strpos($mime_type, '/');
979                 if ( false !== $slashpos ) {
980                         $mime_group = preg_replace('/[^-*.a-zA-Z0-9]/', '', substr($mime_type, 0, $slashpos));
981                         $mime_subgroup = preg_replace('/[^-*.a-zA-Z0-9]/', '', substr($mime_type, $slashpos + 1));
982                         if ( empty($mime_subgroup) )
983                                 $mime_subgroup = '*';
984                         else
985                                 $mime_subgroup = str_replace('/', '', $mime_subgroup);
986                         $mime_pattern = "$mime_group/$mime_subgroup";
987                 } else {
988                         $mime_pattern = preg_replace('/[^-*.a-zA-Z0-9]/', '', $mime_type);
989                         if ( false === strpos($mime_pattern, '*') )
990                                 $mime_pattern .= '/*';
991                 }
992
993                 $mime_pattern = preg_replace('/\*+/', '%', $mime_pattern);
994
995                 if ( in_array( $mime_type, $wildcards ) )
996                         return '';
997
998                 if ( false !== strpos($mime_pattern, '%') )
999                         $wheres[] = "post_mime_type LIKE '$mime_pattern'";
1000                 else
1001                         $wheres[] = "post_mime_type = '$mime_pattern'";
1002         }
1003         if ( !empty($wheres) )
1004                 $where = ' AND (' . join(' OR ', $wheres) . ') ';
1005         return $where;
1006 }
1007
1008 /**
1009  * wp_delete_post() - Deletes a Post
1010  *
1011  * {@internal Missing Long Description}}
1012  *
1013  * @package WordPress
1014  * @subpackage Post
1015  * @since 1.0.0
1016  *
1017  * @param int $postid post ID
1018  * @return mixed {@internal Missing Description}}
1019  */
1020 function wp_delete_post($postid = 0) {
1021         global $wpdb, $wp_rewrite;
1022
1023         if ( !$post = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d", $postid)) )
1024                 return $post;
1025
1026         if ( 'attachment' == $post->post_type )
1027                 return wp_delete_attachment($postid);
1028
1029         do_action('delete_post', $postid);
1030
1031         /** @todo delete for pluggable post taxonomies too */
1032         wp_delete_object_term_relationships($postid, array('category', 'post_tag'));
1033
1034         $parent_data = array( 'post_parent' => $post->post_parent );
1035         $parent_where = array( 'post_parent' => $postid );
1036
1037         if ( 'page' == $post->post_type) {
1038                 // if the page is defined in option page_on_front or post_for_posts,
1039                 // adjust the corresponding options
1040                 if ( get_option('page_on_front') == $postid ) {
1041                         update_option('show_on_front', 'posts');
1042                         delete_option('page_on_front');
1043                 }
1044                 if ( get_option('page_for_posts') == $postid ) {
1045                         delete_option('page_for_posts');
1046                 }
1047
1048                 // Point children of this page to its parent, also clean the cache of affected children
1049                 $children_query = $wpdb->prepare("SELECT * FROM $wpdb->posts WHERE post_parent = %d AND post_type='page'", $postid);
1050                 $children = $wpdb->get_results($children_query);
1051
1052                 $wpdb->update( $wpdb->posts, $parent_data, $parent_where + array( 'post_type' => 'page' ) );
1053         }
1054
1055         // Do raw query.  wp_get_post_revisions() is filtered
1056         $revision_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'revision'", $postid ) );
1057         // Use wp_delete_post (via wp_delete_post_revision) again.  Ensures any meta/misplaced data gets cleaned up.
1058         foreach ( $revision_ids as $revision_id )
1059                 wp_delete_post_revision( $revision_id );
1060
1061         // Point all attachments to this post up one level
1062         $wpdb->update( $wpdb->posts, $parent_data, $parent_where + array( 'post_type' => 'attachment' ) );
1063
1064         $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->posts WHERE ID = %d", $postid ));
1065
1066         $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->comments WHERE comment_post_ID = %d", $postid ));
1067
1068         $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE post_id = %d", $postid ));
1069
1070         if ( 'page' == $post->post_type ) {
1071                 clean_page_cache($postid);
1072
1073                 foreach ( (array) $children as $child )
1074                         clean_page_cache($child->ID);
1075
1076                 $wp_rewrite->flush_rules();
1077         } else {
1078                 clean_post_cache($postid);
1079         }
1080
1081         do_action('deleted_post', $postid);
1082
1083         return $post;
1084 }
1085
1086 /**
1087  * wp_get_post_categories() - Retrieve the list of categories for a post
1088  *
1089  * Compatibility layer for themes and plugins. Also an easy layer of abstraction
1090  * away from the complexity of the taxonomy layer.
1091  *
1092  * @package WordPress
1093  * @subpackage Post
1094  * @since 2.1
1095  *
1096  * @uses wp_get_object_terms() Retrieves the categories. Args details can be found here
1097  *
1098  * @param int $post_id Optional. The Post ID
1099  * @param array $args Optional. Overwrite the defaults
1100  * @return array {@internal Missing Description}}
1101  */
1102 function wp_get_post_categories( $post_id = 0, $args = array() ) {
1103         $post_id = (int) $post_id;
1104
1105         $defaults = array('fields' => 'ids');
1106         $args = wp_parse_args( $args, $defaults );
1107
1108         $cats = wp_get_object_terms($post_id, 'category', $args);
1109         return $cats;
1110 }
1111
1112 /**
1113  * wp_get_post_tags() - Retrieve the post tags
1114  *
1115  * @package WordPress
1116  * @subpackage Post
1117  * @since 2.3
1118  *
1119  * @uses wp_get_object_terms() Gets the tags for returning. Args can be found here
1120  *
1121  * @param int $post_id Optional. The Post ID
1122  * @param array $args Optional. Overwrite the defaults
1123  * @return mixed The tags the post has currently
1124  */
1125 function wp_get_post_tags( $post_id = 0, $args = array() ) {
1126         $post_id = (int) $post_id;
1127
1128         $defaults = array('fields' => 'all');
1129         $args = wp_parse_args( $args, $defaults );
1130
1131         $tags = wp_get_object_terms($post_id, 'post_tag', $args);
1132
1133         return $tags;
1134 }
1135
1136 /**
1137  * wp_get_recent_posts() - Get the $num most recent posts
1138  *
1139  * {@internal Missing Long Description}}
1140  *
1141  * @package WordPress
1142  * @subpackage Post
1143  * @since 1.0.0
1144  *
1145  * @param int $num number of posts to get
1146  * @return array {@internal Missing Description}}
1147  */
1148 function wp_get_recent_posts($num = 10) {
1149         global $wpdb;
1150
1151         // Set the limit clause, if we got a limit
1152         $num = (int) $num;
1153         if ($num) {
1154                 $limit = "LIMIT $num";
1155         }
1156
1157         $sql = "SELECT * FROM $wpdb->posts WHERE post_type = 'post' ORDER BY post_date DESC $limit";
1158         $result = $wpdb->get_results($sql,ARRAY_A);
1159
1160         return $result?$result:array();
1161 }
1162
1163 /**
1164  * wp_get_single_post() - Get one post
1165  *
1166  * {@internal Missing Long Description}}
1167  *
1168  * @package WordPress
1169  * @subpackage Post
1170  * @since 1.0.0
1171  * @uses $wpdb
1172  *
1173  * @param int $postid post ID
1174  * @param string $mode How to return result, either OBJECT, ARRAY_N, or ARRAY_A
1175  * @return object|array Post object or array holding post contents and information
1176  */
1177 function wp_get_single_post($postid = 0, $mode = OBJECT) {
1178         $postid = (int) $postid;
1179
1180         $post = get_post($postid, $mode);
1181
1182         // Set categories and tags
1183         if($mode == OBJECT) {
1184                 $post->post_category = wp_get_post_categories($postid);
1185                 $post->tags_input = wp_get_post_tags($postid, array('fields' => 'names'));
1186         }
1187         else {
1188                 $post['post_category'] = wp_get_post_categories($postid);
1189                 $post['tags_input'] = wp_get_post_tags($postid, array('fields' => 'names'));
1190         }
1191
1192         return $post;
1193 }
1194
1195 /**
1196  * wp_insert_post() - Insert a post
1197  *
1198  * {@internal Missing Long Description}}
1199  *
1200  * @package WordPress
1201  * @subpackage Post
1202  * @since 1.0.0
1203  *
1204  * @uses $wpdb
1205  * @uses $wp_rewrite
1206  * @uses $user_ID
1207  * @uses $allowedtags
1208  *
1209  * @param array $postarr post contents
1210  * @return int post ID or 0 on error
1211  */
1212 function wp_insert_post($postarr = array(), $wp_error = false) {
1213         global $wpdb, $wp_rewrite, $user_ID;
1214
1215         $defaults = array('post_status' => 'draft', 'post_type' => 'post', 'post_author' => $user_ID,
1216                 'ping_status' => get_option('default_ping_status'), 'post_parent' => 0,
1217                 'menu_order' => 0, 'to_ping' =>  '', 'pinged' => '', 'post_password' => '',
1218                 'guid' => '', 'post_content_filtered' => '', 'post_excerpt' => '');
1219
1220         $postarr = wp_parse_args($postarr, $defaults);
1221         $postarr = sanitize_post($postarr, 'db');
1222
1223         // export array as variables
1224         extract($postarr, EXTR_SKIP);
1225
1226         // Are we updating or creating?
1227         $update = false;
1228         if ( !empty($ID) ) {
1229                 $update = true;
1230                 $previous_status = get_post_field('post_status', $ID);
1231         } else {
1232                 $previous_status = 'new';
1233         }
1234
1235         if ( ('' == $post_content) && ('' == $post_title) && ('' == $post_excerpt) ) {
1236                 if ( $wp_error )
1237                         return new WP_Error('empty_content', __('Content, title, and excerpt are empty.'));
1238                 else
1239                         return 0;
1240         }
1241
1242         // Make sure we set a valid category
1243         if (0 == count($post_category) || !is_array($post_category)) {
1244                 $post_category = array(get_option('default_category'));
1245         }
1246
1247         if ( empty($post_author) )
1248                 $post_author = $user_ID;
1249
1250         if ( empty($post_status) )
1251                 $post_status = 'draft';
1252
1253         if ( empty($post_type) )
1254                 $post_type = 'post';
1255
1256         // Get the post ID and GUID
1257         if ( $update ) {
1258                 $post_ID = (int) $ID;
1259                 $guid = get_post_field( 'guid', $post_ID );
1260         }
1261
1262         // Create a valid post name.  Drafts are allowed to have an empty
1263         // post name.
1264         if ( empty($post_name) ) {
1265                 if ( 'draft' != $post_status )
1266                         $post_name = sanitize_title($post_title);
1267         } else {
1268                 $post_name = sanitize_title($post_name);
1269         }
1270
1271         // If the post date is empty (due to having been new or a draft) and status is not 'draft', set date to now
1272         if ( empty($post_date) || '0000-00-00 00:00:00' == $post_date ) {
1273                 if ( !in_array($post_status, array('draft', 'pending')) )
1274                         $post_date = current_time('mysql');
1275                 else
1276                         $post_date = '0000-00-00 00:00:00';
1277         }
1278
1279         if ( empty($post_date_gmt) || '0000-00-00 00:00:00' == $post_date_gmt ) {
1280                 if ( !in_array($post_status, array('draft', 'pending')) )
1281                         $post_date_gmt = get_gmt_from_date($post_date);
1282                 else
1283                         $post_date_gmt = '0000-00-00 00:00:00';
1284         }
1285
1286         if ( $update || '0000-00-00 00:00:00' == $post_date ) {
1287                 $post_modified     = current_time( 'mysql' );
1288                 $post_modified_gmt = current_time( 'mysql', 1 );
1289         } else {
1290                 $post_modified     = $post_date;
1291                 $post_modified_gmt = $post_date_gmt;
1292         }
1293
1294         if ( 'publish' == $post_status ) {
1295                 $now = gmdate('Y-m-d H:i:59');
1296                 if ( mysql2date('U', $post_date_gmt) > mysql2date('U', $now) )
1297                         $post_status = 'future';
1298         }
1299
1300         if ( empty($comment_status) ) {
1301                 if ( $update )
1302                         $comment_status = 'closed';
1303                 else
1304                         $comment_status = get_option('default_comment_status');
1305         }
1306         if ( empty($ping_status) )
1307                 $ping_status = get_option('default_ping_status');
1308
1309         if ( isset($to_ping) )
1310                 $to_ping = preg_replace('|\s+|', "\n", $to_ping);
1311         else
1312                 $to_ping = '';
1313
1314         if ( ! isset($pinged) )
1315                 $pinged = '';
1316
1317         if ( isset($post_parent) )
1318                 $post_parent = (int) $post_parent;
1319         else
1320                 $post_parent = 0;
1321
1322         if ( isset($menu_order) )
1323                 $menu_order = (int) $menu_order;
1324         else
1325                 $menu_order = 0;
1326
1327         if ( !isset($post_password) )
1328                 $post_password = '';
1329
1330         if ( 'draft' != $post_status ) {
1331                 $post_name_check = $wpdb->get_var($wpdb->prepare("SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d AND post_parent = %d LIMIT 1", $post_name, $post_type, $post_ID, $post_parent));
1332
1333                 if ($post_name_check || in_array($post_name, $wp_rewrite->feeds) ) {
1334                         $suffix = 2;
1335                         do {
1336                                 $alt_post_name = substr($post_name, 0, 200-(strlen($suffix)+1)). "-$suffix";
1337                                 $post_name_check = $wpdb->get_var($wpdb->prepare("SELECT post_name FROM $wpdb->posts WHERE post_name = %s AND post_type = %s AND ID != %d AND post_parent = %d LIMIT 1", $alt_post_name, $post_type, $post_ID, $post_parent));
1338                                 $suffix++;
1339                         } while ($post_name_check);
1340                         $post_name = $alt_post_name;
1341                 }
1342         }
1343
1344         // expected_slashed (everything!)
1345         $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' ) );
1346         $data = stripslashes_deep( $data );
1347         $where = array( 'ID' => $post_ID );
1348
1349         if ($update) {
1350                 do_action( 'pre_post_update', $post_ID );
1351                 if ( false === $wpdb->update( $wpdb->posts, $data, $where ) ) {
1352                         if ( $wp_error )
1353                                 return new WP_Error('db_update_error', __('Could not update post in the database'), $wpdb->last_error);
1354                         else
1355                                 return 0;
1356                 }
1357         } else {
1358                 $data['post_mime_type'] = stripslashes( $post_mime_type ); // This isn't in the update
1359                 if ( false === $wpdb->insert( $wpdb->posts, $data ) ) {
1360                         if ( $wp_error )
1361                                 return new WP_Error('db_insert_error', __('Could not insert post into the database'), $wpdb->last_error);
1362                         else
1363                                 return 0;       
1364                 }
1365                 $post_ID = (int) $wpdb->insert_id;
1366
1367                 // use the newly generated $post_ID
1368                 $where = array( 'ID' => $post_ID );
1369         }
1370
1371         if ( empty($post_name) && 'draft' != $post_status ) {
1372                 $post_name = sanitize_title($post_title, $post_ID);
1373                 $wpdb->update( $wpdb->posts, compact( 'post_name' ), $where );
1374         }
1375
1376         wp_set_post_categories( $post_ID, $post_category );
1377         wp_set_post_tags( $post_ID, $tags_input );
1378
1379         $current_guid = get_post_field( 'guid', $post_ID );
1380
1381         if ( 'page' == $post_type )
1382                 clean_page_cache($post_ID);
1383         else
1384                 clean_post_cache($post_ID);
1385
1386         // Set GUID
1387         if ( !$update && '' == $current_guid )
1388                 $wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post_ID ) ), $where );
1389
1390         $post = get_post($post_ID);
1391
1392         if ( !empty($page_template) && 'page' == $post_type ) {
1393                 $post->page_template = $page_template;
1394                 $page_templates = get_page_templates();
1395                 if ( 'default' != $page_template && !in_array($page_template, $page_templates) ) {
1396                         if ( $wp_error )
1397                                 return new WP_Error('invalid_page_template', __('The page template is invalid.'));
1398                         else
1399                                 return 0;
1400                 }
1401                 update_post_meta($post_ID, '_wp_page_template',  $page_template);
1402         }
1403
1404         wp_transition_post_status($post_status, $previous_status, $post);
1405
1406         if ( $update)
1407                 do_action('edit_post', $post_ID, $post);
1408
1409         do_action('save_post', $post_ID, $post);
1410         do_action('wp_insert_post', $post_ID, $post);
1411
1412         return $post_ID;
1413 }
1414
1415 /**
1416  * wp_update_post() - Update a post
1417  *
1418  * {@internal Missing Long Description}}
1419  *
1420  * @package WordPress
1421  * @subpackage Post
1422  * @since 1.0.0
1423  * @uses $wpdb
1424  *
1425  * @param array $postarr post data
1426  * @return int {@internal Missing Description}}
1427  */
1428 function wp_update_post($postarr = array()) {
1429         if ( is_object($postarr) )
1430                 $postarr = get_object_vars($postarr);
1431
1432         // First, get all of the original fields
1433         $post = wp_get_single_post($postarr['ID'], ARRAY_A);
1434
1435         // Escape data pulled from DB.
1436         $post = add_magic_quotes($post);
1437
1438         // Passed post category list overwrites existing category list if not empty.
1439         if ( isset($postarr['post_category']) && is_array($postarr['post_category'])
1440                          && 0 != count($postarr['post_category']) )
1441                 $post_cats = $postarr['post_category'];
1442         else
1443                 $post_cats = $post['post_category'];
1444
1445         // Drafts shouldn't be assigned a date unless explicitly done so by the user
1446         if ( in_array($post['post_status'], array('draft', 'pending')) && empty($postarr['edit_date']) && empty($postarr['post_date']) &&
1447                          ('0000-00-00 00:00:00' == $post['post_date']) )
1448                 $clear_date = true;
1449         else
1450                 $clear_date = false;
1451
1452         // Merge old and new fields with new fields overwriting old ones.
1453         $postarr = array_merge($post, $postarr);
1454         $postarr['post_category'] = $post_cats;
1455         if ( $clear_date ) {
1456                 $postarr['post_date'] = '';
1457                 $postarr['post_date_gmt'] = '';
1458         }
1459
1460         if ($postarr['post_type'] == 'attachment')
1461                 return wp_insert_attachment($postarr);
1462
1463         return wp_insert_post($postarr);
1464 }
1465
1466 /**
1467  * wp_publish_post() - Mark a post as "published"
1468  *
1469  * {@internal Missing Long Description}}
1470  *
1471  * @package WordPress
1472  * @subpackage Post
1473  * @since 2.1
1474  * @uses $wpdb
1475  *
1476  * @param int $post_id Post ID
1477  * @return int|null {@internal Missing Description}}
1478  */
1479 function wp_publish_post($post_id) {
1480         global $wpdb;
1481
1482         $post = get_post($post_id);
1483
1484         if ( empty($post) )
1485                 return;
1486
1487         if ( 'publish' == $post->post_status )
1488                 return;
1489
1490         $wpdb->update( $wpdb->posts, array( 'post_status' => 'publish' ), array( 'ID' => $post_id ) );
1491
1492         $old_status = $post->post_status;
1493         $post->post_status = 'publish';
1494         wp_transition_post_status('publish', $old_status, $post);
1495
1496         // Update counts for the post's terms.
1497         foreach ( get_object_taxonomies('post') as $taxonomy ) {
1498                 $terms = wp_get_object_terms($post_id, $taxonomy, 'fields=tt_ids');
1499                 wp_update_term_count($terms, $taxonomy);
1500         }
1501
1502         do_action('edit_post', $post_id, $post);
1503         do_action('save_post', $post_id, $post);
1504         do_action('wp_insert_post', $post_id, $post);
1505 }
1506
1507 /**
1508  * check_and_publish_future_post() - check to make sure post has correct status before
1509  * passing it on to be published. Invoked by cron 'publish_future_post' event
1510  * This safeguard prevents cron from publishing drafts, etc.
1511  *
1512  * {@internal Missing Long Description}}
1513  *
1514  * @package WordPress
1515  * @subpackage Post
1516  * @since 2.5
1517  * @uses $wpdb
1518  *
1519  * @param int $post_id Post ID
1520  * @return int|null {@internal Missing Description}}
1521  */
1522 function check_and_publish_future_post($post_id) {
1523
1524         $post = get_post($post_id);
1525
1526         if ( empty($post) )
1527                 return;
1528
1529         if ( 'future' != $post->post_status )
1530                 return;
1531
1532         return wp_publish_post($post_id);
1533 }
1534
1535 /**
1536  * wp_add_post_tags() - Adds the tags to a post
1537  *
1538  * @uses wp_set_post_tags() Same first two paraeters, but the last parameter is always set to true.
1539  *
1540  * @package WordPress
1541  * @subpackage Post
1542  * @since 2.3
1543  *
1544  * @param int $post_id Optional. Post ID
1545  * @param string $tags The tags to set for the post
1546  * @return bool|null Will return false if $post_id is not an integer or is 0. Will return null otherwise
1547  */
1548 function wp_add_post_tags($post_id = 0, $tags = '') {
1549         return wp_set_post_tags($post_id, $tags, true);
1550 }
1551
1552 /**
1553  * wp_set_post_tags() - Set the tags for a post
1554  *
1555  * {@internal Missing Long Description}}
1556  *
1557  * @package WordPress
1558  * @subpackage Post
1559  * @since 2.3
1560  * @uses $wpdb
1561  *
1562  * @param int $post_id post ID
1563  * @param string $tags The tags to set for the post
1564  * @param bool $append If true, don't delete existing tags, just add on. If false, replace the tags with the new tags.
1565  * @return bool|null Will return false if $post_id is not an integer or is 0. Will return null otherwise
1566  */
1567 function wp_set_post_tags( $post_id = 0, $tags = '', $append = false ) {
1568
1569         $post_id = (int) $post_id;
1570
1571         if ( !$post_id )
1572                 return false;
1573
1574         if ( empty($tags) )
1575                 $tags = array();
1576         $tags = (is_array($tags)) ? $tags : explode( ',', trim($tags, " \n\t\r\0\x0B,") );
1577         wp_set_object_terms($post_id, $tags, 'post_tag', $append);
1578 }
1579
1580 /**
1581  * wp_set_post_categories() - Set categories for a post
1582  *
1583  * {@internal Missing Long Description}}
1584  *
1585  * @package WordPress
1586  * @subpackage Post
1587  * @since 2.1
1588  * @uses $wpdb
1589  *
1590  * @param int $post_ID post ID
1591  * @param array $post_categories
1592  * @return bool|mixed {@internal Missing Description}}
1593  */
1594 function wp_set_post_categories($post_ID = 0, $post_categories = array()) {
1595         $post_ID = (int) $post_ID;
1596         // If $post_categories isn't already an array, make it one:
1597         if (!is_array($post_categories) || 0 == count($post_categories) || empty($post_categories))
1598                 $post_categories = array(get_option('default_category'));
1599         else if ( 1 == count($post_categories) && '' == $post_categories[0] )
1600                 return true;
1601
1602         $post_categories = array_map('intval', $post_categories);
1603         $post_categories = array_unique($post_categories);
1604
1605         return wp_set_object_terms($post_ID, $post_categories, 'category');
1606 }       // wp_set_post_categories()
1607
1608 /**
1609  * wp_transition_post_status() - Change the post transition status
1610  *
1611  * {@internal Missing Long Description}}
1612  *
1613  * @package WordPress
1614  * @subpackage Post
1615  * @since 2.3
1616  *
1617  * @param string $new_status {@internal Missing Description}}
1618  * @param string $old_status {@internal Missing Description}}
1619  * @param int $post {@internal Missing Description}}
1620  */
1621 function wp_transition_post_status($new_status, $old_status, $post) {
1622         if ( $new_status != $old_status ) {
1623                 do_action('transition_post_status', $new_status, $old_status, $post);
1624                 do_action("${old_status}_to_$new_status", $post);
1625         }
1626         do_action("${new_status}_$post->post_type", $post->ID, $post);
1627 }
1628
1629 //
1630 // Trackback and ping functions
1631 //
1632
1633 /**
1634  * add_ping() - Add a URL to those already pung
1635  *
1636  * {@internal Missing Long Description}}
1637  *
1638  * @package WordPress
1639  * @subpackage Post
1640  * @since 1.5
1641  * @uses $wpdb
1642  *
1643  * @param int $post_id post ID
1644  * @param string $uri {@internal Missing Description}}
1645  * @return mixed {@internal Missing Description}}
1646  */
1647 function add_ping($post_id, $uri) {
1648         global $wpdb;
1649         $pung = $wpdb->get_var( $wpdb->prepare( "SELECT pinged FROM $wpdb->posts WHERE ID = %d", $post_id ));
1650         $pung = trim($pung);
1651         $pung = preg_split('/\s/', $pung);
1652         $pung[] = $uri;
1653         $new = implode("\n", $pung);
1654         $new = apply_filters('add_ping', $new);
1655         // expected_slashed ($new)
1656         $new = stripslashes($new);
1657         return $wpdb->update( $wpdb->posts, array( 'pinged' => $new ), array( 'ID' => $post_id ) );
1658 }
1659
1660 /**
1661  * get_enclosed() - Get enclosures already enclosed for a post
1662  *
1663  * {@internal Missing Long Description}}
1664  *
1665  * @package WordPress
1666  * @subpackage Post
1667  * @since 1.5
1668  * @uses $wpdb
1669  *
1670  * @param int $post_id post ID
1671  * @return array {@internal Missing Description}}
1672  */
1673 function get_enclosed($post_id) {
1674         $custom_fields = get_post_custom( $post_id );
1675         $pung = array();
1676         if ( !is_array( $custom_fields ) )
1677                 return $pung;
1678
1679         foreach ( $custom_fields as $key => $val ) {
1680                 if ( 'enclosure' != $key || !is_array( $val ) )
1681                         continue;
1682                 foreach( $val as $enc ) {
1683                         $enclosure = split( "\n", $enc );
1684                         $pung[] = trim( $enclosure[ 0 ] );
1685                 }
1686         }
1687         $pung = apply_filters('get_enclosed', $pung);
1688         return $pung;
1689 }
1690
1691 /**
1692  * get_pung() - Get URLs already pinged for a post
1693  *
1694  * {@internal Missing Long Description}}
1695  *
1696  * @package WordPress
1697  * @subpackage Post
1698  * @since 1.5
1699  * @uses $wpdb
1700  *
1701  * @param int $post_id post ID
1702  * @return array {@internal Missing Description}}
1703  */
1704 function get_pung($post_id) {
1705         global $wpdb;
1706         $pung = $wpdb->get_var( $wpdb->prepare( "SELECT pinged FROM $wpdb->posts WHERE ID = %d", $post_id ));
1707         $pung = trim($pung);
1708         $pung = preg_split('/\s/', $pung);
1709         $pung = apply_filters('get_pung', $pung);
1710         return $pung;
1711 }
1712
1713 /**
1714  * get_to_ping() - Get any URLs in the todo list
1715  *
1716  * {@internal Missing Long Description}}
1717  *
1718  * @package WordPress
1719  * @subpackage Post
1720  * @since 1.5
1721  * @uses $wpdb
1722  *
1723  * @param int $post_id post ID
1724  * @return array {@internal Missing Description}}
1725  */
1726 function get_to_ping($post_id) {
1727         global $wpdb;
1728         $to_ping = $wpdb->get_var( $wpdb->prepare( "SELECT to_ping FROM $wpdb->posts WHERE ID = %d", $post_id ));
1729         $to_ping = trim($to_ping);
1730         $to_ping = preg_split('/\s/', $to_ping, -1, PREG_SPLIT_NO_EMPTY);
1731         $to_ping = apply_filters('get_to_ping',  $to_ping);
1732         return $to_ping;
1733 }
1734
1735 /**
1736  * trackback_url_list() - Do trackbacks for a list of urls
1737  *
1738  * {@internal Missing Long Description}}
1739  *
1740  * @package WordPress
1741  * @subpackage Post
1742  * @since 1.0.0
1743  *
1744  * @param string $tb_list comma separated list of URLs
1745  * @param int $post_id post ID
1746  */
1747 function trackback_url_list($tb_list, $post_id) {
1748         if (!empty($tb_list)) {
1749                 // get post data
1750                 $postdata = wp_get_single_post($post_id, ARRAY_A);
1751
1752                 // import postdata as variables
1753                 extract($postdata, EXTR_SKIP);
1754
1755                 // form an excerpt
1756                 $excerpt = strip_tags($post_excerpt?$post_excerpt:$post_content);
1757
1758                 if (strlen($excerpt) > 255) {
1759                         $excerpt = substr($excerpt,0,252) . '...';
1760                 }
1761
1762                 $trackback_urls = explode(',', $tb_list);
1763                 foreach($trackback_urls as $tb_url) {
1764                                 $tb_url = trim($tb_url);
1765                                 trackback($tb_url, stripslashes($post_title), $excerpt, $post_id);
1766                 }
1767                 }
1768 }
1769
1770 //
1771 // Page functions
1772 //
1773
1774 /**
1775  * get_all_page_ids() - Get a list of page IDs
1776  *
1777  * {@internal Missing Long Description}}
1778  *
1779  * @package WordPress
1780  * @subpackage Post
1781  * @since 2.0
1782  * @uses $wpdb
1783  *
1784  * @return array {@internal Missing Description}}
1785  */
1786 function get_all_page_ids() {
1787         global $wpdb;
1788
1789         if ( ! $page_ids = wp_cache_get('all_page_ids', 'posts') ) {
1790                 $page_ids = $wpdb->get_col("SELECT ID FROM $wpdb->posts WHERE post_type = 'page'");
1791                 wp_cache_add('all_page_ids', $page_ids, 'posts');
1792         }
1793
1794         return $page_ids;
1795 }
1796
1797 /**
1798  * get_page() - Retrieves page data given a page ID or page object
1799  *
1800  * {@internal Missing Long Description}}
1801  *
1802  * @package WordPress
1803  * @subpackage Post
1804  * @since 1.5.1
1805  *
1806  * @param mixed &$page page object or page ID
1807  * @param string $output what to output
1808  * @param string $filter How the return value should be filtered.
1809  * @return mixed {@internal Missing Description}}
1810  */
1811 function &get_page(&$page, $output = OBJECT, $filter = 'raw') {
1812         if ( empty($page) ) {
1813                 if ( isset( $GLOBALS['page'] ) && isset( $GLOBALS['page']->ID ) )
1814                         return get_post($GLOBALS['page'], $output, $filter);
1815                 else
1816                         return null;
1817         }
1818
1819         return get_post($page, $output, $filter);
1820 }
1821
1822 /**
1823  * get_page_by_path() - Retrieves a page given its path
1824  *
1825  * {@internal Missing Long Description}}
1826  *
1827  * @package WordPress
1828  * @subpackage Post
1829  * @since 2.1
1830  * @uses $wpdb
1831  *
1832  * @param string $page_path page path
1833  * @param string $output output type
1834  * @return mixed {@internal Missing Description}}
1835  */
1836 function get_page_by_path($page_path, $output = OBJECT) {
1837         global $wpdb;
1838         $page_path = rawurlencode(urldecode($page_path));
1839         $page_path = str_replace('%2F', '/', $page_path);
1840         $page_path = str_replace('%20', ' ', $page_path);
1841         $page_paths = '/' . trim($page_path, '/');
1842         $leaf_path  = sanitize_title(basename($page_paths));
1843         $page_paths = explode('/', $page_paths);
1844         foreach($page_paths as $pathdir)
1845                 $full_path .= ($pathdir!=''?'/':'') . sanitize_title($pathdir);
1846
1847         $pages = $wpdb->get_results( $wpdb->prepare( "SELECT ID, post_name, post_parent FROM $wpdb->posts WHERE post_name = %s AND (post_type = 'page' OR post_type = 'attachment')", $leaf_path ));
1848
1849         if ( empty($pages) )
1850                 return NULL;
1851
1852         foreach ($pages as $page) {
1853                 $path = '/' . $leaf_path;
1854                 $curpage = $page;
1855                 while ($curpage->post_parent != 0) {
1856                         $curpage = $wpdb->get_row( $wpdb->prepare( "SELECT ID, post_name, post_parent FROM $wpdb->posts WHERE ID = %d and post_type='page'", $curpage->post_parent ));
1857                         $path = '/' . $curpage->post_name . $path;
1858                 }
1859
1860                 if ( $path == $full_path )
1861                         return get_page($page->ID, $output);
1862         }
1863
1864         return NULL;
1865 }
1866
1867 /**
1868  * get_page_by_title() - Retrieve a page given its title
1869  *
1870  * {@internal Missing Long Description}}
1871  *
1872  * @package WordPress
1873  * @subpackage Post
1874  * @since 2.1
1875  * @uses $wpdb
1876  *
1877  * @param string $page_title page title
1878  * @param string $output output type
1879  * @return mixed {@internal Missing Description}}
1880  */
1881 function get_page_by_title($page_title, $output = OBJECT) {
1882         global $wpdb;
1883         $page = $wpdb->get_var( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_title = %s AND post_type='page'", $page_title ));
1884         if ( $page )
1885                 return get_page($page, $output);
1886
1887         return NULL;
1888 }
1889
1890 /**
1891  * get_page_children() - Retrieve child pages
1892  *
1893  * {@internal Missing Long Description}}
1894  *
1895  * @package WordPress
1896  * @subpackage Post
1897  * @since 1.5.1
1898  *
1899  * @param int $page_id page ID
1900  * @param array $pages list of pages
1901  * @return array {@internal Missing Description}}
1902  */
1903 function &get_page_children($page_id, $pages) {
1904         $page_list = array();
1905         foreach ( $pages as $page ) {
1906                 if ( $page->post_parent == $page_id ) {
1907                         $page_list[] = $page;
1908                         if ( $children = get_page_children($page->ID, $pages) )
1909                                 $page_list = array_merge($page_list, $children);
1910                 }
1911         }
1912         return $page_list;
1913 }
1914
1915 /**
1916  * get_page_hierarchy() - {@internal Missing Short Description}}
1917  *
1918  * Fetches the pages returned as a FLAT list, but arranged in order of their hierarchy,
1919  * i.e., child parents immediately follow their parents.
1920  *
1921  * @package WordPress
1922  * @subpackage Post
1923  * @since 2.0
1924  *
1925  * @param array $posts posts array
1926  * @param int $parent parent page ID
1927  * @return array {@internal Missing Description}}
1928  */
1929 function get_page_hierarchy($posts, $parent = 0) {
1930         $result = array ( );
1931         if ($posts) { foreach ($posts as $post) {
1932                 if ($post->post_parent == $parent) {
1933                         $result[$post->ID] = $post->post_name;
1934                         $children = get_page_hierarchy($posts, $post->ID);
1935                         $result += $children; //append $children to $result
1936                 }
1937         } }
1938         return $result;
1939 }
1940
1941 /**
1942  * get_page_uri() - Builds a page URI
1943  *
1944  * {@internal Missing Long Description}}
1945  *
1946  * @package WordPress
1947  * @subpackage Post
1948  * @since 1.5
1949  *
1950  * @param int $page_id page ID
1951  * @return string {@internal Missing Description}}
1952  */
1953 function get_page_uri($page_id) {
1954         $page = get_page($page_id);
1955         $uri = $page->post_name;
1956
1957         // A page cannot be it's own parent.
1958         if ( $page->post_parent == $page->ID )
1959                 return $uri;
1960
1961         while ($page->post_parent != 0) {
1962                 $page = get_page($page->post_parent);
1963                 $uri = $page->post_name . "/" . $uri;
1964         }
1965
1966         return $uri;
1967 }
1968
1969 /**
1970  * get_pages() - Retrieve a list of pages
1971  *
1972  * {@internal Missing Long Description}}
1973  *
1974  * @package WordPress
1975  * @subpackage Post
1976  * @since 1.5
1977  * @uses $wpdb
1978  *
1979  * @param mixed $args Optional. Array or string of options
1980  * @return array List of pages matching defaults or $args
1981  */
1982 function &get_pages($args = '') {
1983         global $wpdb;
1984
1985         $defaults = array(
1986                 'child_of' => 0, 'sort_order' => 'ASC',
1987                 'sort_column' => 'post_title', 'hierarchical' => 1,
1988                 'exclude' => '', 'include' => '',
1989                 'meta_key' => '', 'meta_value' => '',
1990                 'authors' => ''
1991         );
1992
1993         $r = wp_parse_args( $args, $defaults );
1994         extract( $r, EXTR_SKIP );
1995
1996         $key = md5( serialize( $r ) );
1997         if ( $cache = wp_cache_get( 'get_pages', 'posts' ) )
1998                 if ( isset( $cache[ $key ] ) )
1999                         return apply_filters('get_pages', $cache[ $key ], $r );
2000
2001         $inclusions = '';
2002         if ( !empty($include) ) {
2003                 $child_of = 0; //ignore child_of, exclude, meta_key, and meta_value params if using include
2004                 $exclude = '';
2005                 $meta_key = '';
2006                 $meta_value = '';
2007                 $hierarchical = false;
2008                 $incpages = preg_split('/[\s,]+/',$include);
2009                 if ( count($incpages) ) {
2010                         foreach ( $incpages as $incpage ) {
2011                                 if (empty($inclusions))
2012                                         $inclusions = $wpdb->prepare(' AND ( ID = %d ', $incpage);
2013                                 else
2014                                         $inclusions .= $wpdb->prepare(' OR ID = %d ', $incpage);
2015                         }
2016                 }
2017         }
2018         if (!empty($inclusions))
2019                 $inclusions .= ')';
2020
2021         $exclusions = '';
2022         if ( !empty($exclude) ) {
2023                 $expages = preg_split('/[\s,]+/',$exclude);
2024                 if ( count($expages) ) {
2025                         foreach ( $expages as $expage ) {
2026                                 if (empty($exclusions))
2027                                         $exclusions = $wpdb->prepare(' AND ( ID <> %d ', $expage);
2028                                 else
2029                                         $exclusions .= $wpdb->prepare(' AND ID <> %d ', $expage);
2030                         }
2031                 }
2032         }
2033         if (!empty($exclusions))
2034                 $exclusions .= ')';
2035
2036         $author_query = '';
2037         if (!empty($authors)) {
2038                 $post_authors = preg_split('/[\s,]+/',$authors);
2039
2040                 if ( count($post_authors) ) {
2041                         foreach ( $post_authors as $post_author ) {
2042                                 //Do we have an author id or an author login?
2043                                 if ( 0 == intval($post_author) ) {
2044                                         $post_author = get_userdatabylogin($post_author);
2045                                         if ( empty($post_author) )
2046                                                 continue;
2047                                         if ( empty($post_author->ID) )
2048                                                 continue;
2049                                         $post_author = $post_author->ID;
2050                                 }
2051
2052                                 if ( '' == $author_query )
2053                                         $author_query = $wpdb->prepare(' post_author = %d ', $post_author);
2054                                 else
2055                                         $author_query .= $wpdb->prepare(' OR post_author = %d ', $post_author);
2056                         }
2057                         if ( '' != $author_query )
2058                                 $author_query = " AND ($author_query)";
2059                 }
2060         }
2061
2062         $join = '';
2063         $where = "$exclusions $inclusions ";
2064         if ( ! empty( $meta_key ) || ! empty( $meta_value ) ) {
2065                 $join = " LEFT JOIN $wpdb->postmeta ON ( $wpdb->posts.ID = $wpdb->postmeta.post_id )";
2066                 
2067                 // meta_key and met_value might be slashed 
2068                 $meta_key = stripslashes($meta_key);
2069                 $meta_value = stripslashes($meta_value);
2070                 if ( ! empty( $meta_key ) )
2071                         $where .= $wpdb->prepare(" AND $wpdb->postmeta.meta_key = %s", $meta_key);
2072                 if ( ! empty( $meta_value ) )
2073                         $where .= $wpdb->prepare(" AND $wpdb->postmeta.meta_value = %s", $meta_value);
2074
2075         }
2076         $query = "SELECT * FROM $wpdb->posts $join WHERE (post_type = 'page' AND post_status = 'publish') $where ";
2077         $query .= $author_query;
2078         $query .= " ORDER BY " . $sort_column . " " . $sort_order ;
2079
2080         $pages = $wpdb->get_results($query);
2081
2082         if ( empty($pages) )
2083                 return apply_filters('get_pages', array(), $r);
2084
2085         // Update cache.
2086         update_page_cache($pages);
2087
2088         if ( $child_of || $hierarchical )
2089                 $pages = & get_page_children($child_of, $pages);
2090
2091         $cache[ $key ] = $pages;
2092         wp_cache_set( 'get_pages', $cache, 'posts' );
2093
2094         $pages = apply_filters('get_pages', $pages, $r);
2095
2096         return $pages;
2097 }
2098
2099 //
2100 // Attachment functions
2101 //
2102
2103 /**
2104  * is_local_attachment() - Check if the attachment URI is local one and is really an attachment.
2105  *
2106  * {@internal Missing Long Description}}
2107  *
2108  * @package WordPress
2109  * @subpackage Post
2110  * @since 2.0
2111  *
2112  * @param string $url URL to check
2113  * @return bool {@internal Missing Description}}
2114  */
2115 function is_local_attachment($url) {
2116         if (strpos($url, get_bloginfo('url')) === false)
2117                 return false;
2118         if (strpos($url, get_bloginfo('url') . '/?attachment_id=') !== false)
2119                 return true;
2120         if ( $id = url_to_postid($url) ) {
2121                 $post = & get_post($id);
2122                 if ( 'attachment' == $post->post_type )
2123                         return true;
2124         }
2125         return false;
2126 }
2127
2128 /**
2129  * wp_insert_attachment() - Insert an attachment
2130  *
2131  * {@internal Missing Long Description}}
2132  *
2133  * @package WordPress
2134  * @subpackage Post
2135  * @since 2.0
2136  *
2137  * @uses $wpdb
2138  * @uses $user_ID
2139  *
2140  * @param object $object attachment object
2141  * @param string $file filename
2142  * @param int $post_parent parent post ID
2143  * @return int {@internal Missing Description}}
2144  */
2145 function wp_insert_attachment($object, $file = false, $parent = 0) {
2146         global $wpdb, $user_ID;
2147
2148         $defaults = array('post_status' => 'draft', 'post_type' => 'post', 'post_author' => $user_ID,
2149                 'ping_status' => get_option('default_ping_status'), 'post_parent' => 0,
2150                 'menu_order' => 0, 'to_ping' =>  '', 'pinged' => '', 'post_password' => '',
2151                 'guid' => '', 'post_content_filtered' => '', 'post_excerpt' => '');
2152
2153         $object = wp_parse_args($object, $defaults);
2154         if ( !empty($parent) )
2155                 $object['post_parent'] = $parent;
2156
2157         $object = sanitize_post($object, 'db');
2158
2159         // export array as variables
2160         extract($object, EXTR_SKIP);
2161
2162         // Make sure we set a valid category
2163         if (0 == count($post_category) || !is_array($post_category)) {
2164                 $post_category = array(get_option('default_category'));
2165         }
2166
2167         if ( empty($post_author) )
2168                 $post_author = $user_ID;
2169
2170         $post_type = 'attachment';
2171         $post_status = 'inherit';
2172
2173         // Are we updating or creating?
2174         $update = false;
2175         if ( !empty($ID) ) {
2176                 $update = true;
2177                 $post_ID = (int) $ID;
2178         }
2179
2180         // Create a valid post name.
2181         if ( empty($post_name) )
2182                 $post_name = sanitize_title($post_title);
2183         else
2184                 $post_name = sanitize_title($post_name);
2185
2186         // expected_slashed ($post_name)
2187         $post_name_check = $wpdb->get_var( $wpdb->prepare( "SELECT post_name FROM $wpdb->posts WHERE post_name = '$post_name' AND post_status = 'inherit' AND ID != %d LIMIT 1", $post_ID));
2188
2189         if ($post_name_check) {
2190                 $suffix = 2;
2191                 while ($post_name_check) {
2192                         $alt_post_name = $post_name . "-$suffix";
2193                         // expected_slashed ($alt_post_name, $post_name)
2194                         $post_name_check = $wpdb->get_var( $wpdb->prepare( "SELECT post_name FROM $wpdb->posts WHERE post_name = '$alt_post_name' AND post_status = 'inherit' AND ID != %d AND post_parent = %d LIMIT 1", $post_ID, $post_parent));
2195                         $suffix++;
2196                 }
2197                 $post_name = $alt_post_name;
2198         }
2199
2200         if ( empty($post_date) )
2201                 $post_date = current_time('mysql');
2202         if ( empty($post_date_gmt) )
2203                 $post_date_gmt = current_time('mysql', 1);
2204
2205         if ( empty($post_modified) )
2206                 $post_modified = $post_date;
2207         if ( empty($post_modified_gmt) )
2208                 $post_modified_gmt = $post_date_gmt;
2209
2210         if ( empty($comment_status) ) {
2211                 if ( $update )
2212                         $comment_status = 'closed';
2213                 else
2214                         $comment_status = get_option('default_comment_status');
2215         }
2216         if ( empty($ping_status) )
2217                 $ping_status = get_option('default_ping_status');
2218
2219         if ( isset($to_ping) )
2220                 $to_ping = preg_replace('|\s+|', "\n", $to_ping);
2221         else
2222                 $to_ping = '';
2223
2224         if ( isset($post_parent) )
2225                 $post_parent = (int) $post_parent;
2226         else
2227                 $post_parent = 0;
2228
2229         if ( isset($menu_order) )
2230                 $menu_order = (int) $menu_order;
2231         else
2232                 $menu_order = 0;
2233
2234         if ( !isset($post_password) )
2235                 $post_password = '';
2236
2237         if ( ! isset($pinged) )
2238                 $pinged = '';
2239
2240         // expected_slashed (everything!)
2241         $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' ) );
2242         $data = stripslashes_deep( $data );
2243
2244         if ( $update ) {
2245                 $wpdb->update( $wpdb->posts, $data, array( 'ID' => $post_ID ) );
2246         } else {
2247                 $wpdb->insert( $wpdb->posts, $data );
2248                 $post_ID = (int) $wpdb->insert_id;
2249         }
2250
2251         if ( empty($post_name) ) {
2252                 $post_name = sanitize_title($post_title, $post_ID);
2253                 $wpdb->update( $wpdb->posts, compact("post_name"), array( 'ID' => $post_ID ) );
2254         }
2255
2256         wp_set_post_categories($post_ID, $post_category);
2257
2258         if ( $file )
2259                 update_attached_file( $post_ID, $file );
2260                 
2261         clean_post_cache($post_ID);
2262
2263         if ( $update) {
2264                 do_action('edit_attachment', $post_ID);
2265         } else {
2266                 do_action('add_attachment', $post_ID);
2267         }
2268
2269         return $post_ID;
2270 }
2271
2272 /**
2273  * wp_delete_attachment() - Delete an attachment
2274  *
2275  * {@internal Missing Long Description}}
2276  *
2277  * @package WordPress
2278  * @subpackage Post
2279  * @since 2.0
2280  * @uses $wpdb
2281  *
2282  * @param int $postid attachment Id
2283  * @return mixed {@internal Missing Description}}
2284  */
2285 function wp_delete_attachment($postid) {
2286         global $wpdb;
2287
2288         if ( !$post = $wpdb->get_row(  $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE ID = %d", $postid)) )
2289                 return $post;
2290
2291         if ( 'attachment' != $post->post_type )
2292                 return false;
2293
2294         $meta = wp_get_attachment_metadata( $postid );
2295         $file = get_attached_file( $postid );
2296
2297         /** @todo Delete for pluggable post taxonomies too */
2298         wp_delete_object_term_relationships($postid, array('category', 'post_tag'));
2299
2300         $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->posts WHERE ID = %d", $postid ));
2301
2302         $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->comments WHERE comment_post_ID = %d", $postid ));
2303
2304         $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE post_id = %d ", $postid ));
2305
2306         if ( ! empty($meta['thumb']) ) {
2307                 // Don't delete the thumb if another attachment uses it
2308                 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'].'%', $postid)) ) {
2309                         $thumbfile = str_replace(basename($file), $meta['thumb'], $file);
2310                         $thumbfile = apply_filters('wp_delete_file', $thumbfile);
2311                         @ unlink($thumbfile);
2312                 }
2313         }
2314
2315         // remove intermediate images if there are any
2316         $sizes = apply_filters('intermediate_image_sizes', array('thumbnail', 'medium'));
2317         foreach ( $sizes as $size ) {
2318                 if ( $intermediate = image_get_intermediate_size($postid, $size) ) {
2319                         $intermediate_file = apply_filters('wp_delete_file', $intermediate['path']);
2320                         @ unlink($intermediate_file);
2321                 }
2322         }
2323
2324         $file = apply_filters('wp_delete_file', $file);
2325
2326         if ( ! empty($file) )
2327                 @ unlink($file);
2328
2329         clean_post_cache($postid);
2330
2331         do_action('delete_attachment', $postid);
2332
2333         return $post;
2334 }
2335
2336 /**
2337  * wp_get_attachment_metadata() - Retrieve metadata for an attachment
2338  *
2339  * {@internal Missing Long Description}}
2340  *
2341  * @package WordPress
2342  * @subpackage Post
2343  * @since 2.1
2344  *
2345  * @param int $post_id attachment ID
2346  * @param bool $unfiltered Optional, default is false. If true, filters are not run
2347  * @return array {@internal Missing Description}}
2348  */
2349 function wp_get_attachment_metadata( $post_id, $unfiltered = false ) {
2350         $post_id = (int) $post_id;
2351         if ( !$post =& get_post( $post_id ) )
2352                 return false;
2353
2354         $data = get_post_meta( $post->ID, '_wp_attachment_metadata', true );
2355         if ( $unfiltered )
2356                 return $data;
2357         return apply_filters( 'wp_get_attachment_metadata', $data, $post->ID );
2358 }
2359
2360 /**
2361  * wp_update_attachment_metadata() - Update metadata for an attachment
2362  *
2363  * {@internal Missing Long Description}}
2364  *
2365  * @package WordPress
2366  * @subpackage Post
2367  * @since 2.1
2368  *
2369  * @param int $post_id attachment ID
2370  * @param array $data attachment data
2371  * @return int {@internal Missing Description}}
2372  */
2373 function wp_update_attachment_metadata( $post_id, $data ) {
2374         $post_id = (int) $post_id;
2375         if ( !$post =& get_post( $post_id ) )
2376                 return false;
2377
2378         $data = apply_filters( 'wp_update_attachment_metadata', $data, $post->ID );
2379
2380         return update_post_meta( $post->ID, '_wp_attachment_metadata', $data);
2381 }
2382
2383 /**
2384  * wp_get_attachment_url() - Retrieve the URL for an attachment
2385  *
2386  * {@internal Missing Long Description}}
2387  *
2388  * @package WordPress
2389  * @subpackage Post
2390  * @since 2.1
2391  *
2392  * @param int $post_id attachment ID
2393  * @return string {@internal Missing Description}}
2394  */
2395 function wp_get_attachment_url( $post_id = 0 ) {
2396         $post_id = (int) $post_id;
2397         if ( !$post =& get_post( $post_id ) )
2398                 return false;
2399
2400         $url = get_the_guid( $post->ID );
2401
2402         if ( 'attachment' != $post->post_type || !$url )
2403                 return false;
2404
2405         return apply_filters( 'wp_get_attachment_url', $url, $post->ID );
2406 }
2407
2408 /**
2409  * wp_get_attachment_thumb_file() - Retrieve thumbnail for an attachment
2410  *
2411  * {@internal Missing Long Description}}
2412  *
2413  * @package WordPress
2414  * @subpackage Post
2415  * @since 2.1
2416  *
2417  * @param int $post_id attachment ID
2418  * @return mixed {@internal Missing Description}}
2419  */
2420 function wp_get_attachment_thumb_file( $post_id = 0 ) {
2421         $post_id = (int) $post_id;
2422         if ( !$post =& get_post( $post_id ) )
2423                 return false;
2424         if ( !is_array( $imagedata = wp_get_attachment_metadata( $post->ID ) ) )
2425                 return false;
2426
2427         $file = get_attached_file( $post->ID );
2428
2429         if ( !empty($imagedata['thumb']) && ($thumbfile = str_replace(basename($file), $imagedata['thumb'], $file)) && file_exists($thumbfile) )
2430                 return apply_filters( 'wp_get_attachment_thumb_file', $thumbfile, $post->ID );
2431         return false;
2432 }
2433
2434 /**
2435  * wp_get_attachment_thumb_url() - Retrieve URL for an attachment thumbnail
2436  *
2437  * {@internal Missing Long Description}}
2438  *
2439  * @package WordPress
2440  * @subpackage Post
2441  * @since 2.1
2442  *
2443  * @param int $post_id attachment ID
2444  * @return string {@internal Missing Description}}
2445  */
2446 function wp_get_attachment_thumb_url( $post_id = 0 ) {
2447         $post_id = (int) $post_id;
2448         if ( !$post =& get_post( $post_id ) )
2449                 return false;
2450         if ( !$url = wp_get_attachment_url( $post->ID ) )
2451                 return false;
2452                 
2453         $sized = image_downsize( $post_id, 'thumbnail' );
2454         if ( $sized )
2455                 return $sized[0];
2456
2457         if ( !$thumb = wp_get_attachment_thumb_file( $post->ID ) )
2458                 return false;
2459
2460         $url = str_replace(basename($url), basename($thumb), $url);
2461
2462         return apply_filters( 'wp_get_attachment_thumb_url', $url, $post->ID );
2463 }
2464
2465 /**
2466  * wp_attachment_is_image() - Check if the attachment is an image
2467  *
2468  * {@internal Missing Long Description}}
2469  *
2470  * @package WordPress
2471  * @subpackage Post
2472  * @since 2.1
2473  *
2474  * @param int $post_id attachment ID
2475  * @return bool {@internal Missing Description}}
2476  */
2477 function wp_attachment_is_image( $post_id = 0 ) {
2478         $post_id = (int) $post_id;
2479         if ( !$post =& get_post( $post_id ) )
2480                 return false;
2481
2482         if ( !$file = get_attached_file( $post->ID ) )
2483                 return false;
2484
2485         $ext = preg_match('/\.([^.]+)$/', $file, $matches) ? strtolower($matches[1]) : false;
2486
2487         $image_exts = array('jpg', 'jpeg', 'gif', 'png');
2488
2489         if ( 'image/' == substr($post->post_mime_type, 0, 6) || $ext && 'import' == $post->post_mime_type && in_array($ext, $image_exts) )
2490                 return true;
2491         return false;
2492 }
2493
2494 /**
2495  * wp_mime_type_icon() - Retrieve the icon for a MIME type
2496  *
2497  * {@internal Missing Long Description}}
2498  *
2499  * @package WordPress
2500  * @subpackage Post
2501  * @since 2.1
2502  *
2503  * @param string $mime MIME type
2504  * @return string|bool {@internal Missing Description}}
2505  */
2506 function wp_mime_type_icon( $mime = 0 ) {
2507         if ( !is_numeric($mime) )
2508                 $icon = wp_cache_get("mime_type_icon_$mime");
2509         if ( empty($icon) ) {
2510                 $post_id = 0;
2511                 $post_mimes = array();
2512                 if ( is_numeric($mime) ) {
2513                         $mime = (int) $mime;
2514                         if ( $post =& get_post( $mime ) ) {
2515                                 $post_id = (int) $post->ID;
2516                                 $ext = preg_replace('/^.+?\.([^.]+)$/', '$1', $post->guid);
2517                                 if ( !empty($ext) ) {
2518                                         $post_mimes[] = $ext;
2519                                         if ( $ext_type = wp_ext2type( $ext ) )
2520                                                 $post_mimes[] = $ext_type;
2521                                 }
2522                                 $mime = $post->post_mime_type;
2523                         } else {
2524                                 $mime = 0;
2525                         }
2526                 } else {
2527                         $post_mimes[] = $mime;
2528                 }
2529
2530                 $icon_files = wp_cache_get('icon_files');
2531
2532                 if ( !is_array($icon_files) ) {
2533                         $icon_dir = apply_filters( 'icon_dir', ABSPATH . WPINC . '/images/crystal' );
2534                         $icon_dir_uri = apply_filters( 'icon_dir_uri', includes_url('images/crystal') );
2535                         $dirs = apply_filters( 'icon_dirs', array($icon_dir => $icon_dir_uri) );
2536                         $icon_files = array();
2537                         while ( $dirs ) {
2538                                 $dir = array_shift($keys = array_keys($dirs));
2539                                 $uri = array_shift($dirs);
2540                                 if ( $dh = opendir($dir) ) {
2541                                         while ( false !== $file = readdir($dh) ) {
2542                                                 $file = basename($file);
2543                                                 if ( substr($file, 0, 1) == '.' )
2544                                                         continue;
2545                                                 if ( !in_array(strtolower(substr($file, -4)), array('.png', '.gif', '.jpg') ) ) {
2546                                                         if ( is_dir("$dir/$file") )
2547                                                                 $dirs["$dir/$file"] = "$uri/$file";
2548                                                         continue;
2549                                                 }
2550                                                 $icon_files["$dir/$file"] = "$uri/$file";
2551                                         }
2552                                         closedir($dh);
2553                                 }
2554                         }
2555                         wp_cache_set('icon_files', $icon_files, 600);
2556                 }
2557
2558                 // Icon basename - extension = MIME wildcard
2559                 foreach ( $icon_files as $file => $uri )
2560                         $types[ preg_replace('/^([^.]*).*$/', '$1', basename($file)) ] =& $icon_files[$file];
2561
2562                 if ( ! empty($mime) ) {
2563                         $post_mimes[] = substr($mime, 0, strpos($mime, '/'));
2564                         $post_mimes[] = substr($mime, strpos($mime, '/') + 1);
2565                         $post_mimes[] = str_replace('/', '_', $mime);
2566                 }
2567
2568                 $matches = wp_match_mime_types(array_keys($types), $post_mimes);
2569                 $matches['default'] = array('default');
2570
2571                 foreach ( $matches as $match => $wilds ) {
2572                         if ( isset($types[$wilds[0]])) {
2573                                 $icon = $types[$wilds[0]];
2574                                 if ( !is_numeric($mime) )
2575                                         wp_cache_set("mime_type_icon_$mime", $icon);
2576                                 break;
2577                         }
2578                 }
2579         }
2580
2581         return apply_filters( 'wp_mime_type_icon', $icon, $mime, $post_id ); // Last arg is 0 if function pass mime type.
2582 }
2583
2584 /**
2585  * wp_check_for_changed_slugs() - {@internal Missing Short Description}}
2586  *
2587  * {@internal Missing Long Description}}
2588  *
2589  * @package WordPress
2590  * @subpackage Post
2591  * @since 2.1
2592  *
2593  * @param int $post_id The Post ID
2594  * @return int Same as $post_id
2595  */
2596 function wp_check_for_changed_slugs($post_id) {
2597         if ( !isset($_POST['wp-old-slug']) || !strlen($_POST['wp-old-slug']) )
2598                 return $post_id;
2599
2600         $post = &get_post($post_id);
2601
2602         // we're only concerned with published posts
2603         if ( $post->post_status != 'publish' || $post->post_type != 'post' )
2604                 return $post_id;
2605
2606         // only bother if the slug has changed
2607         if ( $post->post_name == $_POST['wp-old-slug'] )
2608                 return $post_id;
2609
2610         $old_slugs = (array) get_post_meta($post_id, '_wp_old_slug');
2611
2612         // if we haven't added this old slug before, add it now
2613         if ( !count($old_slugs) || !in_array($_POST['wp-old-slug'], $old_slugs) )
2614                 add_post_meta($post_id, '_wp_old_slug', $_POST['wp-old-slug']);
2615
2616         // if the new slug was used previously, delete it from the list
2617         if ( in_array($post->post_name, $old_slugs) )
2618                 delete_post_meta($post_id, '_wp_old_slug', $post->post_name);
2619
2620         return $post_id;
2621 }
2622
2623 /**
2624  * get_private_posts_cap_sql() - {@internal Missing Short Description}}
2625  *
2626  * This function provides a standardized way to appropriately select on
2627  * the post_status of posts/pages. The function will return a piece of
2628  * SQL code that can be added to a WHERE clause; this SQL is constructed
2629  * to allow all published posts, and all private posts to which the user
2630  * has access.
2631  *
2632  * @package WordPress
2633  * @subpackage Post
2634  * @since 2.2
2635  *
2636  * @uses $user_ID
2637  * @uses apply_filters() Call 'pub_priv_sql_capability' filter for plugins with different post types
2638  *
2639  * @param string $post_type currently only supports 'post' or 'page'.
2640  * @return string SQL code that can be added to a where clause.
2641  */
2642 function get_private_posts_cap_sql($post_type) {
2643         global $user_ID;
2644         $cap = '';
2645
2646         // Private posts
2647         if ($post_type == 'post') {
2648                 $cap = 'read_private_posts';
2649         // Private pages
2650         } elseif ($post_type == 'page') {
2651                 $cap = 'read_private_pages';
2652         // Dunno what it is, maybe plugins have their own post type?
2653         } else {
2654                 $cap = apply_filters('pub_priv_sql_capability', $cap);
2655
2656                 if (empty($cap)) {
2657                         // We don't know what it is, filters don't change anything,
2658                         // so set the SQL up to return nothing.
2659                         return '1 = 0';
2660                 }
2661         }
2662
2663         $sql = '(post_status = \'publish\'';
2664
2665         if (current_user_can($cap)) {
2666                 // Does the user have the capability to view private posts? Guess so.
2667                 $sql .= ' OR post_status = \'private\'';
2668         } elseif (is_user_logged_in()) {
2669                 // Users can view their own private posts.
2670                 $sql .= ' OR post_status = \'private\' AND post_author = \'' . $user_ID . '\'';
2671         }
2672
2673         $sql .= ')';
2674
2675         return $sql;
2676 }
2677
2678 /**
2679  * get_lastpostdate() - {@internal Missing Short Description}}
2680  *
2681  * {@internal Missing Long Description}}
2682  *
2683  * @package WordPress
2684  * @subpackage Post
2685  * @since 0.71
2686  *
2687  * @uses $wpdb
2688  * @uses $blog_id
2689  * @uses apply_filters() Calls 'get_lastpostdate' filter
2690  *
2691  * @global mixed $cache_lastpostdate Stores the last post date
2692  * @global mixed $pagenow The current page being viewed
2693  *
2694  * @param string $timezone The location to get the time. Can be 'gmt', 'blog', or 'server'.
2695  * @return string The date of the last post.
2696  */
2697 function get_lastpostdate($timezone = 'server') {
2698         global $cache_lastpostdate, $wpdb, $blog_id;
2699         $add_seconds_server = date('Z');
2700         if ( !isset($cache_lastpostdate[$blog_id][$timezone]) ) {
2701                 switch(strtolower($timezone)) {
2702                         case 'gmt':
2703                                 $lastpostdate = $wpdb->get_var("SELECT post_date_gmt FROM $wpdb->posts WHERE post_status = 'publish' ORDER BY post_date_gmt DESC LIMIT 1");
2704                                 break;
2705                         case 'blog':
2706                                 $lastpostdate = $wpdb->get_var("SELECT post_date FROM $wpdb->posts WHERE post_status = 'publish' ORDER BY post_date_gmt DESC LIMIT 1");
2707                                 break;
2708                         case 'server':
2709                                 $lastpostdate = $wpdb->get_var("SELECT DATE_ADD(post_date_gmt, INTERVAL '$add_seconds_server' SECOND) FROM $wpdb->posts WHERE post_status = 'publish' ORDER BY post_date_gmt DESC LIMIT 1");
2710                                 break;
2711                 }
2712                 $cache_lastpostdate[$blog_id][$timezone] = $lastpostdate;
2713         } else {
2714                 $lastpostdate = $cache_lastpostdate[$blog_id][$timezone];
2715         }
2716         return apply_filters( 'get_lastpostdate', $lastpostdate, $timezone );
2717 }
2718
2719 /**
2720  * get_lastpostmodified() - {@internal Missing Short Description}}
2721  *
2722  * {@internal Missing Long Description}}
2723  *
2724  * @package WordPress
2725  * @subpackage Post
2726  * @since 1.2
2727  *
2728  * @uses $wpdb
2729  * @uses $blog_id
2730  * @uses apply_filters() Calls 'get_lastpostmodified' filter
2731  *
2732  * @global mixed $cache_lastpostmodified Stores the date the last post was modified
2733  * @global mixed $pagenow The current page being viewed
2734  *
2735  * @param string $timezone The location to get the time. Can be 'gmt', 'blog', or 'server'.
2736  * @return string The date the post was last modified.
2737  */
2738 function get_lastpostmodified($timezone = 'server') {
2739         global $cache_lastpostmodified, $wpdb, $blog_id;
2740         $add_seconds_server = date('Z');
2741         if ( !isset($cache_lastpostmodified[$blog_id][$timezone]) ) {
2742                 switch(strtolower($timezone)) {
2743                         case 'gmt':
2744                                 $lastpostmodified = $wpdb->get_var("SELECT post_modified_gmt FROM $wpdb->posts WHERE post_status = 'publish' ORDER BY post_modified_gmt DESC LIMIT 1");
2745                                 break;
2746                         case 'blog':
2747                                 $lastpostmodified = $wpdb->get_var("SELECT post_modified FROM $wpdb->posts WHERE post_status = 'publish' ORDER BY post_modified_gmt DESC LIMIT 1");
2748                                 break;
2749                         case 'server':
2750                                 $lastpostmodified = $wpdb->get_var("SELECT DATE_ADD(post_modified_gmt, INTERVAL '$add_seconds_server' SECOND) FROM $wpdb->posts WHERE post_status = 'publish' ORDER BY post_modified_gmt DESC LIMIT 1");
2751                                 break;
2752                 }
2753                 $lastpostdate = get_lastpostdate($timezone);
2754                 if ( $lastpostdate > $lastpostmodified ) {
2755                         $lastpostmodified = $lastpostdate;
2756                 }
2757                 $cache_lastpostmodified[$blog_id][$timezone] = $lastpostmodified;
2758         } else {
2759                 $lastpostmodified = $cache_lastpostmodified[$blog_id][$timezone];
2760         }
2761         return apply_filters( 'get_lastpostmodified', $lastpostmodified, $timezone );
2762 }
2763
2764 /**
2765  * update_post_cache() - Updates posts in cache
2766  *
2767  * @usedby update_page_cache() update_page_cache() aliased by this function.
2768  *
2769  * @package WordPress
2770  * @subpackage Cache
2771  * @since 1.5.1
2772  *
2773  * @param array $posts Array of post objects
2774  */
2775 function update_post_cache(&$posts) {
2776         if ( !$posts )
2777                 return;
2778
2779         foreach ( $posts as $post )
2780                 wp_cache_add($post->ID, $post, 'posts');
2781 }
2782
2783 /**
2784  * clean_post_cache() - Will clean the post in the cache
2785  *
2786  * Cleaning means delete from the cache of the post. Will call to clean
2787  * the term object cache associated with the post ID.
2788  *
2789  * @package WordPress
2790  * @subpackage Cache
2791  * @since 2.0
2792  *
2793  * @uses do_action() Will call the 'clean_post_cache' hook action.
2794  *
2795  * @param int $id The Post ID in the cache to clean
2796  */
2797 function clean_post_cache($id) {
2798         global $wpdb;
2799         $id = (int) $id;
2800
2801         wp_cache_delete($id, 'posts');
2802         wp_cache_delete($id, 'post_meta');
2803
2804         clean_object_term_cache($id, 'post');
2805
2806         wp_cache_delete( 'wp_get_archives', 'general' );
2807
2808         do_action('clean_post_cache', $id);
2809
2810         if ( $children = $wpdb->get_col( $wpdb->prepare("SELECT ID FROM $wpdb->posts WHERE post_parent = %d", $id) ) ) {
2811                 foreach( $children as $cid )
2812                         clean_post_cache( $cid );
2813         }
2814 }
2815
2816 /**
2817  * update_page_cache() - Alias of update_post_cache()
2818  *
2819  * @see update_post_cache() Posts and pages are the same, alias is intentional
2820  *
2821  * @package WordPress
2822  * @subpackage Cache
2823  * @since 1.5.1
2824  *
2825  * @param array $pages list of page objects
2826  */
2827 function update_page_cache(&$pages) {
2828         update_post_cache($pages);
2829 }
2830
2831 /**
2832  * clean_page_cache() - Will clean the page in the cache
2833  *
2834  * Clean (read: delete) page from cache that matches $id. Will also clean
2835  * cache associated with 'all_page_ids' and 'get_pages'.
2836  *
2837  * @package WordPress
2838  * @subpackage Cache
2839  * @since 2.0
2840  *
2841  * @uses do_action() Will call the 'clean_page_cache' hook action.
2842  *
2843  * @param int $id Page ID to clean
2844  */
2845 function clean_page_cache($id) {
2846         clean_post_cache($id);
2847
2848         wp_cache_delete( 'all_page_ids', 'posts' );
2849         wp_cache_delete( 'get_pages', 'posts' );
2850
2851         do_action('clean_page_cache', $id);
2852 }
2853
2854 /**
2855  * update_post_caches() - Call major cache updating functions for list of Post objects.
2856  *
2857  * @package WordPress
2858  * @subpackage Cache
2859  * @since 1.5
2860  *
2861  * @uses $wpdb
2862  * @uses update_post_cache()
2863  * @uses update_object_term_cache()
2864  * @uses update_postmeta_cache()
2865  *
2866  * @param array $posts Array of Post objects
2867  */
2868 function update_post_caches(&$posts) {
2869         // No point in doing all this work if we didn't match any posts.
2870         if ( !$posts )
2871                 return;
2872
2873         update_post_cache($posts);
2874
2875         $post_ids = array();
2876
2877         for ($i = 0; $i < count($posts); $i++)
2878                 $post_ids[] = $posts[$i]->ID;
2879
2880         update_object_term_cache($post_ids, 'post');
2881
2882         update_postmeta_cache($post_ids);
2883 }
2884
2885 /**
2886  * update_postmeta_cache() - {@internal Missing Short Description}}
2887  *
2888  * {@internal Missing Long Description}}
2889  *
2890  * @package WordPress
2891  * @subpackage Cache
2892  * @since 2.1
2893  *
2894  * @uses $wpdb
2895  *
2896  * @param array $post_ids {@internal Missing Description}}
2897  * @return bool|array Returns false if there is nothing to update or an array of metadata
2898  */
2899 function update_postmeta_cache($post_ids) {
2900         global $wpdb;
2901
2902         if ( empty( $post_ids ) )
2903                 return false;
2904
2905         if ( !is_array($post_ids) ) {
2906                 $post_ids = preg_replace('|[^0-9,]|', '', $post_ids);
2907                 $post_ids = explode(',', $post_ids);
2908         }
2909
2910         $post_ids = array_map('intval', $post_ids);
2911
2912         $ids = array();
2913         foreach ( (array) $post_ids as $id ) {
2914                 if ( false === wp_cache_get($id, 'post_meta') )
2915                         $ids[] = $id;
2916         }
2917
2918         if ( empty( $ids ) )
2919                 return false;
2920
2921         // Get post-meta info
2922         $id_list = join(',', $ids);
2923         $cache = array();
2924         if ( $meta_list = $wpdb->get_results("SELECT post_id, meta_key, meta_value FROM $wpdb->postmeta WHERE post_id IN ($id_list) ORDER BY post_id, meta_key", ARRAY_A) ) {
2925                 foreach ( (array) $meta_list as $metarow) {
2926                         $mpid = (int) $metarow['post_id'];
2927                         $mkey = $metarow['meta_key'];
2928                         $mval = $metarow['meta_value'];
2929
2930                         // Force subkeys to be array type:
2931                         if ( !isset($cache[$mpid]) || !is_array($cache[$mpid]) )
2932                                 $cache[$mpid] = array();
2933                         if ( !isset($cache[$mpid][$mkey]) || !is_array($cache[$mpid][$mkey]) )
2934                                 $cache[$mpid][$mkey] = array();
2935
2936                         // Add a value to the current pid/key:
2937                         $cache[$mpid][$mkey][] = $mval;
2938                 }
2939         }
2940
2941         foreach ( (array) $ids as $id ) {
2942                 if ( ! isset($cache[$id]) )
2943                         $cache[$id] = array();
2944         }
2945
2946         foreach ( array_keys($cache) as $post)
2947                 wp_cache_set($post, $cache[$post], 'post_meta');
2948
2949         return $cache;
2950 }
2951
2952 //
2953 // Hooks
2954 //
2955
2956 /**
2957  * _transition_post_status() - Hook {@internal Missing Short Description}}
2958  *
2959  * {@internal Missing Long Description}}
2960  *
2961  * @package WordPress
2962  * @subpackage Post
2963  * @since 2.3
2964  *
2965  * @uses $wpdb
2966  *
2967  * @param string $new_status {@internal Missing Description}}
2968  * @param string $old_status {@internal Missing Description}}
2969  * @param object $post Object type containing the post information
2970  */
2971 function _transition_post_status($new_status, $old_status, $post) {
2972         global $wpdb;
2973
2974         if ( $old_status != 'publish' && $new_status == 'publish' ) {
2975                 // Reset GUID if transitioning to publish and it is empty
2976                 if ( '' == get_the_guid($post->ID) )
2977                         $wpdb->update( $wpdb->posts, array( 'guid' => get_permalink( $post->ID ) ), array( 'ID' => $post->ID ) );
2978                 do_action('private_to_published', $post->ID);  // Deprecated, use private_to_publish
2979         }
2980
2981         // Always clears the hook in case the post status bounced from future to draft.
2982         wp_clear_scheduled_hook('publish_future_post', $post->ID);
2983 }
2984
2985 /**
2986  * _future_post_hook() - Hook used to schedule publication for a post marked for the future.
2987  *
2988  * The $post properties used and must exist are 'ID' and 'post_date_gmt'.
2989  *
2990  * @package WordPress
2991  * @subpackage Post
2992  * @since 2.3
2993  *
2994  * @param int $post_id Not Used. Can be set to null.
2995  * @param object $post Object type containing the post information
2996  */
2997 function _future_post_hook($deprecated = '', $post) {
2998         wp_clear_scheduled_hook( 'publish_future_post', $post->ID );
2999         wp_schedule_single_event(strtotime($post->post_date_gmt. ' GMT'), 'publish_future_post', array($post->ID));
3000 }
3001
3002 /**
3003  * _publish_post_hook() - Hook {@internal Missing Short Description}}
3004  *
3005  * {@internal Missing Long Description}}
3006  *
3007  * @package WordPress
3008  * @subpackage Post
3009  * @since 2.3
3010  *
3011  * @uses $wpdb
3012  * @uses XMLRPC_REQUEST
3013  * @uses APP_REQUEST
3014  * @uses do_action Calls 'xmlprc_publish_post' action if XMLRPC_REQUEST is defined. Calls 'app_publish_post'
3015  *      action if APP_REQUEST is defined.
3016  *
3017  * @param int $post_id The ID in the database table of the post being published
3018  */
3019 function _publish_post_hook($post_id) {
3020         global $wpdb;
3021
3022         if ( defined('XMLRPC_REQUEST') )
3023                 do_action('xmlrpc_publish_post', $post_id);
3024         if ( defined('APP_REQUEST') )
3025                 do_action('app_publish_post', $post_id);
3026
3027         if ( defined('WP_IMPORTING') )
3028                 return;
3029
3030         $data = array( 'post_id' => $post_id, 'meta_value' => '1' );
3031         if ( get_option('default_pingback_flag') )
3032                 $wpdb->insert( $wpdb->postmeta, $data + array( 'meta_key' => '_pingme' ) );
3033         $wpdb->insert( $wpdb->postmeta, $data + array( 'meta_key' => '_encloseme' ) );
3034         wp_schedule_single_event(time(), 'do_pings');
3035 }
3036
3037 /**
3038  * _save_post_hook() - Hook used to prevent page/post cache and rewrite rules from staying dirty
3039  *
3040  * Does two things. If the post is a page and has a template then it will update/add that
3041  * template to the meta. For both pages and posts, it will clean the post cache to make sure
3042  * that the cache updates to the changes done recently. For pages, the rewrite rules of
3043  * WordPress are flushed to allow for any changes.
3044  *
3045  * The $post parameter, only uses 'post_type' property and 'page_template' property.
3046  *
3047  * @package WordPress
3048  * @subpackage Post
3049  * @since 2.3
3050  *
3051  * @uses $wp_rewrite Flushes Rewrite Rules.
3052  *
3053  * @param int $post_id The ID in the database table for the $post
3054  * @param object $post Object type containing the post information
3055  */
3056 function _save_post_hook($post_id, $post) {
3057         if ( $post->post_type == 'page' ) {
3058                 clean_page_cache($post_id);
3059                 global $wp_rewrite;
3060                 $wp_rewrite->flush_rules();
3061         } else {
3062                 clean_post_cache($post_id);
3063         }
3064 }
3065
3066 //
3067 // Private
3068 //
3069
3070 function _get_post_ancestors(&$_post) {
3071         global $wpdb;
3072
3073         if ( isset($_post->ancestors) )
3074                 return;
3075
3076         $_post->ancestors = array();
3077
3078         if ( empty($_post->post_parent) || $_post->ID == $_post->post_parent )
3079                 return;
3080
3081         $id = $_post->ancestors[] = $_post->post_parent;
3082         while ( $ancestor = $wpdb->get_var( $wpdb->prepare("SELECT `post_parent` FROM $wpdb->posts WHERE ID = %d LIMIT 1", $id) ) ) {
3083                 if ( $id == $ancestor )
3084                         break;
3085                 $id = $_post->ancestors[] = $ancestor;
3086         }
3087 }
3088
3089 /* Post Revisions */
3090
3091 /**
3092  * _wp_post_revision_fields() - determines which fields of posts are to be saved in revisions
3093  *
3094  * Does two things. If passed a post *array*, it will return a post array ready to be
3095  * insterted into the posts table as a post revision.
3096  * Otherwise, returns an array whose keys are the post fields to be saved for post revisions.
3097  *
3098  * @package WordPress
3099  * @subpackage Post Revisions
3100  * @since 2.6
3101  *
3102  * @param array $post optional a post array to be processed for insertion as a post revision
3103  * @param bool $autosave optional Is the revision an autosave?
3104  * @return array post array ready to be inserted as a post revision or array of fields that can be versioned
3105  */
3106 function _wp_post_revision_fields( $post = null, $autosave = false ) {
3107         static $fields = false;
3108
3109         if ( !$fields ) {
3110                 // Allow these to be versioned
3111                 $fields = array(
3112                         'post_title' => __( 'Title' ),
3113                         'post_content' => __( 'Content' ),
3114                         'post_excerpt' => __( 'Excerpt' ),
3115                 );
3116
3117                 // Runs only once
3118                 $fields = apply_filters( '_wp_post_revision_fields', $fields );
3119
3120                 // WP uses these internally either in versioning or elsewhere - they cannot be versioned
3121                 foreach ( array( 'ID', 'post_name', 'post_parent', 'post_date', 'post_date_gmt', 'post_status', 'post_type', 'comment_count', 'post_author' ) as $protect )
3122                         unset( $fields[$protect] );
3123         }
3124
3125         if ( !is_array($post) )
3126                 return $fields;
3127
3128         $return = array();
3129         foreach ( array_intersect( array_keys( $post ), array_keys( $fields ) ) as $field )
3130                 $return[$field] = $post[$field];
3131
3132         $return['post_parent']   = $post['ID'];
3133         $return['post_status']   = 'inherit';
3134         $return['post_type']     = 'revision';
3135         $return['post_name']     = $autosave ? "$post[ID]-autosave" : "$post[ID]-revision";
3136         $return['post_date']     = $post['post_modified'];
3137         $return['post_date_gmt'] = $post['post_modified_gmt'];
3138
3139         return $return;
3140 }
3141
3142 /**
3143  * wp_save_post_revision() - Saves an already existing post as a post revision.  Typically used immediately prior to post updates.
3144  *
3145  * @package WordPress
3146  * @subpackage Post Revisions
3147  * @since 2.6
3148  *
3149  * @uses _wp_put_post_revision()
3150  *
3151  * @param int $post_id The ID of the post to save as a revision
3152  * @return mixed null or 0 if error, new revision ID if success
3153  */
3154 function wp_save_post_revision( $post_id ) {
3155         // We do autosaves manually with wp_create_post_autosave()
3156         if ( @constant( 'DOING_AUTOSAVE' ) )
3157                 return;
3158
3159         // WP_POST_REVISIONS = 0, false
3160         if ( !constant('WP_POST_REVISIONS') )
3161                 return;
3162
3163         if ( !$post = get_post( $post_id, ARRAY_A ) )
3164                 return;
3165
3166         if ( !in_array( $post['post_type'], array( 'post', 'page' ) ) )
3167                 return;
3168
3169         $return = _wp_put_post_revision( $post );
3170
3171         // WP_POST_REVISIONS = true (default), -1
3172         if ( !is_numeric( WP_POST_REVISIONS ) || WP_POST_REVISIONS < 0 )
3173                 return $return;
3174
3175         // all revisions and (possibly) one autosave
3176         $revisions = wp_get_post_revisions( $post_id, array( 'order' => 'ASC' ) );
3177
3178         // WP_POST_REVISIONS = (int) (# of autasaves to save)
3179         $delete = count($revisions) - WP_POST_REVISIONS;
3180
3181         if ( $delete < 1 )
3182                 return $return;
3183
3184         $revisions = array_slice( $revisions, 0, $delete );
3185
3186         for ( $i = 0; isset($revisions[$i]); $i++ ) {
3187                 if ( false !== strpos( $revisions[$i]->post_name, 'autosave' ) )
3188                         continue;
3189                 wp_delete_post_revision( $revisions[$i]->ID );
3190         }
3191
3192         return $return;
3193 }
3194
3195 /**
3196  * wp_get_post_autosave() - returns the autosaved data of the specified post.
3197  *
3198  * Returns a post object containing the information that was autosaved for the specified post.
3199  *
3200  * @package WordPress
3201  * @subpackage Post Revisions
3202  * @since 2.6
3203  *
3204  * @param int $post_id The post ID
3205  * @return object|bool the autosaved data or false on failure or when no autosave exists
3206  */
3207 function wp_get_post_autosave( $post_id ) {
3208         global $wpdb;
3209         if ( !$post = get_post( $post_id ) )
3210                 return false;
3211
3212         $q = array(
3213                 'name' => "{$post->ID}-autosave",
3214                 'post_parent' => $post->ID,
3215                 'post_type' => 'revision',
3216                 'post_status' => 'inherit'
3217         );
3218
3219         // Use WP_Query so that the result gets cached
3220         $autosave_query = new WP_Query;
3221
3222         add_action( 'parse_query', '_wp_get_post_autosave_hack' );
3223         $autosave = $autosave_query->query( $q );
3224         remove_action( 'parse_query', '_wp_get_post_autosave_hack' );
3225
3226         if ( $autosave && is_array($autosave) && is_object($autosave[0]) )
3227                 return $autosave[0];
3228
3229         return false;
3230 }
3231
3232 // Internally used to hack WP_Query into submission
3233 function _wp_get_post_autosave_hack( $query ) {
3234         $query->is_single = false;
3235 }
3236
3237
3238 /**
3239  * wp_is_post_revision() - Determines if the specified post is a revision.
3240  *
3241  * @package WordPress
3242  * @subpackage Post Revisions
3243  * @since 2.6
3244  *
3245  * @param int|object $post post ID or post object
3246  * @return bool|int false if not a revision, ID of revision's parent otherwise
3247  */
3248 function wp_is_post_revision( $post ) {
3249         if ( !$post = wp_get_post_revision( $post ) )
3250                 return false;
3251         return (int) $post->post_parent;
3252 }
3253
3254 /**
3255  * wp_is_post_autosave() - Determines if the specified post is an autosave.
3256  *
3257  * @package WordPress
3258  * @subpackage Post Revisions
3259  * @since 2.6
3260  *
3261  * @param int|object $post post ID or post object
3262  * @return bool|int false if not a revision, ID of autosave's parent otherwise
3263  */
3264 function wp_is_post_autosave( $post ) {
3265         if ( !$post = wp_get_post_revision( $post ) )
3266                 return false;
3267         if ( "{$post->post_parent}-autosave" !== $post->post_name )
3268                 return false;
3269         return (int) $post->post_parent;
3270 }
3271
3272 /**
3273  * _wp_put_post_revision() - Inserts post data into the posts table as a post revision
3274  *
3275  * @package WordPress
3276  * @subpackage Post Revisions
3277  * @since 2.6
3278  *
3279  * @uses wp_insert_post()
3280  *
3281  * @param int|object|array $post post ID, post object OR post array
3282  * @param bool $autosave optional Is the revision an autosave?
3283  * @return mixed null or 0 if error, new revision ID if success
3284  */
3285 function _wp_put_post_revision( $post = null, $autosave = false ) {
3286         if ( is_object($post) )
3287                 $post = get_object_vars( $post );
3288         elseif ( !is_array($post) )
3289                 $post = get_post($post, ARRAY_A);
3290         if ( !$post || empty($post['ID']) )
3291                 return;
3292
3293         if ( isset($post['post_type']) && 'revision' == $post['post_type'] )
3294                 return new WP_Error( 'post_type', __( 'Cannot create a revision of a revision' ) );
3295
3296         $post = _wp_post_revision_fields( $post, $autosave );
3297
3298         $revision_id = wp_insert_post( $post );
3299         if ( is_wp_error($revision_id) )
3300                 return $revision_id;
3301
3302         if ( $revision_id )
3303                 do_action( '_wp_put_post_revision', $revision_id );
3304         return $revision_id;
3305 }
3306
3307 /**
3308  * wp_get_post_revision() - Gets a post revision
3309  *
3310  * @package WordPress
3311  * @subpackage Post Revisions
3312  * @since 2.6
3313  *
3314  * @uses get_post()
3315  *
3316  * @param int|object $post post ID or post object
3317  * @param $output optional OBJECT, ARRAY_A, or ARRAY_N
3318  * @param string $filter optional sanitation filter.  @see sanitize_post()
3319  * @return mixed null if error or post object if success
3320  */
3321 function &wp_get_post_revision(&$post, $output = OBJECT, $filter = 'raw') {
3322         $null = null;
3323         if ( !$revision = get_post( $post, OBJECT, $filter ) )
3324                 return $revision;
3325         if ( 'revision' !== $revision->post_type )
3326                 return $null;
3327
3328         if ( $output == OBJECT ) {
3329                 return $revision;
3330         } elseif ( $output == ARRAY_A ) {
3331                 $_revision = get_object_vars($revision);
3332                 return $_revision;
3333         } elseif ( $output == ARRAY_N ) {
3334                 $_revision = array_values(get_object_vars($revision));
3335                 return $_revision;
3336         }
3337
3338         return $revision;
3339 }
3340
3341 /**
3342  * wp_restore_post_revision() - Restores a post to the specified revision
3343  *
3344  * Can restore a past using all fields of the post revision, or only selected fields.
3345  *
3346  * @package WordPress
3347  * @subpackage Post Revisions
3348  * @since 2.6
3349  *
3350  * @uses wp_get_post_revision()
3351  * @uses wp_update_post()
3352  *
3353  * @param int|object $revision_id revision ID or revision object
3354  * @param array $fields optional What fields to restore from.  Defaults to all.
3355  * @return mixed null if error, false if no fields to restore, (int) post ID if success
3356  */
3357 function wp_restore_post_revision( $revision_id, $fields = null ) {
3358         if ( !$revision = wp_get_post_revision( $revision_id, ARRAY_A ) )
3359                 return $revision;
3360
3361         if ( !is_array( $fields ) )
3362                 $fields = array_keys( _wp_post_revision_fields() );
3363
3364         $update = array();
3365         foreach( array_intersect( array_keys( $revision ), $fields ) as $field )
3366                 $update[$field] = $revision[$field];
3367
3368         if ( !$update )
3369                 return false;
3370
3371         $update['ID'] = $revision['post_parent'];
3372
3373         $post_id = wp_update_post( $update );
3374         if ( is_wp_error( $post_id ) )
3375                 return $post_id;
3376
3377         if ( $post_id )
3378                 do_action( 'wp_restore_post_revision', $post_id, $revision['ID'] );
3379
3380         return $post_id;
3381 }
3382
3383 /**
3384  * wp_delete_post_revision() - Deletes a revision.
3385  *
3386  * Deletes the row from the posts table corresponding to the specified revision
3387  *
3388  * @package WordPress
3389  * @subpackage Post Revisions
3390  * @since 2.6
3391  *
3392  * @uses wp_get_post_revision()
3393  * @uses wp_delete_post()
3394  *
3395  * @param int|object $revision_id revision ID or revision object
3396  * @param array $fields optional What fields to restore from.  Defaults to all.
3397  * @return mixed null if error, false if no fields to restore, (int) post ID if success
3398  */
3399 function wp_delete_post_revision( $revision_id ) {
3400         if ( !$revision = wp_get_post_revision( $revision_id ) )
3401                 return $revision;
3402
3403         $delete = wp_delete_post( $revision->ID );
3404         if ( is_wp_error( $delete ) )
3405                 return $delete;
3406
3407         if ( $delete )
3408                 do_action( 'wp_delete_post_revision', $revision->ID, $revision );
3409
3410         return $delete;
3411 }
3412
3413 /**
3414  * wp_get_post_revisions() - Returns all revisions of specified post
3415  *
3416  * @package WordPress
3417  * @subpackage Post Revisions
3418  * @since 2.6
3419  *
3420  * @uses get_children()
3421  *
3422  * @param int|object $post_id post ID or post object
3423  * @return array empty if no revisions
3424  */
3425 function wp_get_post_revisions( $post_id = 0, $args = null ) {
3426         if ( !constant('WP_POST_REVISIONS') )
3427                 return array();
3428         if ( ( !$post = get_post( $post_id ) ) || empty( $post->ID ) )
3429                 return array();
3430
3431         $defaults = array( 'order' => 'DESC', 'orderby' => 'date' );
3432         $args = wp_parse_args( $args, $defaults );
3433         $args = array_merge( $args, array( 'post_parent' => $post->ID, 'post_type' => 'revision', 'post_status' => 'inherit' ) );
3434
3435         if ( !$revisions = get_children( $args ) )
3436                 return array();
3437         return $revisions;
3438 }