class-wp-comments-list-table.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948
  1. <?php
  2. /**
  3. * List Table API: WP_Comments_List_Table class
  4. *
  5. * @package WordPress
  6. * @subpackage Administration
  7. * @since 3.1.0
  8. */
  9. /**
  10. * Core class used to implement displaying comments in a list table.
  11. *
  12. * @since 3.1.0
  13. * @access private
  14. *
  15. * @see WP_List_Table
  16. */
  17. class WP_Comments_List_Table extends WP_List_Table {
  18. public $checkbox = true;
  19. public $pending_count = array();
  20. public $extra_items;
  21. private $user_can;
  22. /**
  23. * Constructor.
  24. *
  25. * @since 3.1.0
  26. *
  27. * @see WP_List_Table::__construct() for more information on default arguments.
  28. *
  29. * @global int $post_id
  30. *
  31. * @param array $args An associative array of arguments.
  32. */
  33. public function __construct( $args = array() ) {
  34. global $post_id;
  35. $post_id = isset( $_REQUEST['p'] ) ? absint( $_REQUEST['p'] ) : 0;
  36. if ( get_option( 'show_avatars' ) ) {
  37. add_filter( 'comment_author', array( $this, 'floated_admin_avatar' ), 10, 2 );
  38. }
  39. parent::__construct(
  40. array(
  41. 'plural' => 'comments',
  42. 'singular' => 'comment',
  43. 'ajax' => true,
  44. 'screen' => isset( $args['screen'] ) ? $args['screen'] : null,
  45. )
  46. );
  47. }
  48. public function floated_admin_avatar( $name, $comment_ID ) {
  49. $comment = get_comment( $comment_ID );
  50. $avatar = get_avatar( $comment, 32, 'mystery' );
  51. return "$avatar $name";
  52. }
  53. /**
  54. * @return bool
  55. */
  56. public function ajax_user_can() {
  57. return current_user_can( 'edit_posts' );
  58. }
  59. /**
  60. * @global int $post_id
  61. * @global string $comment_status
  62. * @global string $search
  63. * @global string $comment_type
  64. */
  65. public function prepare_items() {
  66. global $post_id, $comment_status, $search, $comment_type;
  67. $comment_status = isset( $_REQUEST['comment_status'] ) ? $_REQUEST['comment_status'] : 'all';
  68. if ( ! in_array( $comment_status, array( 'all', 'mine', 'moderated', 'approved', 'spam', 'trash' ) ) ) {
  69. $comment_status = 'all';
  70. }
  71. $comment_type = ! empty( $_REQUEST['comment_type'] ) ? $_REQUEST['comment_type'] : '';
  72. $search = ( isset( $_REQUEST['s'] ) ) ? $_REQUEST['s'] : '';
  73. $post_type = ( isset( $_REQUEST['post_type'] ) ) ? sanitize_key( $_REQUEST['post_type'] ) : '';
  74. $user_id = ( isset( $_REQUEST['user_id'] ) ) ? $_REQUEST['user_id'] : '';
  75. $orderby = ( isset( $_REQUEST['orderby'] ) ) ? $_REQUEST['orderby'] : '';
  76. $order = ( isset( $_REQUEST['order'] ) ) ? $_REQUEST['order'] : '';
  77. $comments_per_page = $this->get_per_page( $comment_status );
  78. $doing_ajax = wp_doing_ajax();
  79. if ( isset( $_REQUEST['number'] ) ) {
  80. $number = (int) $_REQUEST['number'];
  81. } else {
  82. $number = $comments_per_page + min( 8, $comments_per_page ); // Grab a few extra
  83. }
  84. $page = $this->get_pagenum();
  85. if ( isset( $_REQUEST['start'] ) ) {
  86. $start = $_REQUEST['start'];
  87. } else {
  88. $start = ( $page - 1 ) * $comments_per_page;
  89. }
  90. if ( $doing_ajax && isset( $_REQUEST['offset'] ) ) {
  91. $start += $_REQUEST['offset'];
  92. }
  93. $status_map = array(
  94. 'mine' => '',
  95. 'moderated' => 'hold',
  96. 'approved' => 'approve',
  97. 'all' => '',
  98. );
  99. $args = array(
  100. 'status' => isset( $status_map[ $comment_status ] ) ? $status_map[ $comment_status ] : $comment_status,
  101. 'search' => $search,
  102. 'user_id' => $user_id,
  103. 'offset' => $start,
  104. 'number' => $number,
  105. 'post_id' => $post_id,
  106. 'type' => $comment_type,
  107. 'orderby' => $orderby,
  108. 'order' => $order,
  109. 'post_type' => $post_type,
  110. );
  111. /**
  112. * Filters the arguments for the comment query in the comments list table.
  113. *
  114. * @since 5.1.0
  115. *
  116. * @param array $args An array of get_comments() arguments.
  117. */
  118. $args = apply_filters( 'comments_list_table_query_args', $args );
  119. $_comments = get_comments( $args );
  120. if ( is_array( $_comments ) ) {
  121. update_comment_cache( $_comments );
  122. $this->items = array_slice( $_comments, 0, $comments_per_page );
  123. $this->extra_items = array_slice( $_comments, $comments_per_page );
  124. $_comment_post_ids = array_unique( wp_list_pluck( $_comments, 'comment_post_ID' ) );
  125. $this->pending_count = get_pending_comments_num( $_comment_post_ids );
  126. }
  127. $total_comments = get_comments(
  128. array_merge(
  129. $args,
  130. array(
  131. 'count' => true,
  132. 'offset' => 0,
  133. 'number' => 0,
  134. )
  135. )
  136. );
  137. $this->set_pagination_args(
  138. array(
  139. 'total_items' => $total_comments,
  140. 'per_page' => $comments_per_page,
  141. )
  142. );
  143. }
  144. /**
  145. * @param string $comment_status
  146. * @return int
  147. */
  148. public function get_per_page( $comment_status = 'all' ) {
  149. $comments_per_page = $this->get_items_per_page( 'edit_comments_per_page' );
  150. /**
  151. * Filters the number of comments listed per page in the comments list table.
  152. *
  153. * @since 2.6.0
  154. *
  155. * @param int $comments_per_page The number of comments to list per page.
  156. * @param string $comment_status The comment status name. Default 'All'.
  157. */
  158. return apply_filters( 'comments_per_page', $comments_per_page, $comment_status );
  159. }
  160. /**
  161. * @global string $comment_status
  162. */
  163. public function no_items() {
  164. global $comment_status;
  165. if ( 'moderated' === $comment_status ) {
  166. _e( 'No comments awaiting moderation.' );
  167. } else {
  168. _e( 'No comments found.' );
  169. }
  170. }
  171. /**
  172. * @global int $post_id
  173. * @global string $comment_status
  174. * @global string $comment_type
  175. */
  176. protected function get_views() {
  177. global $post_id, $comment_status, $comment_type;
  178. $status_links = array();
  179. $num_comments = ( $post_id ) ? wp_count_comments( $post_id ) : wp_count_comments();
  180. $stati = array(
  181. /* translators: %s: Number of comments. */
  182. 'all' => _nx_noop(
  183. 'All <span class="count">(%s)</span>',
  184. 'All <span class="count">(%s)</span>',
  185. 'comments'
  186. ), // singular not used
  187. /* translators: %s: Number of comments. */
  188. 'mine' => _nx_noop(
  189. 'Mine <span class="count">(%s)</span>',
  190. 'Mine <span class="count">(%s)</span>',
  191. 'comments'
  192. ),
  193. /* translators: %s: Number of comments. */
  194. 'moderated' => _nx_noop(
  195. 'Pending <span class="count">(%s)</span>',
  196. 'Pending <span class="count">(%s)</span>',
  197. 'comments'
  198. ),
  199. /* translators: %s: Number of comments. */
  200. 'approved' => _nx_noop(
  201. 'Approved <span class="count">(%s)</span>',
  202. 'Approved <span class="count">(%s)</span>',
  203. 'comments'
  204. ),
  205. /* translators: %s: Number of comments. */
  206. 'spam' => _nx_noop(
  207. 'Spam <span class="count">(%s)</span>',
  208. 'Spam <span class="count">(%s)</span>',
  209. 'comments'
  210. ),
  211. /* translators: %s: Number of comments. */
  212. 'trash' => _nx_noop(
  213. 'Trash <span class="count">(%s)</span>',
  214. 'Trash <span class="count">(%s)</span>',
  215. 'comments'
  216. ),
  217. );
  218. if ( ! EMPTY_TRASH_DAYS ) {
  219. unset( $stati['trash'] );
  220. }
  221. $link = admin_url( 'edit-comments.php' );
  222. if ( ! empty( $comment_type ) && 'all' != $comment_type ) {
  223. $link = add_query_arg( 'comment_type', $comment_type, $link );
  224. }
  225. foreach ( $stati as $status => $label ) {
  226. $current_link_attributes = '';
  227. if ( $status === $comment_status ) {
  228. $current_link_attributes = ' class="current" aria-current="page"';
  229. }
  230. if ( 'mine' === $status ) {
  231. $current_user_id = get_current_user_id();
  232. $num_comments->mine = get_comments(
  233. array(
  234. 'post_id' => $post_id ? $post_id : 0,
  235. 'user_id' => $current_user_id,
  236. 'count' => true,
  237. )
  238. );
  239. $link = add_query_arg( 'user_id', $current_user_id, $link );
  240. } else {
  241. $link = remove_query_arg( 'user_id', $link );
  242. }
  243. if ( ! isset( $num_comments->$status ) ) {
  244. $num_comments->$status = 10;
  245. }
  246. $link = add_query_arg( 'comment_status', $status, $link );
  247. if ( $post_id ) {
  248. $link = add_query_arg( 'p', absint( $post_id ), $link );
  249. }
  250. /*
  251. // I toyed with this, but decided against it. Leaving it in here in case anyone thinks it is a good idea. ~ Mark
  252. if ( !empty( $_REQUEST['s'] ) )
  253. $link = add_query_arg( 's', esc_attr( wp_unslash( $_REQUEST['s'] ) ), $link );
  254. */
  255. $status_links[ $status ] = "<a href='$link'$current_link_attributes>" . sprintf(
  256. translate_nooped_plural( $label, $num_comments->$status ),
  257. sprintf(
  258. '<span class="%s-count">%s</span>',
  259. ( 'moderated' === $status ) ? 'pending' : $status,
  260. number_format_i18n( $num_comments->$status )
  261. )
  262. ) . '</a>';
  263. }
  264. /**
  265. * Filters the comment status links.
  266. *
  267. * @since 2.5.0
  268. * @since 5.1.0 The 'Mine' link was added.
  269. *
  270. * @param string[] $status_links An associative array of fully-formed comment status links. Includes 'All', 'Mine',
  271. * 'Pending', 'Approved', 'Spam', and 'Trash'.
  272. */
  273. return apply_filters( 'comment_status_links', $status_links );
  274. }
  275. /**
  276. * @global string $comment_status
  277. *
  278. * @return array
  279. */
  280. protected function get_bulk_actions() {
  281. global $comment_status;
  282. $actions = array();
  283. if ( in_array( $comment_status, array( 'all', 'approved' ) ) ) {
  284. $actions['unapprove'] = __( 'Unapprove' );
  285. }
  286. if ( in_array( $comment_status, array( 'all', 'moderated' ) ) ) {
  287. $actions['approve'] = __( 'Approve' );
  288. }
  289. if ( in_array( $comment_status, array( 'all', 'moderated', 'approved', 'trash' ) ) ) {
  290. $actions['spam'] = _x( 'Mark as Spam', 'comment' );
  291. }
  292. if ( 'trash' === $comment_status ) {
  293. $actions['untrash'] = __( 'Restore' );
  294. } elseif ( 'spam' === $comment_status ) {
  295. $actions['unspam'] = _x( 'Not Spam', 'comment' );
  296. }
  297. if ( in_array( $comment_status, array( 'trash', 'spam' ) ) || ! EMPTY_TRASH_DAYS ) {
  298. $actions['delete'] = __( 'Delete Permanently' );
  299. } else {
  300. $actions['trash'] = __( 'Move to Trash' );
  301. }
  302. return $actions;
  303. }
  304. /**
  305. * @global string $comment_status
  306. * @global string $comment_type
  307. *
  308. * @param string $which
  309. */
  310. protected function extra_tablenav( $which ) {
  311. global $comment_status, $comment_type;
  312. static $has_items;
  313. if ( ! isset( $has_items ) ) {
  314. $has_items = $this->has_items();
  315. }
  316. ?>
  317. <div class="alignleft actions">
  318. <?php
  319. if ( 'top' === $which ) {
  320. ?>
  321. <label class="screen-reader-text" for="filter-by-comment-type"><?php _e( 'Filter by comment type' ); ?></label>
  322. <select id="filter-by-comment-type" name="comment_type">
  323. <option value=""><?php _e( 'All comment types' ); ?></option>
  324. <?php
  325. /**
  326. * Filters the comment types dropdown menu.
  327. *
  328. * @since 2.7.0
  329. *
  330. * @param string[] $comment_types An array of comment types. Accepts 'Comments', 'Pings'.
  331. */
  332. $comment_types = apply_filters(
  333. 'admin_comment_types_dropdown',
  334. array(
  335. 'comment' => __( 'Comments' ),
  336. 'pings' => __( 'Pings' ),
  337. )
  338. );
  339. foreach ( $comment_types as $type => $label ) {
  340. echo "\t" . '<option value="' . esc_attr( $type ) . '"' . selected( $comment_type, $type, false ) . ">$label</option>\n";
  341. }
  342. ?>
  343. </select>
  344. <?php
  345. /**
  346. * Fires just before the Filter submit button for comment types.
  347. *
  348. * @since 3.5.0
  349. */
  350. do_action( 'restrict_manage_comments' );
  351. submit_button( __( 'Filter' ), '', 'filter_action', false, array( 'id' => 'post-query-submit' ) );
  352. }
  353. if ( ( 'spam' === $comment_status || 'trash' === $comment_status ) && current_user_can( 'moderate_comments' ) && $has_items ) {
  354. wp_nonce_field( 'bulk-destroy', '_destroy_nonce' );
  355. $title = ( 'spam' === $comment_status ) ? esc_attr__( 'Empty Spam' ) : esc_attr__( 'Empty Trash' );
  356. submit_button( $title, 'apply', 'delete_all', false );
  357. }
  358. /**
  359. * Fires after the Filter submit button for comment types.
  360. *
  361. * @since 2.5.0
  362. *
  363. * @param string $comment_status The comment status name. Default 'All'.
  364. */
  365. do_action( 'manage_comments_nav', $comment_status );
  366. echo '</div>';
  367. }
  368. /**
  369. * @return string|false
  370. */
  371. public function current_action() {
  372. if ( isset( $_REQUEST['delete_all'] ) || isset( $_REQUEST['delete_all2'] ) ) {
  373. return 'delete_all';
  374. }
  375. return parent::current_action();
  376. }
  377. /**
  378. * @global int $post_id
  379. *
  380. * @return array
  381. */
  382. public function get_columns() {
  383. global $post_id;
  384. $columns = array();
  385. if ( $this->checkbox ) {
  386. $columns['cb'] = '<input type="checkbox" />';
  387. }
  388. $columns['author'] = __( 'Author' );
  389. $columns['comment'] = _x( 'Comment', 'column name' );
  390. if ( ! $post_id ) {
  391. /* translators: Column name or table row header. */
  392. $columns['response'] = __( 'In Response To' );
  393. }
  394. $columns['date'] = _x( 'Submitted On', 'column name' );
  395. return $columns;
  396. }
  397. /**
  398. * @return array
  399. */
  400. protected function get_sortable_columns() {
  401. return array(
  402. 'author' => 'comment_author',
  403. 'response' => 'comment_post_ID',
  404. 'date' => 'comment_date',
  405. );
  406. }
  407. /**
  408. * Get the name of the default primary column.
  409. *
  410. * @since 4.3.0
  411. *
  412. * @return string Name of the default primary column, in this case, 'comment'.
  413. */
  414. protected function get_default_primary_column_name() {
  415. return 'comment';
  416. }
  417. /**
  418. * Displays the comments table.
  419. *
  420. * Overrides the parent display() method to render extra comments.
  421. *
  422. * @since 3.1.0
  423. */
  424. public function display() {
  425. wp_nonce_field( 'fetch-list-' . get_class( $this ), '_ajax_fetch_list_nonce' );
  426. $this->display_tablenav( 'top' );
  427. $this->screen->render_screen_reader_content( 'heading_list' );
  428. ?>
  429. <table class="wp-list-table <?php echo implode( ' ', $this->get_table_classes() ); ?>">
  430. <thead>
  431. <tr>
  432. <?php $this->print_column_headers(); ?>
  433. </tr>
  434. </thead>
  435. <tbody id="the-comment-list" data-wp-lists="list:comment">
  436. <?php $this->display_rows_or_placeholder(); ?>
  437. </tbody>
  438. <tbody id="the-extra-comment-list" data-wp-lists="list:comment" style="display: none;">
  439. <?php
  440. /*
  441. * Back up the items to restore after printing the extra items markup.
  442. * The extra items may be empty, which will prevent the table nav from displaying later.
  443. */
  444. $items = $this->items;
  445. $this->items = $this->extra_items;
  446. $this->display_rows_or_placeholder();
  447. $this->items = $items;
  448. ?>
  449. </tbody>
  450. <tfoot>
  451. <tr>
  452. <?php $this->print_column_headers( false ); ?>
  453. </tr>
  454. </tfoot>
  455. </table>
  456. <?php
  457. $this->display_tablenav( 'bottom' );
  458. }
  459. /**
  460. * @global WP_Post $post Global post object.
  461. * @global WP_Comment $comment Global comment object.
  462. *
  463. * @param WP_Comment $item
  464. */
  465. public function single_row( $item ) {
  466. global $post, $comment;
  467. $comment = $item;
  468. $the_comment_class = wp_get_comment_status( $comment );
  469. if ( ! $the_comment_class ) {
  470. $the_comment_class = '';
  471. }
  472. $the_comment_class = join( ' ', get_comment_class( $the_comment_class, $comment, $comment->comment_post_ID ) );
  473. if ( $comment->comment_post_ID > 0 ) {
  474. $post = get_post( $comment->comment_post_ID );
  475. }
  476. $this->user_can = current_user_can( 'edit_comment', $comment->comment_ID );
  477. echo "<tr id='comment-$comment->comment_ID' class='$the_comment_class'>";
  478. $this->single_row_columns( $comment );
  479. echo "</tr>\n";
  480. unset( $GLOBALS['post'], $GLOBALS['comment'] );
  481. }
  482. /**
  483. * Generate and display row actions links.
  484. *
  485. * @since 4.3.0
  486. *
  487. * @global string $comment_status Status for the current listed comments.
  488. *
  489. * @param WP_Comment $comment The comment object.
  490. * @param string $column_name Current column name.
  491. * @param string $primary Primary column name.
  492. * @return string|void Comment row actions output.
  493. */
  494. protected function handle_row_actions( $comment, $column_name, $primary ) {
  495. global $comment_status;
  496. if ( $primary !== $column_name ) {
  497. return '';
  498. }
  499. if ( ! $this->user_can ) {
  500. return;
  501. }
  502. $the_comment_status = wp_get_comment_status( $comment );
  503. $out = '';
  504. $del_nonce = esc_html( '_wpnonce=' . wp_create_nonce( "delete-comment_$comment->comment_ID" ) );
  505. $approve_nonce = esc_html( '_wpnonce=' . wp_create_nonce( "approve-comment_$comment->comment_ID" ) );
  506. $url = "comment.php?c=$comment->comment_ID";
  507. $approve_url = esc_url( $url . "&action=approvecomment&$approve_nonce" );
  508. $unapprove_url = esc_url( $url . "&action=unapprovecomment&$approve_nonce" );
  509. $spam_url = esc_url( $url . "&action=spamcomment&$del_nonce" );
  510. $unspam_url = esc_url( $url . "&action=unspamcomment&$del_nonce" );
  511. $trash_url = esc_url( $url . "&action=trashcomment&$del_nonce" );
  512. $untrash_url = esc_url( $url . "&action=untrashcomment&$del_nonce" );
  513. $delete_url = esc_url( $url . "&action=deletecomment&$del_nonce" );
  514. // Preorder it: Approve | Reply | Quick Edit | Edit | Spam | Trash.
  515. $actions = array(
  516. 'approve' => '',
  517. 'unapprove' => '',
  518. 'reply' => '',
  519. 'quickedit' => '',
  520. 'edit' => '',
  521. 'spam' => '',
  522. 'unspam' => '',
  523. 'trash' => '',
  524. 'untrash' => '',
  525. 'delete' => '',
  526. );
  527. // Not looking at all comments.
  528. if ( $comment_status && 'all' != $comment_status ) {
  529. if ( 'approved' === $the_comment_status ) {
  530. $actions['unapprove'] = sprintf(
  531. '<a href="%s" data-wp-lists="%s" class="vim-u vim-destructive aria-button-if-js" aria-label="%s">%s</a>',
  532. $unapprove_url,
  533. "delete:the-comment-list:comment-{$comment->comment_ID}:e7e7d3:action=dim-comment&amp;new=unapproved",
  534. esc_attr__( 'Unapprove this comment' ),
  535. __( 'Unapprove' )
  536. );
  537. } elseif ( 'unapproved' === $the_comment_status ) {
  538. $actions['approve'] = sprintf(
  539. '<a href="%s" data-wp-lists="%s" class="vim-a vim-destructive aria-button-if-js" aria-label="%s">%s</a>',
  540. $approve_url,
  541. "delete:the-comment-list:comment-{$comment->comment_ID}:e7e7d3:action=dim-comment&amp;new=approved",
  542. esc_attr__( 'Approve this comment' ),
  543. __( 'Approve' )
  544. );
  545. }
  546. } else {
  547. $actions['approve'] = sprintf(
  548. '<a href="%s" data-wp-lists="%s" class="vim-a aria-button-if-js" aria-label="%s">%s</a>',
  549. $approve_url,
  550. "dim:the-comment-list:comment-{$comment->comment_ID}:unapproved:e7e7d3:e7e7d3:new=approved",
  551. esc_attr__( 'Approve this comment' ),
  552. __( 'Approve' )
  553. );
  554. $actions['unapprove'] = sprintf(
  555. '<a href="%s" data-wp-lists="%s" class="vim-u aria-button-if-js" aria-label="%s">%s</a>',
  556. $unapprove_url,
  557. "dim:the-comment-list:comment-{$comment->comment_ID}:unapproved:e7e7d3:e7e7d3:new=unapproved",
  558. esc_attr__( 'Unapprove this comment' ),
  559. __( 'Unapprove' )
  560. );
  561. }
  562. if ( 'spam' !== $the_comment_status ) {
  563. $actions['spam'] = sprintf(
  564. '<a href="%s" data-wp-lists="%s" class="vim-s vim-destructive aria-button-if-js" aria-label="%s">%s</a>',
  565. $spam_url,
  566. "delete:the-comment-list:comment-{$comment->comment_ID}::spam=1",
  567. esc_attr__( 'Mark this comment as spam' ),
  568. /* translators: "Mark as spam" link. */
  569. _x( 'Spam', 'verb' )
  570. );
  571. } elseif ( 'spam' === $the_comment_status ) {
  572. $actions['unspam'] = sprintf(
  573. '<a href="%s" data-wp-lists="%s" class="vim-z vim-destructive aria-button-if-js" aria-label="%s">%s</a>',
  574. $unspam_url,
  575. "delete:the-comment-list:comment-{$comment->comment_ID}:66cc66:unspam=1",
  576. esc_attr__( 'Restore this comment from the spam' ),
  577. _x( 'Not Spam', 'comment' )
  578. );
  579. }
  580. if ( 'trash' === $the_comment_status ) {
  581. $actions['untrash'] = sprintf(
  582. '<a href="%s" data-wp-lists="%s" class="vim-z vim-destructive aria-button-if-js" aria-label="%s">%s</a>',
  583. $untrash_url,
  584. "delete:the-comment-list:comment-{$comment->comment_ID}:66cc66:untrash=1",
  585. esc_attr__( 'Restore this comment from the Trash' ),
  586. __( 'Restore' )
  587. );
  588. }
  589. if ( 'spam' === $the_comment_status || 'trash' === $the_comment_status || ! EMPTY_TRASH_DAYS ) {
  590. $actions['delete'] = sprintf(
  591. '<a href="%s" data-wp-lists="%s" class="delete vim-d vim-destructive aria-button-if-js" aria-label="%s">%s</a>',
  592. $delete_url,
  593. "delete:the-comment-list:comment-{$comment->comment_ID}::delete=1",
  594. esc_attr__( 'Delete this comment permanently' ),
  595. __( 'Delete Permanently' )
  596. );
  597. } else {
  598. $actions['trash'] = sprintf(
  599. '<a href="%s" data-wp-lists="%s" class="delete vim-d vim-destructive aria-button-if-js" aria-label="%s">%s</a>',
  600. $trash_url,
  601. "delete:the-comment-list:comment-{$comment->comment_ID}::trash=1",
  602. esc_attr__( 'Move this comment to the Trash' ),
  603. _x( 'Trash', 'verb' )
  604. );
  605. }
  606. if ( 'spam' !== $the_comment_status && 'trash' !== $the_comment_status ) {
  607. $actions['edit'] = sprintf(
  608. '<a href="%s" aria-label="%s">%s</a>',
  609. "comment.php?action=editcomment&amp;c={$comment->comment_ID}",
  610. esc_attr__( 'Edit this comment' ),
  611. __( 'Edit' )
  612. );
  613. $format = '<button type="button" data-comment-id="%d" data-post-id="%d" data-action="%s" class="%s button-link" aria-expanded="false" aria-label="%s">%s</button>';
  614. $actions['quickedit'] = sprintf(
  615. $format,
  616. $comment->comment_ID,
  617. $comment->comment_post_ID,
  618. 'edit',
  619. 'vim-q comment-inline',
  620. esc_attr__( 'Quick edit this comment inline' ),
  621. __( 'Quick&nbsp;Edit' )
  622. );
  623. $actions['reply'] = sprintf(
  624. $format,
  625. $comment->comment_ID,
  626. $comment->comment_post_ID,
  627. 'replyto',
  628. 'vim-r comment-inline',
  629. esc_attr__( 'Reply to this comment' ),
  630. __( 'Reply' )
  631. );
  632. }
  633. /** This filter is documented in wp-admin/includes/dashboard.php */
  634. $actions = apply_filters( 'comment_row_actions', array_filter( $actions ), $comment );
  635. $i = 0;
  636. $out .= '<div class="row-actions">';
  637. foreach ( $actions as $action => $link ) {
  638. ++$i;
  639. ( ( ( 'approve' === $action || 'unapprove' === $action ) && 2 === $i ) || 1 === $i ) ? $sep = '' : $sep = ' | ';
  640. // Reply and quickedit need a hide-if-no-js span when not added with ajax
  641. if ( ( 'reply' === $action || 'quickedit' === $action ) && ! wp_doing_ajax() ) {
  642. $action .= ' hide-if-no-js';
  643. } elseif ( ( $action === 'untrash' && $the_comment_status === 'trash' ) || ( $action === 'unspam' && $the_comment_status === 'spam' ) ) {
  644. if ( '1' == get_comment_meta( $comment->comment_ID, '_wp_trash_meta_status', true ) ) {
  645. $action .= ' approve';
  646. } else {
  647. $action .= ' unapprove';
  648. }
  649. }
  650. $out .= "<span class='$action'>$sep$link</span>";
  651. }
  652. $out .= '</div>';
  653. $out .= '<button type="button" class="toggle-row"><span class="screen-reader-text">' . __( 'Show more details' ) . '</span></button>';
  654. return $out;
  655. }
  656. /**
  657. * @param WP_Comment $comment The comment object.
  658. */
  659. public function column_cb( $comment ) {
  660. if ( $this->user_can ) {
  661. ?>
  662. <label class="screen-reader-text" for="cb-select-<?php echo $comment->comment_ID; ?>"><?php _e( 'Select comment' ); ?></label>
  663. <input id="cb-select-<?php echo $comment->comment_ID; ?>" type="checkbox" name="delete_comments[]" value="<?php echo $comment->comment_ID; ?>" />
  664. <?php
  665. }
  666. }
  667. /**
  668. * @param WP_Comment $comment The comment object.
  669. */
  670. public function column_comment( $comment ) {
  671. echo '<div class="comment-author">';
  672. $this->column_author( $comment );
  673. echo '</div>';
  674. if ( $comment->comment_parent ) {
  675. $parent = get_comment( $comment->comment_parent );
  676. if ( $parent ) {
  677. $parent_link = esc_url( get_comment_link( $parent ) );
  678. $name = get_comment_author( $parent );
  679. printf(
  680. /* translators: %s: Comment link. */
  681. __( 'In reply to %s.' ),
  682. '<a href="' . $parent_link . '">' . $name . '</a>'
  683. );
  684. }
  685. }
  686. comment_text( $comment );
  687. if ( $this->user_can ) {
  688. /** This filter is documented in wp-admin/includes/comment.php */
  689. $comment_content = apply_filters( 'comment_edit_pre', $comment->comment_content );
  690. ?>
  691. <div id="inline-<?php echo $comment->comment_ID; ?>" class="hidden">
  692. <textarea class="comment" rows="1" cols="1"><?php echo esc_textarea( $comment_content ); ?></textarea>
  693. <div class="author-email"><?php echo esc_attr( $comment->comment_author_email ); ?></div>
  694. <div class="author"><?php echo esc_attr( $comment->comment_author ); ?></div>
  695. <div class="author-url"><?php echo esc_attr( $comment->comment_author_url ); ?></div>
  696. <div class="comment_status"><?php echo $comment->comment_approved; ?></div>
  697. </div>
  698. <?php
  699. }
  700. }
  701. /**
  702. * @global string $comment_status
  703. *
  704. * @param WP_Comment $comment The comment object.
  705. */
  706. public function column_author( $comment ) {
  707. global $comment_status;
  708. $author_url = get_comment_author_url( $comment );
  709. $author_url_display = untrailingslashit( preg_replace( '|^http(s)?://(www\.)?|i', '', $author_url ) );
  710. if ( strlen( $author_url_display ) > 50 ) {
  711. $author_url_display = wp_html_excerpt( $author_url_display, 49, '&hellip;' );
  712. }
  713. echo '<strong>';
  714. comment_author( $comment );
  715. echo '</strong><br />';
  716. if ( ! empty( $author_url_display ) ) {
  717. printf( '<a href="%s">%s</a><br />', esc_url( $author_url ), esc_html( $author_url_display ) );
  718. }
  719. if ( $this->user_can ) {
  720. if ( ! empty( $comment->comment_author_email ) ) {
  721. /** This filter is documented in wp-includes/comment-template.php */
  722. $email = apply_filters( 'comment_email', $comment->comment_author_email, $comment );
  723. if ( ! empty( $email ) && '@' !== $email ) {
  724. printf( '<a href="%1$s">%2$s</a><br />', esc_url( 'mailto:' . $email ), esc_html( $email ) );
  725. }
  726. }
  727. $author_ip = get_comment_author_IP( $comment );
  728. if ( $author_ip ) {
  729. $author_ip_url = add_query_arg(
  730. array(
  731. 's' => $author_ip,
  732. 'mode' => 'detail',
  733. ),
  734. admin_url( 'edit-comments.php' )
  735. );
  736. if ( 'spam' === $comment_status ) {
  737. $author_ip_url = add_query_arg( 'comment_status', 'spam', $author_ip_url );
  738. }
  739. printf( '<a href="%1$s">%2$s</a>', esc_url( $author_ip_url ), esc_html( $author_ip ) );
  740. }
  741. }
  742. }
  743. /**
  744. * @param WP_Comment $comment The comment object.
  745. */
  746. public function column_date( $comment ) {
  747. $submitted = sprintf(
  748. /* translators: 1: Comment date, 2: Comment time. */
  749. __( '%1$s at %2$s' ),
  750. /* translators: Comment date format. See https://secure.php.net/date */
  751. get_comment_date( __( 'Y/m/d' ), $comment ),
  752. /* translators: Comment time format. See https://secure.php.net/date */
  753. get_comment_date( __( 'g:i a' ), $comment )
  754. );
  755. echo '<div class="submitted-on">';
  756. if ( 'approved' === wp_get_comment_status( $comment ) && ! empty( $comment->comment_post_ID ) ) {
  757. printf(
  758. '<a href="%s">%s</a>',
  759. esc_url( get_comment_link( $comment ) ),
  760. $submitted
  761. );
  762. } else {
  763. echo $submitted;
  764. }
  765. echo '</div>';
  766. }
  767. /**
  768. * @param WP_Comment $comment The comment object.
  769. */
  770. public function column_response( $comment ) {
  771. $post = get_post();
  772. if ( ! $post ) {
  773. return;
  774. }
  775. if ( isset( $this->pending_count[ $post->ID ] ) ) {
  776. $pending_comments = $this->pending_count[ $post->ID ];
  777. } else {
  778. $_pending_count_temp = get_pending_comments_num( array( $post->ID ) );
  779. $pending_comments = $_pending_count_temp[ $post->ID ];
  780. $this->pending_count[ $post->ID ] = $pending_comments;
  781. }
  782. if ( current_user_can( 'edit_post', $post->ID ) ) {
  783. $post_link = "<a href='" . get_edit_post_link( $post->ID ) . "' class='comments-edit-item-link'>";
  784. $post_link .= esc_html( get_the_title( $post->ID ) ) . '</a>';
  785. } else {
  786. $post_link = esc_html( get_the_title( $post->ID ) );
  787. }
  788. echo '<div class="response-links">';
  789. if ( 'attachment' === $post->post_type ) {
  790. $thumb = wp_get_attachment_image( $post->ID, array( 80, 60 ), true );
  791. if ( $thumb ) {
  792. echo $thumb;
  793. }
  794. }
  795. echo $post_link;
  796. $post_type_object = get_post_type_object( $post->post_type );
  797. echo "<a href='" . get_permalink( $post->ID ) . "' class='comments-view-item-link'>" . $post_type_object->labels->view_item . '</a>';
  798. echo '<span class="post-com-count-wrapper post-com-count-', $post->ID, '">';
  799. $this->comments_bubble( $post->ID, $pending_comments );
  800. echo '</span> ';
  801. echo '</div>';
  802. }
  803. /**
  804. * @param WP_Comment $comment The comment object.
  805. * @param string $column_name The custom column's name.
  806. */
  807. public function column_default( $comment, $column_name ) {
  808. /**
  809. * Fires when the default column output is displayed for a single row.
  810. *
  811. * @since 2.8.0
  812. *
  813. * @param string $column_name The custom column's name.
  814. * @param int $comment->comment_ID The custom column's unique ID number.
  815. */
  816. do_action( 'manage_comments_custom_column', $column_name, $comment->comment_ID );
  817. }
  818. }