diff --git a/src/js/_enqueues/admin/inline-edit-post.js b/src/js/_enqueues/admin/inline-edit-post.js index db06072c4a836..5fff8c3f28825 100644 --- a/src/js/_enqueues/admin/inline-edit-post.js +++ b/src/js/_enqueues/admin/inline-edit-post.js @@ -178,6 +178,9 @@ window.wp = window.wp || {}; */ setBulk : function(){ var te = '', type = this.type, c = true; + var checkedPosts = $( 'tbody th.check-column input[type="checkbox"]:checked' ); + var categories = {}; + var indeterminatePostCategoryField = $( '' ); this.revert(); $( '#bulk-edit td' ).attr( 'colspan', $( 'th:visible, td:visible', '.widefat:first thead' ).length ); @@ -217,6 +220,45 @@ window.wp = window.wp || {}; // Populate the list of items to bulk edit. $( '#bulk-titles' ).html( '' ); + // Gather up some statistics on which of these checked posts are in which categories. + checkedPosts.each( function() { + var id = $( this ).val(); + var checked = $( '#category_' + id ).text().split( ',' ); + + checked.map( function( cid ) { + categories[ cid ] || ( categories[ cid ] = 0 ); + // Just record that this category is checked. + categories[ cid ]++; + } ); + } ); + + // Compute initial states. + $( '.inline-edit-categories input[name="post_category[]"]' ).each( function() { + // Clear indeterminate states. + $( '' ).remove(); + + if ( categories[ $( this ).val() ] == checkedPosts.length ) { + // If the number of checked categories matches the number of selected posts, then all posts are in this category. + $( this ).prop( 'checked', true ); + } else if ( categories[ $( this ).val() ] > 0 ) { + // If the number is less than the number of selected posts, then it's indeterminate. + $( this ).prop( 'indeterminate', true ); + + // Set indeterminate states for the backend. + indeterminatePostCategoryField.val( $( this ).val() ); + $( this ).after( indeterminatePostCategoryField ); + } + } ); + + $( '.inline-edit-categories input[name="post_category[]"]' ).on( 'change', function() { + // Remove the indeterminate flags as there was a specific state change. + $( this ).parent().find( 'input[name="indeterminate_post_category[]"]' ).remove(); + } ); + + $( '.inline-edit-save button' ).on( 'click', function() { + $( '.inline-edit-categories input[name="post_category[]"]' ).prop( 'indeterminate', false ); + } ); + /** * Binds on click events to handle the list of items to bulk edit. * diff --git a/src/wp-admin/css/list-tables.css b/src/wp-admin/css/list-tables.css index 07cbc6229d07a..d9a573ae8d054 100644 --- a/src/wp-admin/css/list-tables.css +++ b/src/wp-admin/css/list-tables.css @@ -1147,6 +1147,17 @@ ul.cat-checklist { overflow-y: scroll; } +ul.cat-checklist input[name="post_category[]"]:indeterminate::before { + content: ''; + border-top: 2px solid grey; + width: 65%; + height: 2px; + position: absolute; + top: calc( 50% + 1px ); + left: 50%; + transform: translate( -50%, -50% ); +} + #bulk-titles .ntdelbutton, #bulk-titles .ntdeltitle, .inline-edit-row fieldset ul.cat-checklist label { diff --git a/src/wp-admin/includes/post.php b/src/wp-admin/includes/post.php index 3709ded67fdd5..8d7f76b3fe063 100644 --- a/src/wp-admin/includes/post.php +++ b/src/wp-admin/includes/post.php @@ -641,8 +641,21 @@ function bulk_edit_posts( $post_data = null ) { } if ( isset( $new_cats ) && in_array( 'category', $tax_names, true ) ) { - $cats = (array) wp_get_post_categories( $post_id ); - $post_data['post_category'] = array_unique( array_merge( $cats, $new_cats ) ); + $cats = (array) wp_get_post_categories( $post_id ); + + if ( + isset( $post_data['indeterminate_post_category'] ) + && is_array( $post_data['indeterminate_post_category'] ) + ) { + $indeterminate_post_category = $post_data['indeterminate_post_category']; + } else { + $indeterminate_post_category = array(); + } + + $indeterminate_cats = array_intersect( $cats, $indeterminate_post_category ); + $determinate_cats = array_diff( $new_cats, $indeterminate_post_category ); + $post_data['post_category'] = array_unique( array_merge( $indeterminate_cats, $determinate_cats ) ); + unset( $post_data['tax_input']['category'] ); }