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