comment_post_ID ); $post_author_id = absint( $post->post_author ); if ( $post_author_id === get_current_user_id() && current_user_can( 'fcn_moderate_post_comments' ) ) { return true; } // Nope! return false; } // ============================================================================= // ADD PRIVATE TYPE TO FILTER // ============================================================================= /** * Add private comments to dropdown filter menu * * @since 5.0.0 */ function fictioneer_add_private_to_comment_filter( $comment_types ) { $comment_types['private'] = _x( 'Private', 'Private comments filter in comment screen dropdown.', 'fictioneer' ); return $comment_types; } add_filter( 'admin_comment_types_dropdown', 'fictioneer_add_private_to_comment_filter' ); // ============================================================================= // ADD METABOX TO COMMENT EDIT SCREEN // ============================================================================= /** * Adds Fictioneer comment meta box to the wp-admin comment edit screen * * @since 4.7.0 */ function fictioneer_add_comment_meta_box() { add_meta_box( 'title', __( 'Comment Moderation', 'fictioneer' ), 'fictioneer_comment_meta_box', 'comment', 'normal', 'high' ); } /** * HTML for the Fictioneer comment meta box * * @since 4.7.0 * * @param object $comment The comment object. */ function fictioneer_comment_meta_box ( $comment ) { // Setup $type = get_comment_type( $comment->comment_ID ); $user_reports = get_comment_meta( $comment->comment_ID, 'fictioneer_user_reports', true ); $is_offensive = get_comment_meta( $comment->comment_ID, 'fictioneer_marked_offensive', true ); $is_sticky = get_comment_meta( $comment->comment_ID, 'fictioneer_sticky', true ); $is_closed = get_comment_meta( $comment->comment_ID, 'fictioneer_thread_closed', true ); $ignores_reports = get_comment_meta( $comment->comment_ID, 'fictioneer_ignore_reports', true ); $last_auto_moderation = get_comment_meta( $comment->comment_ID, 'fictioneer_auto_moderation', true ); $edit_stack = get_comment_meta( $comment->comment_ID, 'fictioneer_user_edit_stack', true ); $notification_checksum = get_comment_meta( $comment->comment_ID, 'fictioneer_send_notifications', true ); $notification_validator = get_user_meta( $comment->user_id, 'fictioneer_comment_reply_validator', true ); $email = $comment->comment_author_email; wp_nonce_field( 'fictioneer_comment_update', 'fictioneer_comment_update', false ); ?>
%1$s. The comment will remain hidden even if re-approved as long as the report threshold of %2$s or more is met unless the comment is set to ignore reports.', 'fictioneer' ), wp_date( get_option( 'date_format' ) . ', ' . get_option( 'time_format' ), $last_auto_moderation ), get_option( 'fictioneer_comment_report_threshold', 10 ) ); ?>
0 ) : ?>
user_login, $edit['user_id'] ?? 0, wp_date( sprintf( _x( '%1$s \a\t %2$s', 'Comment moderation edit stack item [date], [time].', 'fictioneer' ), get_option( 'date_format' ), get_option( 'time_format' ) ), $edit['timestamp'] ) ); ?>
comment_parent ) ) : ?>
user_id, 'moderate_comments' ) ) : ?>
user_id, 'fictioneer_admin_disable_avatar', $disable_avatar ); fictioneer_update_user_meta( $comment->user_id, 'fictioneer_admin_disable_reporting', $disable_reports ); fictioneer_update_user_meta( $comment->user_id, 'fictioneer_admin_disable_renaming', $disable_renaming ); fictioneer_update_user_meta( $comment->user_id, 'fictioneer_admin_disable_commenting', $disable_commenting ); fictioneer_update_user_meta( $comment->user_id, 'fictioneer_admin_disable_comment_editing', $disable_editing ); fictioneer_update_user_meta( $comment->user_id, 'fictioneer_admin_disable_comment_notifications', $disable_notifications ); fictioneer_update_user_meta( $comment->user_id, 'fictioneer_admin_always_moderate_comments', $hold_comments ); } // Add filters and actions if not disabled if ( ! get_option( 'fictioneer_disable_comment_form' ) ) { add_action( 'add_meta_boxes_comment', 'fictioneer_add_comment_meta_box' ); add_action( 'edit_comment', 'fictioneer_edit_comment', 10 ); } /** * Tracks comment edits and stores a history of changes * * @since 5.5.3 * * @param array|WP_Error $data The comment data to be saved. * @param array $comment The old comment data. * * @return array The unchanged comment data. */ function fictioneer_track_comment_edit( $data, $comment ) { // Abort if... if ( is_wp_error( $data ) ) { return $data; } // Setup $previous = get_comment( $comment['comment_ID'] ); $edit_stack = get_comment_meta( $comment['comment_ID'], 'fictioneer_user_edit_stack', true ); $edit_stack = is_array( $edit_stack ) ? $edit_stack : []; // Not edited? if ( $previous->comment_content === $data['comment_content'] ) { return $data; } // Add new edit $edit_stack[] = array( 'timestamp' => time(), 'previous_content' => $previous->comment_content, 'user_id' => get_current_user_id() ); // Prevent loop remove_filter( 'wp_update_comment_data', 'fictioneer_track_comment_edit', 10 ); // Update stack fictioneer_update_comment_meta( $comment['comment_ID'], 'fictioneer_user_edit_stack', $edit_stack ); // Restore filter add_filter( 'wp_update_comment_data', 'fictioneer_track_comment_edit', 10, 2 ); // Continue comment update return $data; } add_filter( 'wp_update_comment_data', 'fictioneer_track_comment_edit', 10, 2 ); // ============================================================================= // GET COMMENT ACTION LINK // ============================================================================= if ( ! function_exists( 'fictioneer_get_comment_action_link' ) ) { /** * Returns a link to an admin comment moderation action or false * * @since 4.7.0 * @link https://github.com/WordPress/WordPress/blob/master/wp-admin/comment.php * * @param int $comment_id The ID of the comment. * @param string $action Optional. The action to perform. Default 'edit'. * @param string|boolean $redirect_to Optional. Return URL after action. Default false. * * @return string|boolean The link or false if an invalid call. */ function fictioneer_get_comment_action_link( $comment_id, $action = 'edit', $redirect_to = false ) { // Setup $comment_id = fictioneer_validate_id( $comment_id ); // Validation if ( ! $comment_id ) { return false; } // Data $template = '%2$s'; $admin_url = admin_url( 'comment.php?c=' . $comment_id . '&action=' ); $redirect_to = $redirect_to ? '&redirect_to=' . $redirect_to : ''; $output = false; switch ( $action ) { case 'edit': $output = sprintf( $template, $admin_url . 'editcomment', __( 'Edit' ) ); break; case 'spam': $nonce_url = esc_url( wp_nonce_url( $admin_url . 'spamcomment', 'delete-comment_' . $comment_id ) ); $output = sprintf( $template, $nonce_url . $redirect_to, __( 'Spam' ) ); break; case 'trash': $nonce_url = esc_url( wp_nonce_url( $admin_url . 'trashcomment', 'delete-comment_' . $comment_id ) ); $output = sprintf( $template, $nonce_url . $redirect_to, __( 'Trash' ) ); break; case 'approve': $nonce_url = esc_url( wp_nonce_url( $admin_url . 'approvecomment', 'approve-comment_' . $comment_id ) ); $output = sprintf( $template, $nonce_url . $redirect_to, __( 'Approve' ) ); break; case 'unapprove': $nonce_url = esc_url( wp_nonce_url( $admin_url . 'unapprovecomment', 'approve-comment_' . $comment_id ) ); $output = sprintf( $template, $nonce_url . $redirect_to, __( 'Unapprove' ) ); break; case 'delete': $nonce_url = esc_url( wp_nonce_url( $admin_url . 'deletecomment', 'delete-comment_' . $comment_id ) ); $output = sprintf( $template, $nonce_url . $redirect_to, __( 'Delete' ) ); break; } return $output; } } // ============================================================================= // RENDER COMMENT MODERATION MENU // ============================================================================= if ( ! function_exists( 'fictioneer_comment_mod_menu' ) ) { /** * Renders HTML for the moderation menu * * @since 4.7.0 * * @param WP_Comment $comment Comment object. */ function fictioneer_comment_mod_menu( $comment ) { // Setup $comment_id = $comment->comment_ID; $user_can_moderate = fictioneer_user_can_moderate( $comment ); // Abort conditions... if ( ! current_user_can( 'moderate_comments' ) && ! $user_can_moderate && ! get_option( 'fictioneer_enable_public_cache_compatibility' ) ) { return; } // Start HTML ---> ?> __( 'Not allowed.', 'fictioneer' ) ), 403 ); } // Setup and validations $user = fictioneer_get_validated_ajax_user(); if ( ! $user ) { wp_send_json_error( array( 'error' => __( 'Request did not pass validation.', 'fictioneer' ) ) ); } $comment_id = isset( $_POST['id'] ) ? fictioneer_validate_id( $_POST['id'] ) : false; if ( empty( $_POST['operation'] ) || ! $comment_id ) { wp_send_json_error( array( 'error' => __( 'Missing arguments.', 'fictioneer' ) ) ); } $operation = sanitize_text_field( $_POST['operation'] ); if ( ! in_array( $operation, ['spam', 'trash', 'approve', 'unapprove', 'close', 'open', 'sticky', 'unsticky'] ) ) { wp_send_json_error( array( 'error' => __( 'Invalid operation.', 'fictioneer' ) ) ); } $comment = get_comment( $comment_id ); if ( ! $comment ) { wp_send_json_error( array( 'error' => __( 'Comment not found in database.', 'fictioneer' ) ) ); } // Capabilities? if ( ! fictioneer_user_can_moderate( $comment ) ) { wp_send_json_error( array( 'error' => __( 'Permission denied!', 'fictioneer' ) ) ); } if ( $operation === 'trash' && ! current_user_can( 'moderate_comments' ) ) { wp_send_json_error( array( 'error' => __( 'Permission denied!', 'fictioneer' ) ) ); } // Process and update $result = false; switch ( $operation ) { case 'spam': $result = wp_set_comment_status( $comment_id, 'spam' ); break; case 'trash': $result = wp_set_comment_status( $comment_id, 'trash' ); break; case 'approve': $result = wp_set_comment_status( $comment_id, 'approve' ); break; case 'unapprove': $result = wp_set_comment_status( $comment_id, 'hold' ); break; case 'close': $result = fictioneer_update_comment_meta( $comment_id, 'fictioneer_thread_closed', true ); break; case 'open': $result = fictioneer_update_comment_meta( $comment_id, 'fictioneer_thread_closed', false ); break; case 'sticky': if ( $comment->comment_parent ) { wp_send_json_error( ['error' => __( 'Child comments cannot be sticky.', 'fictioneer' )] ); break; } if ( get_comment_meta( $comment->comment_ID, 'fictioneer_deleted_by_user', true ) ) { wp_send_json_error( ['error' => __( 'Deleted comments cannot be sticky.', 'fictioneer' )] ); break; } $result = fictioneer_update_comment_meta( $comment_id, 'fictioneer_sticky', true ); break; case 'unsticky': $result = fictioneer_update_comment_meta( $comment_id, 'fictioneer_sticky', false ); break; } // Send result if ( $result ) { // Clear cache if necessary if ( fictioneer_caching_active( 'ajax_comment_moderation' ) ) { fictioneer_purge_post_cache( $comment->comment_post_ID ); } wp_send_json_success( array( 'id' => $comment_id, 'operation' => $operation ) ); } else { wp_send_json_error( array( 'error' => __( 'Database error. Comment could not be updated.', 'fictioneer' ) ) ); } } if ( get_option( 'fictioneer_enable_ajax_comment_moderation' ) ) { add_action( 'wp_ajax_fictioneer_ajax_moderate_comment', 'fictioneer_ajax_moderate_comment' ); } // ============================================================================= // REPORT COMMENTS // ============================================================================= /** * Adds user to a comment's reports list via AJAX * * @since 4.7.0 * @link https://developer.wordpress.org/reference/functions/wp_send_json_success/ * @link https://developer.wordpress.org/reference/functions/wp_send_json_error/ */ function fictioneer_ajax_report_comment() { // Enabled? if ( get_option( 'fictioneer_disable_comment_callback' ) || ! get_option( 'fictioneer_enable_comment_reporting' ) ) { wp_send_json_error( array( 'error' => __( 'Not allowed.', 'fictioneer' ) ), 403 ); } // Setup and validations $user = fictioneer_get_validated_ajax_user(); if ( ! $user ) { wp_send_json_error( ['error' => __( 'Request did not pass validation.', 'fictioneer' )] ); } if ( empty( $_POST['id'] ) ) { wp_send_json_error( ['error' => __( 'Missing arguments.', 'fictioneer' )] ); } $comment_id = fictioneer_validate_id( $_POST['id'] ); $dubious = empty( $_POST['dubious'] ) ? false : $_POST['dubious'] == 'true'; // Get comment $comment = get_comment( $comment_id ); if ( ! $comment ) { wp_send_json_error( ['error' => __( 'Comment not found.', 'fictioneer' )] ); } // Check if user is allowed to flag comments if ( get_the_author_meta( 'fictioneer_admin_disable_reporting', $user->ID ) ) { wp_send_json_error( ['error' => __( 'Reporting capability disabled.', 'fictioneer' )] ); } // Get current reports (array of user IDs) $reports = get_comment_meta( $comment_id, 'fictioneer_user_reports', true ); $reports = $reports ? $reports : []; // Dubious if state not clear due to caching if ( $dubious && array_key_exists( $user->ID, $reports ) ) { wp_send_json_success( array( 'id' => $comment_id, 'flagged' => true, 'resync' => __( 'You have already reported this comment. Click the flag again to retract the report.', 'fictioneer' ) ) ); } // Check whether to add or remove the user from the array if ( array_key_exists( $user->ID, $reports ) ) { unset( $reports[ $user->ID ] ); } else { $user_data = get_userdata( $user->ID ); $reports[ $user->ID ] = '' . $user_data->display_name . ''; } // Update comment meta $result = fictioneer_update_comment_meta( $comment_id, 'fictioneer_user_reports', $reports ); if ( ! $result ) { wp_send_json_error( ['error' => __( 'Database error. Report could not be saved.', 'fictioneer' )] ); } // Send back to moderation? $auto_moderation = get_comment_meta( $comment_id, 'fictioneer_auto_moderation', true ); if ( empty( $auto_moderation ) && count( $reports ) >= get_option( 'fictioneer_comment_report_threshold', 10 ) ) { // Only ever auto-moderate once! fictioneer_update_comment_meta( $comment_id, 'fictioneer_auto_moderation', time() ); // Set back to hold wp_set_comment_status( $comment_id, 'hold' ); } // Purge cache if ( fictioneer_caching_active( 'ajax_report_comment' ) ) { fictioneer_purge_post_cache( $comment->comment_post_ID ); } // Return result wp_send_json_success( array( 'id' => $comment_id, 'flagged' => array_key_exists( $user->ID, $reports ) ) ); } /** * Add comments reports column * * @since 4.7.0 * @link https://rudrastyh.com/wordpress/comments-table-columns.html * * @param array $cols Default columns. * * @return array Updated columns. */ function fictioneer_add_comments_report_column( $cols ) { $new_cols = ['fictioneer_reports' => __( 'Reports', 'fictioneer' )]; $new_cols = array_slice( $cols, 0, 3, true ) + $new_cols + array_slice( $cols, 3, NULL, true ); return $new_cols; } /** * Echo content for comments reports column * * @since 4.7.0 * @link https://rudrastyh.com/wordpress/comments-table-columns.html * @link https://developer.wordpress.org/reference/hooks/manage_comments_custom_column/ * * @param string $column The custom column's name. * @param string $comment_id The comment ID as a numeric string. */ function fictioneer_add_comments_report_column_content( $column, $comment_id ) { $reports = get_comment_meta( $comment_id, 'fictioneer_user_reports', true ); if ( $reports && is_array( $reports ) ) { echo count( $reports ); } else { echo 0; } } if ( ! get_option( 'fictioneer_disable_comment_callback' ) && get_option( 'fictioneer_enable_comment_reporting' ) ) { add_action( 'wp_ajax_fictioneer_ajax_report_comment', 'fictioneer_ajax_report_comment' ); add_filter( 'manage_edit-comments_columns', 'fictioneer_add_comments_report_column' ); add_action( 'manage_comments_custom_column', 'fictioneer_add_comments_report_column_content', 10, 2 ); }