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']
)
);
?>
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 = '';
$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 );
}