[ Index ] |
PHP Cross Reference of WordPress Trunk (Updated Daily) |
[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 isContextMenuOpen = false; 1025 1026 rtop = $('#replyrow').offset().top; 1027 rbottom = rtop + $('#replyrow').height(); 1028 scrollTop = window.pageYOffset || document.documentElement.scrollTop; 1029 vp = document.documentElement.clientHeight || window.innerHeight || 0; 1030 scrollBottom = scrollTop + vp; 1031 1032 if ( scrollBottom - 20 < rbottom ) 1033 window.scroll(0, rbottom - vp + 35); 1034 else if ( rtop - 20 < scrollTop ) 1035 window.scroll(0, rtop - 35); 1036 1037 $( '#replycontent' ) 1038 .trigger( 'focus' ) 1039 .on( 'contextmenu keydown', function ( e ) { 1040 // Check if the context menu is open and set state. 1041 if ( e.type === 'contextmenu' ) { 1042 isContextMenuOpen = true; 1043 } 1044 1045 // Update the context menu state if the Escape key is pressed. 1046 if ( e.type === 'keydown' && e.which === 27 && isContextMenuOpen ) { 1047 isContextMenuOpen = false; 1048 } 1049 } ) 1050 .on( 'keyup', function( e ) { 1051 // Close on Escape unless Input Method Editors (IMEs) are in use or the context menu is open. 1052 if ( e.which === 27 && ! isComposing && ! isContextMenuOpen ) { 1053 commentReply.revert(); 1054 } 1055 } ) 1056 .on( 'compositionstart', function() { 1057 isComposing = true; 1058 } ); 1059 }, 600); 1060 1061 return false; 1062 }, 1063 1064 /** 1065 * Submits the comment quick edit or reply form. 1066 * 1067 * @since 2.7.0 1068 * 1069 * @memberof commentReply 1070 * 1071 * @return {void} 1072 */ 1073 send : function() { 1074 var post = {}, 1075 $errorNotice = $( '#replysubmit .error-notice' ); 1076 1077 $errorNotice.addClass( 'hidden' ); 1078 $( '#replysubmit .spinner' ).addClass( 'is-active' ); 1079 1080 $('#replyrow input').not(':button').each(function() { 1081 var t = $(this); 1082 post[ t.attr('name') ] = t.val(); 1083 }); 1084 1085 post.content = $('#replycontent').val(); 1086 post.id = post.comment_post_ID; 1087 post.comments_listing = this.comments_listing; 1088 post.p = $('[name="p"]').val(); 1089 1090 if ( $('#comment-' + $('#comment_ID').val()).hasClass('unapproved') ) 1091 post.approve_parent = 1; 1092 1093 $.ajax({ 1094 type : 'POST', 1095 url : ajaxurl, 1096 data : post, 1097 success : function(x) { commentReply.show(x); }, 1098 error : function(r) { commentReply.error(r); } 1099 }); 1100 }, 1101 1102 /** 1103 * Shows the new or updated comment or reply. 1104 * 1105 * This function needs to be passed the ajax result as received from the server. 1106 * It will handle the response and show the comment that has just been saved to 1107 * the server. 1108 * 1109 * @since 2.7.0 1110 * 1111 * @memberof commentReply 1112 * 1113 * @param {Object} xml Ajax response object. 1114 * 1115 * @return {void} 1116 */ 1117 show : function(xml) { 1118 var t = this, r, c, id, bg, pid; 1119 1120 if ( typeof(xml) == 'string' ) { 1121 t.error({'responseText': xml}); 1122 return false; 1123 } 1124 1125 r = wpAjax.parseAjaxResponse(xml); 1126 if ( r.errors ) { 1127 t.error({'responseText': wpAjax.broken}); 1128 return false; 1129 } 1130 1131 t.revert(); 1132 1133 r = r.responses[0]; 1134 id = '#comment-' + r.id; 1135 1136 if ( 'edit-comment' == t.act ) 1137 $(id).remove(); 1138 1139 if ( r.supplemental.parent_approved ) { 1140 pid = $('#comment-' + r.supplemental.parent_approved); 1141 updatePending( -1, r.supplemental.parent_post_id ); 1142 1143 if ( this.comments_listing == 'moderated' ) { 1144 pid.animate( { 'backgroundColor':'#CCEEBB' }, 400, function(){ 1145 pid.fadeOut(); 1146 }); 1147 return; 1148 } 1149 } 1150 1151 if ( r.supplemental.i18n_comments_text ) { 1152 updateDashboardText( r.supplemental ); 1153 updateInModerationText( r.supplemental ); 1154 updateApproved( 1, r.supplemental.parent_post_id ); 1155 updateCountText( 'span.all-count', 1 ); 1156 } 1157 1158 r.data = r.data || ''; 1159 c = r.data.toString().trim(); // Trim leading whitespaces. 1160 $(c).hide(); 1161 $('#replyrow').after(c); 1162 1163 id = $(id); 1164 t.addEvents(id); 1165 bg = id.hasClass('unapproved') ? '#FFFFE0' : id.closest('.widefat, .postbox').css('backgroundColor'); 1166 1167 id.animate( { 'backgroundColor':'#CCEEBB' }, 300 ) 1168 .animate( { 'backgroundColor': bg }, 300, function() { 1169 if ( pid && pid.length ) { 1170 pid.animate( { 'backgroundColor':'#CCEEBB' }, 300 ) 1171 .animate( { 'backgroundColor': bg }, 300 ) 1172 .removeClass('unapproved').addClass('approved') 1173 .find('div.comment_status').html('1'); 1174 } 1175 }); 1176 1177 }, 1178 1179 /** 1180 * Shows an error for the failed comment update or reply. 1181 * 1182 * @since 2.7.0 1183 * 1184 * @memberof commentReply 1185 * 1186 * @param {string} r The Ajax response. 1187 * 1188 * @return {void} 1189 */ 1190 error : function(r) { 1191 var er = r.statusText, 1192 $errorNotice = $( '#replysubmit .notice-error' ), 1193 $error = $errorNotice.find( '.error' ); 1194 1195 $( '#replysubmit .spinner' ).removeClass( 'is-active' ); 1196 1197 if ( r.responseText ) 1198 er = r.responseText.replace( /<.[^<>]*?>/g, '' ); 1199 1200 if ( er ) { 1201 $errorNotice.removeClass( 'hidden' ); 1202 $error.html( er ); 1203 wp.a11y.speak( er ); 1204 } 1205 }, 1206 1207 /** 1208 * Opens the add comments form in the comments metabox on the post edit page. 1209 * 1210 * @since 3.4.0 1211 * 1212 * @memberof commentReply 1213 * 1214 * @param {number} post_id The post ID. 1215 * 1216 * @return {void} 1217 */ 1218 addcomment: function(post_id) { 1219 var t = this; 1220 1221 $('#add-new-comment').fadeOut(200, function(){ 1222 t.open(0, post_id, 'add'); 1223 $('table.comments-box').css('display', ''); 1224 $('#no-comments').remove(); 1225 }); 1226 }, 1227 1228 /** 1229 * Alert the user if they have unsaved changes on a comment that will be lost if 1230 * they proceed with the intended action. 1231 * 1232 * @since 4.6.0 1233 * 1234 * @memberof commentReply 1235 * 1236 * @return {boolean} Whether it is safe the continue with the intended action. 1237 */ 1238 discardCommentChanges: function() { 1239 var editRow = $( '#replyrow' ); 1240 1241 if ( '' === $( '#replycontent', editRow ).val() || this.originalContent === $( '#replycontent', editRow ).val() ) { 1242 return true; 1243 } 1244 1245 return window.confirm( __( 'Are you sure you want to do this?\nThe comment changes you made will be lost.' ) ); 1246 } 1247 }; 1248 1249 $( function(){ 1250 var make_hotkeys_redirect, edit_comment, toggle_all, make_bulk; 1251 1252 setCommentsList(); 1253 commentReply.init(); 1254 1255 $(document).on( 'click', 'span.delete a.delete', function( e ) { 1256 e.preventDefault(); 1257 }); 1258 1259 if ( typeof $.table_hotkeys != 'undefined' ) { 1260 /** 1261 * Creates a function that navigates to a previous or next page. 1262 * 1263 * @since 2.7.0 1264 * @access private 1265 * 1266 * @param {string} which What page to navigate to: either next or prev. 1267 * 1268 * @return {Function} The function that executes the navigation. 1269 */ 1270 make_hotkeys_redirect = function(which) { 1271 return function() { 1272 var first_last, l; 1273 1274 first_last = 'next' == which? 'first' : 'last'; 1275 l = $('.tablenav-pages .'+which+'-page:not(.disabled)'); 1276 if (l.length) 1277 window.location = l[0].href.replace(/\&hotkeys_highlight_(first|last)=1/g, '')+'&hotkeys_highlight_'+first_last+'=1'; 1278 }; 1279 }; 1280 1281 /** 1282 * Navigates to the edit page for the selected comment. 1283 * 1284 * @since 2.7.0 1285 * @access private 1286 * 1287 * @param {Object} event The event that triggered this action. 1288 * @param {Object} current_row A jQuery object of the selected row. 1289 * 1290 * @return {void} 1291 */ 1292 edit_comment = function(event, current_row) { 1293 window.location = $('span.edit a', current_row).attr('href'); 1294 }; 1295 1296 /** 1297 * Toggles all comments on the screen, for bulk actions. 1298 * 1299 * @since 2.7.0 1300 * @access private 1301 * 1302 * @return {void} 1303 */ 1304 toggle_all = function() { 1305 $('#cb-select-all-1').data( 'wp-toggle', 1 ).trigger( 'click' ).removeData( 'wp-toggle' ); 1306 }; 1307 1308 /** 1309 * Creates a bulk action function that is executed on all selected comments. 1310 * 1311 * @since 2.7.0 1312 * @access private 1313 * 1314 * @param {string} value The name of the action to execute. 1315 * 1316 * @return {Function} The function that executes the bulk action. 1317 */ 1318 make_bulk = function(value) { 1319 return function() { 1320 var scope = $('select[name="action"]'); 1321 $('option[value="' + value + '"]', scope).prop('selected', true); 1322 $('#doaction').trigger( 'click' ); 1323 }; 1324 }; 1325 1326 $.table_hotkeys( 1327 $('table.widefat'), 1328 [ 1329 'a', 'u', 's', 'd', 'r', 'q', 'z', 1330 ['e', edit_comment], 1331 ['shift+x', toggle_all], 1332 ['shift+a', make_bulk('approve')], 1333 ['shift+s', make_bulk('spam')], 1334 ['shift+d', make_bulk('delete')], 1335 ['shift+t', make_bulk('trash')], 1336 ['shift+z', make_bulk('untrash')], 1337 ['shift+u', make_bulk('unapprove')] 1338 ], 1339 { 1340 highlight_first: adminCommentsSettings.hotkeys_highlight_first, 1341 highlight_last: adminCommentsSettings.hotkeys_highlight_last, 1342 prev_page_link_cb: make_hotkeys_redirect('prev'), 1343 next_page_link_cb: make_hotkeys_redirect('next'), 1344 hotkeys_opts: { 1345 disableInInput: true, 1346 type: 'keypress', 1347 noDisable: '.check-column input[type="checkbox"]' 1348 }, 1349 cycle_expr: '#the-comment-list tr', 1350 start_row_index: 0 1351 } 1352 ); 1353 } 1354 1355 // Quick Edit and Reply have an inline comment editor. 1356 $( '#the-comment-list' ).on( 'click', '.comment-inline', function() { 1357 var $el = $( this ), 1358 action = 'replyto'; 1359 1360 if ( 'undefined' !== typeof $el.data( 'action' ) ) { 1361 action = $el.data( 'action' ); 1362 } 1363 1364 $( this ).attr( 'aria-expanded', 'true' ); 1365 commentReply.open( $el.data( 'commentId' ), $el.data( 'postId' ), action ); 1366 } ); 1367 }); 1368 1369 })(jQuery);
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated : Sat Dec 21 08:20:01 2024 | Cross-referenced by PHPXref |