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