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