Skip to content

Commit dd67b5d

Browse files
committed
Taxonomy: Update item count dynamically after adding or deleting a term.Fixes #50082.
1 parent e12ddb3 commit dd67b5d

4 files changed

Lines changed: 155 additions & 16 deletions

File tree

src/js/_enqueues/admin/tags.js

Lines changed: 38 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,24 @@ jQuery( function($) {
1313

1414
var addingTerm = false;
1515

16+
/**
17+
* Updates the displaying-num span with the new total item count.
18+
*
19+
* @since 6.8.0
20+
*
21+
* @param {number} total The new total number of items.
22+
* @return {void}
23+
*/
24+
function updateDisplayingNum( total ) {
25+
$( '.displaying-num' ).text(
26+
wp.i18n.sprintf(
27+
/* translators: %s: Number of items. */
28+
wp.i18n._n( '%s item', '%s items', total ),
29+
total.toLocaleString()
30+
)
31+
);
32+
}
33+
1634
/**
1735
* Adds an event handler to the delete term link on the term overview page.
1836
*
@@ -45,10 +63,11 @@ jQuery( function($) {
4563
*
4664
* @return {void}
4765
*/
48-
$.post(ajaxurl, data, function(r){
66+
$.post( ajaxurl, data, function( r ) {
4967
var message;
50-
if ( '1' == r ) {
51-
$('#ajax-response').empty();
68+
69+
if ( r && r.success ) {
70+
$( '#ajax-response' ).empty();
5271
let nextFocus = tr.next( 'tr' ).find( 'a.row-title' );
5372
let prevFocus = tr.prev( 'tr' ).find( 'a.row-title' );
5473
// If there is neither a next row or a previous row, focus the tag input field.
@@ -60,7 +79,7 @@ jQuery( function($) {
6079
}
6180
}
6281

63-
tr.fadeOut('normal', function(){ tr.remove(); });
82+
tr.fadeOut( 'normal', function() { tr.remove(); } );
6483

6584
/**
6685
* Removes the term from the parent box and the tag cloud.
@@ -69,23 +88,25 @@ jQuery( function($) {
6988
* This term ID is then used to select the relevant HTML elements:
7089
* The parent box and the tag cloud.
7190
*/
72-
$('select#parent option[value="' + data.match(/tag_ID=(\d+)/)[1] + '"]').remove();
73-
$('a.tag-link-' + data.match(/tag_ID=(\d+)/)[1]).remove();
91+
$( 'select#parent option[value="' + data.match( /tag_ID=(\d+)/ )[1] + '"]' ).remove();
92+
$( 'a.tag-link-' + data.match( /tag_ID=(\d+)/ )[1] ).remove();
7493
nextFocus.trigger( 'focus' );
94+
updateDisplayingNum( r.data.total );
7595
message = wp.i18n.__( 'The selected tag has been deleted.' );
76-
77-
} else if ( '-1' == r ) {
96+
97+
} else if ( '-1' === r ) {
7898
message = wp.i18n.__( 'Sorry, you are not allowed to do that.' );
79-
$('#ajax-response').empty().append('<div class="notice notice-error"><p>' + message + '</p></div>');
99+
$( '#ajax-response' ).empty().append( '<div class="notice notice-error"><p>' + message + '</p></div>' );
80100
resetRowAfterFailure( tr );
81101

82102
} else {
83103
message = wp.i18n.__( 'An error occurred while processing your request. Please try again later.' );
84-
$('#ajax-response').empty().append('<div class="notice notice-error"><p>' + message + '</p></div>');
104+
$( '#ajax-response' ).empty().append( '<div class="notice notice-error"><p>' + message + '</p></div>' );
85105
resetRowAfterFailure( tr );
86106
}
107+
87108
wp.a11y.speak( message, 'assertive' );
88-
});
109+
} );
89110
}
90111

91112
return false;
@@ -179,19 +200,22 @@ jQuery( function($) {
179200

180201
$('.tags .no-items').remove();
181202

182-
if ( form.find('select#parent') ) {
203+
if ( form.find( 'select#parent' ) ) {
183204
// Parents field exists, Add new term to the list.
184205
term = res.responses[1].supplemental;
185206

186207
// Create an indent for the Parent field.
187208
indent = '';
188-
for ( i = 0; i < res.responses[1].position; i++ )
209+
for ( i = 0; i < res.responses[1].position; i++ ) {
189210
indent += '&nbsp;&nbsp;&nbsp;';
211+
}
190212

191213
form.find( 'select#parent option:selected' ).after( '<option value="' + term.term_id + '">' + indent + term.name + '</option>' );
192214
}
193215

194-
$('input:not([type="checkbox"]):not([type="radio"]):not([type="button"]):not([type="submit"]):not([type="reset"]):visible, textarea:visible', form).val('');
216+
updateDisplayingNum( res.responses[0].supplemental.total );
217+
218+
$( 'input:not([type="checkbox"]):not([type="radio"]):not([type="button"]):not([type="submit"]):not([type="reset"]):visible, textarea:visible', form ).val( '' );
195219
});
196220

197221
return false;

src/wp-admin/includes/ajax-actions.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -805,9 +805,10 @@ function wp_ajax_delete_tag() {
805805
}
806806

807807
if ( wp_delete_term( $tag_id, $taxonomy ) ) {
808-
wp_die( 1 );
808+
$total = wp_count_terms( array( 'taxonomy' => $taxonomy ) );
809+
wp_send_json_success( array( 'total' => (int) $total ) );
809810
} else {
810-
wp_die( 0 );
811+
wp_send_json_error();
811812
}
812813
}
813814

@@ -1153,6 +1154,8 @@ function wp_ajax_add_tag() {
11531154
$message = $messages['_item'][1];
11541155
}
11551156

1157+
$total = wp_count_terms( array( 'taxonomy' => $taxonomy ) );
1158+
11561159
$response->add(
11571160
array(
11581161
'what' => 'taxonomy',
@@ -1161,6 +1164,7 @@ function wp_ajax_add_tag() {
11611164
'parents' => $parents,
11621165
'noparents' => $no_parents,
11631166
'notice' => $message,
1167+
'total' => (int) $total,
11641168
),
11651169
)
11661170
);

tests/phpunit/tests/ajax/wpAjaxAddTag.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,41 @@ public function test_adding_existing_category_should_error() {
139139
$this->assertSame( $expected, (string) $this->get_xml_response_taxonomy()->wp_error );
140140
}
141141

142+
/**
143+
* Tests that the add tag AJAX response includes the total term count.
144+
*
145+
* @ticket 50082
146+
*
147+
* @covers ::wp_ajax_add_tag
148+
* @covers ::wp_count_terms
149+
*/
150+
public function test_add_tag_returns_total_count() {
151+
$this->_setRole( 'administrator' );
152+
153+
wp_insert_term( 'existing-tag', 'post_tag' );
154+
155+
$_POST = array(
156+
'taxonomy' => 'post_tag',
157+
'post_type' => 'post',
158+
'screen' => 'edit-post_tag',
159+
'action' => 'add-tag',
160+
'tag-name' => 'new-tag',
161+
'_wpnonce_add-tag' => wp_create_nonce( 'add-tag' ),
162+
);
163+
164+
try {
165+
$this->_handleAjax( 'add-tag' );
166+
} catch ( WPAjaxDieContinueException $e ) {
167+
unset( $e );
168+
}
169+
170+
$xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA );
171+
$total = (int) $xml->response->taxonomy->supplemental->total;
172+
173+
// Two terms now exist: 'existing-tag' and 'new-tag'.
174+
$this->assertSame( 2, $total );
175+
}
176+
142177
/**
143178
* Helper method to get the taxonomy's response or error.
144179
*
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
<?php
2+
3+
/**
4+
* Admin ajax functions to be tested.
5+
*/
6+
require_once ABSPATH . 'wp-admin/includes/ajax-actions.php';
7+
8+
/**
9+
* Class for testing ajax delete tag functionality.
10+
*
11+
* @group ajax
12+
*
13+
* @covers ::wp_ajax_delete_tag
14+
*/
15+
class Tests_Ajax_wpAjaxDeleteTag extends WP_Ajax_UnitTestCase {
16+
17+
/**
18+
* Tests that deleting a tag returns JSON success with updated total count.
19+
*
20+
* @ticket 50082
21+
*
22+
* @covers ::wp_ajax_delete_tag
23+
* @covers ::wp_count_terms
24+
*/
25+
public function test_delete_tag_returns_json_success_with_total() {
26+
$this->_setRole( 'administrator' );
27+
28+
$term = wp_insert_term( 'tag-to-delete', 'post_tag' );
29+
wp_insert_term( 'tag-to-keep', 'post_tag' );
30+
31+
$_POST = array(
32+
'action' => 'delete-tag',
33+
'tag_ID' => $term['term_id'],
34+
'taxonomy' => 'post_tag',
35+
'_wpnonce' => wp_create_nonce( 'delete-tag_' . $term['term_id'] ),
36+
);
37+
38+
try {
39+
$this->_handleAjax( 'delete-tag' );
40+
} catch ( WPAjaxDieContinueException $e ) {
41+
unset( $e );
42+
}
43+
44+
$response = json_decode( $this->_last_response, true );
45+
46+
$this->assertTrue( $response['success'] );
47+
// One term remains after deletion.
48+
$this->assertSame( 1, $response['data']['total'] );
49+
}
50+
51+
/**
52+
* Tests that deleting a tag without permission returns -1.
53+
*
54+
* @ticket 50082
55+
*
56+
* @covers ::wp_ajax_delete_tag
57+
*/
58+
public function test_delete_tag_without_capability_should_error() {
59+
$this->_setRole( 'subscriber' );
60+
61+
$term = self::factory()->term->create_and_get(
62+
array( 'taxonomy' => 'post_tag' )
63+
);
64+
65+
$_POST = array(
66+
'action' => 'delete-tag',
67+
'tag_ID' => $term->term_id,
68+
'taxonomy' => 'post_tag',
69+
'_wpnonce' => wp_create_nonce( 'delete-tag_' . $term->term_id ),
70+
);
71+
72+
$this->expectException( 'WPAjaxDieStopException' );
73+
$this->expectExceptionMessage( '-1' );
74+
$this->_handleAjax( 'delete-tag' );
75+
}
76+
}

0 commit comments

Comments
 (0)