diff --git a/src/js/_enqueues/lib/ajax-response.js b/src/js/_enqueues/lib/ajax-response.js index 38816f3c38..659b91e023 100644 --- a/src/js/_enqueues/lib/ajax-response.js +++ b/src/js/_enqueues/lib/ajax-response.js @@ -18,7 +18,7 @@ window.wpAjax = jQuery.extend( { return r; }, parseAjaxResponse: function( x, r, e ) { // 1 = good, 0 = strange (bad data?), -1 = you lack permission. - var parsed = {}, re = jQuery('#' + r).empty(), err = ''; + var parsed = {}, re = jQuery('#' + r).empty(), err = '', successmsg = ''; if ( x && typeof x === 'object' && x.getElementsByTagName('wp_ajax') ) { parsed.responses = []; @@ -27,6 +27,7 @@ window.wpAjax = jQuery.extend( { var th = jQuery(this), child = jQuery(this.firstChild), response; response = { action: th.attr('action'), what: child.get(0).nodeName, id: child.attr('id'), oldId: child.attr('old_id'), position: child.attr('position') }; response.data = jQuery( 'response_data', child ).text(); + successmsg += response.data; response.supplemental = {}; if ( !jQuery( 'supplemental', child ).children().each( function() { response.supplemental[this.nodeName] = jQuery(this).text(); @@ -46,7 +47,14 @@ window.wpAjax = jQuery.extend( { } ).length ) { response.errors = false; } parsed.responses.push( response ); } ); - if ( err.length ) { re.html( '<div class="error">' + err + '</div>' ); } + if ( err.length ) { + re.html( '<div class="error">' + err + '</div>' ); + wp.a11y.speak( err ); + } else { + re.html( '<div class="updated notice is-dismissible"><p>' + successmsg + '</p></div>'); + jQuery(document).trigger( 'wp-updates-notice-added' ); + wp.a11y.speak( successmsg ); + } return parsed; } if ( isNaN(x) ) { return !re.html('<div class="error"><p>' + x + '</p></div>'); } diff --git a/src/wp-admin/includes/ajax-actions.php b/src/wp-admin/includes/ajax-actions.php index 1ba480f2f1..191e5b54dd 100644 --- a/src/wp-admin/includes/ajax-actions.php +++ b/src/wp-admin/includes/ajax-actions.php @@ -1102,9 +1102,19 @@ function wp_ajax_add_tag() { $wp_list_table->single_row( $tag ); $parents = ob_get_clean(); + require ABSPATH . 'wp-admin/includes/edit-tag-messages.php'; + + $message = ''; + if ( isset( $messages[ $tax->name ][1] ) ) { + $message = $messages[ $tax->name ][1]; + } elseif ( isset( $messages['_item'][1] ) ) { + $message = $messages['_item'][1]; + } + $x->add( array( 'what' => 'taxonomy', + 'data' => $message, 'supplemental' => compact( 'parents', 'noparents' ), ) ); diff --git a/tests/phpunit/tests/ajax/AddTag.php b/tests/phpunit/tests/ajax/AddTag.php new file mode 100644 index 0000000000..48b5fc5f24 --- /dev/null +++ b/tests/phpunit/tests/ajax/AddTag.php @@ -0,0 +1,145 @@ +<?php + +/** + * Admin ajax functions to be tested. + */ +require_once ABSPATH . 'wp-admin/includes/ajax-actions.php'; + +/** + * Class for testing ajax add tag functionality. + * + * @group ajax + */ +class Tests_Ajax_AddTag extends WP_Ajax_UnitTestCase { + + /** + * @dataProvider data_add_tag + * + * @ticket 42937 + * + * @param array $post_data Data to populate $_POST. + * @param string $expected Expected response. + * @param array|string|callable $callback Optional. Callback to register to 'term_updated_messages' + * filter. Default empty string (no callback). + */ + public function test_add_tag( array $post_data, $expected, $callback = '' ) { + $this->_setRole( 'administrator' ); + + $_POST = $post_data; + $_POST['_wpnonce_add-tag'] = wp_create_nonce( 'add-tag' ); + + if ( ! empty( $callback ) ) { + add_filter( 'term_updated_messages', $callback ); + } + + try { + $this->_handleAjax( 'add-tag' ); + } catch ( WPAjaxDieContinueException $e ) { + unset( $e ); + } + + $this->assertSame( $expected, (string) $this->get_xml_response_taxonomy()->response_data ); + } + + /** + * Data provider. + * + * @return array + */ + public function data_add_tag() { + return array( + 'add a category' => array( + 'post_data' => array( + 'taxonomy' => 'category', + 'post_type' => 'post', + 'screen' => 'edit-category', + 'action' => 'add-tag', + 'tag-name' => 'blues', + ), + 'expected' => 'Category added.', + ), + 'add a category with message filtering' => array( + 'post_data' => array( + 'taxonomy' => 'category', + 'post_type' => 'post', + 'screen' => 'edit-category', + 'action' => 'add-tag', + 'tag-name' => 'techno', + ), + 'expected' => 'A new category added.', + 'callback' => static function( array $messages ) { + $messages['category'][1] = 'A new category added.'; + return $messages; + }, + ), + 'add a post_tag' => array( + 'post_data' => array( + 'taxonomy' => 'post_tag', + 'post_type' => 'post', + 'screen' => 'edit-post_tag', + 'action' => 'add-tag', + 'tag-name' => 'Louis Armstrong', + ), + 'expected' => 'Tag added.', + ), + ); + } + + /** + * @ticket 42937 + */ + public function test_adding_category_without_capability_should_error() { + $this->_setRole( 'subscriber' ); + + $_POST['taxonomy'] = 'category'; + $_POST['post_type'] = 'post'; + $_POST['screen'] = 'edit-category'; + $_POST['action'] = 'add-tag'; + $_POST['tag - name'] = 'disco'; + $_POST['_wpnonce_add-tag'] = wp_create_nonce( 'add-tag' ); + + $this->expectException( 'WPAjaxDieStopException' ); + $this->expectExceptionMessage( '-1' ); + $this->_handleAjax( 'add-tag' ); + } + + /** + * @ticket 42937 + */ + public function test_adding_existing_category_should_error() { + $this->_setRole( 'administrator' ); + + wp_insert_term( 'testcat', 'category' ); + + $_POST = array( + 'taxonomy' => 'category', + 'post_type' => 'post', + 'screen' => 'edit-category', + 'action' => 'add-tag', + 'tag-name' => 'testcat', + '_wpnonce_add-tag' => wp_create_nonce( 'add-tag' ), + ); + + try { + $this->_handleAjax( 'add-tag' ); + } catch ( WPAjaxDieContinueException $e ) { + unset( $e ); + } + + $expected = 'A term with the name provided already exists with this parent.'; + $this->assertSame( $expected, (string) $this->get_xml_response_taxonomy()->wp_error ); + } + + /** + * Helper method to get the taxonomy's response or error. + * + * @since 5.9.0 + * + * @return SimpleXMLElement Response or error object. + */ + private function get_xml_response_taxonomy() { + $xml = simplexml_load_string( $this->_last_response, 'SimpleXMLElement', LIBXML_NOCDATA ); + + return $xml->response->taxonomy; + } +}