[ Index ] |
PHP Cross Reference of WordPress |
[Summary view] [Print] [Text view]
1 /** 2 * Handles updating and editing comments. 3 * 4 * @file This file contains functionality for the admin comments page. 5 * @since 2.1.0 6 * @output wp-admin/js/edit-comments.js 7 */ 8 9 /* global adminCommentsSettings, thousandsSeparator, list_args, QTags, ajaxurl, wpAjax */ 10 /* global commentReply, theExtraList, theList, setCommentsList */ 11 12 (function($) { 13 var getCount, updateCount, updateCountText, updatePending, updateApproved, 14 updateHtmlTitle, updateDashboardText, updateInModerationText, adminTitle = document.title, 15 isDashboard = $('#dashboard_right_now').length, 16 titleDiv, titleRegEx, 17 __ = wp.i18n.__; 18 19 /** 20 * Extracts a number from the content of a jQuery element. 21 * 22 * @since 2.9.0 23 * @access private 24 * 25 * @param {jQuery} el jQuery element. 26 * 27 * @return {number} The number found in the given element. 28 */ 29 getCount = function(el) { 30 var n = parseInt( el.html().replace(/[^0-9]+/g, ''), 10 ); 31 if ( isNaN(n) ) { 32 return 0; 33 } 34 return n; 35 }; 36 37 /** 38 * Updates an html element with a localized number string. 39 * 40 * @since 2.9.0 41 * @access private 42 * 43 * @param {jQuery} el The jQuery element to update. 44 * @param {number} n Number to be put in the element. 45 * 46 * @return {void} 47 */ 48 updateCount = function(el, n) { 49 var n1 = ''; 50 if ( isNaN(n) ) { 51 return; 52 } 53 n = n < 1 ? '0' : n.toString(); 54 if ( n.length > 3 ) { 55 while ( n.length > 3 ) { 56 n1 = thousandsSeparator + n.substr(n.length - 3) + n1; 57 n = n.substr(0, n.length - 3); 58 } 59 n = n + n1; 60 } 61 el.html(n); 62 }; 63 64 /** 65 * Updates the number of approved comments on a specific post and the filter bar. 66 * 67 * @since 4.4.0 68 * @access private 69 * 70 * @param {number} diff The amount to lower or raise the approved count with. 71 * @param {number} commentPostId The ID of the post to be updated. 72 * 73 * @return {void} 74 */ 75 updateApproved = function( diff, commentPostId ) { 76 var postSelector = '.post-com-count-' + commentPostId, 77 noClass = 'comment-count-no-comments', 78 approvedClass = 'comment-count-approved', 79 approved, 80 noComments; 81 82 updateCountText( 'span.approved-count', diff ); 83 84 if ( ! commentPostId ) { 85 return; 86 } 87 88 // Cache selectors to not get duplicates. 89 approved = $( 'span.' + approvedClass, postSelector ); 90 noComments = $( 'span.' + noClass, postSelector ); 91 92 approved.each(function() { 93 var a = $(this), n = getCount(a) + diff; 94 if ( n < 1 ) 95 n = 0; 96 97 if ( 0 === n ) { 98 a.removeClass( approvedClass ).addClass( noClass ); 99 } else { 100 a.addClass( approvedClass ).removeClass( noClass ); 101 } 102 updateCount( a, n ); 103 }); 104 105 noComments.each(function() { 106 var a = $(this); 107 if ( diff > 0 ) { 108 a.removeClass( noClass ).addClass( approvedClass ); 109 } else { 110 a.addClass( noClass ).removeClass( approvedClass ); 111 } 112 updateCount( a, diff ); 113 }); 114 }; 115 116 /** 117 * Updates a number count in all matched HTML elements 118 * 119 * @since 4.4.0 120 * @access private 121 * 122 * @param {string} selector The jQuery selector for elements to update a count 123 * for. 124 * @param {number} diff The amount to lower or raise the count with. 125 * 126 * @return {void} 127 */ 128 updateCountText = function( selector, diff ) { 129 $( selector ).each(function() { 130 var a = $(this), n = getCount(a) + diff; 131 if ( n < 1 ) { 132 n = 0; 133 } 134 updateCount( a, n ); 135 }); 136 }; 137 138 /** 139 * Updates a text about comment count on the dashboard. 140 * 141 * @since 4.4.0 142 * @access private 143 * 144 * @param {Object} response Ajax response from the server that includes a 145 * translated "comment count" message. 146 * 147 * @return {void} 148 */ 149 updateDashboardText = function( response ) { 150 if ( ! isDashboard || ! response || ! response.i18n_comments_text ) { 151 return; 152 } 153 154 $( '.comment-count a', '#dashboard_right_now' ).text( response.i18n_comments_text ); 155 }; 156 157 /** 158 * Updates the "comments in moderation" text across the UI. 159 * 160 * @since 5.2.0 161 * 162 * @param {Object} response Ajax response from the server that includes a 163 * translated "comments in moderation" message. 164 * 165 * @return {void} 166 */ 167 updateInModerationText = function( response ) { 168 if ( ! response || ! response.i18n_moderation_text ) { 169 return; 170 } 171 172 // Update the "comment in moderation" text across the UI. 173 $( '.comments-in-moderation-text' ).text( response.i18n_moderation_text ); 174 // Hide the "comment in moderation" text in the Dashboard "At a Glance" widget. 175 if ( isDashboard && response.in_moderation ) { 176 $( '.comment-mod-count', '#dashboard_right_now' ) 177 [ response.in_moderation > 0 ? 'removeClass' : 'addClass' ]( 'hidden' ); 178 } 179 }; 180 181 /** 182 * Updates the title of the document with the number comments to be approved. 183 * 184 * @since 4.4.0 185 * @access private 186 * 187 * @param {number} diff The amount to lower or raise the number of to be 188 * approved comments with. 189 * 190 * @return {void} 191 */ 192 updateHtmlTitle = function( diff ) { 193 var newTitle, regExMatch, titleCount, commentFrag; 194 195 /* translators: %s: Comments count. */ 196 titleRegEx = titleRegEx || new RegExp( __( 'Comments (%s)' ).replace( '%s', '\\([0-9' + thousandsSeparator + ']+\\)' ) + '?' ); 197 // Count funcs operate on a $'d element. 198 titleDiv = titleDiv || $( '<div />' ); 199 newTitle = adminTitle; 200 201 commentFrag = titleRegEx.exec( document.title ); 202 if ( commentFrag ) { 203 commentFrag = commentFrag[0]; 204 titleDiv.html( commentFrag ); 205 titleCount = getCount( titleDiv ) + diff; 206 } else { 207 titleDiv.html( 0 ); 208 titleCount = diff; 209 } 210 211 if ( titleCount >= 1 ) { 212 updateCount( titleDiv, titleCount ); 213 regExMatch = titleRegEx.exec( document.title ); 214 if ( regExMatch ) { 215 /* translators: %s: Comments count. */ 216 newTitle = document.title.replace( regExMatch[0], __( 'Comments (%s)' ).replace( '%s', titleDiv.text() ) + ' ' ); 217 } 218 } else { 219 regExMatch = titleRegEx.exec( newTitle ); 220 if ( regExMatch ) { 221 newTitle = newTitle.replace( regExMatch[0], __( 'Comments' ) ); 222 } 223 } 224 document.title = newTitle; 225 }; 226 227 /** 228 * Updates the number of pending comments on a specific post and the filter bar. 229 * 230 * @since 3.2.0 231 * @access private 232 * 233 * @param {number} diff The amount to lower or raise the pending count with. 234 * @param {number} commentPostId The ID of the post to be updated. 235 * 236 * @return {void} 237 */ 238 updatePending = function( diff, commentPostId ) { 239 var postSelector = '.post-com-count-' + commentPostId, 240 noClass = 'comment-count-no-pending', 241 noParentClass = 'post-com-count-no-pending', 242 pendingClass = 'comment-count-pending', 243 pending, 244 noPending; 245 246 if ( ! isDashboard ) { 247 updateHtmlTitle( diff ); 248 } 249 250 $( 'span.pending-count' ).each(function() { 251 var a = $(this), n = getCount(a) + diff; 252 if ( n < 1 ) 253 n = 0; 254 a.closest('.awaiting-mod')[ 0 === n ? 'addClass' : 'removeClass' ]('count-0'); 255 updateCount( a, n ); 256 }); 257 258 if ( ! commentPostId ) { 259 return; 260 } 261 262 // Cache selectors to not get dupes. 263 pending = $( 'span.' + pendingClass, postSelector ); 264 noPending = $( 'span.' + noClass, postSelector ); 265 266 pending.each(function() { 267 var a = $(this), n = getCount(a) + diff; 268 if ( n < 1 ) 269 n = 0; 270 271 if ( 0 === n ) { 272 a.parent().addClass( noParentClass ); 273 a.removeClass( pendingClass ).addClass( noClass ); 274 } else { 275 a.parent().removeClass( noParentClass ); 276 a.addClass( pendingClass ).removeClass( noClass ); 277 } 278 updateCount( a, n ); 279 }); 280 281 noPending.each(function() { 282 var a = $(this); 283 if ( diff > 0 ) { 284 a.parent().removeClass( noParentClass ); 285 a.removeClass( noClass ).addClass( pendingClass ); 286 } else { 287 a.parent().addClass( noParentClass ); 288 a.addClass( noClass ).removeClass( pendingClass ); 289 } 290 updateCount( a, diff ); 291 }); 292 }; 293 294 /** 295 * Initializes the comments list. 296 * 297 * @since 4.4.0 298 * 299 * @global 300 * 301 * @return {void} 302 */ 303 window.setCommentsList = function() { 304 var totalInput, perPageInput, pageInput, dimAfter, delBefore, updateTotalCount, delAfter, refillTheExtraList, diff, 305 lastConfidentTime = 0; 306 307 totalInput = $('input[name="_total"]', '#comments-form'); 308 perPageInput = $('input[name="_per_page"]', '#comments-form'); 309 pageInput = $('input[name="_page"]', '#comments-form'); 310 311 /** 312 * Updates the total with the latest count. 313 * 314 * The time parameter makes sure that we only update the total if this value is 315 * a newer value than we previously received. 316 * 317 * The time and setConfidentTime parameters make sure that we only update the 318 * total when necessary. So a value that has been generated earlier will not 319 * update the total. 320 * 321 * @since 2.8.0 322 * @access private 323 * 324 * @param {number} total Total number of comments. 325 * @param {number} time Unix timestamp of response. 326 * @param {boolean} setConfidentTime Whether to update the last confident time 327 * with the given time. 328 * 329 * @return {void} 330 */ 331 updateTotalCount = function( total, time, setConfidentTime ) { 332 if ( time < lastConfidentTime ) 333 return; 334 335 if ( setConfidentTime ) 336 lastConfidentTime = time; 337 338 totalInput.val( total.toString() ); 339 }; 340 341 /** 342 * Changes DOM that need to be changed after a list item has been dimmed. 343 * 344 * @since 2.5.0 345 * @access private 346 * 347 * @param {Object} r Ajax response object. 348 * @param {Object} settings Settings for the wpList object. 349 * 350 * @return {void} 351 */ 352 dimAfter = function( r, settings ) { 353 var editRow, replyID, replyButton, response, 354 c = $( '#' + settings.element ); 355 356 if ( true !== settings.parsed ) { 357 response = settings.parsed.responses[0]; 358 } 359 360 editRow = $('#replyrow'); 361 replyID = $('#comment_ID', editRow).val(); 362 replyButton = $('#replybtn', editRow); 363 364 if ( c.is('.unapproved') ) { 365 if ( settings.data.id == replyID ) 366 replyButton.text( __( 'Approve and Reply' ) ); 367 368 c.find( '.row-actions span.view' ).addClass( 'hidden' ).end() 369 .find( 'div.comment_status' ).html( '0' ); 370 371 } else { 372 if ( settings.data.id == replyID ) 373 replyButton.text( __( 'Reply' ) ); 374 375 c.find( '.row-actions span.view' ).removeClass( 'hidden' ).end() 376 .find( 'div.comment_status' ).html( '1' ); 377 } 378 379 diff = $('#' + settings.element).is('.' + settings.dimClass) ? 1 : -1; 380 if ( response ) { 381 updateDashboardText( response.supplemental ); 382 updateInModerationText( response.supplemental ); 383 updatePending( diff, response.supplemental.postId ); 384 updateApproved( -1 * diff, response.supplemental.postId ); 385 } else { 386 updatePending( diff ); 387 updateApproved( -1 * diff ); 388 } 389 }; 390 391 /** 392 * Handles marking a comment as spam or trashing the comment. 393 * 394 * Is executed in the list delBefore hook. 395 * 396 * @since 2.8.0 397 * @access private 398 * 399 * @param {Object} settings Settings for the wpList object. 400 * @param {HTMLElement} list Comments table element. 401 * 402 * @return {Object} The settings object. 403 */ 404 delBefore = function( settings, list ) { 405 var note, id, el, n, h, a, author, 406 action = false, 407 wpListsData = $( settings.target ).attr( 'data-wp-lists' ); 408 409 settings.data._total = totalInput.val() || 0; 410 settings.data._per_page = perPageInput.val() || 0; 411 settings.data._page = pageInput.val() || 0; 412 settings.data._url = document.location.href; 413 settings.data.comment_status = $('input[name="comment_status"]', '#comments-form').val(); 414 415 if ( wpListsData.indexOf(':trash=1') != -1 ) 416 action = 'trash'; 417 else if ( wpListsData.indexOf(':spam=1') != -1 ) 418 action = 'spam'; 419 420 if ( action ) { 421 id = wpListsData.replace(/.*?comment-([0-9]+).*/, '$1'); 422 el = $('#comment-' + id); 423 note = $('#' + action + '-undo-holder').html(); 424 425 el.find('.check-column :checkbox').prop('checked', false); // Uncheck the row so as not to be affected by Bulk Edits. 426 427 if ( el.siblings('#replyrow').length && commentReply.cid == id ) 428 commentReply.close(); 429 430 if ( el.is('tr') ) { 431 n = el.children(':visible').length; 432 author = $('.author strong', el).text(); 433 h = $('<tr id="undo-' + id + '" class="undo un' + action + '" style="display:none;"><td colspan="' + n + '">' + note + '</td></tr>'); 434 } else { 435 author = $('.comment-author', el).text(); 436 h = $('<div id="undo-' + id + '" style="display:none;" class="undo un' + action + '">' + note + '</div>'); 437 } 438 439 el.before(h); 440 441 $('strong', '#undo-' + id).text(author); 442 a = $('.undo a', '#undo-' + id); 443 a.attr('href', 'comment.php?action=un' + action + 'comment&c=' + id + '&_wpnonce=' + settings.data._ajax_nonce); 444 a.attr('data-wp-lists', 'delete:the-comment-list:comment-' + id + '::un' + action + '=1'); 445 a.attr('class', 'vim-z vim-destructive aria-button-if-js'); 446 $('.avatar', el).first().clone().prependTo('#undo-' + id + ' .' + action + '-undo-inside'); 447 448 a.on( 'click', function( e ){ 449 e.preventDefault(); 450 e.stopPropagation(); // Ticket #35904. 451 list.wpList.del(this); 452 $('#undo-' + id).css( {backgroundColor:'#ceb'} ).fadeOut(350, function(){ 453 $(this).remove(); 454 $('#comment-' + id).css('backgroundColor', '').fadeIn(300, function(){ $(this).show(); }); 455 }); 456 }); 457 } 458 459 return settings; 460 }; 461 462 /** 463 * Handles actions that need to be done after marking as spam or thrashing a 464 * comment. 465 * 466 * The ajax requests return the unix time stamp a comment was marked as spam or 467 * trashed. We use this to have a correct total amount of comments. 468 * 469 * @since 2.5.0 470 * @access private 471 * 472 * @param {Object} r Ajax response object. 473 * @param {Object} settings Settings for the wpList object. 474 * 475 * @return {void} 476 */ 477 delAfter = function( r, settings ) { 478 var total_items_i18n, total, animated, animatedCallback, 479 response = true === settings.parsed ? {} : settings.parsed.responses[0], 480 commentStatus = true === settings.parsed ? '' : response.supplemental.status, 481 commentPostId = true === settings.parsed ? '' : response.supplemental.postId, 482 newTotal = true === settings.parsed ? '' : response.supplemental, 483 484 targetParent = $( settings.target ).parent(), 485 commentRow = $('#' + settings.element), 486 487 spamDiff, trashDiff, pendingDiff, approvedDiff, 488 489 /* 490 * As `wpList` toggles only the `unapproved` class, the approved comment 491 * rows can have both the `approved` and `unapproved` classes. 492 */ 493 approved = commentRow.hasClass( 'approved' ) && ! commentRow.hasClass( 'unapproved' ), 494 unapproved = commentRow.hasClass( 'unapproved' ), 495 spammed = commentRow.hasClass( 'spam' ), 496 trashed = commentRow.hasClass( 'trash' ), 497 undoing = false; // Ticket #35904. 498 499 updateDashboardText( newTotal ); 500 updateInModerationText( newTotal ); 501 502 /* 503 * The order of these checks is important. 504 * .unspam can also have .approve or .unapprove. 505 * .untrash can also have .approve or .unapprove. 506 */ 507 508 if ( targetParent.is( 'span.undo' ) ) { 509 // The comment was spammed. 510 if ( targetParent.hasClass( 'unspam' ) ) { 511 spamDiff = -1; 512 513 if ( 'trash' === commentStatus ) { 514 trashDiff = 1; 515 } else if ( '1' === commentStatus ) { 516 approvedDiff = 1; 517 } else if ( '0' === commentStatus ) { 518 pendingDiff = 1; 519 } 520 521 // The comment was trashed. 522 } else if ( targetParent.hasClass( 'untrash' ) ) { 523 trashDiff = -1; 524 525 if ( 'spam' === commentStatus ) { 526 spamDiff = 1; 527 } else if ( '1' === commentStatus ) { 528 approvedDiff = 1; 529 } else if ( '0' === commentStatus ) { 530 pendingDiff = 1; 531 } 532 } 533 534 undoing = true; 535 536 // User clicked "Spam". 537 } else if ( targetParent.is( 'span.spam' ) ) { 538 // The comment is currently approved. 539 if ( approved ) { 540 approvedDiff = -1; 541 // The comment is currently pending. 542 } else if ( unapproved ) { 543 pendingDiff = -1; 544 // The comment was in the Trash. 545 } else if ( trashed ) { 546 trashDiff = -1; 547 } 548 // You can't spam an item on the Spam screen. 549 spamDiff = 1; 550 551 // User clicked "Unspam". 552 } else if ( targetParent.is( 'span.unspam' ) ) { 553 if ( approved ) { 554 pendingDiff = 1; 555 } else if ( unapproved ) { 556 approvedDiff = 1; 557 } else if ( trashed ) { 558 // The comment was previously approved. 559 if ( targetParent.hasClass( 'approve' ) ) { 560 approvedDiff = 1; 561 // The comment was previously pending. 562 } else if ( targetParent.hasClass( 'unapprove' ) ) { 563 pendingDiff = 1; 564 } 565 } else if ( spammed ) { 566 if ( targetParent.hasClass( 'approve' ) ) { 567 approvedDiff = 1; 568 569 } else if ( targetParent.hasClass( 'unapprove' ) ) { 570 pendingDiff = 1; 571 } 572 } 573 // You can unspam an item on the Spam screen. 574 spamDiff = -1; 575 576 // User clicked "Trash". 577 } else if ( targetParent.is( 'span.trash' ) ) { 578 if ( approved ) { 579 approvedDiff = -1; 580 } else if ( unapproved ) { 581 pendingDiff = -1; 582 // The comment was in the spam queue. 583 } else if ( spammed ) { 584 spamDiff = -1; 585 } 586 // You can't trash an item on the Trash screen. 587 trashDiff = 1; 588 589 // User clicked "Restore". 590 } else if ( targetParent.is( 'span.untrash' ) ) { 591 if ( approved ) { 592 pendingDiff = 1; 593 } else if ( unapproved ) { 594 approvedDiff = 1; 595 } else if ( trashed ) { 596 if ( targetParent.hasClass( 'approve' ) ) { 597 approvedDiff = 1; 598 } else if ( targetParent.hasClass( 'unapprove' ) ) { 599 pendingDiff = 1; 600 } 601 } 602 // You can't go from Trash to Spam. 603 // You can untrash on the Trash screen. 604 trashDiff = -1; 605 606 // User clicked "Approve". 607 } else if ( targetParent.is( 'span.approve:not(.unspam):not(.untrash)' ) ) { 608 approvedDiff = 1; 609 pendingDiff = -1; 610 611 // User clicked "Unapprove". 612 } else if ( targetParent.is( 'span.unapprove:not(.unspam):not(.untrash)' ) ) { 613 approvedDiff = -1; 614 pendingDiff = 1; 615 616 // User clicked "Delete Permanently". 617 } else if ( targetParent.is( 'span.delete' ) ) { 618 if ( spammed ) { 619 spamDiff = -1; 620 } else if ( trashed ) { 621 trashDiff = -1; 622 } 623 } 624 625 if ( pendingDiff ) { 626 updatePending( pendingDiff, commentPostId ); 627 updateCountText( 'span.all-count', pendingDiff ); 628 } 629 630 if ( approvedDiff ) { 631 updateApproved( approvedDiff, commentPostId ); 632 updateCountText( 'span.all-count', approvedDiff ); 633 } 634 635 if ( spamDiff ) { 636 updateCountText( 'span.spam-count', spamDiff ); 637 } 638 639 if ( trashDiff ) { 640 updateCountText( 'span.trash-count', trashDiff ); 641 } 642 643 if ( 644 ( ( 'trash' === settings.data.comment_status ) && !getCount( $( 'span.trash-count' ) ) ) || 645 ( ( 'spam' === settings.data.comment_status ) && !getCount( $( 'span.spam-count' ) ) ) 646 ) { 647 $( '#delete_all' ).hide(); 648 } 649 650 if ( ! isDashboard ) { 651 total = totalInput.val() ? parseInt( totalInput.val(), 10 ) : 0; 652 if ( $(settings.target).parent().is('span.undo') ) 653 total++; 654 else 655 total--; 656 657 if ( total < 0 ) 658 total = 0; 659 660 if ( 'object' === typeof r ) { 661 if ( response.supplemental.total_items_i18n && lastConfidentTime < response.supplemental.time ) { 662 total_items_i18n = response.supplemental.total_items_i18n || ''; 663 if ( total_items_i18n ) { 664 $('.displaying-num').text( total_items_i18n.replace( ' ', String.fromCharCode( 160 ) ) ); 665 $('.total-pages').text( response.supplemental.total_pages_i18n.replace( ' ', String.fromCharCode( 160 ) ) ); 666 $('.tablenav-pages').find('.next-page, .last-page').toggleClass('disabled', response.supplemental.total_pages == $('.current-page').val()); 667 } 668 updateTotalCount( total, response.supplemental.time, true ); 669 } else if ( response.supplemental.time ) { 670 updateTotalCount( total, response.supplemental.time, false ); 671 } 672 } else { 673 updateTotalCount( total, r, false ); 674 } 675 } 676 677 if ( ! theExtraList || theExtraList.length === 0 || theExtraList.children().length === 0 || undoing ) { 678 return; 679 } 680 681 theList.get(0).wpList.add( theExtraList.children( ':eq(0):not(.no-items)' ).remove().clone() ); 682 683 refillTheExtraList(); 684 685 animated = $( ':animated', '#the-comment-list' ); 686 animatedCallback = function() { 687 if ( ! $( '#the-comment-list tr:visible' ).length ) { 688 theList.get(0).wpList.add( theExtraList.find( '.no-items' ).clone() ); 689 } 690 }; 691 692 if ( animated.length ) { 693 animated.promise().done( animatedCallback ); 694 } else { 695 animatedCallback(); 696 } 697 }; 698 699 /** 700 * Retrieves additional comments to populate the extra list. 701 * 702 * @since 3.1.0 703 * @access private 704 * 705 * @param {boolean} [ev] Repopulate the extra comments list if true. 706 * 707 * @return {void} 708 */ 709 refillTheExtraList = function(ev) { 710 var args = $.query.get(), total_pages = $('.total-pages').text(), per_page = $('input[name="_per_page"]', '#comments-form').val(); 711 712 if (! args.paged) 713 args.paged = 1; 714 715 if (args.paged > total_pages) { 716 return; 717 } 718 719 if (ev) { 720 theExtraList.empty(); 721 args.number = Math.min(8, per_page); // See WP_Comments_List_Table::prepare_items() in class-wp-comments-list-table.php. 722 } else { 723 args.number = 1; 724 args.offset = Math.min(8, per_page) - 1; // Fetch only the next item on the extra list. 725 } 726 727 args.no_placeholder = true; 728 729 args.paged ++; 730 731 // $.query.get() needs some correction to be sent into an Ajax request. 732 if ( true === args.comment_type ) 733 args.comment_type = ''; 734 735 args = $.extend(args, { 736 'action': 'fetch-list', 737 'list_args': list_args, 738 '_ajax_fetch_list_nonce': $('#_ajax_fetch_list_nonce').val() 739 }); 740 741 $.ajax({ 742 url: ajaxurl, 743 global: false, 744 dataType: 'json', 745 data: args, 746 success: function(response) { 747 theExtraList.get(0).wpList.add( response.rows ); 748 } 749 }); 750 }; 751 752 /** 753 * Globally available jQuery object referring to the extra comments list. 754 * 755 * @global 756 */ 757 window.theExtraList = $('#the-extra-comment-list').wpList( { alt: '', delColor: 'none', addColor: 'none' } ); 758 759 /** 760 * Globally available jQuery object referring to the comments list. 761 * 762 * @global 763 */ 764 window.theList = $('#the-comment-list').wpList( { alt: '', delBefore: delBefore, dimAfter: dimAfter, delAfter: delAfter, addColor: 'none' } ) 765 .on('wpListDelEnd', function(e, s){ 766 var wpListsData = $(s.target).attr('data-wp-lists'), id = s.element.replace(/[^0-9]+/g, ''); 767 768 if ( wpListsData.indexOf(':trash=1') != -1 || wpListsData.indexOf(':spam=1') != -1 ) 769 $('#undo-' + id).fadeIn(300, function(){ $(this).show(); }); 770 }); 771 }; 772 773 /** 774 * Object containing functionality regarding the comment quick editor and reply 775 * editor. 776 * 777 * @since 2.7.0 778 * 779 * @global 780 */ 781 window.commentReply = { 782 cid : '', 783 act : '', 784 originalContent : '', 785 786 /** 787 * Initializes the comment reply functionality. 788 * 789 * @since 2.7.0 790 * 791 * @memberof commentReply 792 */ 793 init : function() { 794 var row = $('#replyrow'); 795 796 $( '.cancel', row ).on( 'click', function() { return commentReply.revert(); } ); 797 $( '.save', row ).on( 'click', function() { return commentReply.send(); } ); 798 $( 'input#author-name, input#author-email, input#author-url', row ).on( 'keypress', function( e ) { 799 if ( e.which == 13 ) { 800 commentReply.send(); 801 e.preventDefault(); 802 return false; 803 } 804 }); 805 806 // Add events. 807 $('#the-comment-list .column-comment > p').on( 'dblclick', function(){ 808 commentReply.toggle($(this).parent()); 809 }); 810 811 $('#doaction, #post-query-submit').on( 'click', function(){ 812 if ( $('#the-comment-list #replyrow').length > 0 ) 813 commentReply.close(); 814 }); 815 816 this.comments_listing = $('#comments-form > input[name="comment_status"]').val() || ''; 817 }, 818 819 /** 820 * Adds doubleclick event handler to the given comment list row. 821 * 822 * The double-click event will toggle the comment edit or reply form. 823 * 824 * @since 2.7.0 825 * 826 * @memberof commentReply 827 * 828 * @param {Object} r The row to add double click handlers to. 829 * 830 * @return {void} 831 */ 832 addEvents : function(r) { 833 r.each(function() { 834 $(this).find('.column-comment > p').on( 'dblclick', function(){ 835 commentReply.toggle($(this).parent()); 836 }); 837 }); 838 }, 839 840 /** 841 * Opens the quick edit for the given element. 842 * 843 * @since 2.7.0 844 * 845 * @memberof commentReply 846 * 847 * @param {HTMLElement} el The element you want to open the quick editor for. 848 * 849 * @return {void} 850 */ 851 toggle : function(el) { 852 if ( 'none' !== $( el ).css( 'display' ) && ( $( '#replyrow' ).parent().is('#com-reply') || window.confirm( __( 'Are you sure you want to edit this comment?\nThe changes you made will be lost.' ) ) ) ) { 853 $( el ).find( 'button.vim-q' ).trigger( 'click' ); 854 } 855 }, 856 857 /** 858 * Closes the comment quick edit or reply form and undoes any changes. 859 * 860 * @since 2.7.0 861 * 862 * @memberof commentReply 863 * 864 * @return {void} 865 */ 866 revert : function() { 867 868 if ( $('#the-comment-list #replyrow').length < 1 ) 869 return false; 870 871 $('#replyrow').fadeOut('fast', function(){ 872 commentReply.close(); 873 }); 874 }, 875 876 /** 877 * Closes the comment quick edit or reply form and undoes any changes. 878 * 879 * @since 2.7.0 880 * 881 * @memberof commentReply 882 * 883 * @return {void} 884 */ 885 close : function() { 886 var commentRow = $(), 887 replyRow = $( '#replyrow' ); 888 889 // Return if the replyrow is not showing. 890 if ( replyRow.parent().is( '#com-reply' ) ) { 891 return; 892 } 893 894 if ( this.cid ) { 895 commentRow = $( '#comment-' + this.cid ); 896 } 897 898 /* 899 * When closing the Quick Edit form, show the comment row and move focus 900 * back to the Quick Edit button. 901 */ 902 if ( 'edit-comment' === this.act ) { 903 commentRow.fadeIn( 300, function() { 904 commentRow 905 .show() 906 .find( '.vim-q' ) 907 .attr( 'aria-expanded', 'false' ) 908 .trigger( 'focus' ); 909 } ).css( 'backgroundColor', '' ); 910 } 911 912 // When closing the Reply form, move focus back to the Reply button. 913 if ( 'replyto-comment' === this.act ) { 914 commentRow.find( '.vim-r' ) 915 .attr( 'aria-expanded', 'false' ) 916 .trigger( 'focus' ); 917 } 918 919 // Reset the Quicktags buttons. 920 if ( typeof QTags != 'undefined' ) 921 QTags.closeAllTags('replycontent'); 922 923 $('#add-new-comment').css('display', ''); 924 925 replyRow.hide(); 926 $( '#com-reply' ).append( replyRow ); 927 $('#replycontent').css('height', '').val(''); 928 $('#edithead input').val(''); 929 $( '.notice-error', replyRow ) 930 .addClass( 'hidden' ) 931 .find( '.error' ).empty(); 932 $( '.spinner', replyRow ).removeClass( 'is-active' ); 933 934 this.cid = ''; 935 this.originalContent = ''; 936 }, 937 938 /** 939 * Opens the comment quick edit or reply form. 940 * 941 * @since 2.7.0 942 * 943 * @memberof commentReply 944 * 945 * @param {number} comment_id The comment ID to open an editor for. 946 * @param {number} post_id The post ID to open an editor for. 947 * @param {string} action The action to perform. Either 'edit' or 'replyto'. 948 * 949 * @return {boolean} Always false. 950 */ 951 open : function(comment_id, post_id, action) { 952 var editRow, rowData, act, replyButton, editHeight, 953 t = this, 954 c = $('#comment-' + comment_id), 955 h = c.height(), 956 colspanVal = 0; 957 958 if ( ! this.discardCommentChanges() ) { 959 return false; 960 } 961 962 t.close(); 963 t.cid = comment_id; 964 965 editRow = $('#replyrow'); 966 rowData = $('#inline-'+comment_id); 967 action = action || 'replyto'; 968 act = 'edit' == action ? 'edit' : 'replyto'; 969 act = t.act = act + '-comment'; 970 t.originalContent = $('textarea.comment', rowData).val(); 971 colspanVal = $( '> th:visible, > td:visible', c ).length; 972 973 // Make sure it's actually a table and there's a `colspan` value to apply. 974 if ( editRow.hasClass( 'inline-edit-row' ) && 0 !== colspanVal ) { 975 $( 'td', editRow ).attr( 'colspan', colspanVal ); 976 } 977 978 $('#action', editRow).val(act); 979 $('#comment_post_ID', editRow).val(post_id); 980 $('#comment_ID', editRow).val(comment_id); 981 982 if ( action == 'edit' ) { 983 $( '#author-name', editRow ).val( $( 'div.author', rowData ).text() ); 984 $('#author-email', editRow).val( $('div.author-email', rowData).text() ); 985 $('#author-url', editRow).val( $('div.author-url', rowData).text() ); 986 $('#status', editRow).val( $('div.comment_status', rowData).text() ); 987 $('#replycontent', editRow).val( $('textarea.comment', rowData).val() ); 988 $( '#edithead, #editlegend, #savebtn', editRow ).show(); 989 $('#replyhead, #replybtn, #addhead, #addbtn', editRow).hide(); 990 991 if ( h > 120 ) { 992 // Limit the maximum height when editing very long comments to make it more manageable. 993 // The textarea is resizable in most browsers, so the user can adjust it if needed. 994 editHeight = h > 500 ? 500 : h; 995 $('#replycontent', editRow).css('height', editHeight + 'px'); 996 } 997 998 c.after( editRow ).fadeOut('fast', function(){ 999 $('#replyrow').fadeIn(300, function(){ $(this).show(); }); 1000 }); 1001 } else if ( action == 'add' ) { 1002 $('#addhead, #addbtn', editRow).show(); 1003 $( '#replyhead, #replybtn, #edithead, #editlegend, #savebtn', editRow ) .hide(); 1004 $('#the-comment-list').prepend(editRow); 1005 $('#replyrow').fadeIn(300); 1006 } else { 1007 replyButton = $('#replybtn', editRow); 1008 $( '#edithead, #editlegend, #savebtn, #addhead, #addbtn', editRow ).hide(); 1009 $('#replyhead, #replybtn', editRow).show(); 1010 c.after(editRow); 1011 1012 if ( c.hasClass('unapproved') ) { 1013 replyButton.text( __( 'Approve and Reply' ) ); 1014 } else { 1015 replyButton.text( __( 'Reply' ) ); 1016 } 1017 1018 $('#replyrow').fadeIn(300, function(){ $(this).show(); }); 1019 } 1020 1021 setTimeout(function() { 1022 var rtop, rbottom, scrollTop, vp, scrollBottom, 1023 isComposing = false; 1024 1025 rtop = $('#replyrow').offset().top; 1026 rbottom = rtop + $('#replyrow').height(); 1027 scrollTop = window.pageYOffset || document.documentElement.scrollTop; 1028 vp = document.documentElement.clientHeight || window.innerHeight || 0; 1029 scrollBottom = scrollTop + vp; 1030 1031 if ( scrollBottom - 20 < rbottom ) 1032 window.scroll(0, rbottom - vp + 35); 1033 else if ( rtop - 20 < scrollTop ) 1034 window.scroll(0, rtop - 35); 1035 1036 $( '#replycontent' ) 1037 .trigger( 'focus' ) 1038 .on( 'keyup', function( e ) { 1039 // Close on Escape except when Input Method Editors (IMEs) are in use. 1040 if ( e.which === 27 && ! isComposing ) { 1041 commentReply.revert(); 1042 } 1043 } ) 1044 .on( 'compositionstart', function() { 1045 isComposing = true; 1046 } ); 1047 }, 600); 1048 1049 return false; 1050 }, 1051 1052 /** 1053 * Submits the comment quick edit or reply form. 1054 * 1055 * @since 2.7.0 1056 * 1057 * @memberof commentReply 1058 * 1059 * @return {void} 1060 */ 1061 send : function() { 1062 var post = {}, 1063 $errorNotice = $( '#replysubmit .error-notice' ); 1064 1065 $errorNotice.addClass( 'hidden' ); 1066 $( '#replysubmit .spinner' ).addClass( 'is-active' ); 1067 1068 $('#replyrow input').not(':button').each(function() { 1069 var t = $(this); 1070 post[ t.attr('name') ] = t.val(); 1071 }); 1072 1073 post.content = $('#replycontent').val(); 1074 post.id = post.comment_post_ID; 1075 post.comments_listing = this.comments_listing; 1076 post.p = $('[name="p"]').val(); 1077 1078 if ( $('#comment-' + $('#comment_ID').val()).hasClass('unapproved') ) 1079 post.approve_parent = 1; 1080 1081 $.ajax({ 1082 type : 'POST', 1083 url : ajaxurl, 1084 data : post, 1085 success : function(x) { commentReply.show(x); }, 1086 error : function(r) { commentReply.error(r); } 1087 }); 1088 }, 1089 1090 /** 1091 * Shows the new or updated comment or reply. 1092 * 1093 * This function needs to be passed the ajax result as received from the server. 1094 * It will handle the response and show the comment that has just been saved to 1095 * the server. 1096 * 1097 * @since 2.7.0 1098 * 1099 * @memberof commentReply 1100 * 1101 * @param {Object} xml Ajax response object. 1102 * 1103 * @return {void} 1104 */ 1105 show : function(xml) { 1106 var t = this, r, c, id, bg, pid; 1107 1108 if ( typeof(xml) == 'string' ) { 1109 t.error({'responseText': xml}); 1110 return false; 1111 } 1112 1113 r = wpAjax.parseAjaxResponse(xml); 1114 if ( r.errors ) { 1115 t.error({'responseText': wpAjax.broken}); 1116 return false; 1117 } 1118 1119 t.revert(); 1120 1121 r = r.responses[0]; 1122 id = '#comment-' + r.id; 1123 1124 if ( 'edit-comment' == t.act ) 1125 $(id).remove(); 1126 1127 if ( r.supplemental.parent_approved ) { 1128 pid = $('#comment-' + r.supplemental.parent_approved); 1129 updatePending( -1, r.supplemental.parent_post_id ); 1130 1131 if ( this.comments_listing == 'moderated' ) { 1132 pid.animate( { 'backgroundColor':'#CCEEBB' }, 400, function(){ 1133 pid.fadeOut(); 1134 }); 1135 return; 1136 } 1137 } 1138 1139 if ( r.supplemental.i18n_comments_text ) { 1140 updateDashboardText( r.supplemental ); 1141 updateInModerationText( r.supplemental ); 1142 updateApproved( 1, r.supplemental.parent_post_id ); 1143 updateCountText( 'span.all-count', 1 ); 1144 } 1145 1146 r.data = r.data || ''; 1147 c = r.data.toString().trim(); // Trim leading whitespaces. 1148 $(c).hide(); 1149 $('#replyrow').after(c); 1150 1151 id = $(id); 1152 t.addEvents(id); 1153 bg = id.hasClass('unapproved') ? '#FFFFE0' : id.closest('.widefat, .postbox').css('backgroundColor'); 1154 1155 id.animate( { 'backgroundColor':'#CCEEBB' }, 300 ) 1156 .animate( { 'backgroundColor': bg }, 300, function() { 1157 if ( pid && pid.length ) { 1158 pid.animate( { 'backgroundColor':'#CCEEBB' }, 300 ) 1159 .animate( { 'backgroundColor': bg }, 300 ) 1160 .removeClass('unapproved').addClass('approved') 1161 .find('div.comment_status').html('1'); 1162 } 1163 }); 1164 1165 }, 1166 1167 /** 1168 * Shows an error for the failed comment update or reply. 1169 * 1170 * @since 2.7.0 1171 * 1172 * @memberof commentReply 1173 * 1174 * @param {string} r The Ajax response. 1175 * 1176 * @return {void} 1177 */ 1178 error : function(r) { 1179 var er = r.statusText, 1180 $errorNotice = $( '#replysubmit .notice-error' ), 1181 $error = $errorNotice.find( '.error' ); 1182 1183 $( '#replysubmit .spinner' ).removeClass( 'is-active' ); 1184 1185 if ( r.responseText ) 1186 er = r.responseText.replace( /<.[^<>]*?>/g, '' ); 1187 1188 if ( er ) { 1189 $errorNotice.removeClass( 'hidden' ); 1190 $error.html( er ); 1191 } 1192 }, 1193 1194 /** 1195 * Opens the add comments form in the comments metabox on the post edit page. 1196 * 1197 * @since 3.4.0 1198 * 1199 * @memberof commentReply 1200 * 1201 * @param {number} post_id The post ID. 1202 * 1203 * @return {void} 1204 */ 1205 addcomment: function(post_id) { 1206 var t = this; 1207 1208 $('#add-new-comment').fadeOut(200, function(){ 1209 t.open(0, post_id, 'add'); 1210 $('table.comments-box').css('display', ''); 1211 $('#no-comments').remove(); 1212 }); 1213 }, 1214 1215 /** 1216 * Alert the user if they have unsaved changes on a comment that will be lost if 1217 * they proceed with the intended action. 1218 * 1219 * @since 4.6.0 1220 * 1221 * @memberof commentReply 1222 * 1223 * @return {boolean} Whether it is safe the continue with the intended action. 1224 */ 1225 discardCommentChanges: function() { 1226 var editRow = $( '#replyrow' ); 1227 1228 if ( this.originalContent === $( '#replycontent', editRow ).val() ) { 1229 return true; 1230 } 1231 1232 return window.confirm( __( 'Are you sure you want to do this?\nThe comment changes you made will be lost.' ) ); 1233 } 1234 }; 1235 1236 $( function(){ 1237 var make_hotkeys_redirect, edit_comment, toggle_all, make_bulk; 1238 1239 setCommentsList(); 1240 commentReply.init(); 1241 1242 $(document).on( 'click', 'span.delete a.delete', function( e ) { 1243 e.preventDefault(); 1244 }); 1245 1246 if ( typeof $.table_hotkeys != 'undefined' ) { 1247 /** 1248 * Creates a function that navigates to a previous or next page. 1249 * 1250 * @since 2.7.0 1251 * @access private 1252 * 1253 * @param {string} which What page to navigate to: either next or prev. 1254 * 1255 * @return {Function} The function that executes the navigation. 1256 */ 1257 make_hotkeys_redirect = function(which) { 1258 return function() { 1259 var first_last, l; 1260 1261 first_last = 'next' == which? 'first' : 'last'; 1262 l = $('.tablenav-pages .'+which+'-page:not(.disabled)'); 1263 if (l.length) 1264 window.location = l[0].href.replace(/\&hotkeys_highlight_(first|last)=1/g, '')+'&hotkeys_highlight_'+first_last+'=1'; 1265 }; 1266 }; 1267 1268 /** 1269 * Navigates to the edit page for the selected comment. 1270 * 1271 * @since 2.7.0 1272 * @access private 1273 * 1274 * @param {Object} event The event that triggered this action. 1275 * @param {Object} current_row A jQuery object of the selected row. 1276 * 1277 * @return {void} 1278 */ 1279 edit_comment = function(event, current_row) { 1280 window.location = $('span.edit a', current_row).attr('href'); 1281 }; 1282 1283 /** 1284 * Toggles all comments on the screen, for bulk actions. 1285 * 1286 * @since 2.7.0 1287 * @access private 1288 * 1289 * @return {void} 1290 */ 1291 toggle_all = function() { 1292 $('#cb-select-all-1').data( 'wp-toggle', 1 ).trigger( 'click' ).removeData( 'wp-toggle' ); 1293 }; 1294 1295 /** 1296 * Creates a bulk action function that is executed on all selected comments. 1297 * 1298 * @since 2.7.0 1299 * @access private 1300 * 1301 * @param {string} value The name of the action to execute. 1302 * 1303 * @return {Function} The function that executes the bulk action. 1304 */ 1305 make_bulk = function(value) { 1306 return function() { 1307 var scope = $('select[name="action"]'); 1308 $('option[value="' + value + '"]', scope).prop('selected', true); 1309 $('#doaction').trigger( 'click' ); 1310 }; 1311 }; 1312 1313 $.table_hotkeys( 1314 $('table.widefat'), 1315 [ 1316 'a', 'u', 's', 'd', 'r', 'q', 'z', 1317 ['e', edit_comment], 1318 ['shift+x', toggle_all], 1319 ['shift+a', make_bulk('approve')], 1320 ['shift+s', make_bulk('spam')], 1321 ['shift+d', make_bulk('delete')], 1322 ['shift+t', make_bulk('trash')], 1323 ['shift+z', make_bulk('untrash')], 1324 ['shift+u', make_bulk('unapprove')] 1325 ], 1326 { 1327 highlight_first: adminCommentsSettings.hotkeys_highlight_first, 1328 highlight_last: adminCommentsSettings.hotkeys_highlight_last, 1329 prev_page_link_cb: make_hotkeys_redirect('prev'), 1330 next_page_link_cb: make_hotkeys_redirect('next'), 1331 hotkeys_opts: { 1332 disableInInput: true, 1333 type: 'keypress', 1334 noDisable: '.check-column input[type="checkbox"]' 1335 }, 1336 cycle_expr: '#the-comment-list tr', 1337 start_row_index: 0 1338 } 1339 ); 1340 } 1341 1342 // Quick Edit and Reply have an inline comment editor. 1343 $( '#the-comment-list' ).on( 'click', '.comment-inline', function() { 1344 var $el = $( this ), 1345 action = 'replyto'; 1346 1347 if ( 'undefined' !== typeof $el.data( 'action' ) ) { 1348 action = $el.data( 'action' ); 1349 } 1350 1351 $( this ).attr( 'aria-expanded', 'true' ); 1352 commentReply.open( $el.data( 'commentId' ), $el.data( 'postId' ), action ); 1353 } ); 1354 }); 1355 1356 })(jQuery);
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Fri Dec 6 01:00:02 2024 | Cross-referenced by PHPXref 0.7.1 |