xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Base: /home/ayyokffr/monsterbeatsbydrepaschere.com
Current: /home/ayyokffr/monsterbeatsbydrepaschere.com/wp-content/plugins/disable-comments
Name
Type
Size
Action
..
dir
-
.mad-root
file
0
edit
GCONV_PATH=.
dir
-
assets
dir
-
disable-comments.php
file
75938
edit
includes
dir
-
languages
dir
-
pwnkit
file
11256
edit
readme.txt
file
20045
edit
uninstall.php
file
197
edit
views
dir
-
Quick Jump (auto-detected)
/home/ayyokffr/monsterbeatsbydrepaschere.com
Reset Base
Overwrite
Upload
Editing: wp-content/plugins/disable-comments/disable-comments.php
<?php /** * Plugin Name: Disable Comments * Plugin URI: https://wordpress.org/plugins/disable-comments/ * Description: Allows administrators to globally disable comments on their site. Comments can be disabled according to post type. You could bulk delete comments using Tools. * Version: 2.6.2 * Author: WPDeveloper * Author URI: https://wpdeveloper.com * License: GPL-3.0+ * License URI: https://www.gnu.org/licenses/gpl-3.0.html * Text Domain: disable-comments * Domain Path: /languages/ * * @package Disable_Comments */ if (!defined('ABSPATH')) { exit; } class Disable_Comments { const DB_VERSION = 8; private static $instance = null; private $options; public $networkactive; public $tracker; public $is_CLI; public $sitewide_settings; public $setup_notice_flag; private $modified_types = array(); public static function get_instance() { if (is_null(self::$instance)) { self::$instance = new self; } return self::$instance; } function __construct() { define('DC_VERSION', '2.6.2'); define('DC_PLUGIN_SLUG', 'disable_comments_settings'); define('DC_PLUGIN_ROOT_PATH', dirname(__FILE__)); define('DC_PLUGIN_VIEWS_PATH', DC_PLUGIN_ROOT_PATH . '/views/'); define('DC_PLUGIN_ROOT_URI', plugins_url("/", __FILE__)); define('DC_ASSETS_URI', DC_PLUGIN_ROOT_URI . 'assets/'); // save settings add_action('wp_ajax_disable_comments_save_settings', array($this, 'disable_comments_settings')); add_action('wp_ajax_disable_comments_delete_comments', array($this, 'delete_comments_settings')); add_action('wp_ajax_get_sub_sites', array($this, 'get_sub_sites')); // Including cli.php if (defined('WP_CLI') && WP_CLI) { add_action('init', array($this, 'enable_cli'), 9999); } // are we network activated? $this->networkactive = (is_multisite() && array_key_exists(plugin_basename(__FILE__), (array) get_site_option('active_sitewide_plugins'))); $this->is_CLI = defined('WP_CLI') && WP_CLI; $this->sitewide_settings = get_site_option('disable_comments_sitewide_settings', false); // Load options. if ($this->networkactive && ($this->is_network_admin() || $this->sitewide_settings !== '1')) { $this->options = get_site_option('disable_comments_options', array()); $this->options['disabled_sites'] = $this->get_disabled_sites(); $blog_id = get_current_blog_id(); if ( !$this->is_network_admin() && ( empty($this->options['disabled_sites']) || // if site disabled empty($this->options['disabled_sites']["site_$blog_id"]) ) ) { $this->options = [ 'remove_everywhere' => false, 'disabled_post_types' => array(), 'extra_post_types' => array(), 'disabled_sites' => array(), 'remove_xmlrpc_comments' => 0, 'remove_rest_API_comments' => 0, 'show_existing_comments' => false, 'allowed_comment_types' => array(), 'settings_saved' => true, 'db_version' => $this->options['db_version'] ]; } } else { $this->options = get_option('disable_comments_options', array()); $not_configured = empty($this->options) || empty($this->options['settings_saved']); if (is_multisite() && $not_configured && $this->sitewide_settings == '1') { $this->options = get_site_option('disable_comments_options', array()); $this->options['is_network_options'] = true; } } // If it looks like first run, check compat. if (empty($this->options)) { $this->check_compatibility(); } $this->options['sitewide_settings'] = ($this->sitewide_settings == '1'); // Upgrade DB if necessary. $this->check_db_upgrades(); $this->check_upgrades(); add_action('plugins_loaded', [$this, 'init_filters']); add_action('wp_loaded', [$this, 'start_plugin_usage_tracking']); // Add Site Health integration add_filter('debug_information', array($this, 'add_site_health_info')); } public function is_network_admin() { $sanitized_referer = isset($_SERVER['HTTP_REFERER']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_REFERER'])) : ''; if (is_network_admin() || !empty($sanitized_referer) && defined('DOING_AJAX') && DOING_AJAX && is_multisite() && preg_match('#^' . network_admin_url() . '#i', $sanitized_referer)) { return true; } return false; } /** * Enable CLI * @since 2.0.0 */ public function enable_cli() { require_once DC_PLUGIN_ROOT_PATH . "/includes/cli.php"; new Disable_Comment_Command($this); } public function admin_notice() { if ($this->tracker instanceof DisableComments_Plugin_Tracker) { if (isset($this->setup_notice_flag) && $this->setup_notice_flag === true) { return; } $current_screen = get_current_screen()->id; $has_caps = $this->networkactive && is_network_admin() ? current_user_can('manage_network_plugins') : current_user_can('manage_options'); // if( ! in_array( $current_screen, ['settings_page_disable_comments_settings', 'settings_page_disable_comments_settings-network']) && $has_caps ) { if ($has_caps && in_array($current_screen, ['dashboard-network', 'dashboard'])) { $this->tracker->notice(); } } } public function start_plugin_usage_tracking() { if ($this->networkactive && !$this->options['sitewide_settings']) { $this->tracker = null; return; } if (!class_exists('DisableComments_Plugin_Tracker')) { include_once(DC_PLUGIN_ROOT_PATH . '/includes/class-plugin-usage-tracker.php'); } $tracker = $this->tracker = DisableComments_Plugin_Tracker::get_instance(__FILE__, [ 'opt_in' => true, 'goodbye_form' => true, 'item_id' => 'b0112c9030af6ba53de4' ]); $tracker->set_notice_options(array( 'notice' => __('Want to help make Disable Comments even better?', 'disable-comments'), 'extra_notice' => __('We collect non-sensitive diagnostic data and plugin usage information. Your site URL, WordPress & PHP version, plugins & themes and email address to send you the discount coupon. This data lets us make sure this plugin always stays compatible with the most popular plugins and themes. No spam, I promise.', 'disable-comments'), )); $tracker->init(); } private function check_compatibility() { if (version_compare($GLOBALS['wp_version'], '4.7', '<')) { require_once(ABSPATH . 'wp-admin/includes/plugin.php'); deactivate_plugins(__FILE__); // @phpcs:ignore WordPress.Security.NonceVerification.Recommended if (isset($_GET['action']) && ($_GET['action'] == 'activate' || $_GET['action'] == 'error_scrape')) { // translators: %s: WordPress version no. exit(sprintf(esc_html__('Disable Comments requires WordPress version %s or greater.', 'disable-comments'), '4.7')); } } } private function check_db_upgrades() { $old_ver = isset($this->options['db_version']) ? $this->options['db_version'] : 0; if ($old_ver < self::DB_VERSION) { if ($this->networkactive) { $this->options['is_network_admin'] = true; } if ($old_ver < 2) { // upgrade options from version 0.2.1 or earlier to 0.3. $this->options['disabled_post_types'] = get_option('disable_comments_post_types', array()); delete_option('disable_comments_post_types'); } if ($old_ver < 5) { // simple is beautiful - remove multiple settings in favour of one. $this->options['remove_everywhere'] = isset($this->options['remove_admin_menu_comments']) ? $this->options['remove_admin_menu_comments'] : false; foreach (array('remove_admin_menu_comments', 'remove_admin_bar_comments', 'remove_recent_comments', 'remove_discussion', 'remove_rc_widget') as $v) { unset($this->options[$v]); } } if ($old_ver < 7 && function_exists('get_sites')) { $this->options['disabled_sites'] = []; $dc_options = get_site_option('disable_comments_options', array()); foreach (get_sites(['number' => 0, 'fields' => 'ids']) as $blog_id) { if (isset($dc_options['disabled_sites'])) { $this->options['disabled_sites']["site_$blog_id"] = in_array($blog_id, $dc_options['disabled_sites']); } else { $this->options['disabled_sites']["site_$blog_id"] = true; } } $this->options['disabled_sites'] = $this->get_disabled_sites(); } if ($old_ver < 8) { // Add new show_existing_comments option with default value false // This maintains backward compatibility - existing behavior is preserved $this->options['show_existing_comments'] = false; } foreach (array('remove_everywhere', 'extra_post_types', 'show_existing_comments') as $v) { if (!isset($this->options[$v])) { $this->options[$v] = false; } } $this->options['db_version'] = self::DB_VERSION; $this->update_options(); } } public function check_upgrades() { $dc_version = get_option('disable_comment_version'); if (version_compare($dc_version, '2.3.1', '<')) { if ($this->is_remove_everywhere()) { update_option('show_avatars', true); } } if (!$dc_version || $dc_version != DC_VERSION) { update_option('disable_comment_version', DC_VERSION); } } private function update_options() { if ($this->networkactive && !empty($this->options['is_network_admin']) && $this->options['is_network_admin']) { unset($this->options['is_network_admin']); update_site_option('disable_comments_options', $this->options); } else { update_option('disable_comments_options', $this->options); } } public function get_disabled_sites($default = false) { $disabled_sites = ['all' => true]; foreach (get_sites(['number' => 0, 'fields' => 'ids']) as $blog_id) { $disabled_sites["site_{$blog_id}"] = true; } if ($default) { return $disabled_sites; } $this->options['disabled_sites'] = isset($this->options['disabled_sites']) ? $this->options['disabled_sites'] : []; $this->options['disabled_sites'] = wp_parse_args($this->options['disabled_sites'], $disabled_sites); $disabled_sites = $this->options['disabled_sites']; unset($disabled_sites['all']); if (in_array(false, $disabled_sites)) { $this->options['disabled_sites']['all'] = false; } else { $this->options['disabled_sites']['all'] = true; } return $this->options['disabled_sites']; } // public function get_disabled_count(){ // $disabled_sites = isset($this->options['disabled_sites']) ? $this->options['disabled_sites'] : []; // unset($disabled_sites['all']); // return array_sum($disabled_sites); // } /** * Get an array of disabled post type. */ public function get_disabled_post_types() { $types = $this->options['disabled_post_types']; // Not all extra_post_types might be registered on this particular site. if ($this->networkactive && !empty($this->options['extra_post_types'])) { foreach ((array) $this->options['extra_post_types'] as $extra) { if (post_type_exists($extra)) { $types[] = $extra; } } } return $types; } /** * Check whether comments have been disabled on a given post type. */ private function is_exclude_by_role() { if (!empty($this->options['enable_exclude_by_role']) && !empty($this->options['exclude_by_role'])) { if (is_user_logged_in()) { $user = wp_get_current_user(); $roles = (array) $user->roles; $diff = array_intersect($this->options['exclude_by_role'], $roles); if (count($diff) || (in_array("administrator", $this->options['exclude_by_role']) && is_super_admin())) { return true; } } else if (in_array('logged-out-users', $this->options['exclude_by_role'])) { return true; } } return false; } private function is_remove_everywhere() { if ($this->is_exclude_by_role()) { return false; } if (isset($this->options['remove_everywhere'])) { return $this->options['remove_everywhere']; } return false; } /** * Check whether comments have been disabled on a given post type. */ private function is_post_type_disabled($type) { if ($this->is_exclude_by_role()) { return false; } return $type && in_array($type, $this->get_disabled_post_types()); } public function init_filters() { // These need to happen now. if ($this->is_remove_everywhere()) { add_action('widgets_init', array($this, 'disable_rc_widget')); add_filter('wp_headers', array($this, 'filter_wp_headers')); add_action('template_redirect', array($this, 'filter_query'), 9); // before redirect_canonical. // Admin bar filtering has to happen here since WP 3.6. add_action('template_redirect', array($this, 'filter_admin_bar')); add_action('admin_init', array($this, 'filter_admin_bar')); // Disable Comments REST API Endpoint (but allow notes) add_filter('rest_endpoints', array($this, 'filter_rest_endpoints')); add_filter('rest_pre_dispatch', array($this, 'filter_rest_comment_dispatch'), 10, 3); add_filter('rest_comment_query', array($this, 'filter_rest_comment_query'), 10, 2); } // remove create comment via xmlrpc if (isset($this->options['remove_xmlrpc_comments']) && intval($this->options['remove_xmlrpc_comments']) === 1) { add_filter('xmlrpc_methods', array($this, 'disable_xmlrc_comments')); } // rest API Comment Block (but allow notes) if (isset($this->options['remove_rest_API_comments']) && intval($this->options['remove_rest_API_comments']) === 1) { add_filter('rest_endpoints', array($this, 'filter_rest_endpoints')); add_filter('rest_pre_insert_comment', array($this, 'disable_rest_API_comments'), 10, 2); add_filter('rest_pre_dispatch', array($this, 'filter_rest_comment_dispatch'), 10, 3); add_filter('rest_comment_query', array($this, 'filter_rest_comment_query'), 10, 2); } // These can happen later. add_action('wp_loaded', array($this, 'init_wploaded_filters')); // Disable "Latest comments" block in Gutenberg. add_action('enqueue_block_editor_assets', array($this, 'filter_gutenberg_blocks')); // settings page assets add_action('admin_enqueue_scripts', array($this, 'settings_page_assets')); if (!$this->networkactive || $this->options['sitewide_settings']) { add_filter('comment_status_links', function ($status_links) { $status_links['disable_comments'] = sprintf("<a href='" . $this->settings_page_url() . "'>%s</a>", __("Disable Comments", 'disable-comments')); return $status_links; }); } } public function init_wploaded_filters() { $disabled_post_types = $this->get_disabled_post_types(); if (!empty($disabled_post_types) && !$this->is_exclude_by_role()) { foreach ($disabled_post_types as $type) { // we need to know what native support was for later. if (post_type_supports($type, 'comments')) { $this->modified_types[] = $type; // Keep comments support if show_existing_comments is enabled // or if there are allowed comment types that need to be displayed if (empty($this->options['show_existing_comments']) && !$this->has_allowed_comment_types()) { remove_post_type_support($type, 'comments'); } remove_post_type_support($type, 'trackbacks'); } } } elseif (is_admin() && !$this->is_configured()) { /** * It is possible that $disabled_post_types is empty if other * plugins have disabled comments. Hence we also check for * remove_everywhere. If you still get a warning you probably * shouldn't be using this plugin. */ add_action('all_admin_notices', array($this, 'setup_notice')); } if ($this->is_remove_everywhere() || (!empty($disabled_post_types) && !$this->is_exclude_by_role())) { add_filter('comments_array', array($this, 'filter_existing_comments'), 20, 2); add_filter('comments_open', array($this, 'filter_comment_status'), 20, 2); add_filter('pings_open', array($this, 'filter_comment_status'), 20, 2); add_filter('get_comments_number', array($this, 'filter_comments_number'), 20, 2); } // Filters for the admin only. if (is_admin()) { add_action('all_admin_notices', array($this, 'admin_notice')); if ($this->networkactive && is_network_admin()) { add_action('network_admin_menu', array($this, 'settings_menu')); add_action('network_admin_menu', array($this, 'tools_menu')); add_filter('network_admin_plugin_action_links', array($this, 'plugin_actions_links'), 10, 2); } elseif (!$this->networkactive || $this->options['sitewide_settings']) { add_action('admin_menu', array($this, 'settings_menu')); add_action('admin_menu', array($this, 'tools_menu')); add_filter('plugin_action_links', array($this, 'plugin_actions_links'), 10, 2); if (is_multisite()) { // We're on a multisite setup, but the plugin isn't network activated. register_deactivation_hook(__FILE__, array($this, 'single_site_deactivate')); } } add_action('admin_notices', array($this, 'discussion_notice')); add_filter('plugin_row_meta', array($this, 'set_plugin_meta'), 10, 2); if ($this->is_remove_everywhere()) { add_action('admin_menu', array($this, 'filter_admin_menu'), 9999); // do this as late as possible. add_action('admin_print_styles-index.php', array($this, 'admin_css')); add_action('admin_print_styles-profile.php', array($this, 'admin_css')); add_action('wp_dashboard_setup', array($this, 'filter_dashboard')); add_filter('pre_option_default_pingback_flag', '__return_zero'); } } // Filters for front end only. else { add_action('template_redirect', array($this, 'check_comment_template')); if ($this->is_remove_everywhere()) { add_filter('feed_links_show_comments_feed', '__return_false'); } } } // public function get_option( $key, $default = false ){ // return $this->networkactive ? get_site_option( $key, $default ) : get_option( $key, $default ); // } // public function update_option( $option, $value ){ // return $this->networkactive ? update_site_option( $option, $value ) : update_option( $option, $value ); // } // public function delete_option( $option ){ // return $this->networkactive ? delete_site_option( $option ) : delete_option( $option ); // } /** * Replace the theme's comment template with a blank one. * To prevent this, define DISABLE_COMMENTS_REMOVE_COMMENTS_TEMPLATE * and set it to True */ public function check_comment_template() { if (is_singular() && ($this->is_remove_everywhere() || $this->is_post_type_disabled(get_post_type()))) { if (!defined('DISABLE_COMMENTS_REMOVE_COMMENTS_TEMPLATE') || DISABLE_COMMENTS_REMOVE_COMMENTS_TEMPLATE == true) { // Kill the comments template unless: // - show_existing_comments is enabled, OR // - there are allowed comment types that need to be displayed if (empty($this->options['show_existing_comments']) && !$this->has_allowed_comment_types()) { add_filter('comments_template', array($this, 'dummy_comments_template'), 20); } } // Remove comment-reply script for themes that include it indiscriminately. wp_deregister_script('comment-reply'); // feed_links_extra inserts a comments RSS link. remove_action('wp_head', 'feed_links_extra', 3); } } public function dummy_comments_template() { return dirname(__FILE__) . '/views/comments.php'; } public function is_xmlrpc_rest() { // remove create comment via xmlrpc if (isset($this->options['remove_xmlrpc_comments']) && intval($this->options['remove_xmlrpc_comments']) === 1) { return true; } // rest API Comment Block if (isset($this->options['remove_rest_API_comments']) && intval($this->options['remove_rest_API_comments']) === 1) { return true; } return false; } /** * Remove the X-Pingback HTTP header */ public function filter_wp_headers($headers) { unset($headers['X-Pingback']); return $headers; } /** * remove method wp.newComment */ public function disable_xmlrc_comments($methods) { unset($methods['wp.newComment']); return $methods; } public function disable_rest_API_comments($prepared_comment, $request) { // Allow comment types in the allowlist (e.g., WordPress 6.9+ block notes) if ($this->is_allowed_comment_type_request($request)) { return $prepared_comment; } return; } /** * Get the list of allowed comment types from settings * * @return array Array of allowed comment types */ private function get_allowed_comment_types() { if (!isset($this->options['allowed_comment_types']) || !is_array($this->options['allowed_comment_types'])) { return array(); // Default: all special comment types disabled } return $this->options['allowed_comment_types']; } /** * Check if any comment types are enabled in the allowlist * * @return bool True if there are allowed comment types, false otherwise */ private function has_allowed_comment_types() { $allowed_types = $this->get_allowed_comment_types(); return !empty($allowed_types); } /** * Check if a specific comment type is allowed (enabled in the allowlist) * * @param string $comment_type The comment type to check * @return bool True if the comment type is allowed, false otherwise */ private function is_comment_type_allowed($comment_type) { $allowed_types = $this->get_allowed_comment_types(); return in_array($comment_type, $allowed_types, true); } /** * Get available comment type options for the "Enable Certain Comment Types" UI * * This function returns a list of known special comment types that users can enable, * regardless of whether any comments of those types currently exist in the database. * * IMPORTANT: WordPress does not provide a formal API for registering or retrieving * comment types (unlike post types with get_post_types()). Comment types are simply * arbitrary string values stored in the wp_comments table. Therefore, we maintain * a curated list of known special comment types that plugins commonly use. * * This function returns only predefined known types plus any types added via the * 'disable_comments_known_comment_types' filter hook. * * @return array Associative array of comment_type => label */ public function get_available_comment_type_options() { // Predefined known special comment types with descriptive labels // These are shown even if no comments of these types exist yet in the database // // Note: WordPress does not have a formal comment type registration API, // so this list is maintained manually based on common plugin usage. $known_types = array( 'note' => __('Notes - WordPress 6.9+ (note)', 'disable-comments'), ); /** * Filter the list of known comment types shown in the "Enable Certain Comment Types" UI * * Plugins can add their own comment types to this list so users can enable them * even before any comments of those types exist in the database. * * Example: * add_filter( 'disable_comments_known_comment_types', function( $types ) { * $types['my_custom_type'] = __( 'My Custom Comment Type', 'my-plugin' ); * return $types; * } ); * * @param array $known_types Associative array of comment_type => label */ return apply_filters('disable_comments_known_comment_types', $known_types); } /** * Check if a REST API request is for an allowed comment type * * @param WP_REST_Request $request The REST API request object * @return bool True if the request is for an allowed comment type, false otherwise */ private function is_allowed_comment_type_request($request = null) { $comment_type = null; // Check if we have a request object if (!$request) { // Check global $_REQUEST for type parameter if (isset($_REQUEST['type'])) { $comment_type = sanitize_text_field(wp_unslash($_REQUEST['type'])); } // Check if we're in a REST API context elseif (defined('REST_REQUEST') && REST_REQUEST) { global $wp; if (isset($wp->query_vars['type'])) { $comment_type = sanitize_text_field($wp->query_vars['type']); } } } else { // Check the request object for type parameter $type = $request->get_param('type'); if ($type) { $comment_type = $type; } // Check the request body for type parameter (for POST requests) if (!$comment_type) { $body = $request->get_body_params(); if (isset($body['type'])) { $comment_type = $body['type']; } } // Check JSON body for type parameter if (!$comment_type) { $json = $request->get_json_params(); if (isset($json['type'])) { $comment_type = $json['type']; } } // For UPDATE requests (PUT/PATCH), check if the existing comment is an allowed type // WordPress doesn't send the type parameter when updating, only the ID and content if (!$comment_type) { $comment_id = $request->get_param('id'); if ($comment_id) { $comment = get_comment($comment_id); if ($comment && isset($comment->comment_type)) { $comment_type = $comment->comment_type; } } } // For DELETE requests, extract comment ID from the route path // The comment ID is only in the URL (e.g., /wp/v2/comments/123), not in request params if (!$comment_type && $request->is_method('DELETE')) { $route_parts = explode('/', $request->get_route()); $comment_id = end($route_parts); // Ensure we have a numeric comment ID if (is_numeric($comment_id)) { $comment = get_comment((int) $comment_id); if ($comment && isset($comment->comment_type)) { $comment_type = $comment->comment_type; } } } } // Check if the comment type is in the allowlist if ($comment_type && $this->is_comment_type_allowed($comment_type)) { return true; } return false; } /** * Issue a 403 for all comment feed requests. */ public function filter_query() { if (is_comment_feed()) { wp_die(esc_html__('Comments are closed.', 'disable-comments'), '', array('response' => 403)); } } /** * Remove comment links from the admin bar. */ public function filter_admin_bar() { if (is_admin_bar_showing()) { // Remove comments links from admin bar. remove_action('admin_bar_menu', 'wp_admin_bar_comments_menu', 60); if (is_multisite()) { add_action('admin_bar_menu', array($this, 'remove_network_comment_links'), 500); } } } /** * Remove the comments endpoint for the REST API * But allow WordPress 6.9+ block notes (type=note) to work */ public function filter_rest_endpoints($endpoints) { // Don't remove endpoints entirely - instead we'll use permission callbacks // and other filters to block regular comments while allowing notes // We still need to add a filter to block non-note requests // This is handled by rest_pre_dispatch filter added in init_filters return $endpoints; } /** * Filter REST API comment requests to block comments except allowed types * * @param mixed $result Response to replace the requested version with * @param WP_REST_Server $server Server instance * @param WP_REST_Request $request Request used to generate the response * @return mixed */ public function filter_rest_comment_dispatch($result, $server, $request) { // Only filter comment-related routes $route = $request->get_route(); if (strpos($route, '/wp/v2/comments') === false) { return $result; } // Allow requests for comment types in the allowlist to pass through if ($this->is_allowed_comment_type_request($request)) { return $result; } // Block all other comment requests return new WP_Error( 'rest_comment_disabled', __('Comments are disabled.', 'disable-comments'), array('status' => 403) ); } /** * Filter comment queries in REST API to allow only allowed comment types * * @param array $prepared_args Array of arguments for WP_Comment_Query * @param WP_REST_Request $request The REST API request * @return array */ public function filter_rest_comment_query($prepared_args, $request) { // If this is a request for an allowed comment type, allow it if ($this->is_allowed_comment_type_request($request)) { return $prepared_args; } // For non-allowed requests, return empty results // by setting an impossible condition $prepared_args['comment__in'] = array(0); return $prepared_args; } /** * Determines if scripts should be enqueued */ public function filter_gutenberg_blocks($hook) { global $post; if ($this->is_remove_everywhere() || (isset($post->post_type) && $this->is_post_type_disabled($post->post_type))) { return $this->disable_comments_script(); } } /** * Enqueues scripts */ public function disable_comments_script() { wp_enqueue_script('disable-comments-gutenberg', plugin_dir_url(__FILE__) . 'assets/js/disable-comments.js', array(), DC_VERSION, true); } /** * Enqueues Scripts for Settings Page */ public function settings_page_assets($hook_suffix) { if ( $hook_suffix === 'settings_page_' . DC_PLUGIN_SLUG || $hook_suffix === 'options-general_' . DC_PLUGIN_SLUG ) { // css wp_enqueue_style('sweetalert2', DC_ASSETS_URI . 'css/sweetalert2.min.css', [], DC_VERSION); // wp_enqueue_style('pagination', DC_ASSETS_URI . 'css/pagination.css', [], false); wp_enqueue_style('disable-comments-style', DC_ASSETS_URI . 'css/style.css', [], DC_VERSION); wp_enqueue_style('select2', DC_ASSETS_URI . 'css/select2.min.css', [], DC_VERSION); // js wp_enqueue_script('sweetalert2', DC_ASSETS_URI . 'js/sweetalert2.all.min.js', array('jquery'), DC_VERSION, true); wp_enqueue_script('pagination', DC_ASSETS_URI . 'js/pagination.min.js', array('jquery'), DC_VERSION, true); wp_enqueue_script('select2', DC_ASSETS_URI . 'js/select2.min.js', array('jquery'), DC_VERSION, true); wp_enqueue_script('disable-comments-scripts', DC_ASSETS_URI . 'js/disable-comments-settings-scripts.js', array('jquery', 'select2', 'pagination', 'sweetalert2', 'wp-i18n'), DC_VERSION, true); wp_localize_script( 'disable-comments-scripts', 'disableCommentsObj', array( 'save_action' => 'disable_comments_save_settings', 'delete_action' => 'disable_comments_delete_comments', 'settings_URI' => $this->settings_page_url(), '_nonce' => wp_create_nonce('disable_comments_save_settings') ) ); wp_set_script_translations('disable-comments-scripts', 'disable-comments'); } else { // notice css wp_enqueue_style('disable-comments-notice', DC_ASSETS_URI . 'css/notice.css', [], DC_VERSION); } } /** * Remove comment links from the admin bar in a multisite network. */ public function remove_network_comment_links($wp_admin_bar) { if ($this->networkactive && is_user_logged_in()) { foreach ((array) $wp_admin_bar->user->blogs as $blog) { $wp_admin_bar->remove_menu('blog-' . $blog->userblog_id . '-c'); } } else { // We have no way to know whether the plugin is active on other sites, so only remove this one. $wp_admin_bar->remove_menu('blog-' . get_current_blog_id() . '-c'); } } public function discussion_notice() { $disabled_post_types = $this->get_disabled_post_types(); if (get_current_screen()->id == 'options-discussion' && !empty($disabled_post_types)) { $names_escaped = array(); foreach ($disabled_post_types as $type) { $names_escaped[$type] = esc_html(get_post_type_object($type)->labels->name); } // translators: %s: disabled post types. echo '<div class="notice notice-warning"><p>' . sprintf(esc_html__('Note: The <em>Disable Comments</em> plugin is currently active, and comments are completely disabled on: %s. Many of the settings below will not be applicable for those post types.', 'disable-comments'), implode(esc_html__(', ', 'disable-comments'), $names_escaped)) . '</p></div>'; } } /** * Return context-aware settings page URL */ private function settings_page_url() { $base = $this->networkactive && is_network_admin() ? network_admin_url('settings.php') : admin_url('options-general.php'); return add_query_arg('page', DC_PLUGIN_SLUG, $base); } /** * Return context-aware tools page URL */ private function tools_page_url() { $base = $this->networkactive && is_network_admin() ? network_admin_url('settings.php') : admin_url('tools.php'); return add_query_arg('page', 'disable_comments_tools', $base); } public function setup_notice() { $current_screen = get_current_screen()->id; if (!in_array($current_screen, ['dashboard-network', 'dashboard'])) { return; } $hascaps = $this->networkactive && is_network_admin() ? current_user_can('manage_network_plugins') : current_user_can('manage_options'); if ($this->networkactive && !is_network_admin() && !$this->options['sitewide_settings']) { $hascaps = false; } if ($hascaps) { $this->setup_notice_flag = true; // translators: %s: URL to Disabled Comment settings page. $html = sprintf(__('The <strong>Disable Comments</strong> plugin is active, but isn\'t configured to do anything yet. Visit the <a href="%s">configuration page</a> to choose which post types to disable comments on.', 'disable-comments'), esc_attr($this->settings_page_url())); // phpcs:ignore PluginCheck.CodeAnalysis.ImageFunctions.NonEnqueuedImage echo wp_kses_post('<div class="notice dc-text__block disable__comment__alert mb30"><img height="30" src="' . esc_url(DC_ASSETS_URI . 'img/icon-logo.png') . '" alt=""><p>' . $html . '</p></div>'); } } public function filter_admin_menu() { global $pagenow; if (empty($this->options['show_existing_comments'])) { if ($pagenow == 'comment.php' || $pagenow == 'edit-comments.php') { wp_die(esc_html__('Comments are closed.', 'disable-comments'), '', array('response' => 403)); } remove_menu_page('edit-comments.php'); } if (!$this->discussion_settings_allowed()) { if ($pagenow == 'options-discussion.php') { wp_die(esc_html__('Comments are closed.', 'disable-comments'), '', array('response' => 403)); } remove_submenu_page('options-general.php', 'options-discussion.php'); } } public function filter_dashboard() { remove_meta_box('dashboard_recent_comments', 'dashboard', 'normal'); } public function admin_css() { echo '<style> #dashboard_right_now .comment-count, #dashboard_right_now .comment-mod-count, #latest-comments, #welcome-panel .welcome-comments, .user-comment-shortcuts-wrap { display: none !important; } </style>'; } public function filter_existing_comments($comments, $post_id) { $post_type = get_post_type($post_id); $comments_disabled = $this->is_remove_everywhere() || $this->is_post_type_disabled($post_type); // If comments are disabled but show_existing_comments is enabled, return existing comments if ($comments_disabled && !empty($this->options['show_existing_comments'])) { $comments_disabled = false; } // If comments are disabled, filter out regular comments but keep allowed comment types if ($comments_disabled && !empty($comments)) { $filtered_comments = array(); foreach ($comments as $comment) { // Keep comment types that are in the allowlist even when comments are disabled if (isset($comment->comment_type) && $this->is_comment_type_allowed($comment->comment_type)) { $filtered_comments[] = $comment; } } return $filtered_comments; } // Default behavior: return all comments if not disabled return $comments; } public function filter_comment_status($open, $post_id) { $post_type = get_post_type($post_id); return ($this->is_remove_everywhere() || $this->is_post_type_disabled($post_type) ? false : $open); } public function filter_comments_number($count, $post_id) { $post_type = get_post_type($post_id); $comments_disabled = $this->is_remove_everywhere() || $this->is_post_type_disabled($post_type); // If comments are disabled but show_existing_comments is enabled, return actual count if ($comments_disabled && !empty($this->options['show_existing_comments'])) { return $count; } // If comments are disabled but there are allowed comment types, count only those types if ($comments_disabled && $this->has_allowed_comment_types()) { return $this->count_allowed_comment_types($post_id); } return $comments_disabled ? 0 : $count; } /** * Count comments of allowed types for a specific post * * @param int $post_id The post ID * @return int The count of comments matching allowed types */ private function count_allowed_comment_types($post_id) { $allowed_types = $this->get_allowed_comment_types(); if (empty($allowed_types)) { return 0; } $comments = get_comments(array( 'post_id' => $post_id, 'type__in' => $allowed_types, 'status' => 'approve', 'count' => true, )); return (int) $comments; } public function disable_rc_widget() { unregister_widget('WP_Widget_Recent_Comments'); /** * The widget has added a style action when it was constructed - which will * still fire even if we now unregister the widget... so filter that out */ add_filter('show_recent_comments_widget_style', '__return_false'); } public function set_plugin_meta($links, $file) { static $plugin; $plugin = plugin_basename(__FILE__); if ($file == $plugin) { $links[] = '<a href="https://github.com/WPDevelopers/disable-comments">GitHub</a>'; } return $links; } /** * Add links to Settings page */ public function plugin_actions_links($links, $file) { static $plugin; $plugin = plugin_basename(__FILE__); if ($file == $plugin && current_user_can('manage_options')) { array_unshift( $links, sprintf('<a href="%s">%s</a>', esc_attr($this->settings_page_url()), __('Settings', 'disable-comments')), sprintf('<a href="%s">%s</a>', esc_attr($this->tools_page_url()), __('Tools', 'disable-comments')) ); } return $links; } public function settings_menu() { $title = _x('Disable Comments', 'settings menu title', 'disable-comments'); if ($this->networkactive && is_network_admin()) { add_submenu_page('settings.php', $title, $title, 'manage_network_plugins', DC_PLUGIN_SLUG, array($this, 'settings_page')); } elseif (!$this->networkactive || $this->options['sitewide_settings']) { add_submenu_page('options-general.php', $title, $title, 'manage_options', DC_PLUGIN_SLUG, array($this, 'settings_page')); } } public function tools_menu() { $title = __('Delete Comments', 'disable-comments'); $hook = ''; if ($this->networkactive && is_network_admin()) { $hook = add_submenu_page('settings.php', $title, $title, 'manage_network_plugins', 'disable_comments_tools', array($this, 'tools_page')); } elseif (!$this->networkactive || $this->options['sitewide_settings']) { $hook = add_submenu_page('tools.php', $title, $title, 'manage_options', 'disable_comments_tools', array($this, 'tools_page')); } add_action('load-' . $hook, array($this, 'redirectToMainSettingsPage')); } public function redirectToMainSettingsPage() { wp_safe_redirect($this->settings_page_url() . '#delete'); exit; } public function get_all_comments_number() { global $wpdb; if (is_network_admin() && function_exists('get_sites') && class_exists('WP_Site_Query')) { $count = 0; $sites = get_sites([ 'number' => 0, 'fields' => 'ids', ]); foreach ($sites as $blog_id) { switch_to_blog($blog_id); $count += $this->__get_comment_count(); restore_current_blog(); } return $count; } else { return $this->__get_comment_count(); } } public function get_all_comment_types($exclude_allowed = true) { if ($this->networkactive && is_network_admin() && function_exists('get_sites')) { $comment_types = []; $sites = get_sites([ 'number' => 0, 'fields' => 'ids', ]); foreach ($sites as $blog_id) { switch_to_blog($blog_id); $comment_types = array_merge($this->_get_all_comment_types($exclude_allowed), $comment_types); restore_current_blog(); } return $comment_types; } else { return $this->_get_all_comment_types($exclude_allowed); } } public function _get_all_comment_types($exclude_allowed = true) { global $wpdb; $commenttypes = array(); // we need fresh data in every call. // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery -- We need to count comments across multiple sites $commenttypes_query = $wpdb->get_results("SELECT DISTINCT comment_type FROM $wpdb->comments", ARRAY_A); if (!empty($commenttypes_query) && is_array($commenttypes_query)) { foreach ($commenttypes_query as $entry) { $value = $entry['comment_type']; // Exclude comment types that are in the allowlist from deletable comment types // These are protected and should not appear in the "Delete Certain Comment Types" interface if ($exclude_allowed && $this->is_comment_type_allowed($value)) { continue; } if ('' === $value) { $commenttypes['default'] = __('Default (no type)', 'disable-comments'); } else { $commenttypes[$value] = ucwords(str_replace('_', ' ', $value)) . ' (' . $value . ')'; } } } return $commenttypes; } public function get_all_post_types($network = false) { $typeargs = array('public' => true); if ($network || $this->networkactive && is_network_admin()) { $typeargs['_builtin'] = true; // stick to known types for network. } $types = get_post_types($typeargs, 'objects'); foreach (array_keys($types) as $type) { if (!in_array($type, $this->modified_types) && !post_type_supports($type, 'comments')) { // the type doesn't support comments anyway. unset($types[$type]); } } return $types; } public function get_roles($selected) { $roles = [ [ "id" => 'logged-out-users', "text" => __('Logged out users', 'disable-comments'), "selected" => in_array('logged-out-users', (array) $selected), ] ]; $editable_roles = array_reverse(get_editable_roles()); foreach ($editable_roles as $role => $details) { $roles[] = [ "id" => esc_attr($role), "text" => translate_user_role($details['name']), "selected" => in_array($role, (array) $selected), ]; } return $roles; } public function tools_page() { return; } public function settings_page() { // if( isset( $_GET['cancel'] ) && trim( $_GET['cancel'] ) === 'setup' ){ // $this->update_option('dc_setup_screen_seen', true); // } $avatar_status = '-1'; if ($this->is_network_admin()) { $show_avatars = []; $sites = get_sites([ 'number' => 0, 'fields' => 'ids', ]); foreach ($sites as $blog_id) { switch_to_blog($blog_id); $show_avatars[] = (int) get_option('show_avatars', '0'); restore_current_blog(); } if (count($show_avatars) == array_sum($show_avatars)) { $avatar_status = '0'; } elseif (0 == array_sum($show_avatars)) { $avatar_status = '1'; } } include_once DC_PLUGIN_VIEWS_PATH . 'settings.php'; } public function get_sub_sites() { $nonce = (isset($_REQUEST['nonce']) ? sanitize_text_field(wp_unslash($_REQUEST['nonce'])) : ''); if (!wp_verify_nonce($nonce, 'disable_comments_save_settings')) { wp_send_json(['data' => [], 'totalNumber' => 0]); } $_sub_sites = []; $type = isset($_GET['type']) ? sanitize_text_field(wp_unslash($_GET['type'])) : 'disabled'; $search = isset($_GET['search']) ? sanitize_text_field(wp_unslash($_GET['search'])) : ''; $pageSize = isset($_GET['pageSize']) ? sanitize_text_field(wp_unslash($_GET['pageSize'])) : 50; $pageNumber = isset($_GET['pageNumber']) ? sanitize_text_field(wp_unslash($_GET['pageNumber'])) : 1; $offset = ($pageNumber - 1) * $pageSize; $sub_sites = get_sites([ 'number' => $pageSize, 'offset' => $offset, 'search' => $search, 'fields' => 'ids', ]); $totalNumber = get_sites([ // 'number' => $pageSize, // 'offset' => $offset, 'search' => $search, 'count' => true, ]); if ($type == 'disabled') { $disabled_site_options = isset($this->options['disabled_sites']) ? $this->options['disabled_sites'] : []; } else { // if($type == 'delete') $disabled_site_options = $this->get_disabled_sites(true); } foreach ($sub_sites as $sub_site_id) { $blog = get_blog_details($sub_site_id); $is_checked = checked(!empty($disabled_site_options["site_$sub_site_id"]), true, false); $_sub_sites[] = [ 'site_id' => $sub_site_id, 'is_checked' => $is_checked, 'blogname' => $blog->blogname, ]; } wp_send_json(['data' => $_sub_sites, 'totalNumber' => $totalNumber]); } public function get_form_array_escaped($_args = array()) { $formArray = []; if (!empty($_args)) { $formArray = wp_parse_args($_args); } // nonce is verified in the calling function // phpcs:ignore WordPress.Security.NonceVerification.Missing else if (isset($_POST['data'])) { // need to use wp_parse_args before map_deep sanitize_text_field // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.NonceVerification.Missing $formArray = map_deep(wp_parse_args(wp_unslash($_POST['data'])), 'sanitize_text_field'); } return $formArray; } public function disable_comments_settings($_args = array()) { $nonce = (isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : ''); if (($this->is_CLI && !empty($_args)) || wp_verify_nonce($nonce, 'disable_comments_save_settings')) { $formArray = $this->get_form_array_escaped($_args); $old_options = $this->options; $this->options = []; if ($this->is_CLI) { $this->options = $old_options; } $this->options['is_network_admin'] = isset($formArray['is_network_admin']) && $formArray['is_network_admin'] == '1' ? true : false; if (!empty($this->options['is_network_admin']) && function_exists('get_sites') && empty($formArray['sitewide_settings'])) { $formArray['disabled_sites'] = isset($formArray['disabled_sites']) ? $formArray['disabled_sites'] : []; $this->options['disabled_sites'] = isset($old_options['disabled_sites']) ? $old_options['disabled_sites'] : []; $this->options['disabled_sites'] = array_merge($this->options['disabled_sites'], $formArray['disabled_sites']); } elseif (!empty($this->options['is_network_admin']) && !empty($formArray['sitewide_settings'])) { $this->options['disabled_sites'] = $old_options['disabled_sites']; } if (isset($formArray['mode'])) { $this->options['remove_everywhere'] = (sanitize_text_field($formArray['mode']) == 'remove_everywhere'); } $post_types = $this->get_all_post_types($this->options['is_network_admin']); if ($this->options['remove_everywhere']) { $disabled_post_types = array_keys($post_types); } else { $disabled_post_types = (isset($formArray['disabled_types']) ? array_map('sanitize_key', (array) $formArray['disabled_types']) : ($this->is_CLI && isset($this->options['disabled_post_types']) ? $this->options['disabled_post_types'] : [])); } $disabled_post_types = array_intersect($disabled_post_types, array_keys($post_types)); $this->options['disabled_post_types'] = $disabled_post_types; // Extra custom post types. if ($this->networkactive && isset($formArray['extra_post_types'])) { $extra_post_types = array_filter(array_map('sanitize_key', explode(',', $formArray['extra_post_types']))); $this->options['extra_post_types'] = array_diff($extra_post_types, array_keys($post_types)); // Make sure we don't double up builtins. } if (isset($formArray['sitewide_settings'])) { update_site_option('disable_comments_sitewide_settings', $formArray['sitewide_settings']); } if (isset($formArray['disable_avatar'])) { if ($this->is_network_admin()) { if ($formArray['disable_avatar'] == '0' || $formArray['disable_avatar'] == '1') { $sites = get_sites([ 'number' => 0, 'fields' => 'ids', ]); foreach ($sites as $blog_id) { switch_to_blog($blog_id); update_option('show_avatars', (bool) !$formArray['disable_avatar']); restore_current_blog(); } } } else { update_option('show_avatars', (bool) !$formArray['disable_avatar']); } } if (isset($formArray['enable_exclude_by_role'])) { $this->options['enable_exclude_by_role'] = $formArray['enable_exclude_by_role']; } if (isset($formArray['exclude_by_role'])) { $this->options['exclude_by_role'] = $formArray['exclude_by_role']; } // xml rpc $this->options['remove_xmlrpc_comments'] = (isset($formArray['remove_xmlrpc_comments']) ? intval($formArray['remove_xmlrpc_comments']) : ($this->is_CLI && isset($this->options['remove_xmlrpc_comments']) ? $this->options['remove_xmlrpc_comments'] : 0)); // rest api comments $this->options['remove_rest_API_comments'] = (isset($formArray['remove_rest_API_comments']) ? intval($formArray['remove_rest_API_comments']) : ($this->is_CLI && isset($this->options['remove_rest_API_comments']) ? $this->options['remove_rest_API_comments'] : 0)); // show existing comments $this->options['show_existing_comments'] = (isset($formArray['show_existing_comments']) ? (bool) $formArray['show_existing_comments'] : ($this->is_CLI && isset($this->options['show_existing_comments']) ? $this->options['show_existing_comments'] : false)); // allowed comment types (opt-in allowlist) if (isset($formArray['allowed_comment_types']) && is_array($formArray['allowed_comment_types'])) { // Sanitize and validate the allowed comment types $this->options['allowed_comment_types'] = array_map('sanitize_key', $formArray['allowed_comment_types']); } else { // Default: empty array (all special comment types disabled) $this->options['allowed_comment_types'] = array(); } $this->options['db_version'] = self::DB_VERSION; $this->options['settings_saved'] = true; // save settings $this->update_options(); } if (!$this->is_CLI) { wp_send_json_success(array('message' => __('Saved', 'disable-comments'))); wp_die(); } } public function is_configured() { $disabled_post_types = $this->get_disabled_post_types(); if (empty($disabled_post_types) && empty($this->options['remove_everywhere']) && empty($this->options['remove_rest_API_comments']) && empty($this->options['remove_xmlrpc_comments'])) { return false; } return true; } public function delete_comments_settings($_args = array()) { global $deletedPostTypeNames; $log = ''; $nonce = (isset($_POST['nonce']) ? sanitize_text_field(wp_unslash($_POST['nonce'])) : ''); if (($this->is_CLI && !empty($_args)) || wp_verify_nonce($nonce, 'disable_comments_save_settings')) { $formArray = $this->get_form_array_escaped($_args); if (!empty($formArray['is_network_admin']) && function_exists('get_sites') && class_exists('WP_Site_Query')) { $sites = get_sites([ 'number' => 0, 'fields' => 'ids', ]); foreach ($sites as $blog_id) { // $formArray['disabled_sites'] ids don't include "site_" prefix. if (!empty($formArray['disabled_sites']) && !empty($formArray['disabled_sites']["site_$blog_id"])) { switch_to_blog($blog_id); $log = $this->delete_comments($_args); restore_current_blog(); } } } else { $log = $this->delete_comments($_args); } } // message $deletedPostTypeNames = array_unique((array) $deletedPostTypeNames); $message = (count($deletedPostTypeNames) == 0 ? $log . '.' : $log . ' for ' . implode(", ", $deletedPostTypeNames) . '.'); if (!$this->is_CLI) { wp_send_json_success(array('message' => $message)); wp_die(); } else { return $log; } } private function delete_comments($_args) { global $wpdb; global $deletedPostTypeNames; $formArray = $this->get_form_array_escaped($_args); $types = $this->get_all_post_types(!empty($formArray['is_network_admin'])); $commenttypes = $this->get_all_comment_types(); $log = ""; // comments delete if (isset($formArray['delete_mode'])) { if ($formArray['delete_mode'] == 'delete_everywhere') { // Delete all comment metadata except for allowed comment types $allowed_types = $this->get_allowed_comment_types(); if (empty($allowed_types)) { // No allowed types, delete all comments // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query("DELETE FROM $wpdb->commentmeta"); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query("DELETE FROM $wpdb->comments"); } else { // Build exclusion query for allowed comment types $placeholders = implode(', ', array_fill(0, count($allowed_types), '%s')); // Delete comment metadata $query = $wpdb->prepare( "DELETE cmeta FROM $wpdb->commentmeta cmeta INNER JOIN $wpdb->comments comments ON cmeta.comment_id=comments.comment_ID WHERE comments.comment_type NOT IN ($placeholders)", $allowed_types ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query($query); // Delete comments $query = $wpdb->prepare( "DELETE FROM $wpdb->comments WHERE comment_type NOT IN ($placeholders)", $allowed_types ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query($query); } // Update comment counts // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query("UPDATE $wpdb->posts SET comment_count = 0"); $this->optimize_table($wpdb->commentmeta); $this->optimize_table($wpdb->comments); $log = __('All comments have been deleted', 'disable-comments'); } elseif ($formArray['delete_mode'] == 'selected_delete_types') { $delete_post_types = empty($formArray['delete_types']) ? array() : (array) $formArray['delete_types']; $delete_post_types = array_intersect($delete_post_types, array_keys($types)); // Extra custom post types. if ($this->networkactive && !empty($formArray['delete_extra_post_types'])) { $delete_extra_post_types = array_filter(array_map('sanitize_key', explode(',', $formArray['delete_extra_post_types']))); $delete_extra_post_types = array_diff($delete_extra_post_types, array_keys($types)); // Make sure we don't double up builtins. $delete_post_types = array_merge($delete_post_types, $delete_extra_post_types); } if (!empty($delete_post_types)) { // Loop through post_types and remove comments/meta (excluding allowed comment types) and set posts comment_count to 0. $allowed_types = $this->get_allowed_comment_types(); foreach ($delete_post_types as $delete_post_type) { if (empty($allowed_types)) { // No allowed types, delete all comments for this post type // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query($wpdb->prepare("DELETE cmeta FROM $wpdb->commentmeta cmeta INNER JOIN $wpdb->comments comments ON cmeta.comment_id=comments.comment_ID INNER JOIN $wpdb->posts posts ON comments.comment_post_ID=posts.ID WHERE posts.post_type = %s", $delete_post_type)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query($wpdb->prepare("DELETE comments FROM $wpdb->comments comments INNER JOIN $wpdb->posts posts ON comments.comment_post_ID=posts.ID WHERE posts.post_type = %s", $delete_post_type)); } else { // Build exclusion query for allowed comment types $placeholders = implode(', ', array_fill(0, count($allowed_types), '%s')); $params = array_merge(array($delete_post_type), $allowed_types); // Delete comment metadata $query = $wpdb->prepare( "DELETE cmeta FROM $wpdb->commentmeta cmeta INNER JOIN $wpdb->comments comments ON cmeta.comment_id=comments.comment_ID INNER JOIN $wpdb->posts posts ON comments.comment_post_ID=posts.ID WHERE posts.post_type = %s AND comments.comment_type NOT IN ($placeholders)", $params ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query($query); // Delete comments $query = $wpdb->prepare( "DELETE comments FROM $wpdb->comments comments INNER JOIN $wpdb->posts posts ON comments.comment_post_ID=posts.ID WHERE posts.post_type = %s AND comments.comment_type NOT IN ($placeholders)", $params ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query($query); } // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query($wpdb->prepare("UPDATE $wpdb->posts SET comment_count = 0 WHERE post_author != 0 AND post_type = %s", $delete_post_type)); $post_type_object = get_post_type_object($delete_post_type); $post_type_label = $post_type_object ? $post_type_object->labels->name : $delete_post_type; $deletedPostTypeNames[] = $post_type_label; } $this->optimize_table($wpdb->commentmeta); $this->optimize_table($wpdb->comments); $log = __('All comments have been deleted', 'disable-comments'); } } elseif ($formArray['delete_mode'] == 'selected_delete_comment_types') { $delete_comment_types = empty($formArray['delete_comment_types']) ? array() : (array) $formArray['delete_comment_types']; $delete_comment_types = array_intersect($delete_comment_types, array_keys($commenttypes)); if (!empty($delete_comment_types)) { // Loop through comment_types and remove comments/meta and set posts comment_count to 0. foreach ($delete_comment_types as $delete_comment_type) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query($wpdb->prepare("DELETE cmeta FROM $wpdb->commentmeta cmeta INNER JOIN $wpdb->comments comments ON cmeta.comment_id=comments.comment_ID WHERE comments.comment_type = %s", $delete_comment_type)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query($wpdb->prepare("DELETE comments FROM $wpdb->comments comments WHERE comments.comment_type = %s", $delete_comment_type)); $deletedPostTypeNames[] = $commenttypes[$delete_comment_type]; } // Update comment_count on post_types foreach ($types as $key => $value) { // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $comment_count = $wpdb->get_var($wpdb->prepare("SELECT COUNT(comments.comment_ID) FROM $wpdb->comments comments INNER JOIN $wpdb->posts posts ON comments.comment_post_ID=posts.ID WHERE posts.post_type = %s", $key)); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query($wpdb->prepare("UPDATE $wpdb->posts SET comment_count = %d WHERE post_author != 0 AND post_type = %s", $comment_count, $key)); } $this->optimize_table($wpdb->commentmeta); $this->optimize_table($wpdb->comments); $log = __('All comments have been deleted', 'disable-comments'); } } elseif ($formArray['delete_mode'] == 'delete_spam') { // Delete spam comments and their metadata (excluding allowed comment types) $allowed_types = $this->get_allowed_comment_types(); if (empty($allowed_types)) { // No allowed types, delete all spam comments // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query($wpdb->prepare("DELETE cmeta FROM $wpdb->commentmeta cmeta INNER JOIN $wpdb->comments comments ON cmeta.comment_id=comments.comment_ID WHERE comments.comment_approved = %s", 'spam')); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query($wpdb->prepare("DELETE comments FROM $wpdb->comments comments WHERE comments.comment_approved = %s", 'spam')); } else { // Build exclusion query for allowed comment types $placeholders = implode(', ', array_fill(0, count($allowed_types), '%s')); $params = array_merge(array('spam'), $allowed_types); // Delete comment metadata $query = $wpdb->prepare( "DELETE cmeta FROM $wpdb->commentmeta cmeta INNER JOIN $wpdb->comments comments ON cmeta.comment_id=comments.comment_ID WHERE comments.comment_approved = %s AND comments.comment_type NOT IN ($placeholders)", $params ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query($query); // Delete comments $query = $wpdb->prepare( "DELETE comments FROM $wpdb->comments comments WHERE comments.comment_approved = %s AND comments.comment_type NOT IN ($placeholders)", $params ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery $wpdb->query($query); } $this->optimize_table($wpdb->commentmeta); $this->optimize_table($wpdb->comments); $log = __('All spam comments have been deleted.', 'disable-comments'); } } delete_transient('wc_count_comments'); return $log; } private function discussion_settings_allowed() { if (defined('DISABLE_COMMENTS_ALLOW_DISCUSSION_SETTINGS') && DISABLE_COMMENTS_ALLOW_DISCUSSION_SETTINGS == true) { return true; } } public function single_site_deactivate() { // for single sites, delete the options upon deactivation, not uninstall. delete_option('disable_comments_options'); } /** * We need fresh data in every call. Called after switching to blog in loop. * * @return int The number of comments. */ protected function __get_comment_count() { global $wpdb; // Exclude allowed comment types from the count since they cannot be deleted // and should not be displayed in the "Total Comments" count in the Delete Comments tab $allowed_types = $this->get_allowed_comment_types(); if (empty($allowed_types)) { // No allowed types, count all comments // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery return $wpdb->get_var("SELECT COUNT(comment_id) FROM $wpdb->comments"); } // Build exclusion query for allowed comment types $placeholders = implode(', ', array_fill(0, count($allowed_types), '%s')); $query = $wpdb->prepare( "SELECT COUNT(comment_id) FROM $wpdb->comments WHERE comment_type NOT IN ($placeholders)", $allowed_types ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery return $wpdb->get_var($query); } /** * Optimize a given table in the WordPress database. * * @param string $table_name The name of the table to optimize. */ protected function optimize_table($table_name) { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery return $wpdb->query("OPTIMIZE TABLE " . esc_sql($table_name)); } /** * Truncate a given table in the WordPress database. * * @param string $table_name The name of the table to truncate. */ protected function truncate_table($table_name) { global $wpdb; // phpcs:ignore WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.DirectDatabaseQuery.DirectQuery return $wpdb->query("TRUNCATE TABLE " . esc_sql($table_name)); } /** * Get the current site-wide comment status as a descriptive string. * * This function analyzes the current Disable Comments plugin configuration * and returns a string describing which content types have comments disabled. * * @return string The current comment status: * - 'all' if comments are disabled site-wide for all content types * - 'posts' if comments are disabled only for posts * - 'pages' if comments are disabled only for pages * - 'posts,pages' if comments are disabled for both posts and pages * - 'custom_type_name' for other specific content types * - 'multiple' if multiple specific types are disabled (not all) * - 'none' if comments are not disabled anywhere * * @since 2.5.2 */ public function get_current_comment_status() { try { // Handle case where plugin is not properly initialized if (empty($this->options)) { return 'none'; } // Check if comments are disabled everywhere if ($this->is_remove_everywhere()) { return 'all'; } // Get disabled post types $disabled_post_types = $this->get_disabled_post_types(); // If no post types are disabled, comments are enabled everywhere if (empty($disabled_post_types)) { return 'none'; } // Get all available post types that support comments $all_post_types = $this->get_all_post_types(); $all_post_type_keys = array_keys($all_post_types); // Check if all available post types are disabled if (count($disabled_post_types) >= count($all_post_type_keys)) { $missing_types = array_diff($all_post_type_keys, $disabled_post_types); if (empty($missing_types)) { return 'all'; } } // Handle specific common cases if (count($disabled_post_types) === 1) { $disabled_type = $disabled_post_types[0]; // Return the specific post type name for single disabled types switch ($disabled_type) { case 'post': return 'posts'; case 'page': return 'pages'; default: // For custom post types, return the post type slug return $disabled_type; } } // Handle multiple specific post types if (count($disabled_post_types) === 2 && in_array('post', $disabled_post_types) && in_array('page', $disabled_post_types)) { return 'posts,pages'; } // For other combinations, return 'multiple' to indicate partial disabling return 'multiple'; } catch (Exception $e) { // Error handling - return safe default if (defined('WP_DEBUG') && WP_DEBUG) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging for WP_DEBUG mode error_log('Disable Comments: Error in get_current_comment_status() - ' . $e->getMessage()); } return 'none'; } } /** * Get detailed comment status information including API restrictions. * * This function provides comprehensive information about comment restrictions * including post type restrictions, API-level restrictions, network settings, * role exclusions, and comment counts. * * @return array Associative array with detailed status information: * - 'status' => Main status (same as get_current_comment_status()) * - 'disabled_post_types' => Array of disabled post type slugs * - 'disabled_post_type_labels' => Array of disabled post type labels * - 'remove_everywhere' => Boolean indicating global disable * - 'xmlrpc_disabled' => Boolean indicating XML-RPC comments disabled * - 'rest_api_disabled' => Boolean indicating REST API comments disabled * - 'total_post_types' => Total number of available post types * - 'is_configured' => Boolean indicating if plugin is configured * - 'total_comments' => Total number of comments in database * - 'network_active' => Boolean indicating if plugin is network activated * - 'sitewide_settings' => Site-wide settings status * - 'role_exclusion_enabled' => Boolean indicating if role exclusions are enabled * - 'excluded_roles' => Array of excluded role slugs * - 'excluded_role_labels' => Array of human-readable excluded role names * * @since 2.5.2 */ public function get_detailed_comment_status() { try { $status = $this->get_current_comment_status(); $disabled_post_types = $this->get_disabled_post_types(); $all_post_types = $this->get_all_post_types(); // Get human-readable labels for disabled post types $disabled_labels = array(); foreach ($disabled_post_types as $post_type) { if (isset($all_post_types[$post_type])) { $disabled_labels[] = $all_post_types[$post_type]->labels->name; } else { // Fallback for custom post types not in the main list $post_type_obj = get_post_type_object($post_type); $disabled_labels[] = $post_type_obj ? $post_type_obj->labels->name : $post_type; } } // Get total comments count $total_comments = $this->get_all_comments_number(); // Determine site-wide settings status $sitewide_settings = 'not_applicable'; if ($this->networkactive) { $sitewide_settings = isset($this->options['sitewide_settings']) && $this->options['sitewide_settings'] ? 'enabled' : 'disabled'; } // Process role-based exclusion information $role_exclusion_enabled = isset($this->options['enable_exclude_by_role']) && $this->options['enable_exclude_by_role']; $excluded_roles = isset($this->options['exclude_by_role']) ? $this->options['exclude_by_role'] : array(); // Get human-readable role names $excluded_role_labels = array(); if ($role_exclusion_enabled && !empty($excluded_roles)) { $editable_roles = get_editable_roles(); foreach ($excluded_roles as $role) { if ($role === 'logged-out-users') { $excluded_role_labels[] = __('Logged out users', 'disable-comments'); } elseif (isset($editable_roles[$role])) { $excluded_role_labels[] = translate_user_role($editable_roles[$role]['name']); } else { $excluded_role_labels[] = $role; } } } return array( 'status' => $status, 'disabled_post_types' => $disabled_post_types, 'disabled_post_type_labels' => $disabled_labels, 'remove_everywhere' => $this->is_remove_everywhere(), 'xmlrpc_disabled' => !empty($this->options['remove_xmlrpc_comments']), 'rest_api_disabled' => !empty($this->options['remove_rest_API_comments']), 'show_existing_comments' => !empty($this->options['show_existing_comments']), 'total_post_types' => count($all_post_types), 'is_configured' => $this->is_configured(), 'total_comments' => $total_comments, 'network_active' => $this->networkactive, 'sitewide_settings' => $sitewide_settings, 'role_exclusion_enabled' => $role_exclusion_enabled, 'excluded_roles' => $excluded_roles, 'excluded_role_labels' => $excluded_role_labels ); } catch (Exception $e) { // Error handling - return safe defaults if (defined('WP_DEBUG') && WP_DEBUG) { // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log -- Debug logging for WP_DEBUG mode error_log('Disable Comments: Error in get_detailed_comment_status() - ' . $e->getMessage()); } return array( 'status' => 'none', 'disabled_post_types' => array(), 'disabled_post_type_labels' => array(), 'remove_everywhere' => false, 'xmlrpc_disabled' => false, 'rest_api_disabled' => false, 'show_existing_comments' => false, 'total_post_types' => 0, 'is_configured' => false, 'total_comments' => 0, 'network_active' => false, 'sitewide_settings' => 'not_applicable', 'role_exclusion_enabled' => false, 'excluded_roles' => array(), 'excluded_role_labels' => array() ); } } /** * Add Disable Comments information to WordPress Site Health Info panel. * * This method integrates the plugin's status information into WordPress's * built-in Site Health system for easy debugging and site overview. * * @param array $debug_info The debug information array. * @return array Modified debug information array. * * @since 2.5.2 */ public function add_site_health_info($debug_info) { $data = $this->get_detailed_comment_status(); // Create the main status description $status_descriptions = array( 'all' => __('Comments are disabled site-wide for all content types', 'disable-comments'), 'posts' => __('Comments are disabled only for blog posts', 'disable-comments'), 'pages' => __('Comments are disabled only for pages', 'disable-comments'), 'posts,pages' => __('Comments are disabled for both posts and pages', 'disable-comments'), 'multiple' => __('Comments are disabled for multiple specific content types', 'disable-comments'), 'none' => __('Comments are enabled everywhere', 'disable-comments'), ); // translators: %s: disabled post types. $other_status_description = sprintf(__('Comments are disabled for: %s', 'disable-comments'), $data['status']); $status_description = isset($status_descriptions[$data['status']]) ? $status_descriptions[$data['status']] : $other_status_description; // Format site-wide settings value $sitewide_settings_labels = array( 'enabled' => __('Enabled', 'disable-comments'), 'disabled' => __('Disabled', 'disable-comments'), 'not_applicable' => __('Not applicable', 'disable-comments'), ); // Build the fields array using data from get_detailed_comment_status() $fields = array( 'status' => array( 'label' => __('Comment Status', 'disable-comments'), 'value' => $status_description, ), 'plugin_configured' => array( 'label' => __('Plugin Configured', 'disable-comments'), 'value' => $data['is_configured'] ? __('Yes', 'disable-comments') : __('No', 'disable-comments'), ), 'total_comments' => array( 'label' => __('Total Comments', 'disable-comments'), 'value' => number_format_i18n($data['total_comments']), ), 'global_disable' => array( 'label' => __('Global Disable Active', 'disable-comments'), 'value' => $data['remove_everywhere'] ? __('Yes', 'disable-comments') : __('No', 'disable-comments'), ), 'disabled_post_type_count' => array( 'label' => __('Disabled Post Types Count', 'disable-comments'), 'value' => sprintf('%d of %d', count($data['disabled_post_types']), $data['total_post_types']), ), 'disabled_post_types' => array( 'label' => __('Disabled Post Types', 'disable-comments'), 'value' => !empty($data['disabled_post_type_labels']) ? implode(', ', $data['disabled_post_type_labels']) : __('None', 'disable-comments'), ), 'xmlrpc_comments' => array( 'label' => __('XML-RPC Comments', 'disable-comments'), 'value' => $data['xmlrpc_disabled'] ? __('Disabled', 'disable-comments') : __('Enabled', 'disable-comments'), ), 'rest_api_comments' => array( 'label' => __('REST API Comments', 'disable-comments'), 'value' => $data['rest_api_disabled'] ? __('Disabled', 'disable-comments') : __('Enabled', 'disable-comments'), ), 'show_existing_comments' => array( 'label' => __('Show Existing Comments', 'disable-comments'), 'value' => $data['show_existing_comments'] ? __('Yes', 'disable-comments') : __('No', 'disable-comments'), ), 'network_active' => array( 'label' => __('Network Active', 'disable-comments'), 'value' => $data['network_active'] ? __('Yes', 'disable-comments') : __('No', 'disable-comments'), ), 'sitewide_settings' => array( 'label' => __('Site-wide Settings', 'disable-comments'), 'value' => $sitewide_settings_labels[$data['sitewide_settings']], ), 'role_exclusion_enabled' => array( 'label' => __('Role-based Exclusions', 'disable-comments'), 'value' => $data['role_exclusion_enabled'] ? __('Enabled', 'disable-comments') : __('Disabled', 'disable-comments'), ), 'excluded_roles' => array( 'label' => __('Excluded Roles', 'disable-comments'), 'value' => !empty($data['excluded_role_labels']) ? implode(', ', $data['excluded_role_labels']) : __('None', 'disable-comments'), ), ); // Add the section to Site Health $debug_info['disable-comments'] = array( 'label' => __('Disable Comments', 'disable-comments'), 'description' => __('Complete overview of comment disable settings and configuration.', 'disable-comments'), 'fields' => $fields, ); return $debug_info; } } Disable_Comments::get_instance();
Save