| [ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[Summary view] [Print] [Text view]
1 <?php 2 /** 3 * WordPress Post Administration API. 4 * 5 * @package WordPress 6 * @subpackage Administration 7 */ 8 9 /** 10 * Renames `$_POST` data from form names to DB post columns. 11 * 12 * Manipulates `$_POST` directly. 13 * 14 * @since 2.6.0 15 * 16 * @param bool $update Whether the post already exists. 17 * @param array|null $post_data Optional. The array of post data to process. 18 * Defaults to the `$_POST` superglobal. 19 * @return array|WP_Error Array of post data on success, WP_Error on failure. 20 */ 21 function _wp_translate_postdata( $update = false, $post_data = null ) { 22 23 if ( empty( $post_data ) ) { 24 $post_data = &$_POST; 25 } 26 27 if ( $update ) { 28 $post_data['ID'] = (int) $post_data['post_ID']; 29 } 30 31 $ptype = get_post_type_object( $post_data['post_type'] ); 32 33 if ( $update && ! current_user_can( 'edit_post', $post_data['ID'] ) ) { 34 if ( 'page' === $post_data['post_type'] ) { 35 return new WP_Error( 'edit_others_pages', __( 'Sorry, you are not allowed to edit pages as this user.' ) ); 36 } else { 37 return new WP_Error( 'edit_others_posts', __( 'Sorry, you are not allowed to edit posts as this user.' ) ); 38 } 39 } elseif ( ! $update && ! current_user_can( $ptype->cap->create_posts ) ) { 40 if ( 'page' === $post_data['post_type'] ) { 41 return new WP_Error( 'edit_others_pages', __( 'Sorry, you are not allowed to create pages as this user.' ) ); 42 } else { 43 return new WP_Error( 'edit_others_posts', __( 'Sorry, you are not allowed to create posts as this user.' ) ); 44 } 45 } 46 47 if ( isset( $post_data['content'] ) ) { 48 $post_data['post_content'] = $post_data['content']; 49 } 50 51 if ( isset( $post_data['excerpt'] ) ) { 52 $post_data['post_excerpt'] = $post_data['excerpt']; 53 } 54 55 if ( isset( $post_data['parent_id'] ) ) { 56 $post_data['post_parent'] = (int) $post_data['parent_id']; 57 } 58 59 if ( isset( $post_data['trackback_url'] ) ) { 60 $post_data['to_ping'] = $post_data['trackback_url']; 61 } 62 63 $post_data['user_ID'] = get_current_user_id(); 64 65 if ( ! empty( $post_data['post_author_override'] ) ) { 66 $post_data['post_author'] = (int) $post_data['post_author_override']; 67 } else { 68 if ( ! empty( $post_data['post_author'] ) ) { 69 $post_data['post_author'] = (int) $post_data['post_author']; 70 } else { 71 $post_data['post_author'] = (int) $post_data['user_ID']; 72 } 73 } 74 75 if ( isset( $post_data['user_ID'] ) && ( $post_data['post_author'] !== $post_data['user_ID'] ) 76 && ! current_user_can( $ptype->cap->edit_others_posts ) ) { 77 78 if ( $update ) { 79 if ( 'page' === $post_data['post_type'] ) { 80 return new WP_Error( 'edit_others_pages', __( 'Sorry, you are not allowed to edit pages as this user.' ) ); 81 } else { 82 return new WP_Error( 'edit_others_posts', __( 'Sorry, you are not allowed to edit posts as this user.' ) ); 83 } 84 } else { 85 if ( 'page' === $post_data['post_type'] ) { 86 return new WP_Error( 'edit_others_pages', __( 'Sorry, you are not allowed to create pages as this user.' ) ); 87 } else { 88 return new WP_Error( 'edit_others_posts', __( 'Sorry, you are not allowed to create posts as this user.' ) ); 89 } 90 } 91 } 92 93 if ( ! empty( $post_data['post_status'] ) ) { 94 $post_data['post_status'] = sanitize_key( $post_data['post_status'] ); 95 96 // No longer an auto-draft. 97 if ( 'auto-draft' === $post_data['post_status'] ) { 98 $post_data['post_status'] = 'draft'; 99 } 100 101 if ( ! get_post_status_object( $post_data['post_status'] ) ) { 102 unset( $post_data['post_status'] ); 103 } 104 } 105 106 // What to do based on which button they pressed. 107 if ( isset( $post_data['saveasdraft'] ) && '' !== $post_data['saveasdraft'] ) { 108 $post_data['post_status'] = 'draft'; 109 } 110 if ( isset( $post_data['saveasprivate'] ) && '' !== $post_data['saveasprivate'] ) { 111 $post_data['post_status'] = 'private'; 112 } 113 if ( isset( $post_data['publish'] ) && ( '' !== $post_data['publish'] ) 114 && ( ! isset( $post_data['post_status'] ) || 'private' !== $post_data['post_status'] ) 115 ) { 116 $post_data['post_status'] = 'publish'; 117 } 118 if ( isset( $post_data['advanced'] ) && '' !== $post_data['advanced'] ) { 119 $post_data['post_status'] = 'draft'; 120 } 121 if ( isset( $post_data['pending'] ) && '' !== $post_data['pending'] ) { 122 $post_data['post_status'] = 'pending'; 123 } 124 125 $post_id = $post_data['ID'] ?? false; 126 $previous_status = $post_id ? get_post_field( 'post_status', $post_id ) : false; 127 128 if ( isset( $post_data['post_status'] ) && 'private' === $post_data['post_status'] && ! current_user_can( $ptype->cap->publish_posts ) ) { 129 $post_data['post_status'] = $previous_status ? $previous_status : 'pending'; 130 } 131 132 $published_statuses = array( 'publish', 'future' ); 133 134 /* 135 * Posts 'submitted for approval' are submitted to $_POST the same as if they were being published. 136 * Change status from 'publish' to 'pending' if user lacks permissions to publish or to resave published posts. 137 */ 138 if ( isset( $post_data['post_status'] ) 139 && ( in_array( $post_data['post_status'], $published_statuses, true ) 140 && ! current_user_can( $ptype->cap->publish_posts ) ) 141 ) { 142 if ( ! in_array( $previous_status, $published_statuses, true ) || ! current_user_can( 'edit_post', $post_id ) ) { 143 $post_data['post_status'] = 'pending'; 144 } 145 } 146 147 if ( ! isset( $post_data['post_status'] ) ) { 148 $post_data['post_status'] = 'auto-draft' === $previous_status ? 'draft' : $previous_status; 149 } 150 151 if ( isset( $post_data['post_password'] ) && ! current_user_can( $ptype->cap->publish_posts ) ) { 152 unset( $post_data['post_password'] ); 153 } 154 155 if ( ! isset( $post_data['comment_status'] ) ) { 156 $post_data['comment_status'] = 'closed'; 157 } 158 159 if ( ! isset( $post_data['ping_status'] ) ) { 160 $post_data['ping_status'] = 'closed'; 161 } 162 163 foreach ( array( 'aa', 'mm', 'jj', 'hh', 'mn' ) as $timeunit ) { 164 if ( ! empty( $post_data[ 'hidden_' . $timeunit ] ) && $post_data[ 'hidden_' . $timeunit ] !== $post_data[ $timeunit ] ) { 165 $post_data['edit_date'] = '1'; 166 break; 167 } 168 } 169 170 if ( ! empty( $post_data['edit_date'] ) ) { 171 $aa = $post_data['aa']; 172 $mm = $post_data['mm']; 173 $jj = $post_data['jj']; 174 $hh = $post_data['hh']; 175 $mn = $post_data['mn']; 176 $ss = $post_data['ss']; 177 $aa = ( $aa <= 0 ) ? gmdate( 'Y' ) : $aa; 178 $mm = ( $mm <= 0 ) ? gmdate( 'n' ) : $mm; 179 $jj = ( $jj > 31 ) ? 31 : $jj; 180 $jj = ( $jj <= 0 ) ? gmdate( 'j' ) : $jj; 181 $hh = ( $hh > 23 ) ? $hh - 24 : $hh; 182 $mn = ( $mn > 59 ) ? $mn - 60 : $mn; 183 $ss = ( $ss > 59 ) ? $ss - 60 : $ss; 184 185 $post_data['post_date'] = sprintf( '%04d-%02d-%02d %02d:%02d:%02d', $aa, $mm, $jj, $hh, $mn, $ss ); 186 187 $valid_date = wp_checkdate( $mm, $jj, $aa, $post_data['post_date'] ); 188 if ( ! $valid_date ) { 189 return new WP_Error( 'invalid_date', __( 'Invalid date.' ) ); 190 } 191 192 /* 193 * Only assign a post date if the user has explicitly set a new value. 194 * See #59125 and #19907. 195 */ 196 $previous_date = $post_id ? get_post_field( 'post_date', $post_id ) : false; 197 if ( $previous_date && $previous_date !== $post_data['post_date'] ) { 198 $post_data['edit_date'] = true; 199 $post_data['post_date_gmt'] = get_gmt_from_date( $post_data['post_date'] ); 200 } else { 201 $post_data['edit_date'] = false; 202 unset( $post_data['post_date'] ); 203 unset( $post_data['post_date_gmt'] ); 204 } 205 } 206 207 if ( isset( $post_data['post_category'] ) ) { 208 $category_object = get_taxonomy( 'category' ); 209 if ( ! current_user_can( $category_object->cap->assign_terms ) ) { 210 unset( $post_data['post_category'] ); 211 } 212 } 213 214 return $post_data; 215 } 216 217 /** 218 * Returns only allowed post data fields. 219 * 220 * @since 5.0.1 221 * 222 * @param array|WP_Error|null $post_data The array of post data to process, or an error object. 223 * Defaults to the `$_POST` superglobal. 224 * @return array|WP_Error Array of post data on success, WP_Error on failure. 225 */ 226 function _wp_get_allowed_postdata( $post_data = null ) { 227 if ( empty( $post_data ) ) { 228 $post_data = $_POST; 229 } 230 231 // Pass through errors. 232 if ( is_wp_error( $post_data ) ) { 233 return $post_data; 234 } 235 236 return array_diff_key( $post_data, array_flip( array( 'meta_input', 'file', 'guid' ) ) ); 237 } 238 239 /** 240 * Updates an existing post with values provided in `$_POST`. 241 * 242 * If post data is passed as an argument, it is treated as an array of data 243 * keyed appropriately for turning into a post object. 244 * 245 * If post data is not passed, the `$_POST` global variable is used instead. 246 * 247 * @since 1.5.0 248 * 249 * @global wpdb $wpdb WordPress database abstraction object. 250 * 251 * @param array|null $post_data Optional. The array of post data to process. 252 * Defaults to the `$_POST` superglobal. 253 * @return int Post ID. 254 */ 255 function edit_post( $post_data = null ) { 256 global $wpdb; 257 258 if ( empty( $post_data ) ) { 259 $post_data = &$_POST; 260 } 261 262 // Clear out any data in internal vars. 263 unset( $post_data['filter'] ); 264 265 $post_id = (int) $post_data['post_ID']; 266 $post = get_post( $post_id ); 267 268 $post_data['post_type'] = $post->post_type; 269 $post_data['post_mime_type'] = $post->post_mime_type; 270 271 if ( ! empty( $post_data['post_status'] ) ) { 272 $post_data['post_status'] = sanitize_key( $post_data['post_status'] ); 273 274 if ( 'inherit' === $post_data['post_status'] ) { 275 unset( $post_data['post_status'] ); 276 } 277 } 278 279 $ptype = get_post_type_object( $post_data['post_type'] ); 280 if ( ! current_user_can( 'edit_post', $post_id ) ) { 281 if ( 'page' === $post_data['post_type'] ) { 282 wp_die( __( 'Sorry, you are not allowed to edit this page.' ) ); 283 } else { 284 wp_die( __( 'Sorry, you are not allowed to edit this post.' ) ); 285 } 286 } 287 288 if ( post_type_supports( $ptype->name, 'revisions' ) ) { 289 $revisions = wp_get_post_revisions( 290 $post_id, 291 array( 292 'order' => 'ASC', 293 'posts_per_page' => 1, 294 ) 295 ); 296 $revision = current( $revisions ); 297 298 // Check if the revisions have been upgraded. 299 if ( $revisions && _wp_get_post_revision_version( $revision ) < 1 ) { 300 _wp_upgrade_revisions_of_post( $post, wp_get_post_revisions( $post_id ) ); 301 } 302 } 303 304 if ( isset( $post_data['visibility'] ) ) { 305 switch ( $post_data['visibility'] ) { 306 case 'public': 307 $post_data['post_password'] = ''; 308 break; 309 case 'password': 310 unset( $post_data['sticky'] ); 311 break; 312 case 'private': 313 $post_data['post_status'] = 'private'; 314 $post_data['post_password'] = ''; 315 unset( $post_data['sticky'] ); 316 break; 317 } 318 } 319 320 $post_data = _wp_translate_postdata( true, $post_data ); 321 if ( is_wp_error( $post_data ) ) { 322 wp_die( $post_data->get_error_message() ); 323 } 324 $translated = _wp_get_allowed_postdata( $post_data ); 325 326 // Post formats. 327 if ( isset( $post_data['post_format'] ) ) { 328 set_post_format( $post_id, $post_data['post_format'] ); 329 } 330 331 $format_meta_urls = array( 'url', 'link_url', 'quote_source_url' ); 332 foreach ( $format_meta_urls as $format_meta_url ) { 333 $keyed = '_format_' . $format_meta_url; 334 if ( isset( $post_data[ $keyed ] ) ) { 335 update_post_meta( $post_id, $keyed, wp_slash( sanitize_url( wp_unslash( $post_data[ $keyed ] ) ) ) ); 336 } 337 } 338 339 $format_keys = array( 'quote', 'quote_source_name', 'image', 'gallery', 'audio_embed', 'video_embed' ); 340 341 foreach ( $format_keys as $key ) { 342 $keyed = '_format_' . $key; 343 if ( isset( $post_data[ $keyed ] ) ) { 344 if ( current_user_can( 'unfiltered_html' ) ) { 345 update_post_meta( $post_id, $keyed, $post_data[ $keyed ] ); 346 } else { 347 update_post_meta( $post_id, $keyed, wp_filter_post_kses( $post_data[ $keyed ] ) ); 348 } 349 } 350 } 351 352 if ( 'attachment' === $post_data['post_type'] && preg_match( '#^(audio|video)/#', $post_data['post_mime_type'] ) ) { 353 $id3data = wp_get_attachment_metadata( $post_id ); 354 if ( ! is_array( $id3data ) ) { 355 $id3data = array(); 356 } 357 358 foreach ( wp_get_attachment_id3_keys( $post, 'edit' ) as $key => $label ) { 359 if ( isset( $post_data[ 'id3_' . $key ] ) ) { 360 $id3data[ $key ] = sanitize_text_field( wp_unslash( $post_data[ 'id3_' . $key ] ) ); 361 } 362 } 363 wp_update_attachment_metadata( $post_id, $id3data ); 364 } 365 366 // Meta stuff. 367 if ( isset( $post_data['meta'] ) && $post_data['meta'] ) { 368 foreach ( $post_data['meta'] as $key => $value ) { 369 $meta = get_post_meta_by_id( $key ); 370 if ( ! $meta ) { 371 continue; 372 } 373 374 if ( (int) $meta->post_id !== $post_id ) { 375 continue; 376 } 377 378 if ( is_protected_meta( $meta->meta_key, 'post' ) 379 || ! current_user_can( 'edit_post_meta', $post_id, $meta->meta_key ) 380 ) { 381 continue; 382 } 383 384 if ( is_protected_meta( $value['key'], 'post' ) 385 || ! current_user_can( 'edit_post_meta', $post_id, $value['key'] ) 386 ) { 387 continue; 388 } 389 390 update_meta( $key, $value['key'], $value['value'] ); 391 } 392 } 393 394 if ( isset( $post_data['deletemeta'] ) && $post_data['deletemeta'] ) { 395 foreach ( $post_data['deletemeta'] as $key => $value ) { 396 $meta = get_post_meta_by_id( $key ); 397 if ( ! $meta ) { 398 continue; 399 } 400 401 if ( (int) $meta->post_id !== $post_id ) { 402 continue; 403 } 404 405 if ( is_protected_meta( $meta->meta_key, 'post' ) 406 || ! current_user_can( 'delete_post_meta', $post_id, $meta->meta_key ) 407 ) { 408 continue; 409 } 410 411 delete_meta( $key ); 412 } 413 } 414 415 // Attachment stuff. 416 if ( 'attachment' === $post_data['post_type'] ) { 417 if ( isset( $post_data['_wp_attachment_image_alt'] ) ) { 418 $image_alt = wp_unslash( $post_data['_wp_attachment_image_alt'] ); 419 420 if ( get_post_meta( $post_id, '_wp_attachment_image_alt', true ) !== $image_alt ) { 421 $image_alt = wp_strip_all_tags( $image_alt, true ); 422 423 // update_post_meta() expects slashed. 424 update_post_meta( $post_id, '_wp_attachment_image_alt', wp_slash( $image_alt ) ); 425 } 426 } 427 428 $attachment_data = $post_data['attachments'][ $post_id ] ?? array(); 429 430 /** This filter is documented in wp-admin/includes/media.php */ 431 $translated = apply_filters( 'attachment_fields_to_save', $translated, $attachment_data ); 432 } 433 434 // Convert taxonomy input to term IDs, to avoid ambiguity. 435 if ( isset( $post_data['tax_input'] ) ) { 436 foreach ( (array) $post_data['tax_input'] as $taxonomy => $terms ) { 437 $tax_object = get_taxonomy( $taxonomy ); 438 439 if ( $tax_object && isset( $tax_object->meta_box_sanitize_cb ) ) { 440 $translated['tax_input'][ $taxonomy ] = call_user_func_array( $tax_object->meta_box_sanitize_cb, array( $taxonomy, $terms ) ); 441 } 442 } 443 } 444 445 add_meta( $post_id ); 446 447 update_post_meta( $post_id, '_edit_last', get_current_user_id() ); 448 449 $success = wp_update_post( $translated ); 450 451 // If the save failed, see if we can confidence check the main fields and try again. 452 if ( ! $success && is_callable( array( $wpdb, 'strip_invalid_text_for_column' ) ) ) { 453 $fields = array( 'post_title', 'post_content', 'post_excerpt' ); 454 455 foreach ( $fields as $field ) { 456 if ( isset( $translated[ $field ] ) ) { 457 $translated[ $field ] = $wpdb->strip_invalid_text_for_column( $wpdb->posts, $field, $translated[ $field ] ); 458 } 459 } 460 461 wp_update_post( $translated ); 462 } 463 464 // Now that we have an ID we can fix any attachment anchor hrefs. 465 _fix_attachment_links( $post_id ); 466 467 wp_set_post_lock( $post_id ); 468 469 if ( current_user_can( $ptype->cap->edit_others_posts ) && current_user_can( $ptype->cap->publish_posts ) ) { 470 if ( ! empty( $post_data['sticky'] ) ) { 471 stick_post( $post_id ); 472 } else { 473 unstick_post( $post_id ); 474 } 475 } 476 477 return $post_id; 478 } 479 480 /** 481 * Processes the post data for the bulk editing of posts. 482 * 483 * Updates all bulk edited posts/pages, adding (but not removing) tags and 484 * categories. Skips pages when they would be their own parent or child. 485 * 486 * @since 2.7.0 487 * 488 * @global wpdb $wpdb WordPress database abstraction object. 489 * 490 * @param array|null $post_data Optional. The array of post data to process. 491 * Defaults to the `$_POST` superglobal. 492 * @return array { 493 * An array of updated, skipped, and locked post IDs. 494 * 495 * @type int[] $updated An array of updated post IDs. 496 * @type int[] $skipped An array of skipped post IDs. 497 * @type int[] $locked An array of locked post IDs. 498 * } 499 */ 500 function bulk_edit_posts( $post_data = null ) { 501 global $wpdb; 502 503 if ( empty( $post_data ) ) { 504 $post_data = &$_POST; 505 } 506 507 if ( isset( $post_data['post_type'] ) ) { 508 $ptype = get_post_type_object( $post_data['post_type'] ); 509 } else { 510 $ptype = get_post_type_object( 'post' ); 511 } 512 513 if ( ! current_user_can( $ptype->cap->edit_posts ) ) { 514 if ( 'page' === $ptype->name ) { 515 wp_die( __( 'Sorry, you are not allowed to edit pages.' ) ); 516 } else { 517 wp_die( __( 'Sorry, you are not allowed to edit posts.' ) ); 518 } 519 } 520 521 if ( '-1' === $post_data['_status'] ) { 522 $post_data['post_status'] = null; 523 unset( $post_data['post_status'] ); 524 } else { 525 $post_data['post_status'] = $post_data['_status']; 526 } 527 unset( $post_data['_status'] ); 528 529 if ( ! empty( $post_data['post_status'] ) ) { 530 $post_data['post_status'] = sanitize_key( $post_data['post_status'] ); 531 532 if ( 'inherit' === $post_data['post_status'] ) { 533 unset( $post_data['post_status'] ); 534 } 535 } 536 537 $post_ids = array_map( 'intval', (array) $post_data['post'] ); 538 539 $reset = array( 540 'post_author', 541 'post_status', 542 'post_password', 543 'post_parent', 544 'page_template', 545 'comment_status', 546 'ping_status', 547 'keep_private', 548 'tax_input', 549 'post_category', 550 'sticky', 551 'post_format', 552 ); 553 554 foreach ( $reset as $field ) { 555 if ( isset( $post_data[ $field ] ) && ( '' === $post_data[ $field ] || '-1' === $post_data[ $field ] ) ) { 556 unset( $post_data[ $field ] ); 557 } 558 } 559 560 if ( isset( $post_data['post_category'] ) ) { 561 if ( is_array( $post_data['post_category'] ) && ! empty( $post_data['post_category'] ) ) { 562 $new_cats = array_map( 'absint', $post_data['post_category'] ); 563 } else { 564 unset( $post_data['post_category'] ); 565 } 566 } 567 568 $tax_input = array(); 569 if ( isset( $post_data['tax_input'] ) ) { 570 foreach ( $post_data['tax_input'] as $tax_name => $terms ) { 571 if ( empty( $terms ) ) { 572 continue; 573 } 574 575 if ( is_taxonomy_hierarchical( $tax_name ) ) { 576 $tax_input[ $tax_name ] = array_map( 'absint', $terms ); 577 } else { 578 $comma = _x( ',', 'tag delimiter' ); 579 if ( ',' !== $comma ) { 580 $terms = str_replace( $comma, ',', $terms ); 581 } 582 $tax_input[ $tax_name ] = explode( ',', trim( $terms, " \n\t\r\0\x0B," ) ); 583 } 584 } 585 } 586 587 if ( isset( $post_data['post_parent'] ) && (int) $post_data['post_parent'] ) { 588 $parent = (int) $post_data['post_parent']; 589 $pages = $wpdb->get_results( "SELECT ID, post_parent FROM $wpdb->posts WHERE post_type = 'page'" ); 590 $children = array(); 591 592 for ( $i = 0; $i < 50 && $parent > 0; $i++ ) { 593 $children[] = $parent; 594 595 foreach ( $pages as $page ) { 596 if ( (int) $page->ID === $parent ) { 597 $parent = (int) $page->post_parent; 598 break; 599 } 600 } 601 } 602 } 603 604 $updated = array(); 605 $skipped = array(); 606 $locked = array(); 607 $shared_post_data = $post_data; 608 609 foreach ( $post_ids as $post_id ) { 610 // Start with fresh post data with each iteration. 611 $post_data = $shared_post_data; 612 613 $post_type_object = get_post_type_object( get_post_type( $post_id ) ); 614 615 if ( ! isset( $post_type_object ) 616 || ( isset( $children ) && in_array( $post_id, $children, true ) ) 617 || ! current_user_can( 'edit_post', $post_id ) 618 ) { 619 $skipped[] = $post_id; 620 continue; 621 } 622 623 if ( wp_check_post_lock( $post_id ) ) { 624 $locked[] = $post_id; 625 continue; 626 } 627 628 $post = get_post( $post_id ); 629 $tax_names = get_object_taxonomies( $post ); 630 631 foreach ( $tax_names as $tax_name ) { 632 $taxonomy_obj = get_taxonomy( $tax_name ); 633 634 if ( ! $taxonomy_obj->show_in_quick_edit ) { 635 continue; 636 } 637 638 if ( isset( $tax_input[ $tax_name ] ) && current_user_can( $taxonomy_obj->cap->assign_terms ) ) { 639 $new_terms = $tax_input[ $tax_name ]; 640 } else { 641 $new_terms = array(); 642 } 643 644 if ( $taxonomy_obj->hierarchical ) { 645 $current_terms = (array) wp_get_object_terms( $post_id, $tax_name, array( 'fields' => 'ids' ) ); 646 } else { 647 $current_terms = (array) wp_get_object_terms( $post_id, $tax_name, array( 'fields' => 'names' ) ); 648 } 649 650 $post_data['tax_input'][ $tax_name ] = array_merge( $current_terms, $new_terms ); 651 } 652 653 if ( isset( $new_cats ) && in_array( 'category', $tax_names, true ) ) { 654 $cats = (array) wp_get_post_categories( $post_id ); 655 656 if ( 657 isset( $post_data['indeterminate_post_category'] ) 658 && is_array( $post_data['indeterminate_post_category'] ) 659 ) { 660 $indeterminate_post_category = $post_data['indeterminate_post_category']; 661 } else { 662 $indeterminate_post_category = array(); 663 } 664 665 $indeterminate_cats = array_intersect( $cats, $indeterminate_post_category ); 666 $determinate_cats = array_diff( $new_cats, $indeterminate_post_category ); 667 $post_data['post_category'] = array_unique( array_merge( $indeterminate_cats, $determinate_cats ) ); 668 669 unset( $post_data['tax_input']['category'] ); 670 } 671 672 $post_data['post_ID'] = $post_id; 673 $post_data['post_type'] = $post->post_type; 674 $post_data['post_mime_type'] = $post->post_mime_type; 675 676 foreach ( array( 'comment_status', 'ping_status', 'post_author' ) as $field ) { 677 if ( ! isset( $post_data[ $field ] ) ) { 678 $post_data[ $field ] = $post->$field; 679 } 680 } 681 682 $post_data = _wp_translate_postdata( true, $post_data ); 683 if ( is_wp_error( $post_data ) ) { 684 $skipped[] = $post_id; 685 continue; 686 } 687 $post_data = _wp_get_allowed_postdata( $post_data ); 688 689 if ( isset( $shared_post_data['post_format'] ) ) { 690 set_post_format( $post_id, $shared_post_data['post_format'] ); 691 } 692 693 // Prevent wp_insert_post() from overwriting post format with the old data. 694 unset( $post_data['tax_input']['post_format'] ); 695 696 // Reset post date of scheduled post to be published. 697 if ( 698 in_array( $post->post_status, array( 'future', 'draft' ), true ) && 699 'publish' === $post_data['post_status'] 700 ) { 701 $post_data['post_date'] = current_time( 'mysql' ); 702 $post_data['post_date_gmt'] = ''; 703 } 704 705 $post_id = wp_update_post( $post_data ); 706 update_post_meta( $post_id, '_edit_last', get_current_user_id() ); 707 $updated[] = $post_id; 708 709 if ( isset( $post_data['sticky'] ) && current_user_can( $ptype->cap->edit_others_posts ) ) { 710 if ( 'sticky' === $post_data['sticky'] ) { 711 stick_post( $post_id ); 712 } else { 713 unstick_post( $post_id ); 714 } 715 } 716 } 717 718 /** 719 * Fires after processing the post data for bulk edit. 720 * 721 * @since 6.3.0 722 * 723 * @param int[] $updated An array of updated post IDs. 724 * @param array $shared_post_data Associative array containing the post data. 725 */ 726 do_action( 'bulk_edit_posts', $updated, $shared_post_data ); 727 728 return array( 729 'updated' => $updated, 730 'skipped' => $skipped, 731 'locked' => $locked, 732 ); 733 } 734 735 /** 736 * Returns default post information to use when populating the "Write Post" form. 737 * 738 * @since 2.0.0 739 * 740 * @param string $post_type Optional. A post type string. Default 'post'. 741 * @param bool $create_in_db Optional. Whether to insert the post into database. Default false. 742 * @return WP_Post Post object containing all the default post data as attributes 743 */ 744 function get_default_post_to_edit( $post_type = 'post', $create_in_db = false ) { 745 $post_title = ''; 746 if ( ! empty( $_REQUEST['post_title'] ) ) { 747 $post_title = esc_html( wp_unslash( $_REQUEST['post_title'] ) ); 748 } 749 750 $post_content = ''; 751 if ( ! empty( $_REQUEST['content'] ) ) { 752 $post_content = esc_html( wp_unslash( $_REQUEST['content'] ) ); 753 } 754 755 $post_excerpt = ''; 756 if ( ! empty( $_REQUEST['excerpt'] ) ) { 757 $post_excerpt = esc_html( wp_unslash( $_REQUEST['excerpt'] ) ); 758 } 759 760 if ( $create_in_db ) { 761 $post_id = wp_insert_post( 762 array( 763 'post_title' => post_type_supports( $post_type, 'title' ) ? __( 'Auto Draft' ) : '', 764 'post_type' => $post_type, 765 'post_status' => 'auto-draft', 766 ), 767 true, 768 false 769 ); 770 771 if ( is_wp_error( $post_id ) ) { 772 wp_die( $post_id->get_error_message() ); 773 } 774 775 $post = get_post( $post_id ); 776 777 if ( current_theme_supports( 'post-formats' ) && post_type_supports( $post->post_type, 'post-formats' ) && get_option( 'default_post_format' ) ) { 778 set_post_format( $post, get_option( 'default_post_format' ) ); 779 } 780 781 wp_after_insert_post( $post, false, null ); 782 783 // Schedule auto-draft cleanup. 784 if ( ! wp_next_scheduled( 'wp_scheduled_auto_draft_delete' ) ) { 785 wp_schedule_event( time(), 'daily', 'wp_scheduled_auto_draft_delete' ); 786 } 787 } else { 788 $post = new stdClass(); 789 $post->ID = 0; 790 $post->post_author = ''; 791 $post->post_date = ''; 792 $post->post_date_gmt = ''; 793 $post->post_password = ''; 794 $post->post_name = ''; 795 $post->post_type = $post_type; 796 $post->post_status = 'draft'; 797 $post->to_ping = ''; 798 $post->pinged = ''; 799 $post->comment_status = get_default_comment_status( $post_type ); 800 $post->ping_status = get_default_comment_status( $post_type, 'pingback' ); 801 $post->post_pingback = get_option( 'default_pingback_flag' ); 802 $post->post_category = get_option( 'default_category' ); 803 $post->page_template = 'default'; 804 $post->post_parent = 0; 805 $post->menu_order = 0; 806 $post = new WP_Post( $post ); 807 } 808 809 /** 810 * Filters the default post content initially used in the "Write Post" form. 811 * 812 * @since 1.5.0 813 * 814 * @param string $post_content Default post content. 815 * @param WP_Post $post Post object. 816 */ 817 $post->post_content = (string) apply_filters( 'default_content', $post_content, $post ); 818 819 /** 820 * Filters the default post title initially used in the "Write Post" form. 821 * 822 * @since 1.5.0 823 * 824 * @param string $post_title Default post title. 825 * @param WP_Post $post Post object. 826 */ 827 $post->post_title = (string) apply_filters( 'default_title', $post_title, $post ); 828 829 /** 830 * Filters the default post excerpt initially used in the "Write Post" form. 831 * 832 * @since 1.5.0 833 * 834 * @param string $post_excerpt Default post excerpt. 835 * @param WP_Post $post Post object. 836 */ 837 $post->post_excerpt = (string) apply_filters( 'default_excerpt', $post_excerpt, $post ); 838 839 return $post; 840 } 841 842 /** 843 * Determines if a post exists based on title, content, date and type. 844 * 845 * @since 2.0.0 846 * @since 5.2.0 Added the `$type` parameter. 847 * @since 5.8.0 Added the `$status` parameter. 848 * 849 * @global wpdb $wpdb WordPress database abstraction object. 850 * 851 * @param string $title Post title. 852 * @param string $content Optional. Post content. 853 * @param string $date Optional. Post date. 854 * @param string $type Optional. Post type. 855 * @param string $status Optional. Post status. 856 * @return int Post ID if post exists, 0 otherwise. 857 */ 858 function post_exists( $title, $content = '', $date = '', $type = '', $status = '' ) { 859 global $wpdb; 860 861 $post_title = wp_unslash( sanitize_post_field( 'post_title', $title, 0, 'db' ) ); 862 $post_content = wp_unslash( sanitize_post_field( 'post_content', $content, 0, 'db' ) ); 863 $post_date = wp_unslash( sanitize_post_field( 'post_date', $date, 0, 'db' ) ); 864 $post_type = wp_unslash( sanitize_post_field( 'post_type', $type, 0, 'db' ) ); 865 $post_status = wp_unslash( sanitize_post_field( 'post_status', $status, 0, 'db' ) ); 866 867 $query = "SELECT ID FROM $wpdb->posts WHERE 1=1"; 868 $args = array(); 869 870 if ( ! empty( $date ) ) { 871 $query .= ' AND post_date = %s'; 872 $args[] = $post_date; 873 } 874 875 if ( ! empty( $title ) ) { 876 $query .= ' AND post_title = %s'; 877 $args[] = $post_title; 878 } 879 880 if ( ! empty( $content ) ) { 881 $query .= ' AND post_content = %s'; 882 $args[] = $post_content; 883 } 884 885 if ( ! empty( $type ) ) { 886 $query .= ' AND post_type = %s'; 887 $args[] = $post_type; 888 } 889 890 if ( ! empty( $status ) ) { 891 $query .= ' AND post_status = %s'; 892 $args[] = $post_status; 893 } 894 895 if ( ! empty( $args ) ) { 896 return (int) $wpdb->get_var( $wpdb->prepare( $query, $args ) ); 897 } 898 899 return 0; 900 } 901 902 /** 903 * Creates a new post from the "Write Post" form using `$_POST` information. 904 * 905 * @since 2.1.0 906 * 907 * @global WP_User $current_user 908 * 909 * @return int|WP_Error Post ID on success, WP_Error on failure. 910 */ 911 function wp_write_post() { 912 if ( isset( $_POST['post_type'] ) ) { 913 $ptype = get_post_type_object( $_POST['post_type'] ); 914 } else { 915 $ptype = get_post_type_object( 'post' ); 916 } 917 918 if ( ! current_user_can( $ptype->cap->edit_posts ) ) { 919 if ( 'page' === $ptype->name ) { 920 return new WP_Error( 'edit_pages', __( 'Sorry, you are not allowed to create pages on this site.' ) ); 921 } else { 922 return new WP_Error( 'edit_posts', __( 'Sorry, you are not allowed to create posts or drafts on this site.' ) ); 923 } 924 } 925 926 $_POST['post_mime_type'] = ''; 927 928 // Clear out any data in internal vars. 929 unset( $_POST['filter'] ); 930 931 // Edit, don't write, if we have a post ID. 932 if ( isset( $_POST['post_ID'] ) ) { 933 return edit_post(); 934 } 935 936 if ( isset( $_POST['visibility'] ) ) { 937 switch ( $_POST['visibility'] ) { 938 case 'public': 939 $_POST['post_password'] = ''; 940 break; 941 case 'password': 942 unset( $_POST['sticky'] ); 943 break; 944 case 'private': 945 $_POST['post_status'] = 'private'; 946 $_POST['post_password'] = ''; 947 unset( $_POST['sticky'] ); 948 break; 949 } 950 } 951 952 $translated = _wp_translate_postdata( false ); 953 if ( is_wp_error( $translated ) ) { 954 return $translated; 955 } 956 $translated = _wp_get_allowed_postdata( $translated ); 957 958 // Create the post. 959 $post_id = wp_insert_post( $translated ); 960 if ( is_wp_error( $post_id ) ) { 961 return $post_id; 962 } 963 964 if ( empty( $post_id ) ) { 965 return 0; 966 } 967 968 add_meta( $post_id ); 969 970 add_post_meta( $post_id, '_edit_last', $GLOBALS['current_user']->ID ); 971 972 // Now that we have an ID we can fix any attachment anchor hrefs. 973 _fix_attachment_links( $post_id ); 974 975 wp_set_post_lock( $post_id ); 976 977 return $post_id; 978 } 979 980 /** 981 * Calls wp_write_post() and handles the errors. 982 * 983 * @since 2.0.0 984 * 985 * @return int Post ID on success. Dies on failure. 986 */ 987 function write_post() { 988 $result = wp_write_post(); 989 if ( is_wp_error( $result ) ) { 990 wp_die( $result->get_error_message() ); 991 } 992 993 return $result; 994 } 995 996 // 997 // Post Meta. 998 // 999 1000 /** 1001 * Adds post meta data defined in the `$_POST` superglobal for a post with given ID. 1002 * 1003 * @since 1.2.0 1004 * 1005 * @param int $post_id 1006 * @return int|bool 1007 */ 1008 function add_meta( $post_id ) { 1009 $post_id = (int) $post_id; 1010 1011 $metakeyselect = isset( $_POST['metakeyselect'] ) ? wp_unslash( trim( $_POST['metakeyselect'] ) ) : ''; 1012 $metakeyinput = isset( $_POST['metakeyinput'] ) ? wp_unslash( trim( $_POST['metakeyinput'] ) ) : ''; 1013 $metavalue = $_POST['metavalue'] ?? ''; 1014 if ( is_string( $metavalue ) ) { 1015 $metavalue = trim( $metavalue ); 1016 } 1017 1018 if ( ( ( '#NONE#' !== $metakeyselect ) && ! empty( $metakeyselect ) ) || ! empty( $metakeyinput ) ) { 1019 /* 1020 * We have a key/value pair. If both the select and the input 1021 * for the key have data, the input takes precedence. 1022 */ 1023 if ( '#NONE#' !== $metakeyselect ) { 1024 $metakey = $metakeyselect; 1025 } 1026 1027 if ( $metakeyinput ) { 1028 $metakey = $metakeyinput; // Default. 1029 } 1030 1031 if ( is_protected_meta( $metakey, 'post' ) || ! current_user_can( 'add_post_meta', $post_id, $metakey ) ) { 1032 return false; 1033 } 1034 1035 $metakey = wp_slash( $metakey ); 1036 1037 return add_post_meta( $post_id, $metakey, $metavalue ); 1038 } 1039 1040 return false; 1041 } 1042 1043 /** 1044 * Deletes post meta data by meta ID. 1045 * 1046 * @since 1.2.0 1047 * 1048 * @param int $mid 1049 * @return bool 1050 */ 1051 function delete_meta( $mid ) { 1052 return delete_metadata_by_mid( 'post', $mid ); 1053 } 1054 1055 /** 1056 * Returns a list of previously defined keys. 1057 * 1058 * @since 1.2.0 1059 * 1060 * @global wpdb $wpdb WordPress database abstraction object. 1061 * 1062 * @return string[] Array of meta key names. 1063 */ 1064 function get_meta_keys() { 1065 global $wpdb; 1066 1067 $keys = $wpdb->get_col( 1068 "SELECT meta_key 1069 FROM $wpdb->postmeta 1070 GROUP BY meta_key 1071 ORDER BY meta_key" 1072 ); 1073 1074 return $keys; 1075 } 1076 1077 /** 1078 * Returns post meta data by meta ID. 1079 * 1080 * @since 2.1.0 1081 * 1082 * @param int $mid 1083 * @return object|bool 1084 */ 1085 function get_post_meta_by_id( $mid ) { 1086 return get_metadata_by_mid( 'post', $mid ); 1087 } 1088 1089 /** 1090 * Returns meta data for the given post ID. 1091 * 1092 * @since 1.2.0 1093 * 1094 * @global wpdb $wpdb WordPress database abstraction object. 1095 * 1096 * @param int $post_id A post ID. 1097 * @return array[] { 1098 * Array of meta data arrays for the given post ID. 1099 * 1100 * @type array ...$0 { 1101 * Associative array of meta data. 1102 * 1103 * @type string $meta_key Meta key. 1104 * @type mixed $meta_value Meta value. 1105 * @type string $meta_id Meta ID as a numeric string. 1106 * @type string $post_id Post ID as a numeric string. 1107 * } 1108 * } 1109 */ 1110 function has_meta( $post_id ) { 1111 global $wpdb; 1112 1113 return $wpdb->get_results( 1114 $wpdb->prepare( 1115 "SELECT meta_key, meta_value, meta_id, post_id 1116 FROM $wpdb->postmeta WHERE post_id = %d 1117 ORDER BY meta_key,meta_id", 1118 $post_id 1119 ), 1120 ARRAY_A 1121 ); 1122 } 1123 1124 /** 1125 * Updates post meta data by meta ID. 1126 * 1127 * @since 1.2.0 1128 * 1129 * @param int $meta_id Meta ID. 1130 * @param string $meta_key Meta key. Expect slashed. 1131 * @param string $meta_value Meta value. Expect slashed. 1132 * @return bool 1133 */ 1134 function update_meta( $meta_id, $meta_key, $meta_value ) { 1135 $meta_key = wp_unslash( $meta_key ); 1136 $meta_value = wp_unslash( $meta_value ); 1137 1138 return update_metadata_by_mid( 'post', $meta_id, $meta_value, $meta_key ); 1139 } 1140 1141 // 1142 // Private. 1143 // 1144 1145 /** 1146 * Replaces hrefs of attachment anchors with up-to-date permalinks. 1147 * 1148 * @since 2.3.0 1149 * @access private 1150 * 1151 * @param int|WP_Post $post Post ID or post object. 1152 * @return void|int|WP_Error Void if nothing fixed. 0 or WP_Error on update failure. The post ID on update success. 1153 */ 1154 function _fix_attachment_links( $post ) { 1155 $post = get_post( $post, ARRAY_A ); 1156 $content = $post['post_content']; 1157 1158 // Don't run if no pretty permalinks or post is not published, scheduled, or privately published. 1159 if ( ! get_option( 'permalink_structure' ) || ! in_array( $post['post_status'], array( 'publish', 'future', 'private' ), true ) ) { 1160 return; 1161 } 1162 1163 // Short if there aren't any links or no '?attachment_id=' strings (strpos cannot be zero). 1164 if ( ! strpos( $content, '?attachment_id=' ) || ! preg_match_all( '/<a ([^>]+)>[\s\S]+?<\/a>/', $content, $link_matches ) ) { 1165 return; 1166 } 1167 1168 $site_url = get_bloginfo( 'url' ); 1169 $site_url = substr( $site_url, (int) strpos( $site_url, '://' ) ); // Remove the http(s). 1170 $replace = ''; 1171 1172 foreach ( $link_matches[1] as $key => $value ) { 1173 if ( ! strpos( $value, '?attachment_id=' ) || ! strpos( $value, 'wp-att-' ) 1174 || ! preg_match( '/href=(["\'])[^"\']*\?attachment_id=(\d+)[^"\']*\\1/', $value, $url_match ) 1175 || ! preg_match( '/rel=["\'][^"\']*wp-att-(\d+)/', $value, $rel_match ) ) { 1176 continue; 1177 } 1178 1179 $quote = $url_match[1]; // The quote (single or double). 1180 $url_id = (int) $url_match[2]; 1181 $rel_id = (int) $rel_match[1]; 1182 1183 if ( ! $url_id || ! $rel_id || $url_id !== $rel_id || ! str_contains( $url_match[0], $site_url ) ) { 1184 continue; 1185 } 1186 1187 $link = $link_matches[0][ $key ]; 1188 $replace = str_replace( $url_match[0], 'href=' . $quote . get_attachment_link( $url_id ) . $quote, $link ); 1189 1190 $content = str_replace( $link, $replace, $content ); 1191 } 1192 1193 if ( $replace ) { 1194 $post['post_content'] = $content; 1195 // Escape data pulled from DB. 1196 $post = add_magic_quotes( $post ); 1197 1198 return wp_update_post( $post ); 1199 } 1200 } 1201 1202 /** 1203 * Returns all the possible statuses for a post type. 1204 * 1205 * @since 2.5.0 1206 * 1207 * @param string $type The post_type you want the statuses for. Default 'post'. 1208 * @return string[] An array of all the statuses for the supplied post type. 1209 */ 1210 function get_available_post_statuses( $type = 'post' ) { 1211 $statuses = wp_count_posts( $type ); 1212 1213 return array_keys( get_object_vars( $statuses ) ); 1214 } 1215 1216 /** 1217 * Runs the query to fetch the posts for listing on the edit posts page. 1218 * 1219 * @since 2.5.0 1220 * 1221 * @param array|false $q Optional. Array of query variables to use to build the query. 1222 * Defaults to the `$_GET` superglobal. 1223 * @return string[] An array of all the statuses for the queried post type. 1224 */ 1225 function wp_edit_posts_query( $q = false ) { 1226 if ( false === $q ) { 1227 $q = $_GET; 1228 } 1229 1230 $q['m'] = isset( $q['m'] ) ? (int) $q['m'] : 0; 1231 $q['cat'] = isset( $q['cat'] ) ? (int) $q['cat'] : 0; 1232 1233 $post_statuses = get_post_stati(); 1234 1235 if ( isset( $q['post_type'] ) && in_array( $q['post_type'], get_post_types(), true ) ) { 1236 $post_type = $q['post_type']; 1237 } else { 1238 $post_type = 'post'; 1239 } 1240 1241 $avail_post_stati = get_available_post_statuses( $post_type ); 1242 $post_status = ''; 1243 $perm = ''; 1244 1245 if ( isset( $q['post_status'] ) && in_array( $q['post_status'], $post_statuses, true ) ) { 1246 $post_status = $q['post_status']; 1247 $perm = 'readable'; 1248 } 1249 1250 $orderby = ''; 1251 1252 if ( isset( $q['orderby'] ) ) { 1253 $orderby = $q['orderby']; 1254 } elseif ( isset( $q['post_status'] ) && in_array( $q['post_status'], array( 'pending', 'draft' ), true ) ) { 1255 $orderby = 'modified'; 1256 } 1257 1258 $order = ''; 1259 1260 if ( isset( $q['order'] ) ) { 1261 $order = $q['order']; 1262 } elseif ( isset( $q['post_status'] ) && 'pending' === $q['post_status'] ) { 1263 $order = 'ASC'; 1264 } 1265 1266 $per_page = "edit_{$post_type}_per_page"; 1267 $posts_per_page = (int) get_user_option( $per_page ); 1268 if ( empty( $posts_per_page ) || $posts_per_page < 1 ) { 1269 $posts_per_page = 20; 1270 } 1271 1272 /** 1273 * Filters the number of items per page to show for a specific 'per_page' type. 1274 * 1275 * The dynamic portion of the hook name, `$post_type`, refers to the post type. 1276 * 1277 * Possible hook names include: 1278 * 1279 * - `edit_post_per_page` 1280 * - `edit_page_per_page` 1281 * - `edit_attachment_per_page` 1282 * 1283 * @since 3.0.0 1284 * 1285 * @param int $posts_per_page Number of posts to display per page for the given post 1286 * type. Default 20. 1287 */ 1288 $posts_per_page = apply_filters( "edit_{$post_type}_per_page", $posts_per_page ); 1289 1290 /** 1291 * Filters the number of posts displayed per page when specifically listing "posts". 1292 * 1293 * @since 2.8.0 1294 * 1295 * @param int $posts_per_page Number of posts to be displayed. Default 20. 1296 * @param string $post_type The post type. 1297 */ 1298 $posts_per_page = apply_filters( 'edit_posts_per_page', $posts_per_page, $post_type ); 1299 1300 $query = compact( 'post_type', 'post_status', 'perm', 'order', 'orderby', 'posts_per_page' ); 1301 1302 // Hierarchical types require special args. 1303 if ( is_post_type_hierarchical( $post_type ) && empty( $orderby ) ) { 1304 $query['orderby'] = 'menu_order title'; 1305 $query['order'] = 'asc'; 1306 $query['posts_per_page'] = -1; 1307 $query['posts_per_archive_page'] = -1; 1308 $query['fields'] = 'id=>parent'; 1309 } 1310 1311 if ( ! empty( $q['show_sticky'] ) ) { 1312 $query['post__in'] = (array) get_option( 'sticky_posts' ); 1313 } 1314 1315 wp( $query ); 1316 1317 return $avail_post_stati; 1318 } 1319 1320 /** 1321 * Returns the query variables for the current attachments request. 1322 * 1323 * @since 4.2.0 1324 * 1325 * @param array|false $q Optional. Array of query variables to use to build the query. 1326 * Defaults to the `$_GET` superglobal. 1327 * @return array The parsed query vars. 1328 */ 1329 function wp_edit_attachments_query_vars( $q = false ) { 1330 if ( false === $q ) { 1331 $q = $_GET; 1332 } 1333 $q['m'] = isset( $q['m'] ) ? (int) $q['m'] : 0; 1334 $q['cat'] = isset( $q['cat'] ) ? (int) $q['cat'] : 0; 1335 $q['post_type'] = 'attachment'; 1336 $post_type = get_post_type_object( 'attachment' ); 1337 $states = 'inherit'; 1338 if ( current_user_can( $post_type->cap->read_private_posts ) ) { 1339 $states .= ',private'; 1340 } 1341 1342 $q['post_status'] = isset( $q['status'] ) && 'trash' === $q['status'] ? 'trash' : $states; 1343 $q['post_status'] = isset( $q['attachment-filter'] ) && 'trash' === $q['attachment-filter'] ? 'trash' : $states; 1344 1345 $media_per_page = (int) get_user_option( 'upload_per_page' ); 1346 if ( empty( $media_per_page ) || $media_per_page < 1 ) { 1347 $media_per_page = 20; 1348 } 1349 1350 /** 1351 * Filters the number of items to list per page when listing media items. 1352 * 1353 * @since 2.9.0 1354 * 1355 * @param int $media_per_page Number of media to list. Default 20. 1356 */ 1357 $q['posts_per_page'] = apply_filters( 'upload_per_page', $media_per_page ); 1358 1359 $post_mime_types = get_post_mime_types(); 1360 if ( isset( $q['post_mime_type'] ) && ! array_intersect( (array) $q['post_mime_type'], array_keys( $post_mime_types ) ) ) { 1361 unset( $q['post_mime_type'] ); 1362 } 1363 1364 foreach ( array_keys( $post_mime_types ) as $type ) { 1365 if ( isset( $q['attachment-filter'] ) && "post_mime_type:$type" === $q['attachment-filter'] ) { 1366 $q['post_mime_type'] = $type; 1367 break; 1368 } 1369 } 1370 1371 if ( isset( $q['detached'] ) || ( isset( $q['attachment-filter'] ) && 'detached' === $q['attachment-filter'] ) ) { 1372 $q['post_parent'] = 0; 1373 } 1374 1375 if ( isset( $q['mine'] ) || ( isset( $q['attachment-filter'] ) && 'mine' === $q['attachment-filter'] ) ) { 1376 $q['author'] = get_current_user_id(); 1377 } 1378 1379 // Filter query clauses to include filenames. 1380 if ( isset( $q['s'] ) ) { 1381 add_filter( 'wp_allow_query_attachment_by_filename', '__return_true' ); 1382 } 1383 1384 return $q; 1385 } 1386 1387 /** 1388 * Executes a query for attachments. An array of WP_Query arguments 1389 * can be passed in, which will override the arguments set by this function. 1390 * 1391 * @since 2.5.0 1392 * 1393 * @param array|false $q Optional. Array of query variables to use to build the query. 1394 * Defaults to the `$_GET` superglobal. 1395 * @return array { 1396 * Array containing the post mime types and available post mime types. 1397 * 1398 * @type array[] $post_mime_types Post mime types. 1399 * @type string[] $avail_post_mime_types Available post mime types. 1400 * } 1401 */ 1402 function wp_edit_attachments_query( $q = false ) { 1403 wp( wp_edit_attachments_query_vars( $q ) ); 1404 1405 $post_mime_types = get_post_mime_types(); 1406 $avail_post_mime_types = get_available_post_mime_types( 'attachment' ); 1407 1408 return array( $post_mime_types, $avail_post_mime_types ); 1409 } 1410 1411 /** 1412 * Returns the list of classes to be used by a meta box. 1413 * 1414 * @since 2.5.0 1415 * 1416 * @param string $box_id Meta box ID (used in the 'id' attribute for the meta box). 1417 * @param string $screen_id The screen on which the meta box is shown. 1418 * @return string Space-separated string of class names. 1419 */ 1420 function postbox_classes( $box_id, $screen_id ) { 1421 if ( isset( $_GET['edit'] ) && $_GET['edit'] === $box_id ) { 1422 $classes = array( '' ); 1423 } elseif ( get_user_option( 'closedpostboxes_' . $screen_id ) ) { 1424 $closed = get_user_option( 'closedpostboxes_' . $screen_id ); 1425 if ( ! is_array( $closed ) ) { 1426 $classes = array( '' ); 1427 } else { 1428 $classes = in_array( $box_id, $closed, true ) ? array( 'closed' ) : array( '' ); 1429 } 1430 } else { 1431 $classes = array( '' ); 1432 } 1433 1434 /** 1435 * Filters the postbox classes for a specific screen and box ID combo. 1436 * 1437 * The dynamic portions of the hook name, `$screen_id` and `$box_id`, refer to 1438 * the screen ID and meta box ID, respectively. 1439 * 1440 * @since 3.2.0 1441 * 1442 * @param string[] $classes An array of postbox classes. 1443 */ 1444 $classes = apply_filters( "postbox_classes_{$screen_id}_{$box_id}", $classes ); 1445 1446 return implode( ' ', $classes ); 1447 } 1448 1449 /** 1450 * Returns a sample permalink based on the post name. 1451 * 1452 * @since 2.5.0 1453 * 1454 * @param int|WP_Post $post Post ID or post object. 1455 * @param string|null $title Optional. Title to override the post's current title 1456 * when generating the post name. Default null. 1457 * @param string|null $name Optional. Name to override the post name. Default null. 1458 * @return array { 1459 * Array containing the sample permalink with placeholder for the post name, and the post name. 1460 * 1461 * @type string $0 The permalink with placeholder for the post name. 1462 * @type string $1 The post name. 1463 * } 1464 */ 1465 function get_sample_permalink( $post, $title = null, $name = null ) { 1466 $post = get_post( $post ); 1467 1468 if ( ! $post ) { 1469 return array( '', '' ); 1470 } 1471 1472 $ptype = get_post_type_object( $post->post_type ); 1473 1474 $original_status = $post->post_status; 1475 $original_date = $post->post_date; 1476 $original_name = $post->post_name; 1477 $original_filter = $post->filter; 1478 1479 // Hack: get_permalink() would return plain permalink for drafts, so we will fake that our post is published. 1480 if ( in_array( $post->post_status, array( 'auto-draft', 'draft', 'pending', 'future' ), true ) ) { 1481 $post->post_status = 'publish'; 1482 $post->post_name = sanitize_title( $post->post_name ? $post->post_name : $post->post_title, $post->ID ); 1483 } 1484 1485 /* 1486 * If the user wants to set a new name -- override the current one. 1487 * Note: if empty name is supplied -- use the title instead, see #6072. 1488 */ 1489 if ( ! is_null( $name ) ) { 1490 $post->post_name = sanitize_title( $name ? $name : $title, $post->ID ); 1491 } 1492 1493 $post->post_name = wp_unique_post_slug( $post->post_name, $post->ID, $post->post_status, $post->post_type, $post->post_parent ); 1494 1495 $post->filter = 'sample'; 1496 1497 $permalink = get_permalink( $post, true ); 1498 1499 // Replace custom post_type token with generic pagename token for ease of use. 1500 $permalink = str_replace( "%$post->post_type%", '%pagename%', $permalink ); 1501 1502 // Handle page hierarchy. 1503 if ( $ptype->hierarchical ) { 1504 $uri = get_page_uri( $post ); 1505 if ( $uri ) { 1506 $uri = untrailingslashit( $uri ); 1507 $uri = strrev( stristr( strrev( $uri ), '/' ) ); 1508 $uri = untrailingslashit( $uri ); 1509 } 1510 1511 /** This filter is documented in wp-admin/edit-tag-form.php */ 1512 $uri = apply_filters( 'editable_slug', $uri, $post ); 1513 if ( ! empty( $uri ) ) { 1514 $uri .= '/'; 1515 } 1516 $permalink = str_replace( '%pagename%', "{$uri}%pagename%", $permalink ); 1517 } 1518 1519 /** This filter is documented in wp-admin/edit-tag-form.php */ 1520 $permalink = array( $permalink, apply_filters( 'editable_slug', $post->post_name, $post ) ); 1521 $post->post_status = $original_status; 1522 $post->post_date = $original_date; 1523 $post->post_name = $original_name; 1524 $post->filter = $original_filter; 1525 1526 /** 1527 * Filters the sample permalink. 1528 * 1529 * @since 4.4.0 1530 * 1531 * @param array $permalink { 1532 * Array containing the sample permalink with placeholder for the post name, and the post name. 1533 * 1534 * @type string $0 The permalink with placeholder for the post name. 1535 * @type string $1 The post name. 1536 * } 1537 * @param int $post_id Post ID. 1538 * @param string $title Post title. 1539 * @param string $name Post name (slug). 1540 * @param WP_Post $post Post object. 1541 */ 1542 return apply_filters( 'get_sample_permalink', $permalink, $post->ID, $title, $name, $post ); 1543 } 1544 1545 /** 1546 * Returns the HTML of the sample permalink slug editor. 1547 * 1548 * @since 2.5.0 1549 * 1550 * @param int|WP_Post $post Post ID or post object. 1551 * @param string|null $new_title Optional. New title. Default null. 1552 * @param string|null $new_slug Optional. New slug. Default null. 1553 * @return string The HTML of the sample permalink slug editor. 1554 */ 1555 function get_sample_permalink_html( $post, $new_title = null, $new_slug = null ) { 1556 $post = get_post( $post ); 1557 1558 if ( ! $post ) { 1559 return ''; 1560 } 1561 1562 list($permalink, $post_name) = get_sample_permalink( $post->ID, $new_title, $new_slug ); 1563 1564 $view_link = false; 1565 $preview_target = ''; 1566 1567 if ( current_user_can( 'read_post', $post->ID ) ) { 1568 if ( 'draft' === $post->post_status || empty( $post->post_name ) ) { 1569 $view_link = get_preview_post_link( $post ); 1570 $preview_target = " target='wp-preview-{$post->ID}'"; 1571 } else { 1572 if ( 'publish' === $post->post_status || 'attachment' === $post->post_type ) { 1573 $view_link = get_permalink( $post ); 1574 } else { 1575 // Allow non-published (private, future) to be viewed at a pretty permalink, in case $post->post_name is set. 1576 $view_link = str_replace( array( '%pagename%', '%postname%' ), $post->post_name, $permalink ); 1577 } 1578 } 1579 } 1580 1581 // Permalinks without a post/page name placeholder don't have anything to edit. 1582 if ( ! str_contains( $permalink, '%postname%' ) && ! str_contains( $permalink, '%pagename%' ) ) { 1583 $return = '<strong>' . __( 'Permalink:' ) . "</strong>\n"; 1584 1585 if ( false !== $view_link ) { 1586 $display_link = urldecode( $view_link ); 1587 $return .= '<a id="sample-permalink" href="' . esc_url( $view_link ) . '"' . $preview_target . '>' . esc_html( $display_link ) . "</a>\n"; 1588 } else { 1589 $return .= '<span id="sample-permalink">' . $permalink . "</span>\n"; 1590 } 1591 1592 // Encourage a pretty permalink setting. 1593 if ( ! get_option( 'permalink_structure' ) && current_user_can( 'manage_options' ) 1594 && ! ( 'page' === get_option( 'show_on_front' ) && (int) get_option( 'page_on_front' ) === $post->ID ) 1595 ) { 1596 $return .= '<span id="change-permalinks"><a href="options-permalink.php" class="button button-small">' . __( 'Change Permalink Structure' ) . "</a></span>\n"; 1597 } 1598 } else { 1599 if ( mb_strlen( $post_name ) > 34 ) { 1600 $post_name_abridged = mb_substr( $post_name, 0, 16 ) . '…' . mb_substr( $post_name, -16 ); 1601 } else { 1602 $post_name_abridged = $post_name; 1603 } 1604 1605 $post_name_html = '<span id="editable-post-name">' . esc_html( $post_name_abridged ) . '</span>'; 1606 $display_link = str_replace( array( '%pagename%', '%postname%' ), $post_name_html, esc_html( urldecode( $permalink ) ) ); 1607 1608 $return = '<strong>' . __( 'Permalink:' ) . "</strong>\n"; 1609 $return .= '<span id="sample-permalink"><a href="' . esc_url( $view_link ) . '"' . $preview_target . '>' . $display_link . "</a></span>\n"; 1610 $return .= '‎'; // Fix bi-directional text display defect in RTL languages. 1611 $return .= '<span id="edit-slug-buttons"><button type="button" class="edit-slug button button-small hide-if-no-js" aria-label="' . __( 'Edit permalink' ) . '">' . __( 'Edit' ) . "</button></span>\n"; 1612 $return .= '<span id="editable-post-name-full">' . esc_html( $post_name ) . "</span>\n"; 1613 } 1614 1615 /** 1616 * Filters the sample permalink HTML markup. 1617 * 1618 * @since 2.9.0 1619 * @since 4.4.0 Added `$post` parameter. 1620 * 1621 * @param string $return Sample permalink HTML markup. 1622 * @param int $post_id Post ID. 1623 * @param string|null $new_title New sample permalink title. 1624 * @param string|null $new_slug New sample permalink slug. 1625 * @param WP_Post $post Post object. 1626 */ 1627 $return = apply_filters( 'get_sample_permalink_html', $return, $post->ID, $new_title, $new_slug, $post ); 1628 1629 return $return; 1630 } 1631 1632 /** 1633 * Returns HTML for the post thumbnail meta box. 1634 * 1635 * @since 2.9.0 1636 * 1637 * @param int|null $thumbnail_id Optional. Thumbnail attachment ID. Default null. 1638 * @param int|WP_Post|null $post Optional. The post ID or object associated 1639 * with the thumbnail. Defaults to global $post. 1640 * @return string The post thumbnail HTML. 1641 */ 1642 function _wp_post_thumbnail_html( $thumbnail_id = null, $post = null ) { 1643 $_wp_additional_image_sizes = wp_get_additional_image_sizes(); 1644 1645 $post = get_post( $post ); 1646 $post_type_object = get_post_type_object( $post->post_type ); 1647 $set_thumbnail_link = '<p class="hide-if-no-js"><a href="%s" id="set-post-thumbnail"%s class="thickbox" role="button" aria-haspopup="dialog" aria-controls="wp-media-modal">%s</a></p>'; 1648 $upload_iframe_src = get_upload_iframe_src( 'image', $post->ID ); 1649 1650 $content = sprintf( 1651 $set_thumbnail_link, 1652 esc_url( $upload_iframe_src ), 1653 '', // Empty when there's no featured image set, `aria-describedby` attribute otherwise. 1654 esc_html( $post_type_object->labels->set_featured_image ) 1655 ); 1656 1657 if ( $thumbnail_id && get_post( $thumbnail_id ) ) { 1658 $size = isset( $_wp_additional_image_sizes['post-thumbnail'] ) ? 'post-thumbnail' : array( 266, 266 ); 1659 1660 /** 1661 * Filters the size used to display the post thumbnail image in the 'Featured image' meta box. 1662 * 1663 * Note: When a theme adds 'post-thumbnail' support, a special 'post-thumbnail' 1664 * image size is registered, which differs from the 'thumbnail' image size 1665 * managed via the Settings > Media screen. 1666 * 1667 * @since 4.4.0 1668 * 1669 * @param string|int[] $size Requested image size. Can be any registered image size name, or 1670 * an array of width and height values in pixels (in that order). 1671 * @param int $thumbnail_id Post thumbnail attachment ID. 1672 * @param WP_Post $post The post object associated with the thumbnail. 1673 */ 1674 $size = apply_filters( 'admin_post_thumbnail_size', $size, $thumbnail_id, $post ); 1675 1676 $thumbnail_html = wp_get_attachment_image( $thumbnail_id, $size ); 1677 1678 if ( ! empty( $thumbnail_html ) ) { 1679 $content = sprintf( 1680 $set_thumbnail_link, 1681 esc_url( $upload_iframe_src ), 1682 ' aria-describedby="set-post-thumbnail-desc"', 1683 $thumbnail_html 1684 ); 1685 $content .= '<p class="hide-if-no-js howto" id="set-post-thumbnail-desc">' . __( 'Click the image to edit or update' ) . '</p>'; 1686 $content .= '<p class="hide-if-no-js"><a href="#" id="remove-post-thumbnail" role="button">' . esc_html( $post_type_object->labels->remove_featured_image ) . '</a></p>'; 1687 } 1688 } 1689 1690 $content .= '<input type="hidden" id="_thumbnail_id" name="_thumbnail_id" value="' . esc_attr( $thumbnail_id ? $thumbnail_id : '-1' ) . '" />'; 1691 1692 /** 1693 * Filters the admin post thumbnail HTML markup to return. 1694 * 1695 * @since 2.9.0 1696 * @since 3.5.0 Added the `$post_id` parameter. 1697 * @since 4.6.0 Added the `$thumbnail_id` parameter. 1698 * 1699 * @param string $content Admin post thumbnail HTML markup. 1700 * @param int $post_id Post ID. 1701 * @param int|null $thumbnail_id Thumbnail attachment ID, or null if there isn't one. 1702 */ 1703 return apply_filters( 'admin_post_thumbnail_html', $content, $post->ID, $thumbnail_id ); 1704 } 1705 1706 /** 1707 * Determines whether the post is currently being edited by another user. 1708 * 1709 * @since 2.5.0 1710 * 1711 * @param int|WP_Post $post ID or object of the post to check for editing. 1712 * @return int|false ID of the user with lock. False if the post does not exist, post is not locked, 1713 * the user with lock does not exist, or the post is locked by current user. 1714 */ 1715 function wp_check_post_lock( $post ) { 1716 $post = get_post( $post ); 1717 1718 if ( ! $post ) { 1719 return false; 1720 } 1721 1722 $lock = get_post_meta( $post->ID, '_edit_lock', true ); 1723 1724 if ( ! $lock ) { 1725 return false; 1726 } 1727 1728 $lock = explode( ':', $lock ); 1729 $time = $lock[0]; 1730 $user = isset( $lock[1] ) ? (int) $lock[1] : (int) get_post_meta( $post->ID, '_edit_last', true ); 1731 1732 if ( ! get_userdata( $user ) ) { 1733 return false; 1734 } 1735 1736 /** This filter is documented in wp-admin/includes/ajax-actions.php */ 1737 $time_window = apply_filters( 'wp_check_post_lock_window', 150 ); 1738 1739 if ( $time && $time > time() - $time_window && get_current_user_id() !== $user ) { 1740 return $user; 1741 } 1742 1743 return false; 1744 } 1745 1746 /** 1747 * Marks the post as currently being edited by the current user. 1748 * 1749 * @since 2.5.0 1750 * 1751 * @param int|WP_Post $post ID or object of the post being edited. 1752 * @return array|false { 1753 * Array of the lock time and user ID. False if the post does not exist, or there 1754 * is no current user. 1755 * 1756 * @type int $0 The current time as a Unix timestamp. 1757 * @type int $1 The ID of the current user. 1758 * } 1759 */ 1760 function wp_set_post_lock( $post ) { 1761 $post = get_post( $post ); 1762 1763 if ( ! $post ) { 1764 return false; 1765 } 1766 1767 $user_id = get_current_user_id(); 1768 1769 if ( 0 === $user_id ) { 1770 return false; 1771 } 1772 1773 $now = time(); 1774 $lock = "$now:$user_id"; 1775 1776 update_post_meta( $post->ID, '_edit_lock', $lock ); 1777 1778 return array( $now, $user_id ); 1779 } 1780 1781 /** 1782 * Outputs the HTML for the notice to say that someone else is editing or has taken over editing of this post. 1783 * 1784 * @since 2.8.5 1785 */ 1786 function _admin_notice_post_locked() { 1787 $post = get_post(); 1788 1789 if ( ! $post ) { 1790 return; 1791 } 1792 1793 $user = null; 1794 $user_id = wp_check_post_lock( $post->ID ); 1795 1796 if ( $user_id ) { 1797 $user = get_userdata( $user_id ); 1798 } 1799 1800 if ( $user ) { 1801 /** 1802 * Filters whether to show the post locked dialog. 1803 * 1804 * Returning false from the filter will prevent the dialog from being displayed. 1805 * 1806 * @since 3.6.0 1807 * 1808 * @param bool $display Whether to display the dialog. Default true. 1809 * @param WP_Post $post Post object. 1810 * @param WP_User $user The user with the lock for the post. 1811 */ 1812 if ( ! apply_filters( 'show_post_locked_dialog', true, $post, $user ) ) { 1813 return; 1814 } 1815 1816 $locked = true; 1817 } else { 1818 $locked = false; 1819 } 1820 1821 $sendback = wp_get_referer(); 1822 $sendback_text = __( 'Go back' ); 1823 1824 if ( ! $locked || ! $sendback || str_contains( $sendback, 'post.php' ) || str_contains( $sendback, 'post-new.php' ) ) { 1825 $sendback = admin_url( 'edit.php' ); 1826 1827 if ( 'post' !== $post->post_type ) { 1828 $sendback = add_query_arg( 'post_type', $post->post_type, $sendback ); 1829 } 1830 1831 $post_type_object = get_post_type_object( $post->post_type ); 1832 1833 if ( $post_type_object ) { 1834 $sendback_text = $post_type_object->labels->all_items; 1835 } 1836 } 1837 1838 $hidden = $locked ? '' : ' hidden'; 1839 1840 ?> 1841 <div id="post-lock-dialog" class="notification-dialog-wrap<?php echo $hidden; ?>"> 1842 <div class="notification-dialog-background"></div> 1843 <div class="notification-dialog"> 1844 <?php 1845 1846 if ( $locked ) { 1847 $query_args = array(); 1848 if ( get_post_type_object( $post->post_type )->public ) { 1849 if ( 'publish' === $post->post_status || $user->ID !== (int) $post->post_author ) { 1850 // Latest content is in autosave. 1851 $nonce = wp_create_nonce( 'post_preview_' . $post->ID ); 1852 $query_args['preview_id'] = $post->ID; 1853 $query_args['preview_nonce'] = $nonce; 1854 } 1855 } 1856 1857 $preview_link = get_preview_post_link( $post->ID, $query_args ); 1858 1859 /** 1860 * Filters whether to allow the post lock to be overridden. 1861 * 1862 * Returning false from the filter will disable the ability 1863 * to override the post lock. 1864 * 1865 * @since 3.6.0 1866 * 1867 * @param bool $override Whether to allow the post lock to be overridden. Default true. 1868 * @param WP_Post $post Post object. 1869 * @param WP_User $user The user with the lock for the post. 1870 */ 1871 $override = apply_filters( 'override_post_lock', true, $post, $user ); 1872 $tab_last = $override ? '' : ' wp-tab-last'; 1873 1874 ?> 1875 <div class="post-locked-message"> 1876 <div class="post-locked-avatar"><?php echo get_avatar( $user->ID, 64 ); ?></div> 1877 <p class="currently-editing wp-tab-first" tabindex="0"> 1878 <?php 1879 if ( $override ) { 1880 /* translators: %s: User's display name. */ 1881 printf( __( '%s is currently editing this post. Do you want to take over?' ), esc_html( $user->display_name ) ); 1882 } else { 1883 /* translators: %s: User's display name. */ 1884 printf( __( '%s is currently editing this post.' ), esc_html( $user->display_name ) ); 1885 } 1886 ?> 1887 </p> 1888 <?php 1889 /** 1890 * Fires inside the post locked dialog before the buttons are displayed. 1891 * 1892 * @since 3.6.0 1893 * @since 5.4.0 The `$user` parameter was added. 1894 * 1895 * @param WP_Post $post Post object. 1896 * @param WP_User $user The user with the lock for the post. 1897 */ 1898 do_action( 'post_locked_dialog', $post, $user ); 1899 ?> 1900 <p> 1901 <a class="button" href="<?php echo esc_url( $sendback ); ?>"><?php echo $sendback_text; ?></a> 1902 <?php if ( $preview_link ) { ?> 1903 <a class="button<?php echo $tab_last; ?>" href="<?php echo esc_url( $preview_link ); ?>"><?php _e( 'Preview' ); ?></a> 1904 <?php 1905 } 1906 1907 // Allow plugins to prevent some users overriding the post lock. 1908 if ( $override ) { 1909 ?> 1910 <a class="button button-primary wp-tab-last" href="<?php echo esc_url( add_query_arg( 'get-post-lock', '1', wp_nonce_url( get_edit_post_link( $post->ID, 'url' ), 'lock-post_' . $post->ID ) ) ); ?>"><?php _e( 'Take over' ); ?></a> 1911 <?php 1912 } 1913 1914 ?> 1915 </p> 1916 </div> 1917 <?php 1918 } else { 1919 ?> 1920 <div class="post-taken-over"> 1921 <div class="post-locked-avatar"></div> 1922 <p class="wp-tab-first" tabindex="0"> 1923 <span class="currently-editing"></span><br /> 1924 <span class="locked-saving hidden"><img src="<?php echo esc_url( admin_url( 'images/spinner-2x.gif' ) ); ?>" width="16" height="16" alt="" /> <?php _e( 'Saving revision…' ); ?></span> 1925 <span class="locked-saved hidden"><?php _e( 'Your latest changes were saved as a revision.' ); ?></span> 1926 </p> 1927 <?php 1928 /** 1929 * Fires inside the dialog displayed when a user has lost the post lock. 1930 * 1931 * @since 3.6.0 1932 * 1933 * @param WP_Post $post Post object. 1934 */ 1935 do_action( 'post_lock_lost_dialog', $post ); 1936 ?> 1937 <p><a class="button button-primary wp-tab-last" href="<?php echo esc_url( $sendback ); ?>"><?php echo $sendback_text; ?></a></p> 1938 </div> 1939 <?php 1940 } 1941 1942 ?> 1943 </div> 1944 </div> 1945 <?php 1946 } 1947 1948 /** 1949 * Creates autosave data for the specified post from `$_POST` data. 1950 * 1951 * @since 2.6.0 1952 * 1953 * @param array|int $post_data Associative array containing the post data, or integer post ID. 1954 * If a numeric post ID is provided, will use the `$_POST` superglobal. 1955 * @return int|WP_Error The autosave revision ID. WP_Error or 0 on error. 1956 */ 1957 function wp_create_post_autosave( $post_data ) { 1958 if ( is_numeric( $post_data ) ) { 1959 $post_id = $post_data; 1960 $post_data = $_POST; 1961 } else { 1962 $post_id = (int) $post_data['post_ID']; 1963 } 1964 1965 $post_data = _wp_translate_postdata( true, $post_data ); 1966 if ( is_wp_error( $post_data ) ) { 1967 return $post_data; 1968 } 1969 $post_data = _wp_get_allowed_postdata( $post_data ); 1970 1971 $post_author = get_current_user_id(); 1972 1973 // Store one autosave per author. If there is already an autosave, overwrite it. 1974 $old_autosave = wp_get_post_autosave( $post_id, $post_author ); 1975 if ( $old_autosave ) { 1976 $new_autosave = _wp_post_revision_data( $post_data, true ); 1977 $new_autosave['ID'] = $old_autosave->ID; 1978 $new_autosave['post_author'] = $post_author; 1979 1980 $post = get_post( $post_id ); 1981 1982 // If the new autosave has the same content as the post, delete the autosave. 1983 $autosave_is_different = false; 1984 foreach ( array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ) as $field ) { 1985 if ( normalize_whitespace( $new_autosave[ $field ] ) !== normalize_whitespace( $post->$field ) ) { 1986 $autosave_is_different = true; 1987 break; 1988 } 1989 } 1990 1991 if ( ! $autosave_is_different ) { 1992 wp_delete_post_revision( $old_autosave->ID ); 1993 return 0; 1994 } 1995 1996 /** 1997 * Fires before an autosave is stored. 1998 * 1999 * @since 4.1.0 2000 * @since 6.4.0 The `$is_update` parameter was added to indicate if the autosave is being updated or was newly created. 2001 * 2002 * @param array $new_autosave Post array - the autosave that is about to be saved. 2003 * @param bool $is_update Whether this is an existing autosave. 2004 */ 2005 do_action( 'wp_creating_autosave', $new_autosave, true ); 2006 return wp_update_post( $new_autosave ); 2007 } 2008 2009 // _wp_put_post_revision() expects unescaped. 2010 $post_data = wp_unslash( $post_data ); 2011 2012 // Otherwise create the new autosave as a special post revision. 2013 $revision = _wp_put_post_revision( $post_data, true ); 2014 2015 if ( ! is_wp_error( $revision ) && 0 !== $revision ) { 2016 2017 /** This action is documented in wp-admin/includes/post.php */ 2018 do_action( 'wp_creating_autosave', get_post( $revision, ARRAY_A ), false ); 2019 } 2020 2021 return $revision; 2022 } 2023 2024 /** 2025 * Autosaves the revisioned meta fields. 2026 * 2027 * Iterates through the revisioned meta fields and checks each to see if they are set, 2028 * and have a changed value. If so, the meta value is saved and attached to the autosave. 2029 * 2030 * @since 6.4.0 2031 * 2032 * @param array $new_autosave The new post data being autosaved. 2033 */ 2034 function wp_autosave_post_revisioned_meta_fields( $new_autosave ) { 2035 /* 2036 * The post data arrives as either $_POST['data']['wp_autosave'] or the $_POST 2037 * itself. This sets $posted_data to the correct variable. 2038 * 2039 * Ignoring sanitization to avoid altering meta. Ignoring the nonce check because 2040 * this is hooked on inner core hooks where a valid nonce was already checked. 2041 */ 2042 $posted_data = $_POST['data']['wp_autosave'] ?? $_POST; 2043 2044 $post_type = get_post_type( $new_autosave['post_parent'] ); 2045 2046 /* 2047 * Go through the revisioned meta keys and save them as part of the autosave, 2048 * if the meta key is part of the posted data, the meta value is not blank, 2049 * and the meta value has changes from the last autosaved value. 2050 */ 2051 foreach ( wp_post_revision_meta_keys( $post_type ) as $meta_key ) { 2052 2053 if ( isset( $posted_data[ $meta_key ] ) 2054 && get_post_meta( $new_autosave['ID'], $meta_key, true ) !== wp_unslash( $posted_data[ $meta_key ] ) 2055 ) { 2056 /* 2057 * Use the underlying delete_metadata() and add_metadata() functions 2058 * vs delete_post_meta() and add_post_meta() to make sure we're working 2059 * with the actual revision meta. 2060 */ 2061 delete_metadata( 'post', $new_autosave['ID'], $meta_key ); 2062 2063 // One last check to ensure meta value is not empty. 2064 if ( ! empty( $posted_data[ $meta_key ] ) ) { 2065 // Add the revisions meta data to the autosave. 2066 add_metadata( 'post', $new_autosave['ID'], $meta_key, $posted_data[ $meta_key ] ); 2067 } 2068 } 2069 } 2070 } 2071 2072 /** 2073 * Saves a draft or manually autosaves for the purpose of showing a post preview. 2074 * 2075 * @since 2.7.0 2076 * 2077 * @return string URL to redirect to show the preview. 2078 */ 2079 function post_preview() { 2080 2081 $post_id = (int) $_POST['post_ID']; 2082 $_POST['ID'] = $post_id; 2083 2084 $post = get_post( $post_id ); 2085 2086 if ( ! $post ) { 2087 wp_die( __( 'Sorry, you are not allowed to edit this post.' ) ); 2088 } 2089 2090 if ( ! current_user_can( 'edit_post', $post->ID ) ) { 2091 wp_die( __( 'Sorry, you are not allowed to edit this post.' ) ); 2092 } 2093 2094 $is_autosave = false; 2095 2096 if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() === (int) $post->post_author 2097 && ( 'draft' === $post->post_status || 'auto-draft' === $post->post_status ) 2098 ) { 2099 $saved_post_id = edit_post(); 2100 } else { 2101 $is_autosave = true; 2102 2103 if ( isset( $_POST['post_status'] ) && 'auto-draft' === $_POST['post_status'] ) { 2104 $_POST['post_status'] = 'draft'; 2105 } 2106 2107 $saved_post_id = wp_create_post_autosave( $post->ID ); 2108 } 2109 2110 if ( is_wp_error( $saved_post_id ) ) { 2111 wp_die( $saved_post_id->get_error_message() ); 2112 } 2113 2114 $query_args = array(); 2115 2116 if ( $is_autosave && $saved_post_id ) { 2117 $query_args['preview_id'] = $post->ID; 2118 $query_args['preview_nonce'] = wp_create_nonce( 'post_preview_' . $post->ID ); 2119 2120 if ( isset( $_POST['post_format'] ) ) { 2121 $query_args['post_format'] = empty( $_POST['post_format'] ) ? 'standard' : sanitize_key( $_POST['post_format'] ); 2122 } 2123 2124 if ( isset( $_POST['_thumbnail_id'] ) ) { 2125 $query_args['_thumbnail_id'] = ( (int) $_POST['_thumbnail_id'] <= 0 ) ? '-1' : (int) $_POST['_thumbnail_id']; 2126 } 2127 } 2128 2129 return get_preview_post_link( $post, $query_args ); 2130 } 2131 2132 /** 2133 * Saves a post submitted with XHR. 2134 * 2135 * Intended for use with heartbeat and autosave.js 2136 * 2137 * @since 3.9.0 2138 * 2139 * @param array $post_data Associative array of the submitted post data. 2140 * @return mixed The value 0 or WP_Error on failure. The saved post ID on success. 2141 * The ID can be the draft post_id or the autosave revision post_id. 2142 */ 2143 function wp_autosave( $post_data ) { 2144 // Back-compat. 2145 if ( ! defined( 'DOING_AUTOSAVE' ) ) { 2146 define( 'DOING_AUTOSAVE', true ); 2147 } 2148 2149 $post_id = (int) $post_data['post_id']; 2150 $post_data['ID'] = $post_id; 2151 $post_data['post_ID'] = $post_id; 2152 2153 if ( false === wp_verify_nonce( $post_data['_wpnonce'], 'update-post_' . $post_id ) ) { 2154 return new WP_Error( 'invalid_nonce', __( 'Error while saving.' ) ); 2155 } 2156 2157 $post = get_post( $post_id ); 2158 2159 if ( ! current_user_can( 'edit_post', $post->ID ) ) { 2160 return new WP_Error( 'edit_posts', __( 'Sorry, you are not allowed to edit this item.' ) ); 2161 } 2162 2163 if ( 'auto-draft' === $post->post_status ) { 2164 $post_data['post_status'] = 'draft'; 2165 } 2166 2167 if ( 'page' !== $post_data['post_type'] && ! empty( $post_data['catslist'] ) ) { 2168 $post_data['post_category'] = explode( ',', $post_data['catslist'] ); 2169 } 2170 2171 if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() === (int) $post->post_author 2172 && ( 'auto-draft' === $post->post_status || 'draft' === $post->post_status ) 2173 ) { 2174 // Drafts and auto-drafts are just overwritten by autosave for the same user if the post is not locked. 2175 return edit_post( wp_slash( $post_data ) ); 2176 } else { 2177 /* 2178 * Non-drafts or other users' drafts are not overwritten. 2179 * The autosave is stored in a special post revision for each user. 2180 */ 2181 return wp_create_post_autosave( wp_slash( $post_data ) ); 2182 } 2183 } 2184 2185 /** 2186 * Redirects to previous page. 2187 * 2188 * @since 2.7.0 2189 * 2190 * @param int $post_id Optional. Post ID. 2191 */ 2192 function redirect_post( $post_id = 0 ) { 2193 if ( isset( $_POST['save'] ) || isset( $_POST['publish'] ) ) { 2194 $status = get_post_status( $post_id ); 2195 2196 switch ( $status ) { 2197 case 'pending': 2198 $message = 8; 2199 break; 2200 case 'future': 2201 $message = 9; 2202 break; 2203 case 'draft': 2204 $message = 10; 2205 break; 2206 default: 2207 $message = isset( $_POST['publish'] ) ? 6 : 1; 2208 break; 2209 } 2210 2211 $location = add_query_arg( 'message', $message, get_edit_post_link( $post_id, 'url' ) ); 2212 } elseif ( isset( $_POST['addmeta'] ) && $_POST['addmeta'] ) { 2213 $location = add_query_arg( 'message', 2, wp_get_referer() ); 2214 $location = explode( '#', $location ); 2215 $location = $location[0] . '#postcustom'; 2216 } elseif ( isset( $_POST['deletemeta'] ) && $_POST['deletemeta'] ) { 2217 $location = add_query_arg( 'message', 3, wp_get_referer() ); 2218 $location = explode( '#', $location ); 2219 $location = $location[0] . '#postcustom'; 2220 } else { 2221 $location = add_query_arg( 'message', 4, get_edit_post_link( $post_id, 'url' ) ); 2222 } 2223 2224 /** 2225 * Filters the post redirect destination URL. 2226 * 2227 * @since 2.9.0 2228 * 2229 * @param string $location The destination URL. 2230 * @param int $post_id The post ID. 2231 */ 2232 wp_redirect( apply_filters( 'redirect_post_location', $location, $post_id ) ); 2233 exit; 2234 } 2235 2236 /** 2237 * Sanitizes POST values from a checkbox taxonomy metabox. 2238 * 2239 * @since 5.1.0 2240 * 2241 * @param string $taxonomy The taxonomy name. 2242 * @param array $terms Raw term data from the 'tax_input' field. 2243 * @return int[] Array of sanitized term IDs. 2244 */ 2245 function taxonomy_meta_box_sanitize_cb_checkboxes( $taxonomy, $terms ) { 2246 return array_map( 'intval', $terms ); 2247 } 2248 2249 /** 2250 * Sanitizes POST values from an input taxonomy metabox. 2251 * 2252 * @since 5.1.0 2253 * 2254 * @param string $taxonomy The taxonomy name. 2255 * @param array|string $terms Raw term data from the 'tax_input' field. 2256 * @return array 2257 */ 2258 function taxonomy_meta_box_sanitize_cb_input( $taxonomy, $terms ) { 2259 /* 2260 * Assume that a 'tax_input' string is a comma-separated list of term names. 2261 * Some languages may use a character other than a comma as a delimiter, so we standardize on 2262 * commas before parsing the list. 2263 */ 2264 if ( ! is_array( $terms ) ) { 2265 $comma = _x( ',', 'tag delimiter' ); 2266 if ( ',' !== $comma ) { 2267 $terms = str_replace( $comma, ',', $terms ); 2268 } 2269 $terms = explode( ',', trim( $terms, " \n\t\r\0\x0B," ) ); 2270 } 2271 2272 $clean_terms = array(); 2273 foreach ( $terms as $term ) { 2274 // Empty terms are invalid input. 2275 if ( empty( $term ) ) { 2276 continue; 2277 } 2278 2279 $_term = get_terms( 2280 array( 2281 'taxonomy' => $taxonomy, 2282 'name' => $term, 2283 'fields' => 'ids', 2284 'hide_empty' => false, 2285 ) 2286 ); 2287 2288 if ( ! empty( $_term ) ) { 2289 $clean_terms[] = (int) $_term[0]; 2290 } else { 2291 // No existing term was found, so pass the string. A new term will be created. 2292 $clean_terms[] = $term; 2293 } 2294 } 2295 2296 return $clean_terms; 2297 } 2298 2299 /** 2300 * Prepares server-registered blocks for the block editor. 2301 * 2302 * Returns an associative array of registered block data keyed by block name. Data includes properties 2303 * of a block relevant for client registration. 2304 * 2305 * @since 5.0.0 2306 * @since 6.3.0 Added `selectors` field. 2307 * @since 6.4.0 Added `block_hooks` field. 2308 * 2309 * @return array An associative array of registered block data. 2310 */ 2311 function get_block_editor_server_block_settings() { 2312 $block_registry = WP_Block_Type_Registry::get_instance(); 2313 $blocks = array(); 2314 $fields_to_pick = array( 2315 'api_version' => 'apiVersion', 2316 'title' => 'title', 2317 'description' => 'description', 2318 'icon' => 'icon', 2319 'attributes' => 'attributes', 2320 'provides_context' => 'providesContext', 2321 'uses_context' => 'usesContext', 2322 'block_hooks' => 'blockHooks', 2323 'selectors' => 'selectors', 2324 'supports' => 'supports', 2325 'category' => 'category', 2326 'styles' => 'styles', 2327 'textdomain' => 'textdomain', 2328 'parent' => 'parent', 2329 'ancestor' => 'ancestor', 2330 'keywords' => 'keywords', 2331 'example' => 'example', 2332 'variations' => 'variations', 2333 'allowed_blocks' => 'allowedBlocks', 2334 ); 2335 2336 foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) { 2337 foreach ( $fields_to_pick as $field => $key ) { 2338 if ( ! isset( $block_type->{ $field } ) ) { 2339 continue; 2340 } 2341 2342 if ( ! isset( $blocks[ $block_name ] ) ) { 2343 $blocks[ $block_name ] = array(); 2344 } 2345 2346 $blocks[ $block_name ][ $key ] = $block_type->{ $field }; 2347 } 2348 } 2349 2350 return $blocks; 2351 } 2352 2353 /** 2354 * Renders the meta boxes forms. 2355 * 2356 * @since 5.0.0 2357 * 2358 * @global WP_Post $post Global post object. 2359 * @global WP_Screen $current_screen WordPress current screen object. 2360 * @global array $wp_meta_boxes Global meta box state. 2361 */ 2362 function the_block_editor_meta_boxes() { 2363 global $post, $current_screen, $wp_meta_boxes; 2364 2365 // Handle meta box state. 2366 $_original_meta_boxes = $wp_meta_boxes; 2367 2368 /** 2369 * Fires right before the meta boxes are rendered. 2370 * 2371 * This allows for the filtering of meta box data, that should already be 2372 * present by this point. Do not use as a means of adding meta box data. 2373 * 2374 * @since 5.0.0 2375 * 2376 * @param array $wp_meta_boxes Global meta box state. 2377 */ 2378 $wp_meta_boxes = apply_filters( 'filter_block_editor_meta_boxes', $wp_meta_boxes ); 2379 $locations = array( 'side', 'normal', 'advanced' ); 2380 $priorities = array( 'high', 'sorted', 'core', 'default', 'low' ); 2381 2382 // Render meta boxes. 2383 ?> 2384 <form class="metabox-base-form"> 2385 <?php the_block_editor_meta_box_post_form_hidden_fields( $post ); ?> 2386 </form> 2387 <form id="toggle-custom-fields-form" method="post" action="<?php echo esc_url( admin_url( 'post.php' ) ); ?>"> 2388 <?php wp_nonce_field( 'toggle-custom-fields', 'toggle-custom-fields-nonce' ); ?> 2389 <input type="hidden" name="action" value="toggle-custom-fields" /> 2390 </form> 2391 <?php foreach ( $locations as $location ) : ?> 2392 <form class="metabox-location-<?php echo esc_attr( $location ); ?>" onsubmit="return false;"> 2393 <div id="poststuff" class="sidebar-open"> 2394 <div id="postbox-container-2" class="postbox-container"> 2395 <?php 2396 do_meta_boxes( 2397 $current_screen, 2398 $location, 2399 $post 2400 ); 2401 ?> 2402 </div> 2403 </div> 2404 </form> 2405 <?php endforeach; ?> 2406 <?php 2407 2408 $meta_boxes_per_location = array(); 2409 foreach ( $locations as $location ) { 2410 $meta_boxes_per_location[ $location ] = array(); 2411 2412 if ( ! isset( $wp_meta_boxes[ $current_screen->id ][ $location ] ) ) { 2413 continue; 2414 } 2415 2416 foreach ( $priorities as $priority ) { 2417 if ( ! isset( $wp_meta_boxes[ $current_screen->id ][ $location ][ $priority ] ) ) { 2418 continue; 2419 } 2420 2421 $meta_boxes = (array) $wp_meta_boxes[ $current_screen->id ][ $location ][ $priority ]; 2422 foreach ( $meta_boxes as $meta_box ) { 2423 if ( false === $meta_box || ! $meta_box['title'] ) { 2424 continue; 2425 } 2426 2427 // If a meta box is just here for back compat, don't show it in the block editor. 2428 if ( isset( $meta_box['args']['__back_compat_meta_box'] ) && $meta_box['args']['__back_compat_meta_box'] ) { 2429 continue; 2430 } 2431 2432 $meta_boxes_per_location[ $location ][] = array( 2433 'id' => $meta_box['id'], 2434 'title' => $meta_box['title'], 2435 ); 2436 } 2437 } 2438 } 2439 2440 /* 2441 * Sadly we probably cannot add this data directly into editor settings. 2442 * 2443 * Some meta boxes need `admin_head` to fire for meta box registry. 2444 * `admin_head` fires after `admin_enqueue_scripts`, which is where we create 2445 * our editor instance. 2446 */ 2447 $script = 'window._wpLoadBlockEditor.then( function() { 2448 wp.data.dispatch( \'core/edit-post\' ).setAvailableMetaBoxesPerLocation( ' . wp_json_encode( $meta_boxes_per_location, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) . ' ); 2449 } );'; 2450 2451 wp_add_inline_script( 'wp-edit-post', $script ); 2452 2453 /* 2454 * When `wp-edit-post` is output in the `<head>`, the inline script needs to be manually printed. 2455 * Otherwise, meta boxes will not display because inline scripts for `wp-edit-post` 2456 * will not be printed again after this point. 2457 */ 2458 if ( wp_script_is( 'wp-edit-post', 'done' ) ) { 2459 printf( "<script>\n%s\n</script>\n", trim( $script ) ); 2460 } 2461 2462 /* 2463 * If the 'postcustom' meta box is enabled, then we need to perform 2464 * some extra initialization on it. 2465 */ 2466 $enable_custom_fields = (bool) get_user_meta( get_current_user_id(), 'enable_custom_fields', true ); 2467 2468 if ( $enable_custom_fields ) { 2469 $script = "( function( $ ) { 2470 if ( $('#postcustom').length ) { 2471 $( '#the-list' ).wpList( { 2472 addBefore: function( s ) { 2473 s.data += '&post_id=$post->ID'; 2474 return s; 2475 }, 2476 addAfter: function() { 2477 $('table#list-table').show(); 2478 } 2479 }); 2480 } 2481 } )( jQuery );"; 2482 wp_enqueue_script( 'wp-lists' ); 2483 wp_add_inline_script( 'wp-lists', $script ); 2484 } 2485 2486 /* 2487 * Refresh nonces used by the meta box loader. 2488 * 2489 * The logic is very similar to that provided by post.js for the classic editor. 2490 */ 2491 $script = "( function( $ ) { 2492 var check, timeout; 2493 2494 function schedule() { 2495 check = false; 2496 window.clearTimeout( timeout ); 2497 timeout = window.setTimeout( function() { check = true; }, 300000 ); 2498 } 2499 2500 $( document ).on( 'heartbeat-send.wp-refresh-nonces', function( e, data ) { 2501 var post_id, \$authCheck = $( '#wp-auth-check-wrap' ); 2502 2503 if ( check || ( \$authCheck.length && ! \$authCheck.hasClass( 'hidden' ) ) ) { 2504 if ( ( post_id = $( '#post_ID' ).val() ) && $( '#_wpnonce' ).val() ) { 2505 data['wp-refresh-metabox-loader-nonces'] = { 2506 post_id: post_id 2507 }; 2508 } 2509 } 2510 }).on( 'heartbeat-tick.wp-refresh-nonces', function( e, data ) { 2511 var nonces = data['wp-refresh-metabox-loader-nonces']; 2512 2513 if ( nonces ) { 2514 if ( nonces.replace ) { 2515 if ( nonces.replace.metabox_loader_nonce && window._wpMetaBoxUrl && wp.url ) { 2516 window._wpMetaBoxUrl= wp.url.addQueryArgs( window._wpMetaBoxUrl, { 'meta-box-loader-nonce': nonces.replace.metabox_loader_nonce } ); 2517 } 2518 2519 if ( nonces.replace._wpnonce ) { 2520 $( '#_wpnonce' ).val( nonces.replace._wpnonce ); 2521 } 2522 } 2523 } 2524 }).ready( function() { 2525 schedule(); 2526 }); 2527 } )( jQuery );"; 2528 wp_add_inline_script( 'heartbeat', $script ); 2529 2530 // Reset meta box data. 2531 $wp_meta_boxes = $_original_meta_boxes; 2532 } 2533 2534 /** 2535 * Renders the hidden form required for the meta boxes form. 2536 * 2537 * @since 5.0.0 2538 * 2539 * @param WP_Post $post Current post object. 2540 */ 2541 function the_block_editor_meta_box_post_form_hidden_fields( $post ) { 2542 $form_extra = ''; 2543 if ( 'auto-draft' === $post->post_status ) { 2544 $form_extra .= "<input type='hidden' id='auto_draft' name='auto_draft' value='1' />"; 2545 } 2546 $form_action = 'editpost'; 2547 $nonce_action = 'update-post_' . $post->ID; 2548 $form_extra .= "<input type='hidden' id='post_ID' name='post_ID' value='" . esc_attr( $post->ID ) . "' />"; 2549 $referer = wp_get_referer(); 2550 $current_user = wp_get_current_user(); 2551 $user_id = $current_user->ID; 2552 wp_nonce_field( $nonce_action ); 2553 2554 /* 2555 * Some meta boxes hook into these actions to add hidden input fields in the classic post form. 2556 * For backward compatibility, we can capture the output from these actions, 2557 * and extract the hidden input fields. 2558 */ 2559 ob_start(); 2560 /** This filter is documented in wp-admin/edit-form-advanced.php */ 2561 do_action( 'edit_form_after_title', $post ); 2562 /** This filter is documented in wp-admin/edit-form-advanced.php */ 2563 do_action( 'edit_form_advanced', $post ); 2564 $classic_output = ob_get_clean(); 2565 2566 $classic_elements = wp_html_split( $classic_output ); 2567 2568 foreach ( $classic_elements as $element ) { 2569 if ( ! str_starts_with( $element, '<input ' ) ) { 2570 continue; 2571 } 2572 2573 if ( preg_match( '/\stype=[\'"]hidden[\'"]\s/', $element ) ) { 2574 echo $element; 2575 } 2576 } 2577 ?> 2578 <input type="hidden" id="user-id" name="user_ID" value="<?php echo (int) $user_id; ?>" /> 2579 <input type="hidden" id="hiddenaction" name="action" value="<?php echo esc_attr( $form_action ); ?>" /> 2580 <input type="hidden" id="originalaction" name="originalaction" value="<?php echo esc_attr( $form_action ); ?>" /> 2581 <input type="hidden" id="post_type" name="post_type" value="<?php echo esc_attr( $post->post_type ); ?>" /> 2582 <input type="hidden" id="original_post_status" name="original_post_status" value="<?php echo esc_attr( $post->post_status ); ?>" /> 2583 <input type="hidden" id="referredby" name="referredby" value="<?php echo $referer ? esc_url( $referer ) : ''; ?>" /> 2584 2585 <?php 2586 if ( 'draft' !== get_post_status( $post ) ) { 2587 wp_original_referer_field( true, 'previous' ); 2588 } 2589 echo $form_extra; 2590 wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); 2591 wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); 2592 // Permalink title nonce. 2593 wp_nonce_field( 'samplepermalink', 'samplepermalinknonce', false ); 2594 2595 /** 2596 * Adds hidden input fields to the meta box save form. 2597 * 2598 * Hook into this action to print `<input type="hidden" ... />` fields, which will be POSTed back to 2599 * the server when meta boxes are saved. 2600 * 2601 * @since 5.0.0 2602 * 2603 * @param WP_Post $post The post that is being edited. 2604 */ 2605 do_action( 'block_editor_meta_box_hidden_fields', $post ); 2606 } 2607 2608 /** 2609 * Disables block editor for wp_navigation type posts so they can be managed via the UI. 2610 * 2611 * @since 5.9.0 2612 * @access private 2613 * 2614 * @param bool $value Whether the CPT supports block editor or not. 2615 * @param string $post_type Post type. 2616 * @return bool Whether the block editor should be disabled or not. 2617 */ 2618 function _disable_block_editor_for_navigation_post_type( $value, $post_type ) { 2619 if ( 'wp_navigation' === $post_type ) { 2620 return false; 2621 } 2622 2623 return $value; 2624 } 2625 2626 /** 2627 * This callback disables the content editor for wp_navigation type posts. 2628 * Content editor cannot handle wp_navigation type posts correctly. 2629 * We cannot disable the "editor" feature in the wp_navigation's CPT definition 2630 * because it disables the ability to save navigation blocks via REST API. 2631 * 2632 * @since 5.9.0 2633 * @access private 2634 * 2635 * @param WP_Post $post An instance of WP_Post class. 2636 */ 2637 function _disable_content_editor_for_navigation_post_type( $post ) { 2638 $post_type = get_post_type( $post ); 2639 if ( 'wp_navigation' !== $post_type ) { 2640 return; 2641 } 2642 2643 remove_post_type_support( $post_type, 'editor' ); 2644 } 2645 2646 /** 2647 * This callback enables content editor for wp_navigation type posts. 2648 * We need to enable it back because we disable it to hide 2649 * the content editor for wp_navigation type posts. 2650 * 2651 * @since 5.9.0 2652 * @access private 2653 * 2654 * @see _disable_content_editor_for_navigation_post_type 2655 * 2656 * @param WP_Post $post An instance of WP_Post class. 2657 */ 2658 function _enable_content_editor_for_navigation_post_type( $post ) { 2659 $post_type = get_post_type( $post ); 2660 if ( 'wp_navigation' !== $post_type ) { 2661 return; 2662 } 2663 2664 add_post_type_support( $post_type, 'editor' ); 2665 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Mon May 4 08:20:14 2026 | Cross-referenced by PHPXref |