root/trunk/wp-includes/post.php

Revision 9123, 102.9 kB (checked in by markjaquith, 1 day ago)

Prevent future posts from publishing early. fixes #7441

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1 <?php
2 /**
3  * Post functions and post utility function.
4  *
5  * @package WordPress
6  * @subpackage Post
7  * @since 1.5.0
8  */
9
10 /**
11  * Retrieve attached file path based on attachment ID.
12  *
13  * You can optionally send it through the 'get_attached_file' filter, but by
14  * default it will just return the file path unfiltered.
15  *
16  * The function works by getting the single post meta name, named
17  * '_wp_attached_file' and returning it. This is a convenience function to
18  * prevent looking up the meta name and provide a mechanism for sending the
19  * attached filename through a filter.
20  *
21  * @since 2.0.0
22  * @uses apply_filters() Calls 'get_attached_file' on file path and attachment ID.
23  *
24  * @param int $attachment_id Attachment ID
25  * @param bool $unfiltered Whether to apply filters or not
26  * @return string The file path to the attached file.
27  */
28 function get_attached_file( $attachment_id, $unfiltered = false ) {
29     $file = get_post_meta( $attachment_id, '_wp_attached_file', true );
30     // If the file is relative, prepend upload dir
31     if ( 0 !== strpos($file, '/') ) {
32         $uploads = wp_upload_dir();
33         $file = $uploads['basedir'] . "/$file";
34     }
35         
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  * @since 2.1.0
48  * @uses apply_filters() Calls 'update_attached_file' on file path and attachment ID.
49  *
50  * @param int $attachment_id Attachment ID
51  * @param string $file File path for the attachment
52  * @return bool False on failure, true on success.
53  */
54 function update_attached_file( $attachment_id, $file ) {
55     if ( !get_post( $attachment_id ) )
56         return false;
57
58     $file = apply_filters( 'update_attached_file', $file, $attachment_id );
59
60     // Make the file path relative to the upload dir
61     if ( ($uploads = wp_upload_dir()) && false === $uploads['error'] ) { // Get upload directory
62         if ( 0 === strpos($file, $uploads['basedir']) ) {// Check that the upload base exists in the file path
63                 $file = str_replace($uploads['basedir'], '', $file); // Remove upload dir from the file path
64                 $file = ltrim($file, '/');
65         }
66     }
67
68     return update_post_meta( $attachment_id, '_wp_attached_file', $file );
69 }
70
71 /**
72  * Retrieve all children of the post parent ID.
73  *
74  * Normally, without any enhancements, the children would apply to pages. In the
75  * context of the inner workings of WordPress, pages, posts, and attachments
76  * share the same table, so therefore the functionality could apply to any one
77  * of them. It is then noted that while this function does not work on posts, it
78  * does not mean that it won't work on posts. It is recommended that you know
79  * what context you wish to retrieve the children of.
80  *
81  * Attachments may also be made the child of a post, so if that is an accurate
82  * statement (which needs to be verified), it would then be possible to get
83  * all of the attachments for a post. Attachments have since changed since
84  * version 2.5, so this is most likely unaccurate, but serves generally as an
85  * example of what is possible.
86  *
87  * The arguments listed as defaults are for this function and also of the
88  * {@link get_posts()} function. The arguments are combined with the
89  * get_children defaults and are then passed to the {@link get_posts()}
90  * function, which accepts additional arguments. You can replace the defaults in
91  * this function, listed below and the additional arguments listed in the
92  * {@link get_posts()} function.
93  *
94  * The 'post_parent' is the most important argument and important attention
95  * needs to be paid to the $args parameter. If you pass either an object or an
96  * integer (number), then just the 'post_parent' is grabbed and everything else
97  * is lost. If you don't specify any arguments, then it is assumed that you are
98  * in The Loop and the post parent will be grabbed for from the current post.
99  *
100  * The 'post_parent' argument is the ID to get the children. The 'numberposts'
101  * is the amount of posts to retrieve that has a default of '-1', which is
102  * used to get all of the posts. Giving a number higher than 0 will only
103  * retrieve that amount of posts.
104  *
105  * The 'post_type' and 'post_status' arguments can be used to choose what
106  * criteria of posts to retrieve. The 'post_type' can be anything, but WordPress
107  * post types are 'post', 'pages', and 'attachments'. The 'post_status'
108  * argument will accept any post status within the write administration panels.
109  *
110  * @see get_posts() Has additional arguments that can be replaced.
111  * @internal Claims made in the long description might be inaccurate.
112  *
113  * @since 2.0.0
114  *
115  * @param mixed $args Optional. User defined arguments for replacing the defaults.
116  * @param string $output Optional. Constant for return type, either OBJECT (default), ARRAY_A, ARRAY_N.
117  * @return array|bool False on failure and the type will be determined by $output parameter.
118  */
119 function &get_children($args = '', $output = OBJECT) {
120     if ( empty( $args ) ) {
121         if ( isset( $GLOBALS['post'] ) ) {
122             $args = array('post_parent' => (int) $GLOBALS['post']->post_parent );
123         } else {
124             return false;
125         }
126     } elseif ( is_object( $args ) ) {
127         $args = array('post_parent' => (int) $args->post_parent );
128     } elseif ( is_numeric( $args ) ) {
129         $args = array('post_parent' => (int) $args);
130     }
131
132     $defaults = array(
133         'numberposts' => -1, 'post_type' => '',
134         'post_status' => '', 'post_parent' => 0
135     );
136
137     $r = wp_parse_args( $args, $defaults );
138
139     $children = get_posts( $r );
140     if ( !$children ) {
141         $kids = false;
142         return $kids;
143     }
144
145     update_post_cache($children);
146
147     foreach ( $children as $key => $child )
148         $kids[$child->ID] =& $children[$key];
149
150     if ( $output == OBJECT ) {
151         return $kids;
152     } elseif ( $output == ARRAY_A ) {
153         foreach ( (array) $kids as $kid )
154             $weeuns[$kid->ID] = get_object_vars($kids[$kid->ID]);
155         return $weeuns;
156     } elseif ( $output == ARRAY_N ) {
157         foreach ( (array) $kids as $kid )
158             $babes[$kid->ID] = array_values(get_object_vars($kids[$kid->ID]));
159         return $babes;
160     } else {
161         return $kids;
162     }
163 }
164
165 /**
166  * Get extended entry info (<!--more-->).
167  *
168  * There should not be any space after the second dash and before the word
169  * 'more'. There can be text or space(s) after the word 'more', but won't be
170  * referenced.
171  *
172  * The returned array has 'main' and 'extended' keys. Main has the text before
173  * the <code><!--more--></code>. The 'extended' key has the content after the
174  * <code><!--more--></code> comment.
175  *
176  * @since 1.0.0
177  *
178  * @param string $post Post content.
179  * @return array Post before ('main') and after ('extended').
180  */
181 function get_extended($post) {
182     //Match the new style more links
183     if ( preg_match('/<!--more(.*?)?-->/', $post, $matches) ) {
184         list($main, $extended) = explode($matches[0], $post, 2);
185     } else {
186         $main = $post;
187         $extended = '';
188     }
189
190     // Strip leading and trailing whitespace
191     $main = preg_replace('/^[\s]*(.*)[\s]*$/', '\\1', $main);
192     $extended = preg_replace('/^[\s]*(.*)[\s]*$/', '\\1', $extended);
193
194     return array('main' => $main, 'extended' => $extended);
195 }
196
197 /**
198  * Retrieves post data given a post ID or post object.
199  *
200  * See {@link sanitize_post()} for optional $filter values. Also, the parameter
201  * $post, must be given as a variable, since it is passed by reference.
202  *
203  * @since 1.5.1
204  * @uses $wpdb
205  * @link http://codex.wordpress.org/Function_Reference/get_post
206  *
207  * @param int|object $post Post ID or post object.
208  * @param string $output Optional, default is Object. Either OBJECT, ARRAY_A, or ARRAY_N.
209  * @param string $filter Optional, default is raw.
210  * @return mixed Post data
211  */
212 function &get_post(&$post, $output = OBJECT, $filter = 'raw') {
213     global $wpdb;
214     $null = null;
215
216     if ( empty($post) ) {
217         if ( isset($GLOBALS['post']) )
218             $_post = & $GLOBALS['post'];
219         else
220             return $null;
221     } elseif ( is_object($post) ) {
222         _get_post_ancestors($post);
223         wp_cache_add($post->ID, $post, 'posts');
224         $_post = &$post;
225     } else {
226         $post = (int) $post;
227         if ( ! $_post = wp_cache_get($post, 'posts') ) {
228             $_post = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d LIMIT 1", $post));
229             if ( ! $_post )
230                 return $null;
231             _get_post_ancestors($_post);
232             wp_cache_add($_post->ID, $_post, 'posts');
233         }
234     }
235
236     $_post = sanitize_post($_post, $filter);
237
238     if ( $output == OBJECT ) {
239         return $_post;
240     } elseif ( $output == ARRAY_A ) {
241         $__post = get_object_vars($_post);
242         return $__post;
243     } elseif ( $output == ARRAY_N ) {
244         $__post = array_values(get_object_vars($_post));
245         return $__post;
246     } else {
247         return $_post;
248     }
249 }
250
251 /**
252  * Retrieve ancestors of a post.
253  *
254  * @since 2.5.0
255  *
256  * @param int|object $post Post ID or post object
257  * @return array Ancestor IDs or empty array if none are found.
258  */
259 function get_post_ancestors($post) {
260     $post = get_post($post);
261
262     if ( !empty($post->ancestors) )
263         return $post->ancestors;
264
265     return array();
266 }
267
268 /**
269  * Retrieve data from a post field based on Post ID.
270  *
271  * Examples of the post field will be, 'post_type', 'post_status', 'content',
272  * etc and based off of the post object property or key names.
273  *
274  * The context values are based off of the taxonomy filter functions and
275  * supported values are found within those functions.
276  *
277  * @since 2.3.0
278  * @uses sanitize_post_field() See for possible $context values.
279  *
280  * @param string $field Post field name
281  * @param id $post Post ID
282  * @param string $context Optional. How to filter the field. Default is display.
283  * @return WP_Error|string Value in post field or WP_Error on failure
284  */
285 function get_post_field( $field, $post, $context = 'display' ) {
286     $post = (int) $post;
287     $post = get_post( $post );
288
289     if ( is_wp_error($post) )
290         return $post;
291
292     if ( !is_object($post) )
293         return '';
294
295     if ( !isset($post->$field) )
296         return '';
297
298     return sanitize_post_field($field, $post->$field, $post->ID, $context);
299 }
300
301 /**
302  * Retrieve the mime type of an attachment based on the ID.
303  *
304  * This function can be used with any post type, but it makes more sense with
305  * attachments.
306  *
307  * @since 2.0.0
308  *
309  * @param int $ID Optional. Post ID.
310  * @return bool|string False on failure or returns the mime type
311  */
312 function get_post_mime_type($ID = '') {
313     $post = & get_post($ID);
314
315     if ( is_object($post) )
316         return $post->post_mime_type;
317
318     return false;
319 }
320
321 /**
322  * Retrieve the post status based on the Post ID.
323  *
324  * If the post ID is of an attachment, then the parent post status will be given
325  * instead.
326  *
327  * @since 2.0.0
328  *
329  * @param int $ID Post ID
330  * @return string|bool Post status or false on failure.
331  */
332 function get_post_status($ID = '') {
333     $post = get_post($ID);
334
335     if ( is_object($post) ) {
336         if ( ('attachment' == $post->post_type) && $post->post_parent && ($post->ID != $post->post_parent) )
337             return get_post_status($post->post_parent);
338         else
339             return $post->post_status;
340     }
341
342     return false;
343 }
344
345 /**
346  * Retrieve all of the WordPress supported post statuses.
347  *
348  * Posts have a limited set of valid status values, this provides the
349  * post_status values and descriptions.
350  *
351  * @since 2.5.0
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  * @since 2.5.0
373  *
374  * @return array List of page statuses.
375  */
376 function get_page_statuses( ) {
377     $status = array(
378         'draft'            => __('Draft'),
379         'private'        => __('Private'),
380         'publish'        => __('Published')
381     );
382
383     return $status;
384 }
385
386 /**
387  * Retrieve the post type of the current post or of a given post.
388  *
389  * @since 2.1.0
390  *
391  * @uses $wpdb
392  * @uses $posts The Loop post global
393  *
394  * @param mixed $post Optional. Post object or post ID.
395  * @return bool|string post type or false on failure.
396  */
397 function get_post_type($post = false) {
398     global $posts;
399
400     if ( false === $post )
401         $post = $posts[0];
402     elseif ( (int) $post )
403         $post = get_post($post, OBJECT);
404
405     if ( is_object($post) )
406         return $post->post_type;
407
408     return false;
409 }
410
411 /**
412  * Updates the post type for the post ID.
413  *
414  * The page or post cache will be cleaned for the post ID.
415  *
416  * @since 2.5.0
417  *
418  * @uses $wpdb
419  *
420  * @param int $post_id Post ID to change post type. Not actually optional.
421  * @param string $post_type Optional, default is post. Supported values are 'post' or 'page' to name a few.
422  * @return int Amount of rows changed. Should be 1 for success and 0 for failure.
423  */
424 function set_post_type( $post_id = 0, $post_type = 'post' ) {
425     global $wpdb;
426
427     $post_type = sanitize_post_field('post_type', $post_type, $post_id, 'db');
428     $return = $wpdb->query( $wpdb->prepare("UPDATE $wpdb->posts SET post_type = %s WHERE ID = %d", $post_type, $post_id) );
429
430     if ( 'page' == $post_type )
431         clean_page_cache($post_id);
432     else
433         clean_post_cache($post_id);
434
435     return $return;
436 }
437
438 /**
439  * Retrieve list of latest posts or posts matching criteria.
440  *
441  * The defaults are as follows:
442  *     'numberposts' - Default is 5. Total number of posts to retrieve.
443  *     'offset' - Default is 0. See {@link WP_Query::query()} for more.
444  *     'category' - What category to pull the posts from.
445  *     'orderby' - Default is 'post_date'. How to order the posts.
446  *     'order' - Default is 'DESC'. The order to retrieve the posts.
447  *     'include' - See {@link WP_Query::query()} for more.
448  *     'exclude' - See {@link WP_Query::query()} for more.
449  *     'meta_key' - See {@link WP_Query::query()} for more.
450  *     'meta_value' - See {@link WP_Query::query()} for more.
451  *     'post_type' - Default is 'post'. Can be 'page', or 'attachment' to name a few.
452  *     'post_parent' - The parent of the post or post type.
453  *     'post_status' - Default is 'published'. Post status to retrieve.
454  *
455  * @since 1.2.0
456  * @uses $wpdb
457  * @uses WP_Query::query() See for more default arguments and information.
458  * @link http://codex.wordpress.org/Template_Tags/get_posts
459  *
460  * @param array $args Optional. Override defaults.
461  * @return array List of posts.
462  */
463 function get_posts($args = null) {
464     $defaults = array(
465         'numberposts' => 5, 'offset' => 0,
466         'category' => 0, 'orderby' => 'post_date',
467         'order' => 'DESC', 'include' => '',
468         'exclude' => '', 'meta_key' => '',
469         'meta_value' =>'', 'post_type' => 'post',
470         'post_parent' => 0, 'suppress_filters' => true
471     );
472
473     $r = wp_parse_args( $args, $defaults );
474     if ( empty( $r['post_status'] ) )
475         $r['post_status'] = ( 'attachment' == $r['post_type'] ) ? 'inherit' : 'publish';
476     if ( ! empty($r['numberposts']) )
477         $r['posts_per_page'] = $r['numberposts'];
478     if ( ! empty($r['category']) )
479         $r['cat'] = $r['category'];
480     if ( ! empty($r['include']) ) {
481         $incposts = preg_split('/[\s,]+/',$r['include']);
482         $r['posts_per_page'] = count($incposts);  // only the number of posts included
483         $r['post__in'] = $incposts;
484     } elseif ( ! empty($r['exclude']) )
485         $r['post__not_in'] = preg_split('/[\s,]+/',$r['exclude']);
486
487     $r['caller_get_posts'] = true;
488
489     $get_posts = new WP_Query;
490     return $get_posts->query($r);
491
492 }
493
494 //
495 // Post meta functions
496 //
497
498 /**
499  * Add meta data field to a post.
500  *
501  * Post meta data is called "Custom Fields" on the Administration Panels.
502  *
503  * @since 1.5.0
504  * @uses $wpdb
505  * @link http://codex.wordpress.org/Function_Reference/add_post_meta
506  *
507  * @param int $post_id Post ID.
508  * @param string $key Metadata name.
509  * @param mixed $value Metadata value.
510  * @param bool $unique Optional, default is false. Whether the same key should not be added.
511  * @return bool False for failure. True for success.
512  */
513 function add_post_meta($post_id, $meta_key, $meta_value, $unique = false) {
514     global $wpdb;
515
516     // make sure meta is added to the post, not a revision
517     if ( $the_post = wp_is_post_revision($post_id) )
518         $post_id = $the_post;
519
520     // expected_slashed ($meta_key)
521     $meta_key = stripslashes($meta_key);
522     $meta_value = stripslashes($meta_value);
523
524     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 ) ) )
525         return false;
526
527     $meta_value = maybe_serialize($meta_value);
528
529     $wpdb->insert( $wpdb->postmeta, compact( 'post_id', 'meta_key', 'meta_value' ) );
530
531     wp_cache_delete($post_id, 'post_meta');
532
533     return true;
534 }
535
536 /**
537  * Remove metadata matching criteria from a post.
538  *
539  * You can match based on the key, or key and value. Removing based on key and
540  * value, will keep from removing duplicate metadata with the same key. It also
541  * allows removing all metadata matching key, if needed.
542  *
543  * @since 1.5.0
544  * @uses $wpdb
545  * @link http://codex.wordpress.org/Function_Reference/delete_post_meta
546  *
547  * @param int $post_id post ID
548  * @param string $key Metadata name.
549  * @param mixed $value Optional. Metadata value.
550  * @return bool False for failure. True for success.
551  */
552 function delete_post_meta($post_id, $key, $value = '') {
553     global $wpdb;
554
555     $post_id = absint( $post_id );
556
557     // expected_slashed ($key, $value)
558     $key = stripslashes( $key );
559     $value = stripslashes( $value );
560
561     if ( empty( $value ) )
562         $meta_id = $wpdb->get_var( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s", $post_id, $key ) );
563     else
564         $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 ) );
565
566     if ( !$meta_id )
567         return false;
568
569     if ( empty( $value ) )
570         $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s", $post_id, $key ) );
571     else
572         $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->postmeta WHERE post_id = %d AND meta_key = %s AND meta_value = %s", $post_id, $key, $value ) );
573
574     wp_cache_delete($post_id, 'post_meta');
575
576     return true;
577 }
578
579 /**
580  * Retrieve post meta field for a post.
581  *
582  * @since 1.5.0
583  * @uses $wpdb
584  * @link http://codex.wordpress.org/Function_Reference/get_post_meta
585  *
586  * @param int $post_id Post ID.
587  * @param string $key The meta key to retrieve.
588  * @param bool $single Whether to return a single value.
589  * @return mixed Will be an array if $single is false. Will be value of meta data field if $single is true.
590  */
591 function get_post_meta($post_id, $key, $single = false) {
592     $post_id = (int) $post_id;
593
594     $meta_cache = wp_cache_get($post_id, 'post_meta');
595
596     if ( !$meta_cache ) {
597         update_postmeta_cache($post_id);
598         $meta_cache = wp_cache_get($post_id, 'post_meta');
599     }
600
601     if ( isset($meta_cache[$key]) ) {
602         if ( $single ) {
603             return maybe_unserialize( $meta_cache[$key][0] );
604         } else {
605             return array_map('maybe_unserialize', $meta_cache[$key]);
606         }
607     }
608
609     return '';
610 }
611
612 /**
613  * Update post meta field based on post ID.
614  *
615  * Use the $prev_value parameter to differentiate between meta fields with the
616  * same key and post ID.
617  *
618  * If the meta field for the post does not exist, it will be added.
619  *
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 Metadata key.
626  * @param mixed $value Metadata value.
627  * @param mixed $prev_value Optional. Previous value to check before removing.
628  * @return bool False on failure, true if success.
629  */
630 function update_post_meta($post_id, $meta_key, $meta_value, $prev_value = '') {
631     global $wpdb;
632
633     // expected_slashed (