| [ 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 *