| [ 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 * @phpstan-return array{ 1417 * 0: array<string, array{ 1418 * 0: string, 1419 * 1: string, 1420 * 2: array{ 1421 * singular: literal-string, 1422 * plural: literal-string, 1423 * context: literal-string|null, 1424 * domain: literal-string|null, 1425 * ... 1426 * }, 1427 * }>, 1428 * 1: string[], 1429 * } 1430 */ 1431 function wp_edit_attachments_query( $q = false ) { 1432 wp( wp_edit_attachments_query_vars( $q ) ); 1433 1434 $post_mime_types = get_post_mime_types(); 1435 $avail_post_mime_types = get_available_post_mime_types( 'attachment' ); 1436 1437 return array( $post_mime_types, $avail_post_mime_types ); 1438 } 1439 1440 /** 1441 * Returns the list of classes to be used by a meta box. 1442 * 1443 * @since 2.5.0 1444 * 1445 * @param string $box_id Meta box ID (used in the 'id' attribute for the meta box). 1446 * @param string $screen_id The screen on which the meta box is shown. 1447 * @return string Space-separated string of class names. 1448 */ 1449 function postbox_classes( $box_id, $screen_id ) { 1450 if ( isset( $_GET['edit'] ) && $_GET['edit'] === $box_id ) { 1451 $classes = array( '' ); 1452 } elseif ( get_user_option( 'closedpostboxes_' . $screen_id ) ) { 1453 $closed = get_user_option( 'closedpostboxes_' . $screen_id ); 1454 if ( ! is_array( $closed ) ) { 1455 $classes = array( '' ); 1456 } else { 1457 $classes = in_array( $box_id, $closed, true ) ? array( 'closed' ) : array( '' ); 1458 } 1459 } else { 1460 $classes = array( '' ); 1461 } 1462 1463 /** 1464 * Filters the postbox classes for a specific screen and box ID combo. 1465 * 1466 * The dynamic portions of the hook name, `$screen_id` and `$box_id`, refer to 1467 * the screen ID and meta box ID, respectively. 1468 * 1469 * @since 3.2.0 1470 * 1471 * @param string[] $classes An array of postbox classes. 1472 */ 1473 $classes = apply_filters( "postbox_classes_{$screen_id}_{$box_id}", $classes ); 1474 1475 return implode( ' ', $classes ); 1476 } 1477 1478 /** 1479 * Returns a sample permalink based on the post name. 1480 * 1481 * @since 2.5.0 1482 * 1483 * @param int|WP_Post $post Post ID or post object. 1484 * @param string|null $title Optional. Title to override the post's current title 1485 * when generating the post name. Default null. 1486 * @param string|null $name Optional. Name to override the post name. Default null. 1487 * @return array { 1488 * Array containing the sample permalink with placeholder for the post name, and the post name. 1489 * 1490 * @type string $0 The permalink with placeholder for the post name. 1491 * @type string $1 The post name. 1492 * } 1493 */ 1494 function get_sample_permalink( $post, $title = null, $name = null ) { 1495 $post = get_post( $post ); 1496 1497 if ( ! $post ) { 1498 return array( '', '' ); 1499 } 1500 1501 $ptype = get_post_type_object( $post->post_type ); 1502 1503 $original_status = $post->post_status; 1504 $original_date = $post->post_date; 1505 $original_name = $post->post_name; 1506 $original_filter = $post->filter; 1507 1508 // Hack: get_permalink() would return plain permalink for drafts, so we will fake that our post is published. 1509 if ( in_array( $post->post_status, array( 'auto-draft', 'draft', 'pending', 'future' ), true ) ) { 1510 $post->post_status = 'publish'; 1511 $post->post_name = sanitize_title( $post->post_name ? $post->post_name : $post->post_title, $post->ID ); 1512 } 1513 1514 /* 1515 * If the user wants to set a new name -- override the current one. 1516 * Note: if empty name is supplied -- use the title instead, see #6072. 1517 */ 1518 if ( ! is_null( $name ) ) { 1519 $post->post_name = sanitize_title( $name ? $name : $title, $post->ID ); 1520 } 1521 1522 $post->post_name = wp_unique_post_slug( $post->post_name, $post->ID, $post->post_status, $post->post_type, $post->post_parent ); 1523 1524 $post->filter = 'sample'; 1525 1526 $permalink = get_permalink( $post, true ); 1527 1528 // Replace custom post_type token with generic pagename token for ease of use. 1529 $permalink = str_replace( "%$post->post_type%", '%pagename%', $permalink ); 1530 1531 // Handle page hierarchy. 1532 if ( $ptype->hierarchical ) { 1533 $uri = get_page_uri( $post ); 1534 if ( $uri ) { 1535 $uri = untrailingslashit( $uri ); 1536 $uri = strrev( stristr( strrev( $uri ), '/' ) ); 1537 $uri = untrailingslashit( $uri ); 1538 } 1539 1540 /** This filter is documented in wp-admin/edit-tag-form.php */ 1541 $uri = apply_filters( 'editable_slug', $uri, $post ); 1542 if ( ! empty( $uri ) ) { 1543 $uri .= '/'; 1544 } 1545 $permalink = str_replace( '%pagename%', "{$uri}%pagename%", $permalink ); 1546 } 1547 1548 /** This filter is documented in wp-admin/edit-tag-form.php */ 1549 $permalink = array( $permalink, apply_filters( 'editable_slug', $post->post_name, $post ) ); 1550 $post->post_status = $original_status; 1551 $post->post_date = $original_date; 1552 $post->post_name = $original_name; 1553 $post->filter = $original_filter; 1554 1555 /** 1556 * Filters the sample permalink. 1557 * 1558 * @since 4.4.0 1559 * 1560 * @param array $permalink { 1561 * Array containing the sample permalink with placeholder for the post name, and the post name. 1562 * 1563 * @type string $0 The permalink with placeholder for the post name. 1564 * @type string $1 The post name. 1565 * } 1566 * @param int $post_id Post ID. 1567 * @param string $title Post title. 1568 * @param string $name Post name (slug). 1569 * @param WP_Post $post Post object. 1570 */ 1571 return apply_filters( 'get_sample_permalink', $permalink, $post->ID, $title, $name, $post ); 1572 } 1573 1574 /** 1575 * Returns the HTML of the sample permalink slug editor. 1576 * 1577 * @since 2.5.0 1578 * 1579 * @param int|WP_Post $post Post ID or post object. 1580 * @param string|null $new_title Optional. New title. Default null. 1581 * @param string|null $new_slug Optional. New slug. Default null. 1582 * @return string The HTML of the sample permalink slug editor. 1583 */ 1584 function get_sample_permalink_html( $post, $new_title = null, $new_slug = null ) { 1585 $post = get_post( $post ); 1586 1587 if ( ! $post ) { 1588 return ''; 1589 } 1590 1591 list($permalink, $post_name) = get_sample_permalink( $post->ID, $new_title, $new_slug ); 1592 1593 $view_link = false; 1594 $preview_target = ''; 1595 1596 if ( current_user_can( 'read_post', $post->ID ) ) { 1597 if ( 'draft' === $post->post_status || empty( $post->post_name ) ) { 1598 $view_link = get_preview_post_link( $post ); 1599 $preview_target = " target='wp-preview-{$post->ID}'"; 1600 } else { 1601 if ( 'publish' === $post->post_status || 'attachment' === $post->post_type ) { 1602 $view_link = get_permalink( $post ); 1603 } else { 1604 // Allow non-published (private, future) to be viewed at a pretty permalink, in case $post->post_name is set. 1605 $view_link = str_replace( array( '%pagename%', '%postname%' ), $post->post_name, $permalink ); 1606 } 1607 } 1608 } 1609 1610 // Permalinks without a post/page name placeholder don't have anything to edit. 1611 if ( ! str_contains( $permalink, '%postname%' ) && ! str_contains( $permalink, '%pagename%' ) ) { 1612 $return = '<strong>' . __( 'Permalink:' ) . "</strong>\n"; 1613 1614 if ( false !== $view_link ) { 1615 $display_link = urldecode( $view_link ); 1616 $return .= '<a id="sample-permalink" href="' . esc_url( $view_link ) . '"' . $preview_target . '>' . esc_html( $display_link ) . "</a>\n"; 1617 } else { 1618 $return .= '<span id="sample-permalink">' . $permalink . "</span>\n"; 1619 } 1620 1621 // Encourage a pretty permalink setting. 1622 if ( ! get_option( 'permalink_structure' ) && current_user_can( 'manage_options' ) 1623 && ! ( 'page' === get_option( 'show_on_front' ) && (int) get_option( 'page_on_front' ) === $post->ID ) 1624 ) { 1625 $return .= '<span id="change-permalinks"><a href="options-permalink.php" class="button button-small">' . __( 'Change Permalink Structure' ) . "</a></span>\n"; 1626 } 1627 } else { 1628 if ( mb_strlen( $post_name ) > 34 ) { 1629 $post_name_abridged = mb_substr( $post_name, 0, 16 ) . '…' . mb_substr( $post_name, -16 ); 1630 } else { 1631 $post_name_abridged = $post_name; 1632 } 1633 1634 $post_name_html = '<span id="editable-post-name">' . esc_html( $post_name_abridged ) . '</span>'; 1635 $display_link = str_replace( array( '%pagename%', '%postname%' ), $post_name_html, esc_html( urldecode( $permalink ) ) ); 1636 1637 $return = '<strong>' . __( 'Permalink:' ) . "</strong>\n"; 1638 $return .= '<span id="sample-permalink"><a href="' . esc_url( $view_link ) . '"' . $preview_target . '>' . $display_link . "</a></span>\n"; 1639 $return .= '‎'; // Fix bi-directional text display defect in RTL languages. 1640 $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"; 1641 $return .= '<span id="editable-post-name-full">' . esc_html( $post_name ) . "</span>\n"; 1642 } 1643 1644 /** 1645 * Filters the sample permalink HTML markup. 1646 * 1647 * @since 2.9.0 1648 * @since 4.4.0 Added `$post` parameter. 1649 * 1650 * @param string $return Sample permalink HTML markup. 1651 * @param int $post_id Post ID. 1652 * @param string|null $new_title New sample permalink title. 1653 * @param string|null $new_slug New sample permalink slug. 1654 * @param WP_Post $post Post object. 1655 */ 1656 $return = apply_filters( 'get_sample_permalink_html', $return, $post->ID, $new_title, $new_slug, $post ); 1657 1658 return $return; 1659 } 1660 1661 /** 1662 * Returns HTML for the post thumbnail meta box. 1663 * 1664 * @since 2.9.0 1665 * 1666 * @param int|null $thumbnail_id Optional. Thumbnail attachment ID. Default null. 1667 * @param int|WP_Post|null $post Optional. The post ID or object associated 1668 * with the thumbnail. Defaults to global $post. 1669 * @return string The post thumbnail HTML. 1670 */ 1671 function _wp_post_thumbnail_html( $thumbnail_id = null, $post = null ) { 1672 $_wp_additional_image_sizes = wp_get_additional_image_sizes(); 1673 1674 $post = get_post( $post ); 1675 $post_type_object = get_post_type_object( $post->post_type ); 1676 $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>'; 1677 $upload_iframe_src = get_upload_iframe_src( 'image', $post->ID ); 1678 1679 $content = sprintf( 1680 $set_thumbnail_link, 1681 esc_url( $upload_iframe_src ), 1682 '', // Empty when there's no featured image set, `aria-describedby` attribute otherwise. 1683 esc_html( $post_type_object->labels->set_featured_image ) 1684 ); 1685 1686 if ( $thumbnail_id && get_post( $thumbnail_id ) ) { 1687 $size = isset( $_wp_additional_image_sizes['post-thumbnail'] ) ? 'post-thumbnail' : array( 266, 266 ); 1688 1689 /** 1690 * Filters the size used to display the post thumbnail image in the 'Featured image' meta box. 1691 * 1692 * Note: When a theme adds 'post-thumbnail' support, a special 'post-thumbnail' 1693 * image size is registered, which differs from the 'thumbnail' image size 1694 * managed via the Settings > Media screen. 1695 * 1696 * @since 4.4.0 1697 * 1698 * @param string|int[] $size Requested image size. Can be any registered image size name, or 1699 * an array of width and height values in pixels (in that order). 1700 * @param int $thumbnail_id Post thumbnail attachment ID. 1701 * @param WP_Post $post The post object associated with the thumbnail. 1702 */ 1703 $size = apply_filters( 'admin_post_thumbnail_size', $size, $thumbnail_id, $post ); 1704 1705 $thumbnail_html = wp_get_attachment_image( $thumbnail_id, $size ); 1706 1707 if ( ! empty( $thumbnail_html ) ) { 1708 $content = sprintf( 1709 $set_thumbnail_link, 1710 esc_url( $upload_iframe_src ), 1711 ' aria-describedby="set-post-thumbnail-desc"', 1712 $thumbnail_html 1713 ); 1714 $content .= '<p class="hide-if-no-js howto" id="set-post-thumbnail-desc">' . __( 'Click the image to edit or update' ) . '</p>'; 1715 $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>'; 1716 } 1717 } 1718 1719 $content .= '<input type="hidden" id="_thumbnail_id" name="_thumbnail_id" value="' . esc_attr( $thumbnail_id ? $thumbnail_id : '-1' ) . '" />'; 1720 1721 /** 1722 * Filters the admin post thumbnail HTML markup to return. 1723 * 1724 * @since 2.9.0 1725 * @since 3.5.0 Added the `$post_id` parameter. 1726 * @since 4.6.0 Added the `$thumbnail_id` parameter. 1727 * 1728 * @param string $content Admin post thumbnail HTML markup. 1729 * @param int $post_id Post ID. 1730 * @param int|null $thumbnail_id Thumbnail attachment ID, or null if there isn't one. 1731 */ 1732 return apply_filters( 'admin_post_thumbnail_html', $content, $post->ID, $thumbnail_id ); 1733 } 1734 1735 /** 1736 * Determines whether the post is currently being edited by another user. 1737 * 1738 * @since 2.5.0 1739 * 1740 * @param int|WP_Post $post ID or object of the post to check for editing. 1741 * @return int|false ID of the user with lock. False if the post does not exist, post is not locked, 1742 * the user with lock does not exist, or the post is locked by current user. 1743 */ 1744 function wp_check_post_lock( $post ) { 1745 $post = get_post( $post ); 1746 1747 if ( ! $post ) { 1748 return false; 1749 } 1750 1751 $lock = get_post_meta( $post->ID, '_edit_lock', true ); 1752 1753 if ( ! $lock ) { 1754 return false; 1755 } 1756 1757 $lock = explode( ':', $lock ); 1758 $time = $lock[0]; 1759 $user = isset( $lock[1] ) ? (int) $lock[1] : (int) get_post_meta( $post->ID, '_edit_last', true ); 1760 1761 if ( ! get_userdata( $user ) ) { 1762 return false; 1763 } 1764 1765 /** This filter is documented in wp-admin/includes/ajax-actions.php */ 1766 $time_window = apply_filters( 'wp_check_post_lock_window', 150 ); 1767 1768 if ( $time && $time > time() - $time_window && get_current_user_id() !== $user ) { 1769 return $user; 1770 } 1771 1772 return false; 1773 } 1774 1775 /** 1776 * Marks the post as currently being edited by the current user. 1777 * 1778 * @since 2.5.0 1779 * 1780 * @param int|WP_Post $post ID or object of the post being edited. 1781 * @return array|false { 1782 * Array of the lock time and user ID. False if the post does not exist, or there 1783 * is no current user. 1784 * 1785 * @type int $0 The current time as a Unix timestamp. 1786 * @type int $1 The ID of the current user. 1787 * } 1788 */ 1789 function wp_set_post_lock( $post ) { 1790 $post = get_post( $post ); 1791 1792 if ( ! $post ) { 1793 return false; 1794 } 1795 1796 $user_id = get_current_user_id(); 1797 1798 if ( 0 === $user_id ) { 1799 return false; 1800 } 1801 1802 $now = time(); 1803 $lock = "$now:$user_id"; 1804 1805 update_post_meta( $post->ID, '_edit_lock', $lock ); 1806 1807 return array( $now, $user_id ); 1808 } 1809 1810 /** 1811 * Outputs the HTML for the notice to say that someone else is editing or has taken over editing of this post. 1812 * 1813 * @since 2.8.5 1814 */ 1815 function _admin_notice_post_locked() { 1816 $post = get_post(); 1817 1818 if ( ! $post ) { 1819 return; 1820 } 1821 1822 $user = null; 1823 $user_id = wp_check_post_lock( $post->ID ); 1824 1825 if ( $user_id ) { 1826 $user = get_userdata( $user_id ); 1827 } 1828 1829 if ( $user ) { 1830 /** 1831 * Filters whether to show the post locked dialog. 1832 * 1833 * Returning false from the filter will prevent the dialog from being displayed. 1834 * 1835 * @since 3.6.0 1836 * 1837 * @param bool $display Whether to display the dialog. Default true. 1838 * @param WP_Post $post Post object. 1839 * @param WP_User $user The user with the lock for the post. 1840 */ 1841 if ( ! apply_filters( 'show_post_locked_dialog', true, $post, $user ) ) { 1842 return; 1843 } 1844 1845 $locked = true; 1846 } else { 1847 $locked = false; 1848 } 1849 1850 $sendback = wp_get_referer(); 1851 $sendback_text = __( 'Go back' ); 1852 1853 if ( ! $locked || ! $sendback || str_contains( $sendback, 'post.php' ) || str_contains( $sendback, 'post-new.php' ) ) { 1854 $sendback = admin_url( 'edit.php' ); 1855 1856 if ( 'post' !== $post->post_type ) { 1857 $sendback = add_query_arg( 'post_type', $post->post_type, $sendback ); 1858 } 1859 1860 $post_type_object = get_post_type_object( $post->post_type ); 1861 1862 if ( $post_type_object ) { 1863 $sendback_text = $post_type_object->labels->all_items; 1864 } 1865 } 1866 1867 $hidden = $locked ? '' : ' hidden'; 1868 1869 ?> 1870 <div id="post-lock-dialog" class="notification-dialog-wrap<?php echo $hidden; ?>"> 1871 <div class="notification-dialog-background"></div> 1872 <div class="notification-dialog"> 1873 <?php 1874 1875 if ( $locked ) { 1876 $query_args = array(); 1877 if ( get_post_type_object( $post->post_type )->public ) { 1878 if ( 'publish' === $post->post_status || $user->ID !== (int) $post->post_author ) { 1879 // Latest content is in autosave. 1880 $nonce = wp_create_nonce( 'post_preview_' . $post->ID ); 1881 $query_args['preview_id'] = $post->ID; 1882 $query_args['preview_nonce'] = $nonce; 1883 } 1884 } 1885 1886 $preview_link = get_preview_post_link( $post->ID, $query_args ); 1887 1888 /** 1889 * Filters whether to allow the post lock to be overridden. 1890 * 1891 * Returning false from the filter will disable the ability 1892 * to override the post lock. 1893 * 1894 * @since 3.6.0 1895 * 1896 * @param bool $override Whether to allow the post lock to be overridden. Default true. 1897 * @param WP_Post $post Post object. 1898 * @param WP_User $user The user with the lock for the post. 1899 */ 1900 $override = apply_filters( 'override_post_lock', true, $post, $user ); 1901 $tab_last = $override ? '' : ' wp-tab-last'; 1902 1903 ?> 1904 <div class="post-locked-message"> 1905 <div class="post-locked-avatar"><?php echo get_avatar( $user->ID, 64 ); ?></div> 1906 <p class="currently-editing wp-tab-first" tabindex="0"> 1907 <?php 1908 if ( $override ) { 1909 /* translators: %s: User's display name. */ 1910 printf( __( '%s is currently editing this post. Do you want to take over?' ), esc_html( $user->display_name ) ); 1911 } else { 1912 /* translators: %s: User's display name. */ 1913 printf( __( '%s is currently editing this post.' ), esc_html( $user->display_name ) ); 1914 } 1915 ?> 1916 </p> 1917 <?php 1918 /** 1919 * Fires inside the post locked dialog before the buttons are displayed. 1920 * 1921 * @since 3.6.0 1922 * @since 5.4.0 The `$user` parameter was added. 1923 * 1924 * @param WP_Post $post Post object. 1925 * @param WP_User $user The user with the lock for the post. 1926 */ 1927 do_action( 'post_locked_dialog', $post, $user ); 1928 ?> 1929 <p> 1930 <a class="button" href="<?php echo esc_url( $sendback ); ?>"><?php echo $sendback_text; ?></a> 1931 <?php if ( $preview_link ) { ?> 1932 <a class="button<?php echo $tab_last; ?>" href="<?php echo esc_url( $preview_link ); ?>"><?php echo esc_html_x( 'Preview', 'verb' ); ?></a> 1933 <?php 1934 } 1935 1936 // Allow plugins to prevent some users overriding the post lock. 1937 if ( $override ) { 1938 ?> 1939 <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> 1940 <?php 1941 } 1942 1943 ?> 1944 </p> 1945 </div> 1946 <?php 1947 } else { 1948 ?> 1949 <div class="post-taken-over"> 1950 <div class="post-locked-avatar"></div> 1951 <p class="wp-tab-first" tabindex="0"> 1952 <span class="currently-editing"></span><br /> 1953 <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> 1954 <span class="locked-saved hidden"><?php _e( 'Your latest changes were saved as a revision.' ); ?></span> 1955 </p> 1956 <?php 1957 /** 1958 * Fires inside the dialog displayed when a user has lost the post lock. 1959 * 1960 * @since 3.6.0 1961 * 1962 * @param WP_Post $post Post object. 1963 */ 1964 do_action( 'post_lock_lost_dialog', $post ); 1965 ?> 1966 <p><a class="button button-primary wp-tab-last" href="<?php echo esc_url( $sendback ); ?>"><?php echo $sendback_text; ?></a></p> 1967 </div> 1968 <?php 1969 } 1970 1971 ?> 1972 </div> 1973 </div> 1974 <?php 1975 } 1976 1977 /** 1978 * Creates autosave data for the specified post from `$_POST` data. 1979 * 1980 * @since 2.6.0 1981 * 1982 * @param array|int $post_data Associative array containing the post data, or integer post ID. 1983 * If a numeric post ID is provided, will use the `$_POST` superglobal. 1984 * @return int|WP_Error The autosave revision ID. WP_Error or 0 on error. 1985 */ 1986 function wp_create_post_autosave( $post_data ) { 1987 if ( is_numeric( $post_data ) ) { 1988 $post_id = $post_data; 1989 $post_data = $_POST; 1990 } else { 1991 $post_id = (int) $post_data['post_ID']; 1992 } 1993 1994 $post_data = _wp_translate_postdata( true, $post_data ); 1995 if ( is_wp_error( $post_data ) ) { 1996 return $post_data; 1997 } 1998 $post_data = _wp_get_allowed_postdata( $post_data ); 1999 2000 $post_author = get_current_user_id(); 2001 2002 // Store one autosave per author. If there is already an autosave, overwrite it. 2003 $old_autosave = wp_get_post_autosave( $post_id, $post_author ); 2004 if ( $old_autosave ) { 2005 $new_autosave = _wp_post_revision_data( $post_data, true ); 2006 $new_autosave['ID'] = $old_autosave->ID; 2007 $new_autosave['post_author'] = $post_author; 2008 2009 $post = get_post( $post_id ); 2010 2011 // If the new autosave has the same content as the post, delete the autosave. 2012 $fields = array_intersect( array_keys( $new_autosave ), array_keys( _wp_post_revision_fields( $post ) ) ); 2013 $autosave_is_different = array_any( $fields, fn( $field ) => normalize_whitespace( $new_autosave[ $field ] ) !== normalize_whitespace( $post->$field ) ); 2014 2015 if ( ! $autosave_is_different ) { 2016 wp_delete_post_revision( $old_autosave->ID ); 2017 return 0; 2018 } 2019 2020 /** 2021 * Fires before an autosave is stored. 2022 * 2023 * @since 4.1.0 2024 * @since 6.4.0 The `$is_update` parameter was added to indicate if the autosave is being updated or was newly created. 2025 * 2026 * @param array $new_autosave Post array - the autosave that is about to be saved. 2027 * @param bool $is_update Whether this is an existing autosave. 2028 */ 2029 do_action( 'wp_creating_autosave', $new_autosave, true ); 2030 return wp_update_post( $new_autosave ); 2031 } 2032 2033 // _wp_put_post_revision() expects unescaped. 2034 $post_data = wp_unslash( $post_data ); 2035 2036 // Otherwise create the new autosave as a special post revision. 2037 $revision = _wp_put_post_revision( $post_data, true ); 2038 2039 if ( ! is_wp_error( $revision ) && 0 !== $revision ) { 2040 2041 /** This action is documented in wp-admin/includes/post.php */ 2042 do_action( 'wp_creating_autosave', get_post( $revision, ARRAY_A ), false ); 2043 } 2044 2045 return $revision; 2046 } 2047 2048 /** 2049 * Autosaves the revisioned meta fields. 2050 * 2051 * Iterates through the revisioned meta fields and checks each to see if they are set, 2052 * and have a changed value. If so, the meta value is saved and attached to the autosave. 2053 * 2054 * @since 6.4.0 2055 * 2056 * @param array $new_autosave The new post data being autosaved. 2057 */ 2058 function wp_autosave_post_revisioned_meta_fields( $new_autosave ) { 2059 /* 2060 * The post data arrives as either $_POST['data']['wp_autosave'] or the $_POST 2061 * itself. This sets $posted_data to the correct variable. 2062 * 2063 * Ignoring sanitization to avoid altering meta. Ignoring the nonce check because 2064 * this is hooked on inner core hooks where a valid nonce was already checked. 2065 */ 2066 $posted_data = $_POST['data']['wp_autosave'] ?? $_POST; 2067 2068 $post_type = get_post_type( $new_autosave['post_parent'] ); 2069 2070 /* 2071 * Go through the revisioned meta keys and save them as part of the autosave, 2072 * if the meta key is part of the posted data, the meta value is not blank, 2073 * and the meta value has changes from the last autosaved value. 2074 */ 2075 foreach ( wp_post_revision_meta_keys( $post_type ) as $meta_key ) { 2076 2077 if ( isset( $posted_data[ $meta_key ] ) 2078 && get_post_meta( $new_autosave['ID'], $meta_key, true ) !== wp_unslash( $posted_data[ $meta_key ] ) 2079 ) { 2080 /* 2081 * Use the underlying delete_metadata() and add_metadata() functions 2082 * vs delete_post_meta() and add_post_meta() to make sure we're working 2083 * with the actual revision meta. 2084 */ 2085 delete_metadata( 'post', $new_autosave['ID'], $meta_key ); 2086 2087 // One last check to ensure meta value is not empty. 2088 if ( ! empty( $posted_data[ $meta_key ] ) ) { 2089 // Add the revisions meta data to the autosave. 2090 add_metadata( 'post', $new_autosave['ID'], $meta_key, $posted_data[ $meta_key ] ); 2091 } 2092 } 2093 } 2094 } 2095 2096 /** 2097 * Saves a draft or manually autosaves for the purpose of showing a post preview. 2098 * 2099 * @since 2.7.0 2100 * 2101 * @return string URL to redirect to show the preview. 2102 */ 2103 function post_preview() { 2104 2105 $post_id = (int) $_POST['post_ID']; 2106 $_POST['ID'] = $post_id; 2107 2108 $post = get_post( $post_id ); 2109 2110 if ( ! $post ) { 2111 wp_die( __( 'Sorry, you are not allowed to edit this post.' ) ); 2112 } 2113 2114 if ( ! current_user_can( 'edit_post', $post->ID ) ) { 2115 wp_die( __( 'Sorry, you are not allowed to edit this post.' ) ); 2116 } 2117 2118 $is_autosave = false; 2119 2120 if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() === (int) $post->post_author 2121 && ( 'draft' === $post->post_status || 'auto-draft' === $post->post_status ) 2122 ) { 2123 $saved_post_id = edit_post(); 2124 } else { 2125 $is_autosave = true; 2126 2127 if ( isset( $_POST['post_status'] ) && 'auto-draft' === $_POST['post_status'] ) { 2128 $_POST['post_status'] = 'draft'; 2129 } 2130 2131 $saved_post_id = wp_create_post_autosave( $post->ID ); 2132 } 2133 2134 if ( is_wp_error( $saved_post_id ) ) { 2135 wp_die( $saved_post_id->get_error_message() ); 2136 } 2137 2138 $query_args = array(); 2139 2140 if ( $is_autosave && $saved_post_id ) { 2141 $query_args['preview_id'] = $post->ID; 2142 $query_args['preview_nonce'] = wp_create_nonce( 'post_preview_' . $post->ID ); 2143 2144 if ( isset( $_POST['post_format'] ) ) { 2145 $query_args['post_format'] = empty( $_POST['post_format'] ) ? 'standard' : sanitize_key( $_POST['post_format'] ); 2146 } 2147 2148 if ( isset( $_POST['_thumbnail_id'] ) ) { 2149 $query_args['_thumbnail_id'] = ( (int) $_POST['_thumbnail_id'] <= 0 ) ? '-1' : (int) $_POST['_thumbnail_id']; 2150 } 2151 } 2152 2153 return get_preview_post_link( $post, $query_args ); 2154 } 2155 2156 /** 2157 * Saves a post submitted with XHR. 2158 * 2159 * Intended for use with heartbeat and autosave.js 2160 * 2161 * @since 3.9.0 2162 * 2163 * @param array $post_data Associative array of the submitted post data. 2164 * @return mixed The value 0 or WP_Error on failure. The saved post ID on success. 2165 * The ID can be the draft post_id or the autosave revision post_id. 2166 */ 2167 function wp_autosave( $post_data ) { 2168 // Back-compat. 2169 if ( ! defined( 'DOING_AUTOSAVE' ) ) { 2170 define( 'DOING_AUTOSAVE', true ); 2171 } 2172 2173 $post_id = (int) $post_data['post_id']; 2174 $post_data['ID'] = $post_id; 2175 $post_data['post_ID'] = $post_id; 2176 2177 if ( false === wp_verify_nonce( $post_data['_wpnonce'], 'update-post_' . $post_id ) ) { 2178 return new WP_Error( 'invalid_nonce', __( 'Error while saving.' ) ); 2179 } 2180 2181 $post = get_post( $post_id ); 2182 2183 if ( ! current_user_can( 'edit_post', $post->ID ) ) { 2184 return new WP_Error( 'edit_posts', __( 'Sorry, you are not allowed to edit this item.' ) ); 2185 } 2186 2187 if ( 'auto-draft' === $post->post_status ) { 2188 $post_data['post_status'] = 'draft'; 2189 } 2190 2191 if ( 'page' !== $post_data['post_type'] && ! empty( $post_data['catslist'] ) ) { 2192 $post_data['post_category'] = explode( ',', $post_data['catslist'] ); 2193 } 2194 2195 if ( ! wp_check_post_lock( $post->ID ) && get_current_user_id() === (int) $post->post_author 2196 && ( 'auto-draft' === $post->post_status || 'draft' === $post->post_status ) 2197 ) { 2198 // Drafts and auto-drafts are just overwritten by autosave for the same user if the post is not locked. 2199 return edit_post( wp_slash( $post_data ) ); 2200 } else { 2201 /* 2202 * Non-drafts or other users' drafts are not overwritten. 2203 * The autosave is stored in a special post revision for each user. 2204 */ 2205 return wp_create_post_autosave( wp_slash( $post_data ) ); 2206 } 2207 } 2208 2209 /** 2210 * Redirects to previous page. 2211 * 2212 * @since 2.7.0 2213 * 2214 * @param int $post_id Optional. Post ID. 2215 * @return never 2216 */ 2217 function redirect_post( $post_id = 0 ) { 2218 if ( isset( $_POST['save'] ) || isset( $_POST['publish'] ) ) { 2219 $status = get_post_status( $post_id ); 2220 2221 switch ( $status ) { 2222 case 'pending': 2223 $message = 8; 2224 break; 2225 case 'future': 2226 $message = 9; 2227 break; 2228 case 'draft': 2229 $message = 10; 2230 break; 2231 default: 2232 $message = isset( $_POST['publish'] ) ? 6 : 1; 2233 break; 2234 } 2235 2236 $location = add_query_arg( 'message', $message, get_edit_post_link( $post_id, 'url' ) ); 2237 } elseif ( isset( $_POST['addmeta'] ) && $_POST['addmeta'] ) { 2238 $location = add_query_arg( 'message', 2, wp_get_referer() ); 2239 $location = explode( '#', $location ); 2240 $location = $location[0] . '#postcustom'; 2241 } elseif ( isset( $_POST['deletemeta'] ) && $_POST['deletemeta'] ) { 2242 $location = add_query_arg( 'message', 3, wp_get_referer() ); 2243 $location = explode( '#', $location ); 2244 $location = $location[0] . '#postcustom'; 2245 } else { 2246 $location = add_query_arg( 'message', 4, get_edit_post_link( $post_id, 'url' ) ); 2247 } 2248 2249 /** 2250 * Filters the post redirect destination URL. 2251 * 2252 * @since 2.9.0 2253 * 2254 * @param string $location The destination URL. 2255 * @param int $post_id The post ID. 2256 */ 2257 wp_redirect( apply_filters( 'redirect_post_location', $location, $post_id ) ); 2258 exit; 2259 } 2260 2261 /** 2262 * Sanitizes POST values from a checkbox taxonomy metabox. 2263 * 2264 * @since 5.1.0 2265 * 2266 * @param string $taxonomy The taxonomy name. 2267 * @param array $terms Raw term data from the 'tax_input' field. 2268 * @return int[] Array of sanitized term IDs. 2269 */ 2270 function taxonomy_meta_box_sanitize_cb_checkboxes( $taxonomy, $terms ) { 2271 return array_map( 'intval', $terms ); 2272 } 2273 2274 /** 2275 * Sanitizes POST values from an input taxonomy metabox. 2276 * 2277 * @since 5.1.0 2278 * 2279 * @param string $taxonomy The taxonomy name. 2280 * @param array|string $terms Raw term data from the 'tax_input' field. 2281 * @return array 2282 */ 2283 function taxonomy_meta_box_sanitize_cb_input( $taxonomy, $terms ) { 2284 /* 2285 * Assume that a 'tax_input' string is a comma-separated list of term names. 2286 * Some languages may use a character other than a comma as a delimiter, so we standardize on 2287 * commas before parsing the list. 2288 */ 2289 if ( ! is_array( $terms ) ) { 2290 $comma = _x( ',', 'tag delimiter' ); 2291 if ( ',' !== $comma ) { 2292 $terms = str_replace( $comma, ',', $terms ); 2293 } 2294 $terms = explode( ',', trim( $terms, " \n\t\r\0\x0B," ) ); 2295 } 2296 2297 $clean_terms = array(); 2298 foreach ( $terms as $term ) { 2299 // Empty terms are invalid input. 2300 if ( empty( $term ) ) { 2301 continue; 2302 } 2303 2304 $_term = get_terms( 2305 array( 2306 'taxonomy' => $taxonomy, 2307 'name' => $term, 2308 'fields' => 'ids', 2309 'hide_empty' => false, 2310 ) 2311 ); 2312 2313 if ( ! empty( $_term ) ) { 2314 $clean_terms[] = (int) $_term[0]; 2315 } else { 2316 // No existing term was found, so pass the string. A new term will be created. 2317 $clean_terms[] = $term; 2318 } 2319 } 2320 2321 return $clean_terms; 2322 } 2323 2324 /** 2325 * Prepares server-registered blocks for the block editor. 2326 * 2327 * Returns an associative array of registered block data keyed by block name. Data includes properties 2328 * of a block relevant for client registration. 2329 * 2330 * @since 5.0.0 2331 * @since 6.3.0 Added `selectors` field. 2332 * @since 6.4.0 Added `block_hooks` field. 2333 * 2334 * @return array An associative array of registered block data. 2335 */ 2336 function get_block_editor_server_block_settings() { 2337 $block_registry = WP_Block_Type_Registry::get_instance(); 2338 $blocks = array(); 2339 $fields_to_pick = array( 2340 'api_version' => 'apiVersion', 2341 'title' => 'title', 2342 'description' => 'description', 2343 'icon' => 'icon', 2344 'attributes' => 'attributes', 2345 'provides_context' => 'providesContext', 2346 'uses_context' => 'usesContext', 2347 'block_hooks' => 'blockHooks', 2348 'selectors' => 'selectors', 2349 'supports' => 'supports', 2350 'category' => 'category', 2351 'styles' => 'styles', 2352 'textdomain' => 'textdomain', 2353 'parent' => 'parent', 2354 'ancestor' => 'ancestor', 2355 'keywords' => 'keywords', 2356 'example' => 'example', 2357 'variations' => 'variations', 2358 'allowed_blocks' => 'allowedBlocks', 2359 ); 2360 2361 foreach ( $block_registry->get_all_registered() as $block_name => $block_type ) { 2362 foreach ( $fields_to_pick as $field => $key ) { 2363 if ( ! isset( $block_type->{ $field } ) ) { 2364 continue; 2365 } 2366 2367 if ( ! isset( $blocks[ $block_name ] ) ) { 2368 $blocks[ $block_name ] = array(); 2369 } 2370 2371 $blocks[ $block_name ][ $key ] = $block_type->{ $field }; 2372 } 2373 } 2374 2375 return $blocks; 2376 } 2377 2378 /** 2379 * Renders the meta boxes forms. 2380 * 2381 * @since 5.0.0 2382 * 2383 * @global WP_Post $post Global post object. 2384 * @global WP_Screen $current_screen WordPress current screen object. 2385 * @global array $wp_meta_boxes Global meta box state. 2386 */ 2387 function the_block_editor_meta_boxes() { 2388 global $post, $current_screen, $wp_meta_boxes; 2389 2390 // Handle meta box state. 2391 $_original_meta_boxes = $wp_meta_boxes; 2392 2393 /** 2394 * Fires right before the meta boxes are rendered. 2395 * 2396 * This allows for the filtering of meta box data, that should already be 2397 * present by this point. Do not use as a means of adding meta box data. 2398 * 2399 * @since 5.0.0 2400 * 2401 * @param array $wp_meta_boxes Global meta box state. 2402 */ 2403 $wp_meta_boxes = apply_filters( 'filter_block_editor_meta_boxes', $wp_meta_boxes ); 2404 $locations = array( 'side', 'normal', 'advanced' ); 2405 $priorities = array( 'high', 'sorted', 'core', 'default', 'low' ); 2406 2407 // Render meta boxes. 2408 ?> 2409 <form class="metabox-base-form"> 2410 <?php the_block_editor_meta_box_post_form_hidden_fields( $post ); ?> 2411 </form> 2412 <form id="toggle-custom-fields-form" method="post" action="<?php echo esc_url( admin_url( 'post.php' ) ); ?>"> 2413 <?php wp_nonce_field( 'toggle-custom-fields', 'toggle-custom-fields-nonce' ); ?> 2414 <input type="hidden" name="action" value="toggle-custom-fields" /> 2415 </form> 2416 <?php foreach ( $locations as $location ) : ?> 2417 <form class="metabox-location-<?php echo esc_attr( $location ); ?>" onsubmit="return false;"> 2418 <div id="poststuff" class="sidebar-open"> 2419 <div id="postbox-container-2" class="postbox-container"> 2420 <?php 2421 do_meta_boxes( 2422 $current_screen, 2423 $location, 2424 $post 2425 ); 2426 ?> 2427 </div> 2428 </div> 2429 </form> 2430 <?php endforeach; ?> 2431 <?php 2432 2433 $meta_boxes_per_location = array(); 2434 foreach ( $locations as $location ) { 2435 $meta_boxes_per_location[ $location ] = array(); 2436 2437 if ( ! isset( $wp_meta_boxes[ $current_screen->id ][ $location ] ) ) { 2438 continue; 2439 } 2440 2441 foreach ( $priorities as $priority ) { 2442 if ( ! isset( $wp_meta_boxes[ $current_screen->id ][ $location ][ $priority ] ) ) { 2443 continue; 2444 } 2445 2446 $meta_boxes = (array) $wp_meta_boxes[ $current_screen->id ][ $location ][ $priority ]; 2447 foreach ( $meta_boxes as $meta_box ) { 2448 if ( false === $meta_box || ! $meta_box['title'] ) { 2449 continue; 2450 } 2451 2452 // If a meta box is just here for back compat, don't show it in the block editor. 2453 if ( isset( $meta_box['args']['__back_compat_meta_box'] ) && $meta_box['args']['__back_compat_meta_box'] ) { 2454 continue; 2455 } 2456 2457 $meta_boxes_per_location[ $location ][] = array( 2458 'id' => $meta_box['id'], 2459 'title' => $meta_box['title'], 2460 ); 2461 } 2462 } 2463 } 2464 2465 /* 2466 * Sadly we probably cannot add this data directly into editor settings. 2467 * 2468 * Some meta boxes need `admin_head` to fire for meta box registry. 2469 * `admin_head` fires after `admin_enqueue_scripts`, which is where we create 2470 * our editor instance. 2471 */ 2472 $script = 'window._wpLoadBlockEditor.then( function() { 2473 wp.data.dispatch( \'core/edit-post\' ).setAvailableMetaBoxesPerLocation( ' . wp_json_encode( $meta_boxes_per_location, JSON_HEX_TAG | JSON_UNESCAPED_SLASHES ) . ' ); 2474 } );'; 2475 2476 wp_add_inline_script( 'wp-edit-post', $script ); 2477 2478 /* 2479 * When `wp-edit-post` is output in the `<head>`, the inline script needs to be manually printed. 2480 * Otherwise, meta boxes will not display because inline scripts for `wp-edit-post` 2481 * will not be printed again after this point. 2482 */ 2483 if ( wp_script_is( 'wp-edit-post', 'done' ) ) { 2484 printf( "<script>\n%s\n</script>\n", trim( $script ) ); 2485 } 2486 2487 /* 2488 * If the 'postcustom' meta box is enabled, then we need to perform 2489 * some extra initialization on it. 2490 */ 2491 $enable_custom_fields = (bool) get_user_meta( get_current_user_id(), 'enable_custom_fields', true ); 2492 2493 if ( $enable_custom_fields ) { 2494 $script = "( function( $ ) { 2495 if ( $('#postcustom').length ) { 2496 $( '#the-list' ).wpList( { 2497 addBefore: function( s ) { 2498 s.data += '&post_id=$post->ID'; 2499 return s; 2500 }, 2501 addAfter: function() { 2502 $('table#list-table').show(); 2503 } 2504 }); 2505 } 2506 } )( jQuery );"; 2507 wp_enqueue_script( 'wp-lists' ); 2508 wp_add_inline_script( 'wp-lists', $script ); 2509 } 2510 2511 /* 2512 * Refresh nonces used by the meta box loader. 2513 * 2514 * The logic is very similar to that provided by post.js for the classic editor. 2515 */ 2516 $script = "( function( $ ) { 2517 var check, timeout; 2518 2519 function schedule() { 2520 check = false; 2521 window.clearTimeout( timeout ); 2522 timeout = window.setTimeout( function() { check = true; }, 300000 ); 2523 } 2524 2525 $( document ).on( 'heartbeat-send.wp-refresh-nonces', function( e, data ) { 2526 var post_id, \$authCheck = $( '#wp-auth-check-wrap' ); 2527 2528 if ( check || ( \$authCheck.length && ! \$authCheck.hasClass( 'hidden' ) ) ) { 2529 if ( ( post_id = $( '#post_ID' ).val() ) && $( '#_wpnonce' ).val() ) { 2530 data['wp-refresh-metabox-loader-nonces'] = { 2531 post_id: post_id 2532 }; 2533 } 2534 } 2535 }).on( 'heartbeat-tick.wp-refresh-nonces', function( e, data ) { 2536 var nonces = data['wp-refresh-metabox-loader-nonces']; 2537 2538 if ( nonces ) { 2539 if ( nonces.replace ) { 2540 if ( nonces.replace.metabox_loader_nonce && window._wpMetaBoxUrl && wp.url ) { 2541 window._wpMetaBoxUrl= wp.url.addQueryArgs( window._wpMetaBoxUrl, { 'meta-box-loader-nonce': nonces.replace.metabox_loader_nonce } ); 2542 } 2543 2544 if ( nonces.replace._wpnonce ) { 2545 $( '#_wpnonce' ).val( nonces.replace._wpnonce ); 2546 } 2547 } 2548 } 2549 }).ready( function() { 2550 schedule(); 2551 }); 2552 } )( jQuery );"; 2553 wp_add_inline_script( 'heartbeat', $script ); 2554 2555 // Reset meta box data. 2556 $wp_meta_boxes = $_original_meta_boxes; 2557 } 2558 2559 /** 2560 * Renders the hidden form required for the meta boxes form. 2561 * 2562 * @since 5.0.0 2563 * 2564 * @param WP_Post $post Current post object. 2565 */ 2566 function the_block_editor_meta_box_post_form_hidden_fields( $post ) { 2567 $form_extra = ''; 2568 if ( 'auto-draft' === $post->post_status ) { 2569 $form_extra .= "<input type='hidden' id='auto_draft' name='auto_draft' value='1' />"; 2570 } 2571 $form_action = 'editpost'; 2572 $nonce_action = 'update-post_' . $post->ID; 2573 $form_extra .= "<input type='hidden' id='post_ID' name='post_ID' value='" . esc_attr( $post->ID ) . "' />"; 2574 $referer = wp_get_referer(); 2575 $current_user = wp_get_current_user(); 2576 $user_id = $current_user->ID; 2577 wp_nonce_field( $nonce_action ); 2578 2579 /* 2580 * Some meta boxes hook into these actions to add hidden input fields in the classic post form. 2581 * For backward compatibility, we can capture the output from these actions, 2582 * and extract the hidden input fields. 2583 */ 2584 ob_start(); 2585 /** This filter is documented in wp-admin/edit-form-advanced.php */ 2586 do_action( 'edit_form_after_title', $post ); 2587 /** This filter is documented in wp-admin/edit-form-advanced.php */ 2588 do_action( 'edit_form_advanced', $post ); 2589 $classic_output = ob_get_clean(); 2590 2591 $classic_elements = wp_html_split( $classic_output ); 2592 2593 foreach ( $classic_elements as $element ) { 2594 if ( ! str_starts_with( $element, '<input ' ) ) { 2595 continue; 2596 } 2597 2598 if ( preg_match( '/\stype=[\'"]hidden[\'"]\s/', $element ) ) { 2599 echo $element; 2600 } 2601 } 2602 ?> 2603 <input type="hidden" id="user-id" name="user_ID" value="<?php echo (int) $user_id; ?>" /> 2604 <input type="hidden" id="hiddenaction" name="action" value="<?php echo esc_attr( $form_action ); ?>" /> 2605 <input type="hidden" id="originalaction" name="originalaction" value="<?php echo esc_attr( $form_action ); ?>" /> 2606 <input type="hidden" id="post_type" name="post_type" value="<?php echo esc_attr( $post->post_type ); ?>" /> 2607 <input type="hidden" id="original_post_status" name="original_post_status" value="<?php echo esc_attr( $post->post_status ); ?>" /> 2608 <input type="hidden" id="referredby" name="referredby" value="<?php echo $referer ? esc_url( $referer ) : ''; ?>" /> 2609 2610 <?php 2611 if ( 'draft' !== get_post_status( $post ) ) { 2612 wp_original_referer_field( true, 'previous' ); 2613 } 2614 echo $form_extra; 2615 wp_nonce_field( 'meta-box-order', 'meta-box-order-nonce', false ); 2616 wp_nonce_field( 'closedpostboxes', 'closedpostboxesnonce', false ); 2617 // Permalink title nonce. 2618 wp_nonce_field( 'samplepermalink', 'samplepermalinknonce', false ); 2619 2620 /** 2621 * Adds hidden input fields to the meta box save form. 2622 * 2623 * Hook into this action to print `<input type="hidden" ... />` fields, which will be POSTed back to 2624 * the server when meta boxes are saved. 2625 * 2626 * @since 5.0.0 2627 * 2628 * @param WP_Post $post The post that is being edited. 2629 */ 2630 do_action( 'block_editor_meta_box_hidden_fields', $post ); 2631 } 2632 2633 /** 2634 * Disables block editor for wp_navigation type posts so they can be managed via the UI. 2635 * 2636 * @since 5.9.0 2637 * @access private 2638 * 2639 * @param bool $value Whether the CPT supports block editor or not. 2640 * @param string $post_type Post type. 2641 * @return bool Whether the block editor should be disabled or not. 2642 */ 2643 function _disable_block_editor_for_navigation_post_type( $value, $post_type ) { 2644 if ( 'wp_navigation' === $post_type ) { 2645 return false; 2646 } 2647 2648 return $value; 2649 } 2650 2651 /** 2652 * This callback disables the content editor for wp_navigation type posts. 2653 * Content editor cannot handle wp_navigation type posts correctly. 2654 * We cannot disable the "editor" feature in the wp_navigation's CPT definition 2655 * because it disables the ability to save navigation blocks via REST API. 2656 * 2657 * @since 5.9.0 2658 * @access private 2659 * 2660 * @param WP_Post $post An instance of WP_Post class. 2661 */ 2662 function _disable_content_editor_for_navigation_post_type( $post ) { 2663 $post_type = get_post_type( $post ); 2664 if ( 'wp_navigation' !== $post_type ) { 2665 return; 2666 } 2667 2668 remove_post_type_support( $post_type, 'editor' ); 2669 } 2670 2671 /** 2672 * This callback enables content editor for wp_navigation type posts. 2673 * We need to enable it back because we disable it to hide 2674 * the content editor for wp_navigation type posts. 2675 * 2676 * @since 5.9.0 2677 * @access private 2678 * 2679 * @see _disable_content_editor_for_navigation_post_type 2680 * 2681 * @param WP_Post $post An instance of WP_Post class. 2682 */ 2683 function _enable_content_editor_for_navigation_post_type( $post ) { 2684 $post_type = get_post_type( $post ); 2685 if ( 'wp_navigation' !== $post_type ) { 2686 return; 2687 } 2688 2689 add_post_type_support( $post_type, 'editor' ); 2690 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated : Tue Oct 13 08:20:33 2026 | Cross-referenced by PHPXref |