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