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