| [ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * Post functions and post utility function. 4 * 5 * @package WordPress 6 * @subpackage Post 7 * @since 1.5.0 8 */ 9 10 // 11 // Post Type Registration 12 // 13 14 /** 15 * Creates the initial post types when 'init' action is fired. 16 * 17 * @since 2.9.0 18 */ 19 function create_initial_post_types() { 20 register_post_type( 'post', array( 21 'labels' => array( 22 'name_admin_bar' => _x( 'Post', 'add new on admin bar' ), 23 ), 24 'public' => true, 25 '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ 26 '_edit_link' => 'post.php?post=%d', /* internal use only. don't use this when registering your own post type. */ 27 'capability_type' => 'post', 28 'map_meta_cap' => true, 29 'hierarchical' => false, 30 'rewrite' => false, 31 'query_var' => false, 32 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'trackbacks', 'custom-fields', 'comments', 'revisions', 'post-formats' ), 33 ) ); 34 35 register_post_type( 'page', array( 36 'labels' => array( 37 'name_admin_bar' => _x( 'Page', 'add new on admin bar' ), 38 ), 39 'public' => true, 40 'publicly_queryable' => false, 41 '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ 42 '_edit_link' => 'post.php?post=%d', /* internal use only. don't use this when registering your own post type. */ 43 'capability_type' => 'page', 44 'map_meta_cap' => true, 45 'hierarchical' => true, 46 'rewrite' => false, 47 'query_var' => false, 48 'supports' => array( 'title', 'editor', 'author', 'thumbnail', 'page-attributes', 'custom-fields', 'comments', 'revisions' ), 49 ) ); 50 51 register_post_type( 'attachment', array( 52 'labels' => array( 53 'name' => __( 'Media' ), 54 'edit_item' => __( 'Edit Media' ), 55 ), 56 'public' => true, 57 'show_ui' => false, 58 '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ 59 '_edit_link' => 'media.php?attachment_id=%d', /* internal use only. don't use this when registering your own post type. */ 60 'capability_type' => 'post', 61 'map_meta_cap' => true, 62 'hierarchical' => false, 63 'rewrite' => false, 64 'query_var' => false, 65 'show_in_nav_menus' => false, 66 'supports' => array( 'comments' ), 67 ) ); 68 69 register_post_type( 'revision', array( 70 'labels' => array( 71 'name' => __( 'Revisions' ), 72 'singular_name' => __( 'Revision' ), 73 ), 74 'public' => false, 75 '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ 76 '_edit_link' => 'revision.php?revision=%d', /* internal use only. don't use this when registering your own post type. */ 77 'capability_type' => 'post', 78 'map_meta_cap' => true, 79 'hierarchical' => false, 80 'rewrite' => false, 81 'query_var' => false, 82 'can_export' => false, 83 ) ); 84 85 register_post_type( 'nav_menu_item', array( 86 'labels' => array( 87 'name' => __( 'Navigation Menu Items' ), 88 'singular_name' => __( 'Navigation Menu Item' ), 89 ), 90 'public' => false, 91 '_builtin' => true, /* internal use only. don't use this when registering your own post type. */ 92 'hierarchical' => false, 93 'rewrite' => false, 94 'query_var' => false, 95 ) ); 96 97 register_post_status( 'publish', array( 98 'label' => _x( 'Published', 'post' ), 99 'public' => true, 100 '_builtin' => true, /* internal use only. */ 101 'label_count' => _n_noop( 'Published <span class="count">(%s)</span>', 'Published <span class="count">(%s)</span>' ), 102 ) ); 103 104 register_post_status( 'future', array( 105 'label' => _x( 'Scheduled', 'post' ), 106 'protected' => true, 107 '_builtin' => true, /* internal use only. */ 108 'label_count' => _n_noop('Scheduled <span class="count">(%s)</span>', 'Scheduled <span class="count">(%s)</span>' ), 109 ) ); 110 111 register_post_status( 'draft', array( 112 'label' => _x( 'Draft', 'post' ), 113 'protected' => true, 114 '_builtin' => true, /* internal use only. */ 115 'label_count' => _n_noop( 'Draft <span class="count">(%s)</span>', 'Drafts <span class="count">(%s)</span>' ), 116 ) ); 117 118 register_post_status( 'pending', array( 119 'label' => _x( 'Pending', 'post' ), 120 'protected' => true, 121 '_builtin' => true, /* internal use only. */ 122 'label_count' => _n_noop( 'Pending <span class="count">(%s)</span>', 'Pending <span class="count">(%s)</span>' ), 123 ) ); 124 125 register_post_status( 'private', array( 126 'label' => _x( 'Private', 'post' ), 127 'private' => true, 128 '_builtin' => true, /* internal use only. */ 129 'label_count' => _n_noop( 'Private <span class="count">(%s)</span>', 'Private <span class="count">(%s)</span>' ), 130 ) ); 131 132 register_post_status( 'trash', array( 133 'label' => _x( 'Trash', 'post' ), 134 'internal' => true, 135 '_builtin' => true, /* internal use only. */ 136 'label_count' => _n_noop( 'Trash <span class="count">(%s)</span>', 'Trash <span class="count">(%s)</span>' ), 137 'show_in_admin_status_list' => true, 138 ) ); 139 140 register_post_status( 'auto-draft', array( 141 'label' => 'auto-draft', 142 'internal' => true, 143 '_builtin' => true, /* internal use only. */ 144 ) ); 145 146 register_post_status( 'inherit', array( 147 'label' => 'inherit', 148 'internal' => true, 149 '_builtin' => true, /* internal use only. */ 150 'exclude_from_search' => false, 151 ) ); 152 } 153 add_action( 'init', 'create_initial_post_types', 0 ); // highest priority 154 155 /** 156 * Retrieve attached file path based on attachment ID. 157 * 158 * You can optionally send it through the 'get_attached_file' filter, but by 159 * default it will just return the file path unfiltered. 160 * 161 * The function works by getting the single post meta name, named 162 * '_wp_attached_file' and returning it. This is a convenience function to 163 * prevent looking up the meta name and provide a mechanism for sending the 164 * attached filename through a filter. 165 * 166 * @since 2.0.0 167 * @uses apply_filters() Calls 'get_attached_file' on file path and attachment ID. 168 * 169 * @param int $attachment_id Attachment ID. 170 * @param bool $unfiltered Whether to apply filters. 171 * @return string The file path to the attached file. 172 */ 173 function get_attached_file( $attachment_id, $unfiltered = false ) { 174 $file = get_post_meta( $attachment_id, '_wp_attached_file', true ); 175 // If the file is relative, prepend upload dir 176 if ( 0 !== strpos($file, '/') && !preg_match('|^.:\\\|', $file) && ( ($uploads = wp_upload_dir()) && false === $uploads['error'] ) ) 177 $file = $uploads['basedir'] . "/$file"; 178 if ( $unfiltered ) 179 return $file; 180 return apply_filters( 'get_attached_file', $file, $attachment_id ); 181 } 182 183 /** 184 * Update attachment file path based on attachment ID. 185 * 186 * Used to update the file path of the attachment, which uses post meta name 187 * '_wp_attached_file' to store the path of the attachment. 188 * 189 * @since 2.1.0 190 * @uses apply_filters() Calls 'update_attached_file' on file path and attachment ID. 191 * 192 * @param int $attachment_id Attachment ID 193 * @param string $file File path for the attachment 194 * @return bool False on failure, true on success. 195 */ 196 function update_attached_file( $attachment_id, $file ) { 197 if ( !get_post( $attachment_id ) ) 198 return false; 199 200 $file = apply_filters( 'update_attached_file', $file, $attachment_id ); 201 $file = _wp_relative_upload_path($file); 202 203 return update_post_meta( $attachment_id, '_wp_attached_file', $file ); 204 } 205 206 /** 207 * Return relative path to an uploaded file. 208 * 209 * The path is relative to the current upload dir. 210 * 211 * @since 2.9.0 212 * @uses apply_filters() Calls '_wp_relative_upload_path' on file path. 213 * 214 * @param string $path Full path to the file 215 * @return string relative path on success, unchanged path on failure. 216 */ 217 function _wp_relative_upload_path( $path ) { 218 $new_path = $path; 219 220 if ( ($uploads = wp_upload_dir()) && false === $uploads['error'] ) { 221 if ( 0 === strpos($new_path, $uploads['basedir']) ) { 222 $new_path = str_replace($uploads['basedir'], '', $new_path); 223 $new_path = ltrim($new_path, '/'); 224 } 225 } 226 227 return apply_filters( '_wp_relative_upload_path', $new_path, $path ); 228 } 229 230 /** 231 * Retrieve all children of the post parent ID. 232 * 233 * Normally, without any enhancements, the children would apply to pages. In the 234 * context of the inner workings of WordPress, pages, posts, and attachments 235 * share the same table, so therefore the functionality could apply to any one 236 * of them. It is then noted that while this function does not work on posts, it 237 * does not mean that it won't work on posts. It is recommended that you know 238 * what context you wish to retrieve the children of. 239 * 240 * Attachments may also be made the child of a post, so if that is an accurate 241 * statement (which needs to be verified), it would then be possible to get 242 * all of the attachments for a post. Attachments have since changed since 243 * version 2.5, so this is most likely unaccurate, but serves generally as an 244 * example of what is possible. 245 * 246 * The arguments listed as defaults are for this function and also of the 247 * {@link get_posts()} function. The arguments are combined with the 248 * get_children defaults and are then passed to the {@link get_posts()} 249 * function, which accepts additional arguments. You can replace the defaults in 250 * this function, listed below and the additional arguments listed in the 251 * {@link get_posts()} function. 252 * 253 * The 'post_parent' is the most important argument and important attention 254 * needs to be paid to the $args parameter. If you pass either an object or an 255 * integer (number), then just the 'post_parent' is grabbed and everything else 256 * is lost. If you don't specify any arguments, then it is assumed that you are 257 * in The Loop and the post parent will be grabbed for from the current post. 258 * 259 * The 'post_parent' argument is the ID to get the children. The 'numberposts' 260 * is the amount of posts to retrieve that has a default of '-1', which is 261 * used to get all of the posts. Giving a number higher than 0 will only 262 * retrieve that amount of posts. 263 * 264 * The 'post_type' and 'post_status' arguments can be used to choose what 265 * criteria of posts to retrieve. The 'post_type' can be anything, but WordPress 266 * post types are 'post', 'pages', and 'attachments'. The 'post_status' 267 * argument will accept any post status within the write administration panels. 268 * 269 * @see get_posts() Has additional arguments that can be replaced. 270 * @internal Claims made in the long description might be inaccurate. 271 * 272 * @since 2.0.0 273 * 274 * @param mixed $args Optional. User defined arguments for replacing the defaults. 275 * @param string $output Optional. Constant for return type, either OBJECT (default), ARRAY_A, ARRAY_N. 276 * @return array|bool False on failure and the type will be determined by $output parameter. 277 */ 278 function get_children($args = '', $output = OBJECT) { 279 $kids = array(); 280 if ( empty( $args ) ) { 281 if ( isset( $GLOBALS['post'] ) ) { 282 $args = array('post_parent' => (int) $GLOBALS['post']->post_parent ); 283 } else { 284 return $kids; 285 } 286 } elseif ( is_object( $args ) ) { 287 $args = array('post_parent' => (int) $args->post_parent ); 288 } elseif ( is_numeric( $args ) ) { 289 $args = array('post_parent' => (int) $args); 290 } 291 292 $defaults = array( 293 'numberposts' => -1, 'post_type' => 'any', 294 'post_status' => 'any', 'post_parent' => 0, 295 ); 296 297 $r = wp_parse_args( $args, $defaults ); 298 299 $children = get_posts( $r ); 300 301 if ( !$children ) 302 return $kids; 303 304 update_post_cache($children); 305 306 foreach ( $children as $key => $child ) 307 $kids[$child->ID] = $children[$key]; 308 309 if ( $output == OBJECT ) { 310 return $kids; 311 } elseif ( $output == ARRAY_A ) { 312 foreach ( (array) $kids as $kid ) 313 $weeuns[$kid->ID] = get_object_vars($kids[$kid->ID]); 314 return $weeuns; 315 } elseif ( $output == ARRAY_N ) { 316 foreach ( (array) $kids as $kid ) 317 $babes[$kid->ID] = array_values(get_object_vars($kids[$kid->ID])); 318 return $babes; 319 } else { 320 return $kids; 321 } 322 } 323 324 /** 325 * Get extended entry info (<!--more-->). 326 * 327 * There should not be any space after the second dash and before the word 328 * 'more'. There can be text or space(s) after the word 'more', but won't be 329 * referenced. 330 * 331 * The returned array has 'main' and 'extended' keys. Main has the text before 332 * the <code><!--more--></code>. The 'extended' key has the content after the 333 * <code><!--more--></code> comment. 334 * 335 * @since 1.0.0 336 * 337 * @param string $post Post content. 338 * @return array Post before ('main') and after ('extended'). 339 */ 340 function get_extended($post) { 341 //Match the new style more links 342 if ( preg_match('/<!--more(.*?)?-->/', $post, $matches) ) { 343 list($main, $extended) = explode($matches[0], $post, 2); 344 } else { 345 $main = $post; 346 $extended = ''; 347 } 348 349 // Strip leading and trailing whitespace 350 $main = preg_replace('/^[\s]*(.*)[\s]*$/', '\\1', $main); 351 $extended = preg_replace('/^[\s]*(.*)[\s]*$/', '\\1', $extended); 352 353 return array('main' => $main, 'extended' => $extended); 354 } 355 356 /** 357 * Retrieves post data given a post ID or post object. 358 * 359 * See {@link sanitize_post()} for optional $filter values. Also, the parameter 360 * $post, must be given as a variable, since it is passed by reference. 361 * 362 * @since 1.5.1 363 * @uses $wpdb 364 * @link http://codex.wordpress.org/Function_Reference/get_post 365 * 366 * @param int|object $post Post ID or post object. 367 * @param string $output Optional, default is Object. Either OBJECT, ARRAY_A, or ARRAY_N. 368 * @param string $filter Optional, default is raw. 369 * @return mixed Post data 370 */ 371 function &get_post(&$post, $output = OBJECT, $filter = 'raw') { 372 global $wpdb; 373 $null = null; 374 375 if ( empty($post) ) { 376 if ( isset($GLOBALS['post']) ) 377 $_post = & $GLOBALS['post']; 378 else 379 return $null; 380 } elseif ( is_object($post) && empty($post->filter) ) { 381 _get_post_ancestors($post); 382 $_post = sanitize_post($post, 'raw'); 383 wp_cache_add($post->ID, $_post, 'posts'); 384 } elseif ( is_object($post) && 'raw' == $post->filter ) { 385 $_post = $post; 386 } else { 387 if ( is_object($post) ) 388 $post_id = $post->ID; 389 else 390 $post_id = $post; 391 392 $post_id = (int) $post_id; 393 if ( ! $_post = wp_cache_get($post_id, 'posts') ) { 394 $_post = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d LIMIT 1", $post_id)); 395 if ( ! $_post ) 396 return $null; 397 _get_post_ancestors($_post); 398 $_post = sanitize_post($_post, 'raw'); 399 wp_cache_add($_post->ID, $_post, 'posts'); 400 } 401 } 402 403 if ($filter != 'raw') 404 $_post = sanitize_post($_post, $filter); 405 406 if ( $output == OBJECT ) { 407 return $_post; 408 } elseif ( $output == ARRAY_A ) { 409 $__post = get_object_vars($_post); 410 return $__post; 411 } elseif ( $output == ARRAY_N ) { 412 $__post = array_values(get_object_vars($_post)); 413 return $__post; 414 } else { 415 return $_post; 416 } 417 } 418 419 /** 420 * Retrieve ancestors of a post. 421 * 422 * @since 2.5.0 423 * 424 * @param int|object $post Post ID or post object 425 * @return array Ancestor IDs or empty array if none are found. 426 */ 427 function get_post_ancestors($post) { 428 $post = get_post($post); 429 430 if ( !empty($post->ancestors) ) 431 return $post->ancestors; 432 433 return array(); 434 } 435 436 /** 437 * Retrieve data from a post field based on Post ID. 438 * 439 * Examples of the post field will be, 'post_type', 'post_status', 'post_content', 440 * etc and based off of the post object property or key names. 441 * 442 * The context values are based off of the taxonomy filter functions and 443 * supported values are found within those functions. 444 * 445 * @since 2.3.0 446 * @uses sanitize_post_field() See for possible $context values. 447 * 448 * @param string $field Post field name 449 * @param id $post Post ID 450 * @param string $context Optional. How to filter the field. Default is display. 451 * @return WP_Error|string Value in post field or WP_Error on failure 452 */ 453 function get_post_field( $field, $post, $context = 'display' ) { 454 $post = (int) $post; 455 $post = get_post( $post ); 456 457 if ( is_wp_error($post) ) 458 return $post; 459 460 if ( !is_object($post) ) 461 return ''; 462 463 if ( !isset($post->$field) ) 464 return ''; 465 466 return sanitize_post_field($field, $post->$field, $post->ID, $context); 467 } 468 469 /** 470 * Retrieve the mime type of an attachment based on the ID. 471 * 472 * This function can be used with any post type, but it makes more sense with 473 * attachments. 474 * 475 * @since 2.0.0 476 * 477 * @param int $ID Optional. Post ID. 478 * @return bool|string False on failure or returns the mime type 479 */ 480 function get_post_mime_type($ID = '') { 481 $post = & get_post($ID); 482 483 if ( is_object($post) ) 484 return $post->post_mime_type; 485 486 return false; 487 } 488 489 /** 490 * Retrieve the format slug for a post 491 * 492 * @since 3.1.0 493 * 494 * @param int|object $post A post 495 * 496 * @return mixed The format if successful. False if no format is set. WP_Error if errors. 497 */ 498 function get_post_format( $post = null ) { 499 $post = get_post($post); 500 501 if ( ! post_type_supports( $post->post_type, 'post-formats' ) ) 502 return false; 503 504 $_format = get_the_terms( $post->ID, 'post_format' ); 505 506 if ( empty( $_format ) ) 507 return false; 508 509 $format = array_shift( $_format ); 510 511 return ( str_replace('post-format-', '', $format->slug ) ); 512 } 513 514 /** 515 * Check if a post has a particular format 516 * 517 * @since 3.1.0 518 * @uses has_term() 519 * 520 * @param string $format The format to check for 521 * @param object|id $post The post to check. If not supplied, defaults to the current post if used in the loop. 522 * @return bool True if the post has the format, false otherwise. 523 */ 524 function has_post_format( $format, $post = null ) { 525 return has_term('post-format-' . sanitize_key($format), 'post_format', $post); 526 } 527 528 /** 529 * Assign a format to a post 530 * 531 * @since 3.1.0 532 * 533 * @param int|object $post The post for which to assign a format 534 * @param string $format A format to assign. Use an empty string or array to remove all formats from the post. 535 * @return mixed WP_Error on error. Array of affected term IDs on success. 536 */ 537 function set_post_format( $post, $format ) { 538 $post = get_post($post); 539 540 if ( empty($post) ) 541 return new WP_Error('invalid_post', __('Invalid post')); 542 543 if ( !empty($format) ) { 544 $format = sanitize_key($format); 545 if ( 'standard' == $format || !in_array( $format, array_keys( get_post_format_slugs() ) ) ) 546 $format = ''; 547 else 548 $format = 'post-format-' . $format; 549 } 550 551 return wp_set_post_terms($post->ID, $format, 'post_format'); 552 } 553 554 /** 555 * Retrieve the post status based on the Post ID. 556 * 557 * If the post ID is of an attachment, then the parent post status will be given 558 * instead. 559 * 560 * @since 2.0.0 561 * 562 * @param int $ID Post ID 563 * @return string|bool Post status or false on failure. 564 */ 565 function get_post_status($ID = '') { 566 $post = get_post($ID); 567 568 if ( !is_object($post) ) 569 return false; 570 571 if ( 'attachment' == $post->post_type ) { 572 if ( 'private' == $post->post_status ) 573 return 'private'; 574 575 // Unattached attachments are assumed to be published 576 if ( ( 'inherit' == $post->post_status ) && ( 0 == $post->post_parent) ) 577 return 'publish'; 578 579 // Inherit status from the parent 580 if ( $post->post_parent && ( $post->ID != $post->post_parent ) ) 581 return get_post_status($post->post_parent); 582 } 583 584 return $post->post_status; 585 } 586 587 /** 588 * Retrieve all of the WordPress supported post statuses. 589 * 590 * Posts have a limited set of valid status values, this provides the 591 * post_status values and descriptions. 592 * 593 * @since 2.5.0 594 * 595 * @return array List of post statuses. 596 */ 597 function get_post_statuses( ) { 598 $status = array( 599 'draft' => __('Draft'), 600 'pending' => __('Pending Review'), 601 'private' => __('Private'), 602 'publish' => __('Published') 603 ); 604 605 return $status; 606 } 607 608 /** 609 * Retrieve all of the WordPress support page statuses. 610 * 611 * Pages have a limited set of valid status values, this provides the 612 * post_status values and descriptions. 613 * 614 * @since 2.5.0 615 * 616 * @return array List of page statuses. 617 */ 618 function get_page_statuses( ) { 619 $status = array( 620 'draft' => __('Draft'), 621 'private' => __('Private'), 622 'publish' => __('Published') 623 ); 624 625 return $status; 626 } 627 628 /** 629 * Register a post type. Do not use before init. 630 * 631 * A simple function for creating or modifying a post status based on the 632 * parameters given. The function will accept an array (second optional 633 * parameter), along with a string for the post status name. 634 * 635 * 636 * Optional $args contents: 637 * 638 * label - A descriptive name for the post status marked for translation. Defaults to $post_status. 639 * public - Whether posts of this status should be shown in the front end of the site. Defaults to true. 640 * exclude_from_search - Whether to exclude posts with this post status from search results. Defaults to false. 641 * show_in_admin_all_list - Whether to include posts in the edit listing for their post type 642 * show_in_admin_status_list - Show in the list of statuses with post counts at the top of the edit 643 * listings, e.g. All (12) | Published (9) | My Custom Status (2) ... 644 * 645 * Arguments prefixed with an _underscore shouldn't be used by plugins and themes. 646 * 647 * @package WordPress 648 * @subpackage Post 649 * @since 3.0.0 650 * @uses $wp_post_statuses Inserts new post status object into the list 651 * 652 * @param string $post_status Name of the post status. 653 * @param array|string $args See above description. 654 */ 655 function register_post_status($post_status, $args = array()) { 656 global $wp_post_statuses; 657 658 if (!is_array($wp_post_statuses)) 659 $wp_post_statuses = array(); 660 661 // Args prefixed with an underscore are reserved for internal use. 662 $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); 663 $args = wp_parse_args($args, $defaults); 664 $args = (object) $args; 665 666 $post_status = sanitize_key($post_status); 667 $args->name = $post_status; 668 669 if ( null === $args->public && null === $args->internal && null === $args->protected && null === $args->private ) 670 $args->internal = true; 671 672 if ( null === $args->public ) 673 $args->public = false; 674 675 if ( null === $args->private ) 676 $args->private = false; 677 678 if ( null === $args->protected ) 679 $args->protected = false; 680 681 if ( null === $args->internal ) 682 $args->internal = false; 683 684 if ( null === $args->publicly_queryable ) 685 $args->publicly_queryable = $args->public; 686 687 if ( null === $args->exclude_from_search ) 688 $args->exclude_from_search = $args->internal; 689 690 if ( null === $args->show_in_admin_all_list ) 691 $args->show_in_admin_all_list = !$args->internal; 692 693 if ( null === $args->show_in_admin_status_list ) 694 $args->show_in_admin_status_list = !$args->internal; 695 696 if ( null === $args->single_view_cap ) 697 $args->single_view_cap = $args->public ? '' : 'edit'; 698 699 if ( false === $args->label ) 700 $args->label = $post_status; 701 702 if ( false === $args->label_count ) 703 $args->label_count = array( $args->label, $args->label ); 704 705 $wp_post_statuses[$post_status] = $args; 706 707 return $args; 708 } 709 710 /** 711 * Retrieve a post status object by name 712 * 713 * @package WordPress 714 * @subpackage Post 715 * @since 3.0.0 716 * @uses $wp_post_statuses 717 * @see register_post_status 718 * @see get_post_statuses 719 * 720 * @param string $post_status The name of a registered post status 721 * @return object A post status object 722 */ 723 function get_post_status_object( $post_status ) { 724 global $wp_post_statuses; 725 726 if ( empty($wp_post_statuses[$post_status]) ) 727 return null; 728 729 return $wp_post_statuses[$post_status]; 730 } 731 732 /** 733 * Get a list of all registered post status objects. 734 * 735 * @package WordPress 736 * @subpackage Post 737 * @since 3.0.0 738 * @uses $wp_post_statuses 739 * @see register_post_status 740 * @see get_post_status_object 741 * 742 * @param array|string $args An array of key => value arguments to match against the post status objects. 743 * @param string $output The type of output to return, either post status 'names' or 'objects'. 'names' is the default. 744 * @param string $operator The logical operation to perform. 'or' means only one element 745 * from the array needs to match; 'and' means all elements must match. The default is 'and'. 746 * @return array A list of post type names or objects 747 */ 748 function get_post_stati( $args = array(), $output = 'names', $operator = 'and' ) { 749 global $wp_post_statuses; 750 751 $field = ('names' == $output) ? 'name' : false; 752 753 return wp_filter_object_list($wp_post_statuses, $args, $operator, $field); 754 } 755 756 /** 757 * Whether the post type is hierarchical. 758 * 759 * A false return value might also mean that the post type does not exist. 760 * 761 * @since 3.0.0 762 * @see get_post_type_object 763 * 764 * @param string $post_type Post type name 765 * @return bool Whether post type is hierarchical. 766 */ 767 function is_post_type_hierarchical( $post_type ) { 768 if ( ! post_type_exists( $post_type ) ) 769 return false; 770 771 $post_type = get_post_type_object( $post_type ); 772 return $post_type->hierarchical; 773 } 774 775 /** 776 * Checks if a post type is registered. 777 * 778 * @since 3.0.0 779 * @uses get_post_type_object() 780 * 781 * @param string $post_type Post type name 782 * @return bool Whether post type is registered. 783 */ 784 function post_type_exists( $post_type ) { 785 return (bool) get_post_type_object( $post_type ); 786 } 787 788 /** 789 * Retrieve the post type of the current post or of a given post. 790 * 791 * @since 2.1.0 792 * 793 * @uses $post The Loop current post global 794 * 795 * @param mixed $the_post Optional. Post object or post ID. 796 * @return bool|string post type or false on failure. 797 */ 798 function get_post_type( $the_post = false ) { 799 global $post; 800 801 if ( false === $the_post ) 802 $the_post = $post; 803 elseif ( is_numeric($the_post) ) 804 $the_post = get_post($the_post); 805 806 if ( is_object($the_post) ) 807 return $the_post->post_type; 808 809 return false; 810 } 811 812 /** 813 * Retrieve a post type object by name 814 * 815 * @package WordPress 816 * @subpackage Post 817 * @since 3.0.0 818 * @uses $wp_post_types 819 * @see register_post_type 820 * @see get_post_types 821 * 822 * @param string $post_type The name of a registered post type 823 * @return object A post type object 824 */ 825 function get_post_type_object( $post_type ) { 826 global $wp_post_types; 827 828 if ( empty($wp_post_types[$post_type]) ) 829 return null; 830 831 return $wp_post_types[$post_type]; 832 } 833 834 /** 835 * Get a list of all registered post type objects. 836 * 837 * @package WordPress 838 * @subpackage Post 839 * @since 2.9.0 840 * @uses $wp_post_types 841 * @see register_post_type 842 * 843 * @param array|string $args An array of key => value arguments to match against the post type objects. 844 * @param string $output The type of output to return, either post type 'names' or 'objects'. 'names' is the default. 845 * @param string $operator The logical operation to perform. 'or' means only one element 846 * from the array needs to match; 'and' means all elements must match. The default is 'and'. 847 * @return array A list of post type names or objects 848 */ 849 function get_post_types( $args = array(), $output = 'names', $operator = 'and' ) { 850 global $wp_post_types; 851 852 $field = ('names' == $output) ? 'name' : false; 853 854 return wp_filter_object_list($wp_post_types, $args, $operator, $field); 855 } 856 857 /** 858 * Register a post type. Do not use before init. 859 * 860 * A function for creating or modifying a post type based on the 861 * parameters given. The function will accept an array (second optional 862 * parameter), along with a string for the post type name. 863 * 864 * Optional $args contents: 865 * 866 * - label - Name of the post type shown in the menu. Usually plural. If not set, labels['name'] will be used. 867 * - description - A short descriptive summary of what the post type is. Defaults to blank. 868 * - public - Whether posts of this type should be shown in the admin UI. Defaults to false. 869 * - exclude_from_search - Whether to exclude posts with this post type from search results. 870 * Defaults to true if the type is not public, false if the type is public. 871 * - publicly_queryable - Whether post_type queries can be performed from the front page. 872 * Defaults to whatever public is set as. 873 * - show_ui - Whether to generate a default UI for managing this post type. Defaults to true 874 * if the type is public, false if the type is not public. 875 * - show_in_menu - Where to show the post type in the admin menu. True for a top level menu, 876 * false for no menu, or can be a top level page like 'tools.php' or 'edit.php?post_type=page'. 877 * show_ui must be true. 878 * - menu_position - The position in the menu order the post type should appear. Defaults to the bottom. 879 * - menu_icon - The url to the icon to be used for this menu. Defaults to use the posts icon. 880 * - capability_type - The string to use to build the read, edit, and delete capabilities. Defaults to 'post'. 881 * May be passed as an array to allow for alternative plurals when using this argument as a base to construct the 882 * capabilities, e.g. array('story', 'stories'). 883 * - capabilities - Array of capabilities for this post type. By default the capability_type is used 884 * as a base to construct capabilities. You can see accepted values in {@link get_post_type_capabilities()}. 885 * - map_meta_cap - Whether to use the internal default meta capability handling. Defaults to false. 886 * - hierarchical - Whether the post type is hierarchical. Defaults to false. 887 * - supports - An alias for calling add_post_type_support() directly. See {@link add_post_type_support()} 888 * for documentation. Defaults to none. 889 * - register_meta_box_cb - Provide a callback function that will be called when setting up the 890 * meta boxes for the edit form. Do remove_meta_box() and add_meta_box() calls in the callback. 891 * - taxonomies - An array of taxonomy identifiers that will be registered for the post type. 892 * Default is no taxonomies. Taxonomies can be registered later with register_taxonomy() or 893 * register_taxonomy_for_object_type(). 894 * - labels - An array of labels for this post type. By default post labels are used for non-hierarchical 895 * types and page labels for hierarchical ones. You can see accepted values in {@link get_post_type_labels()}. 896 * - has_archive - True to enable post type archives. Will generate the proper rewrite rules if rewrite is enabled. 897 * - rewrite - false to prevent rewrite. Defaults to true. Use array('slug'=>$slug) to customize permastruct; 898 * default will use $post_type as slug. Other options include 'with_front', 'feeds', 'pages', and 'ep_mask'. 899 * - query_var - false to prevent queries, or string to value of the query var to use for this post type 900 * - can_export - true allows this post type to be exported. 901 * - show_in_nav_menus - true makes this post type available for selection in navigation menus. 902 * - _builtin - true if this post type is a native or "built-in" post_type. THIS IS FOR INTERNAL USE ONLY! 903 * - _edit_link - URL segement to use for edit link of this post type. THIS IS FOR INTERNAL USE ONLY! 904 * 905 * @since 2.9.0 906 * @uses $wp_post_types Inserts new post type object into the list 907 * 908 * @param string $post_type Name of the post type. 909 * @param array|string $args See above description. 910 * @return object|WP_Error the registered post type object, or an error object 911 */ 912 function register_post_type($post_type, $args = array()) { 913 global $wp_post_types, $wp_rewrite, $wp; 914 915 if ( !is_array($wp_post_types) ) 916 $wp_post_types = array(); 917 918 // Args prefixed with an underscore are reserved for internal use. 919 $defaults = array( 920 'labels' => array(), 'description' => '', 'publicly_queryable' => null, 'exclude_from_search' => null, 921 'capability_type' => 'post', 'capabilities' => array(), 'map_meta_cap' => null, 922 '_builtin' => false, '_edit_link' => 'post.php?post=%d', 'hierarchical' => false, 923 'public' => false, 'rewrite' => true, 'has_archive' => false, 'query_var' => true, 924 'supports' => array(), 'register_meta_box_cb' => null, 925 'taxonomies' => array(), 'show_ui' => null, 'menu_position' => null, 'menu_icon' => null, 926 'can_export' => true, 927 'show_in_nav_menus' => null, 'show_in_menu' => null, 'show_in_admin_bar' => null, 928 ); 929 $args = wp_parse_args($args, $defaults); 930 $args = (object) $args; 931 932 $post_type = sanitize_key($post_type); 933 $args->name = $post_type; 934 935 if ( strlen( $post_type ) > 20 ) 936 return new WP_Error( 'post_type_too_long', __( 'Post types cannot exceed 20 characters in length' ) ); 937 938 // If not set, default to the setting for public. 939 if ( null === $args->publicly_queryable ) 940 $args->publicly_queryable = $args->public; 941 942 // If not set, default to the setting for public. 943 if ( null === $args->show_ui ) 944 $args->show_ui = $args->public; 945 946 // If not set, default to the setting for show_ui. 947 if ( null === $args->show_in_menu || ! $args->show_ui ) 948 $args->show_in_menu = $args->show_ui; 949 950 // If not set, default to the whether the full UI is shown. 951 if ( null === $args->show_in_admin_bar ) 952 $args->show_in_admin_bar = true === $args->show_in_menu; 953 954 // Whether to show this type in nav-menus.php. Defaults to the setting for public. 955 if ( null === $args->show_in_nav_menus ) 956 $args->show_in_nav_menus = $args->public; 957 958 // If not set, default to true if not public, false if public. 959 if ( null === $args->exclude_from_search ) 960 $args->exclude_from_search = !$args->public; 961 962 // Back compat with quirky handling in version 3.0. #14122 963 if ( empty( $args->capabilities ) && null === $args->map_meta_cap && in_array( $args->capability_type, array( 'post', 'page' ) ) ) 964 $args->map_meta_cap = true; 965 966 if ( null === $args->map_meta_cap ) 967 $args->map_meta_cap = false; 968 969 $args->cap = get_post_type_capabilities( $args ); 970 unset($args->capabilities); 971 972 if ( is_array( $args->capability_type ) ) 973 $args->capability_type = $args->capability_type[0]; 974 975 if ( ! empty($args->supports) ) { 976 add_post_type_support($post_type, $args->supports); 977 unset($args->supports); 978 } else { 979 // Add default features 980 add_post_type_support($post_type, array('title', 'editor')); 981 } 982 983 if ( false !== $args->query_var && !empty($wp) ) { 984 if ( true === $args->query_var ) 985 $args->query_var = $post_type; 986 $args->query_var = sanitize_title_with_dashes($args->query_var); 987 $wp->add_query_var($args->query_var); 988 } 989 990 if ( false !== $args->rewrite && ( is_admin() || '' != get_option('permalink_structure') ) ) { 991 if ( ! is_array( $args->rewrite ) ) 992 $args->rewrite = array(); 993 if ( empty( $args->rewrite['slug'] ) ) 994 $args->rewrite['slug'] = $post_type; 995 if ( ! isset( $args->rewrite['with_front'] ) ) 996 $args->rewrite['with_front'] = true; 997 if ( ! isset( $args->rewrite['pages'] ) ) 998 $args->rewrite['pages'] = true; 999 if ( ! isset( $args->rewrite['feeds'] ) || ! $args->has_archive ) 1000 $args->rewrite['feeds'] = (bool) $args->has_archive; 1001 if ( ! isset( $args->rewrite['ep_mask'] ) ) { 1002 if ( isset( $args->permalink_epmask ) ) 1003 $args->rewrite['ep_mask'] = $args->permalink_epmask; 1004 else 1005 $args->rewrite['ep_mask'] = EP_PERMALINK; 1006 } 1007 1008 if ( $args->hierarchical ) 1009 $wp_rewrite->add_rewrite_tag("%$post_type%", '(.+?)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&name="); 1010 else 1011 $wp_rewrite->add_rewrite_tag("%$post_type%", '([^/]+)', $args->query_var ? "{$args->query_var}=" : "post_type=$post_type&name="); 1012 1013 if ( $args->has_archive ) { 1014 $archive_slug = $args->has_archive === true ? $args->rewrite['slug'] : $args->has_archive; 1015 if ( $args->rewrite['with_front'] ) 1016 $archive_slug = substr( $wp_rewrite->front, 1 ) . $archive_slug; 1017 else 1018 $archive_slug = $wp_rewrite->root . $archive_slug; 1019 1020 $wp_rewrite->add_rule( "{$archive_slug}/?$", "index.php?post_type=$post_type", 'top' ); 1021 if ( $args->rewrite['feeds'] && $wp_rewrite->feeds ) { 1022 $feeds = '(' . trim( implode( '|', $wp_rewrite->feeds ) ) . ')'; 1023 $wp_rewrite->add_rule( "{$archive_slug}/feed/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' ); 1024 $wp_rewrite->add_rule( "{$archive_slug}/$feeds/?$", "index.php?post_type=$post_type" . '&feed=$matches[1]', 'top' ); 1025 } 1026 if ( $args->rewrite['pages'] ) 1027 $wp_rewrite->add_rule( "{$archive_slug}/{$wp_rewrite->pagination_base}/([0-9]{1,})/?$", "index.php?post_type=$post_type" . '&paged=$matches[1]', 'top' ); 1028 } 1029 1030 $wp_rewrite->add_permastruct( $post_type, "{$args->rewrite['slug']}/%$post_type%", $args->rewrite ); 1031 } 1032 1033 if ( $args->register_meta_box_cb ) 1034 add_action('add_meta_boxes_' . $post_type, $args->register_meta_box_cb, 10, 1); 1035 1036 $args->labels = get_post_type_labels( $args ); 1037 $args->label = $args->labels->name; 1038 1039 $wp_post_types[$post_type] = $args; 1040 1041 add_action( 'future_' . $post_type, '_future_post_hook', 5, 2 ); 1042 1043 foreach ( $args->taxonomies as $taxonomy ) { 1044 register_taxonomy_for_object_type( $taxonomy, $post_type ); 1045 } 1046 1047 do_action( 'registered_post_type', $post_type, $args ); 1048 1049 return $args; 1050 } 1051 1052 /** 1053 * Builds an object with all post type capabilities out of a post type object 1054 * 1055 * Post type capabilities use the 'capability_type' argument as a base, if the 1056 * capability is not set in the 'capabilities' argument array or if the 1057 * 'capabilities' argument is not supplied. 1058 * 1059 * The capability_type argument can optionally be registered as an array, with 1060 * the first value being singular and the second plural, e.g. array('story, 'stories') 1061 * Otherwise, an 's' will be added to the value for the plural form. After 1062 * registration, capability_type will always be a string of the singular value. 1063 * 1064 * By default, seven keys are accepted as part of the capabilities array: 1065 * 1066 * - edit_post, read_post, and delete_post are meta capabilities, which are then 1067 * generally mapped to corresponding primitive capabilities depending on the 1068 * context, which would be the post being edited/read/deleted and the user or 1069 * role being checked. Thus these capabilities would generally not be granted 1070 * directly to users or roles. 1071 * 1072 * - edit_posts - Controls whether objects of this post type can be edited. 1073 * - edit_others_posts - Controls whether objects of this type owned by other users 1074 * can be edited. If the post type does not support an author, then this will 1075 * behave like edit_posts. 1076 * - publish_posts - Controls publishing objects of this post type. 1077 * - read_private_posts - Controls whether private objects can be read. 1078 1079 * These four primitive capabilities are checked in core in various locations. 1080 * There are also seven other primitive capabilities which are not referenced 1081 * directly in core, except in map_meta_cap(), which takes the three aforementioned 1082 * meta capabilities and translates them into one or more primitive capabilities 1083 * that must then be checked against the user or role, depending on the context. 1084 * 1085 * - read - Controls whether objects of this post type can be read. 1086 * - delete_posts - Controls whether objects of this post type can be deleted. 1087 * - delete_private_posts - Controls whether private objects can be deleted. 1088 * - delete_published_posts - Controls whether published objects can be deleted. 1089 * - delete_others_posts - Controls whether objects owned by other users can be 1090 * can be deleted. If the post type does not support an author, then this will 1091 * behave like delete_posts. 1092 * - edit_private_posts - Controls whether private objects can be edited. 1093 * - edit_published_posts - Controls whether published objects can be edited. 1094 * 1095 * These additional capabilities are only used in map_meta_cap(). Thus, they are 1096 * only assigned by default if the post type is registered with the 'map_meta_cap' 1097 * argument set to true (default is false). 1098 * 1099 * @see map_meta_cap() 1100 * @since 3.0.0 1101 * 1102 * @param object $args Post type registration arguments 1103 * @return object object with all the capabilities as member variables 1104 */ 1105 function get_post_type_capabilities( $args ) { 1106 if ( ! is_array( $args->capability_type ) ) 1107 $args->capability_type = array( $args->capability_type, $args->capability_type . 's' ); 1108 1109 // Singular base for meta capabilities, plural base for primitive capabilities. 1110 list( $singular_base, $plural_base ) = $args->capability_type; 1111 1112 $default_capabilities = array( 1113 // Meta capabilities 1114 'edit_post' => 'edit_' . $singular_base, 1115 'read_post' => 'read_' . $singular_base, 1116 'delete_post' => 'delete_' . $singular_base, 1117 // Primitive capabilities used outside of map_meta_cap(): 1118 'edit_posts' => 'edit_' . $plural_base, 1119 'edit_others_posts' => 'edit_others_' . $plural_base, 1120 'publish_posts' => 'publish_' . $plural_base, 1121 'read_private_posts' => 'read_private_' . $plural_base, 1122 ); 1123 1124 // Primitive capabilities used within map_meta_cap(): 1125 if ( $args->map_meta_cap ) { 1126 $default_capabilities_for_mapping = array( 1127 'read' => 'read', 1128 'delete_posts' => 'delete_' . $plural_base, 1129 'delete_private_posts' => 'delete_private_' . $plural_base, 1130 'delete_published_posts' => 'delete_published_' . $plural_base, 1131 'delete_others_posts' => 'delete_others_' . $plural_base, 1132 'edit_private_posts' => 'edit_private_' . $plural_base, 1133 'edit_published_posts' => 'edit_published_' . $plural_base, 1134 ); 1135 $default_capabilities = array_merge( $default_capabilities, $default_capabilities_for_mapping ); 1136 } 1137 1138 $capabilities = array_merge( $default_capabilities, $args->capabilities ); 1139 1140 // Remember meta capabilities for future reference. 1141 if ( $args->map_meta_cap ) 1142 _post_type_meta_capabilities( $capabilities ); 1143 1144 return (object) $capabilities; 1145 } 1146 1147 /** 1148 * Stores or returns a list of post type meta caps for map_meta_cap(). 1149 * 1150 * @since 3.1.0 1151 * @access private 1152 */ 1153 function _post_type_meta_capabilities( $capabilities = null ) { 1154 static $meta_caps = array(); 1155 if ( null === $capabilities ) 1156 return $meta_caps; 1157 foreach ( $capabilities as $core => $custom ) { 1158 if ( in_array( $core, array( 'read_post', 'delete_post', 'edit_post' ) ) ) 1159 $meta_caps[ $custom ] = $core; 1160 } 1161 } 1162 1163 /** 1164 * Builds an object with all post type labels out of a post type object 1165 * 1166 * Accepted keys of the label array in the post type object: 1167 * - name - general name for the post type, usually plural. The same and overridden by $post_type_object->label. Default is Posts/Pages 1168 * - singular_name - name for one object of this post type. Default is Post/Page 1169 * - 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> 1170 * - add_new_item - Default is Add New Post/Add New Page 1171 * - edit_item - Default is Edit Post/Edit Page 1172 * - new_item - Default is New Post/New Page 1173 * - view_item - Default is View Post/View Page 1174 * - search_items - Default is Search Posts/Search Pages 1175 * - not_found - Default is No posts found/No pages found 1176 * - not_found_in_trash - Default is No posts found in Trash/No pages found in Trash 1177 * - parent_item_colon - This string isn't used on non-hierarchical types. In hierarchical ones the default is Parent Page: 1178 * - all_items - String for the submenu. Default is All Posts/All Pages 1179 * - menu_name - Default is the same as <code>name</code> 1180 * 1181 * Above, the first default value is for non-hierarchical post types (like posts) and the second one is for hierarchical post types (like pages). 1182 * 1183 * @since 3.0.0 1184 * @param object $post_type_object 1185 * @return object object with all the labels as member variables 1186 */ 1187 function get_post_type_labels( $post_type_object ) { 1188 $nohier_vs_hier_defaults = array( 1189 'name' => array( _x('Posts', 'post type general name'), _x('Pages', 'post type general name') ), 1190 'singular_name' => array( _x('Post', 'post type singular name'), _x('Page', 'post type singular name') ), 1191 'add_new' => array( _x('Add New', 'post'), _x('Add New', 'page') ), 1192 'add_new_item' => array( __('Add New Post'), __('Add New Page') ), 1193 'edit_item' => array( __('Edit Post'), __('Edit Page') ), 1194 'new_item' => array( __('New Post'), __('New Page') ), 1195 'view_item' => array( __('View Post'), __('View Page') ), 1196 'search_items' => array( __('Search Posts'), __('Search Pages') ), 1197 'not_found' => array( __('No posts found.'), __('No pages found.') ), 1198 'not_found_in_trash' => array( __('No posts found in Trash.'), __('No pages found in Trash.') ), 1199 'parent_item_colon' => array( null, __('Parent Page:') ), 1200 'all_items' => array( __( 'All Posts' ), __( 'All Pages' ) ) 1201 ); 1202 $nohier_vs_hier_defaults['menu_name'] = $nohier_vs_hier_defaults['name']; 1203 return _get_custom_object_labels( $post_type_object, $nohier_vs_hier_defaults ); 1204 } 1205 1206 /** 1207 * Builds an object with custom-something object (post type, taxonomy) labels out of a custom-something object 1208 * 1209 * @access private 1210 * @since 3.0.0 1211 */ 1212 function _get_custom_object_labels( $object, $nohier_vs_hier_defaults ) { 1213 1214 if ( isset( $object->label ) && empty( $object->labels['name'] ) ) 1215 $object->labels['name'] = $object->label; 1216 1217 if ( !isset( $object->labels['singular_name'] ) && isset( $object->labels['name'] ) ) 1218 $object->labels['singular_name'] = $object->labels['name']; 1219 1220 if ( ! isset( $object->labels['name_admin_bar'] ) ) 1221 $object->labels['name_admin_bar'] = isset( $object->labels['singular_name'] ) ? $object->labels['singular_name'] : $object->name; 1222 1223 if ( !isset( $object->labels['menu_name'] ) && isset( $object->labels['name'] ) ) 1224 $object->labels['menu_name'] = $object->labels['name']; 1225 1226 if ( !isset( $object->labels['all_items'] ) && isset( $object->labels['menu_name'] ) ) 1227 $object->labels['all_items'] = $object->labels['menu_name']; 1228 1229 foreach ( $nohier_vs_hier_defaults as $key => $value ) 1230 $defaults[$key] = $object->hierarchical ? $value[1] : $value[0]; 1231 1232 $labels = array_merge( $defaults, $object->labels ); 1233 return (object)$labels; 1234 } 1235 1236 /** 1237 * Adds submenus for post types. 1238 * 1239 * @access private 1240 * @since 3.1.0 1241 */ 1242 function _add_post_type_submenus() { 1243 foreach ( get_post_types( array( 'show_ui' => true ) ) as $ptype ) { 1244 $ptype_obj = get_post_type_object( $ptype ); 1245 // Submenus only. 1246 if ( ! $ptype_obj->show_in_menu || $ptype_obj->show_in_menu === true ) 1247 continue; 1248 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" ); 1249 } 1250 } 1251 add_action( 'admin_menu', '_add_post_type_submenus' ); 1252 1253 /** 1254 * Register support of certain features for a post type. 1255 * 1256 * All features are directly associated with a functional area of the edit screen, such as the 1257 * editor or a meta box: 'title', 'editor', 'comments', 'revisions', 'trackbacks', 'author', 1258 * 'excerpt', 'page-attributes', 'thumbnail', and 'custom-fields'. 1259 * 1260 * Additionally, the 'revisions' feature dictates whether the post type will store revisions, 1261 * and the 'comments' feature dictates whether the comments count will show on the edit screen. 1262 * 1263 * @since 3.0.0 1264 * @param string $post_type The post type for which to add the feature 1265 * @param string|array $feature the feature being added, can be an array of feature strings or a single string 1266 */ 1267 function add_post_type_support( $post_type, $feature ) { 1268 global $_wp_post_type_features; 1269 1270 $features = (array) $feature; 1271 foreach ($features as $feature) { 1272 if ( func_num_args() == 2 ) 1273 $_wp_post_type_features[$post_type][$feature] = true; 1274 else 1275 $_wp_post_type_features[$post_type][$feature] = array_slice( func_get_args(), 2 ); 1276 } 1277 } 1278 1279 /** 1280 * Remove support for a feature from a post type. 1281 * 1282 * @since 3.0.0 1283 * @param string $post_type The post type for which to remove the feature 1284 * @param string $feature The feature being removed 1285 */ 1286 function remove_post_type_support( $post_type, $feature ) { 1287 global $_wp_post_type_features; 1288 1289 if ( !isset($_wp_post_type_features[$post_type]) ) 1290 return; 1291 1292 if ( isset($_wp_post_type_features[$post_type][$feature]) ) 1293 unset($_wp_post_type_features[$post_type][$feature]); 1294 } 1295 1296 /** 1297 * Checks a post type's support for a given feature 1298 * 1299 * @since 3.0.0 1300 * @param string $post_type The post type being checked 1301 * @param string $feature the feature being checked 1302 * @return boolean 1303 */ 1304 1305 function post_type_supports( $post_type, $feature ) { 1306 global $_wp_post_type_features; 1307 1308 if ( !isset( $_wp_post_type_features[$post_type][$feature] ) ) 1309 return false; 1310 1311 // If no args passed then no extra checks need be performed 1312 if ( func_num_args() <= 2 ) 1313 return true; 1314 1315 // @todo Allow pluggable arg checking 1316 //$args = array_slice( func_get_args(), 2 ); 1317 1318 return true; 1319 } 1320 1321 /** 1322 * Updates the post type for the post ID. 1323 * 1324 * The page or post cache will be cleaned for the post ID. 1325 * 1326 * @since 2.5.0 1327 * 1328 * @uses $wpdb 1329 * 1330 * @param int $post_id Post ID to change post type. Not actually optional. 1331 * @param string $post_type Optional, default is post. Supported values are 'post' or 'page' to 1332 * name a few. 1333 * @return int Amount of rows changed. Should be 1 for success and 0 for failure. 1334 */ 1335 function set_post_type( $post_id = 0, $post_type = 'post' ) { 1336 global $wpdb; 1337 1338 $post_type = sanitize_post_field('post_type', $post_type, $post_id, 'db'); 1339 $return = $wpdb->update($wpdb->posts, array('post_type' => $post_type), array('ID' => $post_id) ); 1340 1341 if ( 'page' == $post_type ) 1342 clean_page_cache($post_id); 1343 else 1344 clean_post_cache($post_id); 1345 1346 return $return; 1347 } 1348 1349 /** 1350 * Retrieve list of latest posts or posts matching criteria. 1351 * 1352 * The defaults are as follows: 1353 * 'numberposts' - Default is 5. Total number of posts to retrieve. 1354 * 'offset' - Default is 0. See {@link WP_Query::query()} for more. 1355 * 'category' - What category to pull the posts from. 1356 * 'orderby' - Default is 'post_date'. How to order the posts. 1357 * 'order' - Default is 'DESC'. The order to retrieve the posts. 1358 * 'include' - See {@link WP_Query::query()} for more. 1359 * 'exclude' - See {@link WP_Query::query()} for more. 1360 * 'meta_key' - See {@link WP_Query::query()} for more. 1361 * 'meta_value' - See {@link WP_Query::query()} for more. 1362 * 'post_type' - Default is 'post'. Can be 'page', or 'attachment' to name a few. 1363 * 'post_parent' - The parent of the post or post type. 1364 * 'post_status' - Default is 'publish'. Post status to retrieve. 1365 * 1366 * @since 1.2.0 1367 * @uses $wpdb 1368 * @uses WP_Query::query() See for more default arguments and information. 1369 * @link http://codex.wordpress.org/Template_Tags/get_posts 1370 * 1371 * @param array $args Optional. Overrides defaults. 1372 * @return array List of posts. 1373 */ 1374 function get_posts($args = null) { 1375 $defaults = array( 1376 'numberposts' => 5, 'offset' => 0, 1377 'category' => 0, 'orderby' => 'post_date', 1378 'order' => 'DESC', 'include' => array(), 1379 'exclude' => array(), 'meta_key' => '', 1380 'meta_value' =>'', 'post_type' => 'post', 1381 'suppress_filters' => true 1382 ); 1383 1384 $r = wp_parse_args( $args, $defaults ); 1385 if ( empty( $r['post_status'] ) ) 1386 $r['post_status'] = ( 'attachment' == $r['post_type'] ) ? 'inherit' : 'publish'; 1387 if ( ! empty($r['numberposts']) && empty($r['posts_per_page']) ) 1388 $r['posts_per_page'] = $r['numberposts']; 1389 if ( ! empty($r['category']) ) 1390 $r['cat'] = $r['category']; 1391 if ( ! empty($r['include']) ) { 1392 $incposts = wp_parse_id_list( $r['include'] ); 1393 $r['posts_per_page'] = count($incposts); // only the number of posts included 1394 $r['post__in'] = $incposts; 1395 } elseif ( ! empty($r['exclude']) ) 1396 $r['post__not_in'] = wp_parse_id_list( $r['exclude'] ); 1397 1398 $r['ignore_sticky_posts'] = true; 1399 $r['no_found_rows'] = true; 1400 1401 $get_posts = new WP_Query; 1402 return $get_posts->query($r); 1403 1404 } 1405 1406 // 1407 // Post meta functions 1408 // 1409 1410 /** 1411 * Add meta data field to a post. 1412 * 1413 * Post meta data is called "Custom Fields" on the Administration Screen. 1414 * 1415 * @since 1.5.0 1416 * @uses $wpdb 1417 * @link http://codex.wordpress.org/Function_Reference/add_post_meta 1418 * 1419 * @param int $post_id Post ID. 1420 * @param string $meta_key Metadata name. 1421 * @param mixed $meta_value Metadata value. 1422 * @param bool $unique Optional, default is false. Whether the same key should not be added. 1423 * @return bool False for failure. True for success. 1424 */ 1425 function add_post_meta($post_id, $meta_key, $meta_value, $unique = false) { 1426 // make sure meta is added to the post, not a revision 1427 if ( $the_post = wp_is_post_revision($post_id) ) 1428 $post_id = $the_post; 1429 1430 return add_metadata('post', $post_id, $meta_key, $meta_value, $unique); 1431 } 1432 1433 /** 1434 * Remove metadata matching criteria from a post. 1435 * 1436 * You can match based on the key, or key and value. Removing based on key and 1437 * value, will keep from removing duplicate metadata with the same key. It also 1438 * allows removing all metadata matching key, if needed. 1439 * 1440 * @since 1.5.0 1441 * @uses $wpdb 1442 * @link http://codex.wordpress.org/Function_Reference/delete_post_meta 1443 * 1444 * @param int $post_id post ID 1445 * @param string $meta_key Metadata name. 1446 * @param mixed $meta_value Optional. Metadata value. 1447 * @return bool False for failure. True for success. 1448 */ 1449 function delete_post_meta($post_id, $meta_key, $meta_value = '') { 1450 // make sure meta is added to the post, not a revision 1451 if ( $the_post = wp_is_post_revision($post_id) ) 1452 $post_id = $the_post; 1453 1454 return delete_metadata('post', $post_id, $meta_key, $meta_value); 1455 } 1456 1457 /** 1458 * Retrieve post meta field for a post. 1459 * 1460 * @since 1.5.0 1461 * @uses $wpdb 1462 * @link http://codex.wordpress.org/Function_Reference/get_post_meta 1463 * 1464 * @param int $post_id Post ID. 1465 * @param string $key The meta key to retrieve. 1466 * @param bool $single Whether to return a single value. 1467 * @return mixed Will be an array if $single is false. Will be value of meta data field if $single 1468 * is true. 1469 */ 1470 function get_post_meta($post_id, $key, $single = false) { 1471 return get_metadata('post', $post_id, $key, $single); 1472 } 1473 1474 /** 1475 * Update post meta field based on post ID. 1476 * 1477 * Use the $prev_value parameter to differentiate between meta fields with the 1478 * same key and post ID. 1479 * 1480 * If the meta field for the post does not exist, it will be added. 1481 * 1482 * @since 1.5.0 1483 * @uses $wpdb 1484 * @link http://codex.wordpress.org/Function_Reference/update_post_meta 1485 * 1486 * @param int $post_id Post ID. 1487 * @param string $meta_key Metadata key. 1488 * @param mixed $meta_value Metadata value. 1489 * @param mixed $prev_value Optional. Previous value to check before removing. 1490 * @return bool False on failure, true if success. 1491 */ 1492 function update_post_meta($post_id, $meta_key, $meta_value, $prev_value = '') { 1493 // make sure meta is added to the post, not a revision 1494 if ( $the_post = wp_is_post_revision($post_id) ) 1495 $post_id = $the_post; 1496 1497 return update_metadata('post', $post_id, $meta_key, $meta_value, $prev_value); 1498 } 1499 1500 /** 1501 * Delete everything from post meta matching meta key. 1502 * 1503 * @since 2.3.0 1504 * @uses $wpdb 1505 * 1506 * @param string $post_meta_key Key to search for when deleting. 1507 * @return bool Whether the post meta key was deleted from the database 1508 */ 1509 function delete_post_meta_by_key($post_meta_key) { 1510 return delete_metadata( 'post', null, $post_meta_key, '', true ); 1511 } 1512 1513 /** 1514 * Retrieve post meta fields, based on post ID. 1515 * 1516 * The post meta fields are retrieved from the cache, so the function is 1517 * optimized to be called more than once. It also applies to the functions, that 1518 * use this function. 1519 * 1520 * @since 1.2.0 1521 * @link http://codex.wordpress.org/Function_Reference/get_post_custom 1522 * 1523 * @uses $id Current Loop Post ID 1524 * 1525 * @param int $post_id post ID 1526 * @return array 1527 */ 1528 function get_post_custom( $post_id = 0 ) { 1529 $post_id = absint( $post_id ); 1530 1531 if ( ! $post_id ) 1532 $post_id = get_the_ID(); 1533 1534 if ( ! wp_cache_get( $post_id, 'post_meta' ) ) 1535 update_postmeta_cache( $post_id ); 1536 1537 return wp_cache_get( $post_id, 'post_meta' ); 1538 } 1539 1540 /** 1541 * Retrieve meta field names for a post. 1542 * 1543 * If there are no meta fields, then nothing (null) will be returned. 1544 * 1545 * @since 1.2.0 1546 * @link http://codex.wordpress.org/Function_Reference/get_post_custom_keys 1547 * 1548 * @param int $post_id post ID 1549 * @return array|null Either array of the keys, or null if keys could not be retrieved. 1550 */ 1551 function get_post_custom_keys( $post_id = 0 ) { 1552 $custom = get_post_custom( $post_id ); 1553 1554 if ( !is_array($custom) ) 1555 return; 1556 1557 if ( $keys = array_keys($custom) ) 1558 return $keys; 1559 } 1560 1561 /** 1562 * Retrieve values for a custom post field. 1563 * 1564 * The parameters must not be considered optional. All of the post meta fields 1565 * will be retrieved and only the meta field key values returned. 1566 * 1567 * @since 1.2.0 1568 * @link http://codex.wordpress.org/Function_Reference/get_post_custom_values 1569 * 1570 * @param string $key Meta field key. 1571 * @param int $post_id Post ID 1572 * @return array Meta field values. 1573 */ 1574 function get_post_custom_values( $key = '', $post_id = 0 ) { 1575 if ( !$key ) 1576 return null; 1577 1578 $custom = get_post_custom($post_id); 1579 1580 return isset($custom[$key]) ? $custom[$key] : null; 1581 } 1582 1583 /** 1584 * Check if post is sticky. 1585 * 1586 * Sticky posts should remain at the top of The Loop. If the post ID is not 1587 * given, then The Loop ID for the current post will be used. 1588 * 1589 * @since 2.7.0 1590 * 1591 * @param int $post_id Optional. Post ID. 1592 * @return bool Whether post is sticky. 1593 */ 1594 function is_sticky( $post_id = 0 ) { 1595 $post_id = absint( $post_id ); 1596 1597 if ( ! $post_id ) 1598 $post_id = get_the_ID(); 1599 1600 $stickies = get_option( 'sticky_posts' ); 1601 1602 if ( ! is_array( $stickies ) ) 1603 return false; 1604 1605 if ( in_array( $post_id, $stickies ) ) 1606 return true; 1607 1608 return false; 1609 } 1610 1611 /** 1612 * Sanitize every post field. 1613 * 1614 * If the context is 'raw', then the post object or array will get minimal santization of the int fields. 1615 * 1616 * @since 2.3.0 1617 * @uses sanitize_post_field() Used to sanitize the fields. 1618 * 1619 * @param object|array $post The Post Object or Array 1620 * @param string $context Optional, default is 'display'. How to sanitize post fields. 1621 * @return object|array The now sanitized Post Object or Array (will be the same type as $post) 1622 */ 1623 function sanitize_post($post, $context = 'display') { 1624 if ( is_object($post) ) { 1625 // Check if post already filtered for this context 1626 if ( isset($post->filter) && $context == $post->filter ) 1627 return $post; 1628 if ( !isset($post->ID) ) 1629 $post->ID = 0; 1630 foreach ( array_keys(get_object_vars($post)) as $field ) 1631 $post->$field = sanitize_post_field($field, $post->$field, $post->ID, $context); 1632 $post->filter = $context; 1633 } else { 1634 // Check if post already filtered for this context 1635 if ( isset($post['filter']) && $context == $post['filter'] ) 1636 return $post; 1637 if ( !isset($post['ID']) ) 1638 $post['ID'] = 0; 1639 foreach ( array_keys($post) as $field ) 1640 $post[$field] = sanitize_post_field($field, $post[$field], $post['ID'], $context); 1641 $post['filter'] = $context; 1642 } 1643 return $post; 1644 } 1645 1646 /** 1647 * Sanitize post field based on context. 1648 * 1649 * Possible context values are: 'raw', 'edit', 'db', 'display', 'attribute' and 'js'. The 1650 * 'display' context is used by default. 'attribute' and 'js' contexts are treated like 'display' 1651 * when calling filters. 1652 * 1653 * @since 2.3.0 1654 * @uses apply_filters() Calls 'edit_$field' and '{$field_no_prefix}_edit_pre' passing $value and 1655 * $post_id if $context == 'edit' and field name prefix == 'post_'. 1656 * 1657 * @uses apply_filters() Calls 'edit_post_$field' passing $value and $post_id if $context == 'db'. 1658 * @uses apply_filters() Calls 'pre_$field' passing $value if $context == 'db' and field name prefix == 'post_'. 1659 * @uses apply_filters() Calls '{$field}_pre' passing $value if $context == 'db' and field name prefix != 'post_'. 1660 * 1661 * @uses apply_filters() Calls '$field' passing $value, $post_id and $context if $context == anything 1662 * other than 'raw', 'edit' and 'db' and field name prefix == 'post_'. 1663 * @uses apply_filters() Calls 'post_$field' passing $value if $context == anything other than 'raw', 1664 * 'edit' and 'db' and field name prefix != 'post_'. 1665 * 1666 * @param string $field The Post Object field name. 1667 * @param mixed $value The Post Object value. 1668 * @param int $post_id Post ID. 1669 * @param string $context How to sanitize post fields. Looks for 'raw', 'edit', 'db', 'display', 1670 * 'attribute' and 'js'. 1671 * @return mixed Sanitized value. 1672 */ 1673 function sanitize_post_field($field, $value, $post_id, $context) { 1674 $int_fields = array('ID', 'post_parent', 'menu_order'); 1675 if ( in_array($field, $int_fields) ) 1676 $value = (int) $value; 1677 1678 // Fields which contain arrays of ints. 1679 $array_int_fields = array( 'ancestors' ); 1680 if ( in_array($field, $array_int_fields) ) { 1681 $value = array_map( 'absint', $value); 1682 return $value; 1683 } 1684 1685 if ( 'raw' == $context ) 1686 return $value; 1687 1688 $prefixed = false; 1689 if ( false !== strpos($field, 'post_') ) { 1690 $prefixed = true; 1691 $field_no_prefix = str_replace('post_', '', $field); 1692 } 1693 1694 if ( 'edit' == $context ) { 1695 $format_to_edit = array('post_content', 'post_excerpt', 'post_title', 'post_password'); 1696 1697 if ( $prefixed ) { 1698 $value = apply_filters("edit_{$field}", $value, $post_id); 1699 // Old school 1700 $value = apply_filters("{$field_no_prefix}_edit_pre", $value, $post_id); 1701 } else { 1702 $value = apply_filters("edit_post_{$field}", $value, $post_id); 1703 } 1704 1705 if ( in_array($field, $format_to_edit) ) { 1706 if ( 'post_content' == $field ) 1707 $value = format_to_edit($value, user_can_richedit()); 1708 else 1709 $value = format_to_edit($value); 1710 } else { 1711 $value = esc_attr($value); 1712 } 1713 } else if ( 'db' == $context ) { 1714 if ( $prefixed ) { 1715 $value = apply_filters("pre_{$field}", $value); 1716 $value = apply_filters("{$field_no_prefix}_save_pre", $value); 1717 } else { 1718 $value = apply_filters("pre_post_{$field}", $value); 1719 $value = apply_filters("{$field}_pre", $value); 1720 } 1721 } else { 1722 // Use display filters by default. 1723 if ( $prefixed ) 1724 $value = apply_filters($field, $value, $post_id, $context); 1725 else 1726 $value = apply_filters("post_{$field}", $value, $post_id, $context); 1727 } 1728 1729 if ( 'attribute' == $context ) 1730 $value = esc_attr($value); 1731 else if ( 'js' == $context ) 1732 $value = esc_js($value); 1733 1734 return $value; 1735 } 1736 1737 /** 1738 * Make a post sticky. 1739 * 1740 * Sticky posts should be displayed at the top of the front page. 1741 * 1742 * @since 2.7.0 1743 * 1744 * @param int $post_id Post ID. 1745 */ 1746 function stick_post($post_id) { 1747 $stickies = get_option('sticky_posts'); 1748 1749 if ( !is_array($stickies) ) 1750 $stickies = array($post_id); 1751 1752 if ( ! in_array($post_id, $stickies) ) 1753 $stickies[] = $post_id; 1754 1755 update_option('sticky_posts', $stickies); 1756 } 1757 1758 /** 1759 * Unstick a post. 1760 * 1761 * Sticky posts should be displayed at the top of the front page. 1762 * 1763 * @since 2.7.0 1764 * 1765 * @param int $post_id Post ID. 1766 */ 1767 function unstick_post($post_id) { 1768 $stickies = get_option('sticky_posts'); 1769 1770 if ( !is_array($stickies) ) 1771 return; 1772 1773 if ( ! in_array($post_id, $stickies) ) 1774 return; 1775 1776 $offset = array_search($post_id, $stickies); 1777 if ( false === $offset ) 1778 return; 1779 1780 array_splice($stickies, $offset, 1); 1781 1782 update_option('sticky_posts', $stickies); 1783 } 1784 1785 /** 1786 * Count number of posts of a post type and is user has permissions to view. 1787 * 1788 * This function provides an efficient method of finding the amount of post's 1789 * type a blog has. Another method is to count the amount of items in 1790 * get_posts(), but that method has a lot of overhead with doing so. Therefore, 1791 * when developing for 2.5+, use this function instead. 1792 * 1793 * The $perm parameter checks for 'readable' value and if the user can read 1794 * private posts, it will display that for the user that is signed in. 1795 * 1796 * @since 2.5.0 1797 * @link http://codex.wordpress.org/Template_Tags/wp_count_posts 1798 * 1799 * @param string $type Optional. Post type to retrieve count 1800 * @param string $perm Optional. 'readable' or empty. 1801 * @return object Number of posts for each status 1802 */ 1803 function wp_count_posts( $type = 'post', $perm = '' ) { 1804 global $wpdb; 1805 1806 $user = wp_get_current_user(); 1807 1808 $cache_key = $type; 1809 1810 $query = "SELECT post_status, COUNT( * ) AS num_posts FROM {$wpdb->posts} WHERE post_type = %s"; 1811 if ( 'readable' == $perm && is_user_logged_in() ) { 1812 $post_type_object = get_post_type_object($type); 1813 if ( !current_user_can( $post_type_object->cap->read_private_posts ) ) { 1814 $cache_key .= '_' . $perm . '_' . $user->ID; 1815 $query .= " AND (post_status != 'private' OR ( post_author = '$user->ID' AND post_status = 'private' ))"; 1816 } 1817 } 1818 $query .= ' GROUP BY post_status'; 1819 1820 $count = wp_cache_get($cache_key, 'counts'); 1821 if ( false !== $count ) 1822 return $count; 1823 1824 $count = $wpdb->get_results( $wpdb->prepare( $query, $type ), ARRAY_A ); 1825 1826 $stats = array(); 1827 foreach ( get_post_stati() as $state ) 1828 $stats[$state] = 0; 1829 1830 foreach ( (array) $count as $row ) 1831 $stats[$row['post_status']] = $row['num_posts']; 1832 1833 $stats = (object) $stats; 1834 wp_cache_set($cache_key, $stats, 'counts'); 1835 1836 return $stats; 1837 } 1838 1839 /** 1840 * Count number of attachments for the mime type(s). 1841 * 1842 * If you set the optional mime_type parameter, then an array will still be 1843 * returned, but will only have the item you are looking for. It does not give 1844 * you the number of attachments that are children of a post. You can get that 1845 * by counting the number of children that post has. 1846 * 1847 * @since 2.5.0 1848 * 1849 * @param string|array $mime_type Optional. Array or comma-separated list of MIME patterns. 1850 * @return array Number of posts for each mime type. 1851 */ 1852 function wp_count_attachments( $mime_type = '' ) { 1853 global $wpdb; 1854 1855 $and = wp_post_mime_type_where( $mime_type ); 1856 $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 ); 1857 1858 $stats = array( ); 1859 foreach( (array) $count as $row ) { 1860 $stats[$row['post_mime_type']] = $row['num_posts']; 1861 } 1862 $stats['trash'] = $wpdb->get_var( "SELECT COUNT( * ) FROM $wpdb->posts WHERE post_type = 'attachment' AND post_status = 'trash' $and"); 1863 1864 return (object) $stats; 1865 } 1866 1867 /** 1868 * Check a MIME-Type against a list. 1869 * 1870 * If the wildcard_mime_types parameter is a string, it must be comma separated 1871 * list. If the real_mime_types is a string, it is also comma separated to 1872 * create the list. 1873 * 1874 * @since 2.5.0 1875 * 1876 * @param string|array $wildcard_mime_types e.g. audio/mpeg or image (same as image/*) or 1877 * flash (same as *flash*). 1878 * @param string|array $real_mime_types post_mime_type values 1879 * @return array array(wildcard=>array(real types)) 1880 */ 1881 function wp_match_mime_types($wildcard_mime_types, $real_mime_types) { 1882 $matches = array(); 1883 if ( is_string($wildcard_mime_types) ) 1884 $wildcard_mime_types = array_map('trim', explode(',', $wildcard_mime_types)); 1885 if ( is_string($real_mime_types) ) 1886 $real_mime_types = array_map('trim', explode(',', $real_mime_types)); 1887 $wild = '[-._a-z0-9]*'; 1888 foreach ( (array) $wildcard_mime_types as $type ) { 1889 $type = str_replace('*', $wild, $type); 1890 $patternses[1][$type] = "^$type$"; 1891 if ( false === strpos($type, '/') ) { 1892 $patternses[2][$type] = "^$type/"; 1893 $patternses[3][$type] = $type; 1894 } 1895 } 1896 asort($patternses); 1897 foreach ( $patternses as $patterns ) 1898 foreach ( $patterns as $type => $pattern ) 1899 foreach ( (array) $real_mime_types as $real ) 1900 if ( preg_match("#$pattern#", $real) && ( empty($matches[$type]) || false === array_search($real, $matches[$type]) ) ) 1901 $matches[$type][] = $real; 1902 return $matches; 1903 } 1904 1905 /** 1906 * Convert MIME types into SQL. 1907 * 1908 * @since 2.5.0 1909 * 1910 * @param string|array $post_mime_types List of mime types or comma separated string of mime types. 1911 * @param string $table_alias Optional. Specify a table alias, if needed. 1912 * @return string The SQL AND clause for mime searching. 1913 */ 1914 function wp_post_mime_type_where($post_mime_types, $table_alias = '') { 1915 $where = ''; 1916 $wildcards = array('', '%', '%/%'); 1917 if ( is_string($post_mime_types) ) 1918 $post_mime_types = array_map('trim', explode(',', $post_mime_types)); 1919 foreach ( (array) $post_mime_types as $mime_type ) { 1920 $mime_type = preg_replace('/\s/', '', $mime_type); 1921 $slashpos = strpos($mime_type, '/'); 1922 if ( false !== $slashpos ) { 1923 $mime_group = preg_replace('/[^-*.a-zA-Z0-9]/', '', substr($mime_type, 0, $slashpos)); 1924 $mime_subgroup = preg_replace('/[^-*.+a-zA-Z0-9]/', '', substr($mime_type, $slashpos + 1)); 1925 if ( empty($mime_subgroup) ) 1926 $mime_subgroup = '*'; 1927 else 1928 $mime_subgroup = str_replace('/', '', $mime_subgroup); 1929 $mime_pattern = "$mime_group/$mime_subgroup"; 1930 } else { 1931 $mime_pattern = preg_replace('/[^-*.a-zA-Z0-9]/', '', $mime_type); 1932 if ( false === strpos($mime_pattern, '*') ) 1933 $mime_pattern .= '/*'; 1934 } 1935 1936 $mime_pattern = preg_replace('/\*+/', '%', $mime_pattern); 1937 1938 if ( in_array( $mime_type, $wildcards ) ) 1939 return ''; 1940 1941 if ( false !== strpos($mime_pattern, '%') ) 1942 $wheres[] = empty($table_alias) ? "post_mime_type LIKE '$mime_pattern'" : "$table_alias.post_mime_type LIKE '$mime_pattern'"; 1943 else 1944 $wheres[] = empty($table_alias) ? "post_mime_type = '$mime_pattern'" : "$table_alias.post_mime_type = '$mime_pattern'"; 1945 } 1946 if ( !empty($wheres) ) 1947 $where = ' AND (' . join(' OR ', $wheres) . ') '; 1948 return $where; 1949 } 1950 1951 /** 1952 * Trashes or deletes a post or page. 1953 * 1954 * When the post and page is permanently deleted, everything that is tied to it is deleted also. 1955 * This includes comments, post meta fields, and terms associated with the post. 1956 * 1957 * The post or page is moved to trash instead of permanently deleted unless trash is 1958 * disabled, item is already in the trash, or $force_delete is true. 1959 * 1960 * @since 1.0.0 1961 * @uses do_action() on 'delete_post' before deletion unless post type is 'attachment'. 1962 * @uses do_action() on 'deleted_post' after deletion unless post type is 'attachment'. 1963 * @uses wp_delete_attachment() if post type is 'attachment'. 1964 * @uses wp_trash_post() if item should be trashed. 1965 * 1966 * @param int $postid Post ID. 1967 * @param bool $force_delete Whether to bypass trash and force deletion. Defaults to false. 1968 * @return mixed False on failure 1969 */ 1970 function wp_delete_post( $postid = 0, $force_delete = false ) { 1971 global $wpdb, $wp_rewrite; 1972 1973 if ( !$post = $wpdb->get_row($wpdb->prepare("SELECT * FROM $wpdb->posts WHERE ID = %d", $postid)) ) 1974 return $post; 1975 1976 if ( !$force_delete && ( $post->post_type == 'post' || $post->post_type == 'page') && get_post_status( $postid ) != 'trash' && EMPTY_TRASH_DAYS ) 1977 return wp_trash_post($postid); 1978 1979 if ( $post->post_type == 'attachment' ) 1980 return wp_delete_attachment( $postid, $force_delete ); 1981 1982 do_action('before_delete_post', $postid); 1983 1984 delete_post_meta($postid,'_wp_trash_meta_status'); 1985 delete_post_meta($postid,'_wp_trash_meta_time'); 1986 1987 wp_delete_object_term_relationships($postid, get_object_taxonomies($post->post_type)); 1988 1989 $parent_data = array( 'post_parent' => $post->post_parent ); 1990 $parent_where = array( 'post_parent' => $postid ); 1991 1992 if ( is_post_type_hierarchical( $post->post_type ) ) { 1993 // Point children of this page to its parent, also clean the cache of affected children 1994 $children_query = $wpdb->prepare( "SELECT * FROM $wpdb->posts WHERE post_parent = %d AND post_type = %s", $postid, $post->post_type ); 1995 $children = $wpdb->get_results( $children_query ); 1996 1997 $wpdb->update( $wpdb->posts, $parent_data, $parent_where + array( 'post_type' => $post->post_type ) ); 1998 } 1999 2000 if ( 'page' == $post->post_type) { 2001 // if the page is defined in option page_on_front or post_for_posts, 2002 // adjust the corresponding options 2003 if ( get_option('page_on_front') == $postid ) { 2004 update_option('show_on_front', 'posts'); 2005 delete_option('page_on_front'); 2006 } 2007 if ( get_option('page_for_posts') == $postid ) { 2008 delete_option('page_for_posts'); 2009 } 2010 } else { 2011 unstick_post($postid); 2012 } 2013 2014 // Do raw query. wp_get_post_revisions() is filtered 2015 $revision_ids = $wpdb->get_col( $wpdb->prepare( "SELECT ID FROM $wpdb->posts WHERE post_parent = %d AND post_type = 'revision'", $postid ) ); 2016 // Use wp_delete_post (via wp_delete_post_revision) again. Ensures any meta/misplaced data gets cleaned up. 2017 foreach ( $revision_ids as $revision_id ) 2018 wp_delete_post_revision( $revision_id ); 2019 2020 // Point all attachments to this post up one level 2021 $wpdb->update( $wpdb->posts, $parent_data, $parent_where + array( 'post_type' => 'attachment' ) ); 2022 2023 $comment_ids = $wpdb->get_col( $wpdb->prepare( "SELECT comment_ID FROM $wpdb->comments WHERE comment_post_ID = %d", $postid )); 2024 if ( ! empty($comment_ids) ) { 2025 do_action( 'delete_comment', $comment_ids ); 2026 foreach ( $comment_ids as $comment_id ) 2027 wp_delete_comment( $comment_id, true ); 2028 do_action( 'deleted_comment', $comment_ids ); 2029 } 2030 2031 $post_meta_ids = $wpdb->get_col( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE post_id = %d ", $postid )); 2032 if ( !empty($post_meta_ids) ) { 2033 do_action( 'delete_postmeta', $post_meta_ids ); 2034 $in_post_meta_ids = "'" . implode("', '", $post_meta_ids) . "'"; 2035 $wpdb->query( "DELETE FROM $wpdb->postmeta WHERE meta_id IN($in_post_meta_ids)" ); 2036 do_action( 'deleted_postmeta', $post_meta_ids ); 2037 } 2038 2039 do_action( 'delete_post', $postid ); 2040 $wpdb->query( $wpdb->prepare( "DELETE FROM $wpdb->posts WHERE ID = %d", $postid )); 2041 do_action( 'deleted_post', $postid ); 2042 2043 if ( 'page' == $post->post_type ) { 2044 clean_page_cache($postid); 2045 } else { 2046 clean_post_cache($postid); 2047 } 2048 2049 if ( is_post_type_hierarchical( $post->post_type ) ) { 2050 foreach ( (array) $children as $child ) 2051 clean_post_cache( $child->ID ); 2052 } 2053 2054 wp_clear_scheduled_hook('publish_future_post', array( $postid ) ); 2055 2056 do_action('after_delete_post', $postid); 2057 2058 return $post; 2059 } 2060 2061 /** 2062 * Moves a post or page to the Trash 2063 * 2064 * If trash is disabled, the post or page is permanently deleted. 2065 * 2066 * @since 2.9.0 2067 * @uses do_action() on 'trash_post' before trashing 2068 * @uses do_action() on 'trashed_post' after trashing 2069 * @uses wp_delete_post() if trash is disabled 2070 * 2071 * @param int $post_id Post ID. 2072 * @return mixed False on failure 2073 */ 2074 function wp_trash_post($post_id = 0) { 2075 if ( !EMPTY_TRASH_DAYS ) 2076 return wp_delete_post($post_id, true); 2077 2078 if ( !$post = wp_get_single_post($post_id, ARRAY_A) ) 2079 return $post; 2080 2081 if ( $post['post_status'] == 'trash' ) 2082 return false; 2083 2084 do_action('wp_trash_post', $post_id); 2085 2086 add_post_meta($post_id,'_wp_trash_meta_status', $post['post_status']); 2087 add_post_meta($post_id,'_wp_trash_meta_time', time()); 2088 2089 $post['post_status'] = 'trash'; 2090 wp_insert_post($post); 2091 2092 wp_trash_post_comments($post_id); 2093 2094 do_action('trashed_post', $post_id); 2095 2096 return $post; 2097 } 2098 2099 /** 2100 * Restores a post or page from the Trash 2101 * 2102 * @since 2.9.0 2103 * @uses do_action() on 'untrash_post' before undeletion 2104 * @uses do_action() on 'untrashed_post' after undeletion 2105 * 2106 * @param int $post_id Post ID. 2107 * @return mixed False on failure 2108 */ 2109 function wp_untrash_post($post_id = 0) { 2110 if ( !$post = wp_get_single_post($post_id, ARRAY_A) ) 2111 return $post; 2112 2113 if ( $post['post_status'] != 'trash' ) 2114 return false; 2115 2116 do_action('untrash_post', $post_id); 2117 2118 $post_status = get_post_meta($post_id, '_wp_trash_meta_status', true); 2119 2120 $post['post_status'] = $post_status; 2121 2122 delete_post_meta($post_id, '_wp_trash_meta_status'); 2123 delete_post_meta($post_id, '_wp_trash_meta_time'); 2124 2125 wp_insert_post($post); 2126 2127 wp_untrash_post_comments($post_id); 2128 2129 do_action('untrashed_post', $post_id); 2130 2131 return $post; 2132 } 2133 2134 /** 2135 * Moves comments for a post to the trash 2136 * 2137 * @since 2.9.0 2138 * @uses do_action() on 'trash_post_comments' before trashing 2139 * @uses do_action() on 'trashed_post_comments' after trashing 2140 * 2141 * @param int $post Post ID or object. 2142 * @return mixed False on failure 2143 */ 2144 function wp_trash_post_comments($post = null) { 2145 global $wpdb; 2146 2147 $post = get_post($post); 2148 if ( empty($post) ) 2149 return; 2150 2151 $post_id = $post->ID; 2152 2153 do_action('trash_post_comments', $post_id); 2154 2155 $comments = $wpdb->get_results( $wpdb->prepare("SELECT comment_ID, comment_approved FROM $wpdb->comments WHERE comment_post_ID = %d", $post_id) ); 2156 if ( empty($comments) ) 2157 return; 2158 2159 // Cache current status for each comment 2160 $statuses = array(); 2161 foreach ( $comments as $comment ) 2162 $statuses[$comment->comment_ID] = $comment->comment_approved; 2163 add_post_meta($post_id, '_wp_trash_meta_comments_status', $statuses); 2164 2165 // Set status for all comments to post-trashed 2166 $result = $wpdb->update($wpdb->comments, array('comment_approved' => 'post-trashed'), array('comment_post_ID' => $post_id)); 2167 2168 clean_comment_cache( array_keys($statuses) ); 2169 2170 do_action('trashed_post_comments', $post_id, $statuses); 2171 2172 return $result; 2173 } 2174 2175 /** 2176 * Restore comments for a post from the trash 2177 * 2178 * @since 2.9.0 2179 * @uses do_action() on 'untrash_post_comments' before trashing 2180 * @uses do_action() on 'untrashed_post_comments' after trashing 2181 * 2182 * @param int $post Post ID or object. 2183 * @return mixed False on failure 2184 */ 2185 function wp_untrash_post_comments($post = null) { 2186 global $wpdb; 2187 2188 $post = get_post($post); 2189 if ( empty($post) ) 2190 return; 2191 2192 $post_id = $post->ID; 2193 2194 $statuses = get_post_meta($post_id, '_wp_trash_meta_comments_status', true); 2195 2196 if ( empty($statuses) ) 2197 return true; 2198 2199 do_action('untrash_post_comments', $post_id); 2200 2201 // Restore each comment to its original status 2202 $group_by_status = array(); 2203 foreach ( $statuses as $comment_id => $comment_status ) 2204 $group_by_status[$comment_status][] = $comment_id; 2205 2206 foreach ( $group_by_status as $status => $comments ) { 2207 // Sanity check. This shouldn't happen. 2208 if ( 'post-trashed' == $status ) 2209 $status = '0'; 2210 $comments_in = implode( "', '", $comments ); 2211 $wpdb->query( "UPDATE $wpdb->comments SET comment_approved = '$status' WHERE comment_ID IN ('" . $comments_in . "')" ); 2212 } 2213 2214 clean_comment_cache( array_keys($statuses) ); 2215 2216 delete_post_meta($post_id, '_wp_trash_meta_comments_status'); 2217 2218 do_action('untrashed_post_comments', $post_id); 2219 } 2220 2221 /** 2222 * Retrieve the list of categories for a post. 2223 * 2224 * Compatibility layer for themes and plugins. Also an easy layer of abstraction 2225 * away from the complexity of the taxonomy layer. 2226 * 2227 * @since 2.1.0 2228 * 2229 * @uses wp_get_object_terms() Retrieves the categories. Args details can be found here. 2230 * 2231 * @param int $post_id Optional. The Post ID. 2232 * @param array $args Optional. Overwrite the defaults. 2233 * @return array 2234 */ 2235 function wp_get_post_categories( $post_id = 0, $args = array() ) { 2236 $post_id = (int) $post_id; 2237 2238 $defaults = array('fields' => 'ids'); 2239 $args = wp_parse_args( $args, $defaults ); 2240 2241 $cats = wp_get_object_terms($post_id, 'category', $args); 2242 return $cats; 2243 } 2244 2245 /** 2246 * Retrieve the tags for a post. 2247 * 2248 * There is only one default for this function, called 'fields' and by default 2249 * is set to 'all'. There are other defaults that can be overridden in 2250 * {@link wp_get_object_terms()}. 2251 * 2252 * @package WordPress 2253 * @subpackage Post 2254 * @since 2.3.0 2255 * 2256 * @uses wp_get_object_terms() Gets the tags for returning. Args can be found here 2257 * 2258 * @param int $post_id Optional. The Post ID 2259 * @param array $args Optional. Overwrite the defaults 2260 * @return array List of post tags. 2261 */ 2262 function wp_get_post_tags( $post_id = 0, $args = array() ) { 2263 return wp_get_post_terms( $post_id, 'post_tag', $args); 2264 } 2265 2266 /** 2267 * Retrieve the terms for a post. 2268 * 2269 * There is only one default for this function, called 'fields' and by default 2270 * is set to 'all'. There are other defaults that can be overridden in 2271 * {@link wp_get_object_terms()}. 2272 * 2273 * @package WordPress 2274 * @subpackage Post 2275 * @since 2.8.0 2276 * 2277 * @uses wp_get_object_terms() Gets the tags for returning. Args can be found here 2278 * 2279 * @param int $post_id Optional. The Post ID 2280 * @param string $taxonomy The taxonomy for which to retrieve terms. Defaults to post_tag. 2281 * @param array $args Optional. Overwrite the defaults 2282 * @return array List of post tags. 2283 */ 2284 function wp_get_post_terms( $post_id = 0, $taxonomy = 'post_tag', $args = array() ) { 2285 $post_id = (int) $post_id; 2286 2287 $defaults = array('fields' => 'all'); 2288 $args = wp_parse_args( $args, $defaults ); 2289 2290 $tags = wp_get_object_terms($post_id, $taxonomy, $args); 2291 2292 return $tags; 2293 } 2294 2295 /** 2296 * Retrieve number of recent posts. 2297 * 2298 * @since 1.0.0 2299 * @uses wp_parse_args() 2300 * @uses get_posts() 2301 * 2302 * @param string $deprecated Deprecated. 2303 * @param array $args Optional. Overrides defaults. 2304 * @param string $output Optional. 2305 * @return unknown. 2306 */ 2307 function wp_get_recent_posts( $args = array(), $output = ARRAY_A ) { 2308 2309 if ( is_numeric( $args ) ) { 2310 _deprecated_argument( __FUNCTION__, '3.1', __( 'Passing an integer number of posts is deprecated. Pass an array of arguments instead.' ) ); 2311 $args = array( 'numberposts' => absint( $args ) ); 2312 } 2313 2314 // Set default arguments 2315 $defaults = array( 2316 'numberposts' => 10, 'offset' => 0, 2317 'category' => 0, 'orderby' => 'post_date', 2318 'order' => 'DESC', 'include' => '', 2319 'exclude' => '', 'meta_key' => '', 2320 'meta_value' =>'', 'post_type' => 'post', 'post_status' => 'draft, publish, future, pending, private', 2321 'suppress_filters' => true 2322 ); 2323 2324