<?php

use CleantalkSP\Common\RateLimit\RateLimiterConfig;
use CleantalkSP\Common\TextPlateStatic;
use CleantalkSP\Security\LoginCollectingProtector;
use CleantalkSP\SpbctWP\API;
use CleantalkSP\SpbctWP\Cron as SpbcCron;
use CleantalkSP\SpbctWP\Escape;
use CleantalkSP\SpbctWP\Helpers\Arr;
use CleantalkSP\SpbctWP\Helpers\IP;
use CleantalkSP\SpbctWP\LinkConstructor;
use CleantalkSP\SpbctWP\ListTable;
use CleantalkSP\SpbctWP\Scanner;
use CleantalkSP\SpbctWP\Scanner\DBTrigger\DBTriggerService;
use CleantalkSP\SpbctWP\Scanner\DBTrigger\DBTriggerView;
use CleantalkSP\SpbctWP\Scanner\OSCron\Storages\OsCronTasksStorage;
use CleantalkSP\SpbctWP\Scanner\OSCron\View\OSCronView;
use CleantalkSP\SpbctWP\Scanner\ScannerActions\LinksActions;
use CleantalkSP\SpbctWP\Scanner\ScannerActions\BackupsActions;
use CleantalkSP\SpbctWP\Scanner\ScannerActions\ScanResultsTableActions;
use CleantalkSP\SpbctWP\Scanner\ScanningLog\ScanningLogFacade;
use CleantalkSP\SpbctWP\Settings\FilesScanPathExclusion;
use CleantalkSP\SpbctWP\SpbcRateLimit\SpbcRateLimiter;
use CleantalkSP\SpbctWP\Variables\Cookie;
use CleantalkSP\SpbctWP\Views\Settings;
use CleantalkSP\Variables\Post;
use CleantalkSP\Variables\Server;

// Prevent direct call
if ( ! defined('ABSPATH') ) {
    die('Not allowed!');
}

// Scanner AJAX actions
require_once(SPBC_PLUGIN_DIR . 'inc/spbc-scanner.php');
require_once(SPBC_PLUGIN_DIR . 'inc/spbc-settings-summary-and-stats.php');

/**
 * Action 'admin_menu' - Add the admin options page
 *
 * @global \CleantalkSP\SpbctWP\State $spbc
 */
function spbc_admin_add_page()
{
    global $spbc;

    $is_critical = false;
    if ($spbc->key_is_ok && $spbc->data['display_scanner_warnings']['critical'] > 0) {
        $is_critical = true;
    }

    // Adding setting page
    if (is_network_admin()) {
        add_submenu_page(
            "settings.php",
            __($spbc->data["wl_brandname"] . ' Settings', 'security-malware-firewall'),
            $is_critical ? '<span style="color: red;">' . $spbc->data["wl_brandname"] . '</span>' : $spbc->data["wl_brandname"],
            'manage_options',
            'spbc',
            [Settings::class, 'page']
        );
    } else {
        add_options_page(
            __($spbc->data["wl_brandname"] . ' Settings', 'security-malware-firewall'),
            $is_critical ? '<span style="color: red;">' . $spbc->data["wl_brandname"] . '</span>' : $spbc->data["wl_brandname"],
            'manage_options',
            'spbc',
            [Settings::class, 'page']
        );
    }

    // Register setting
    register_setting(SPBC_SETTINGS, SPBC_SETTINGS, array(
        'sanitize_callback' => 'spbc_sanitize_settings'
    ));

    // Initialize parameters for all tabs
    spbc_settings__register();
}

/**
 * @return void
 * @psalm-suppress ComplexFunction
 * @ToDo The function need to be refactored and `psalm-suppress` removed
 */
function spbc_settings__register()
{
    global $spbc, $wp_version;

    // Show debug if CONNECTION_ERROR exists
    if ( ! empty($spbc->errors)) {
        $errors = $spbc->errors;
        foreach ($errors as $_type => $error) {
            if ( ! empty($error)) {
                if (is_array(current($error))) {
                    foreach ($error as $_sub_type => $sub_error) {
                        if (strpos($sub_error['error'], 'CONNECTION') !== false) {
                            $spbc->show_debug = true;
                        }
                    }
                } elseif (
                    isset($error['error']) &&
                    is_string($error['error']) && strpos($error['error'], 'CONNECTION') !== false
                ) {
                    $spbc->show_debug = true;
                }
            }
        }
    }

    $spbc->settings__elements = spbc_settings__register_sections_and_fields(
        array(
            // TABS
            // Scanner
            'scanner'          => array(
                'type'         => 'tab',
                'display'      => $spbc->scaner_enabled,
                'title'        => __('Malware Scanner', 'security-malware-firewall'),
                'icon'         => 'spbc-icon-search',
                'class_prefix' => 'spbc',
                'ajax'         => true,
                'sections'     => array(
                    'section_top_banner' => array(
                        'type'   => 'section_banner',
                        'fields' => array(
                            'security_log' => array(
                                'type'     => 'field',
                            ),
                        ),
                    ),
                    'scanner' => array(
                        'type'   => 'section',
                        'fields' => array(
                            'scanner' => array(
                                'type'     => 'field',
                                'callback' => 'spbc_field_scanner'
                            ),
                        ),
                    ),
                ),
            ),
            // Backups
            'backups'          => array(
                'type'         => 'tab',
                'display'      => $spbc->scaner_enabled,
                'title'        => __('Backups', 'security-malware-firewall'),
                'icon'         => 'spbc-icon-download',
                'class_prefix' => 'spbc',
                'active'       => false,
                'ajax'         => true,
                'sections'     => array(
                    'scanner' => array(
                        'type'   => 'section',
                        'fields' => array(
                            'scanner' => array(
                                'type'     => 'field',
                                'callback' => 'spbc_field_backups'
                            ),
                        ),
                    ),
                ),
            ),
            // Security log
            'security_log'     => array(
                'type'         => 'tab',
                'title'        => __('Security Log', 'security-malware-firewall'),
                'icon'         => 'spbc-icon-user-secret',
                'class_prefix' => 'spbc',
                'ajax'         => true,
                'sections'     => array(
                    'section_top_banner' => array(
                        'type'   => 'section_banner',
                        'fields' => array(
                            'security_log' => array(
                                'type'     => 'field',
                            ),
                        ),
                    ),
                    'security_log' => array(
                        'type'   => 'section',
                        'fields' => array(
                            'security_log' => array(
                                'type'     => 'field',
                                'callback' => 'spbc_field_security_logs'
                            ),
                        ),
                    ),
                ),
            ),
            // Summary
            'summary'          => array(
                'type'         => 'tab',
                'title'        => __('Summary and Support', 'security-malware-firewall'),
                'icon'         => 'spbc-icon-info',
                'class_prefix' => 'spbc',
                'ajax'         => false,
                'sections'     => array(
                    'section_top_banner' => array(
                        'type'   => 'section_banner',
                        'fields' => array(
                            'security_log' => array(
                                'type'     => 'field',
                            ),
                        ),
                    ),
                    'summary_section' => array(
                        'type'   => 'section',
                        'fields' => array(
                            'security_log' => array(
                                'type'     => 'field',
                                'callback'     => 'spbc_tab__summary',
                            ),
                        ),
                    ),

                ),
            ),
            // Debug
            'debug'            => array(
                'type'         => 'tab',
                'display'      => in_array(Server::getDomain(), array(
                        'lc',
                        'loc',
                        'local',
                        'lh',
                        'wordpress'
                    )) || $spbc->debug || $spbc->show_debug,
                'title'        => __('Debug', 'security-malware-firewall'),
                'class_prefix' => 'spbc',
                'ajax'         => true,
                'sections'     => array(
                    'debug' => array(
                        'type'   => 'section',
                        'fields' => array(
                            'drop_debug'               => array(
                                'type'     => 'field',
                                'callback' => 'spbc_field_debug_drop'
                            ),
                            'debug_check_connection'   => array(
                                'type'     => 'field',
                                'callback' => 'spbc_field_debug__check_connection'
                            ),
                            'debug_set_fw_update_cron' => array(
                                'type'     => 'field',
                                'callback' => 'spbc_field_debug__set_fw_update_cron'
                            ),
                            'debug_set_scan_cron'      => array(
                                'type'     => 'field',
                                'callback' => 'spbc_field_debug__set_scan_cron'
                            ),
                            'debug_set__check_vulnerabilities_cron'      => array(
                                'type'     => 'field',
                                'callback' => 'spbc_field_debug__set_check_vulnerabilities_cron'
                            ),
                            'debug_data'               => array(
                                'type'     => 'field',
                                'callback' => 'spbc_field_debug'
                            ),
                            'debug_user_pass_check'               => array(
                                'type'     => 'field',
                                'callback' => 'spbc_field_debug_user_pass_check'
                            ),
                        ),
                    ),
                ),
            ),
        )
    );
}

/**
 * Preprocess the elements. Registering sections and fields and other stuff
 *
 * @param array $elems Array of elements
 * @param string $section_name Section name to register
 *
 * @return array Processed elements
 */
function spbc_settings__register_sections_and_fields($elems)
{
    global $spbc;

    $elems_original = $elems;

    $_plain_default_params = array(
        'title'   => '',
        'html'    => '',
        'display' => true,
    );

    $_tab_default_params = array(
        'name'        => '',
        'title'       => '',
        'description' => '',
        'active'      => false,
        'icon'        => '',
        'display'     => true,
        'preloader'   => '<img class="spbc_spinner_big" src="' . SPBC_PATH . '/images/preloader2.gif" />',
        'ajax'        => true,
    );

    $_section_default_params = array(
        'title'       => '',
        'description' => '',
        'html_before' => '',
        'html_after'  => '',
        'display'     => true,
    );

    $_section_banner_default_params = array(
        'title'       => '',
        'description' => '',
        'html_before' => '',
        'html_after'  => '',
        'display'     => true,
    );

    $_field_default_params = array(
        'callback'            => 'spbc_settings__field__draw',
        'input_type'          => 'checkbox',
        'def_class'           => 'spbc_wrapper_field',
        'title_first'         => false,
        'class'               => null,
        'parent'              => null,
        'children'            => null,
        'children_by_ids'     => null,
        'display'             => true, // Draw element or not
        'disabled'            => false,
        'required'            => false,
        'value_source'        => 'settings',
        'parent_value_source' => 'settings',
    );

    foreach ($elems as $elem_name => &$elem) {
        // Merging with default params
        $elem = array_merge(${'_' . $elem['type'] . '_default_params'}, $elem);

        switch ($elem['type']) {
            case 'plain':
                break;
            case 'tab':
                if (isset($elem['sections'])) {
                    $elem['sections'] = spbc_settings__register_sections_and_fields($elem['sections']);
                }
                // Creating new elements with tabs headings (before tabs)
                if ($elem['display']) {
                    // Hiding a tab 'Backups' except for a direct link
                    if ($elem_name === 'backups' && ! (isset($_GET['spbc_tab']) && $_GET['spbc_tab'] === 'backups')) {
                        break;
                    }

                    $tab_head = '<h2 class="spbc_tab_nav spbc_tab_nav-' . $elem_name . ' ' . (! empty($elem['active']) ? 'spbc_tab_nav--active' : '') . '">'
                                . '<i class="' . (isset($elem['icon']) ? $elem['icon'] : 'spbc-icon-search') . '"></i>'
                                . $elem['title']
                                . '</h2>';
                    if (empty($elems_original['tab_headings'])) {
                        Arr::insert(
                            $elems_original,
                            $elem_name,
                            array(
                                'tab_headings' => array(
                                    'type'    => 'tab_headings',
                                    'html'    => $tab_head,
                                    'display' => true,
                                )
                            )
                        );
                    } else {
                        $elems_original['tab_headings']['html'] .= $tab_head;
                    }
                }
                break;
            case 'section':
                if ( ! $elem['display']) {
                    break;
                }
                if (isset($elem['fields'])) {
                    $elem['fields'] = spbc_settings__register_sections_and_fields($elem['fields']);
                }
                break;
            case 'field':
                $elem['name'] = $elem_name;

                if ( ! isset($elem['value']) ) {
                    $elem['value'] = isset($spbc->{$elem['value_source']}[ $elem_name ])
                        ? $spbc->{$elem['value_source']}[ $elem_name ]
                        : 0;
                }

                if (isset($elem['parent'])) {
                    $elem['parent_value'] = isset($spbc->{$elem['parent_value_source']}[ $elem['parent'] ])
                        ? $spbc->{$elem['parent_value_source']}[ $elem['parent'] ]
                        : 0;
                }
                break;
        }

        $elems_original[ $elem_name ] = $elem;
    }

    return $elems_original;
}

/**
 * Outputs elements and tabs
 *
 * @global \CleantalkSP\SpbctWP\State $spbc
 */
function spbc_settings__draw_elements($elems_to_draw = null, $direct_call = false)
{
    global $spbc;

    if ( ! $direct_call && Post::getString('security')) {
        spbc_settings__register();
        spbc_check_ajax_referer('spbc_secret_nonce', 'security');
        if (Post::get('tab_name')) {
            if ( $_POST['tab_name'] === 'firewall' ) {
                $tab_name = 'traffic_control';
            } else {
                $tab_name = $_POST['tab_name'];
            }
            /** @psalm-suppress InvalidArrayOffset */
            $elems_to_draw = array($tab_name => $spbc->settings__elements[$tab_name]);
        }
    }

    foreach ($elems_to_draw as $elem_name => &$elem) {
        if ( ! $elem['display']) {
            continue;
        }

        switch ($elem['type']) {
            case 'plain':
                if (isset($elem['callback']) && function_exists($elem['callback'])) {
                    call_user_func($elem['callback']);
                } else {
                    echo $elem['html'];
                }
                break;
            case 'tab':
                if ( ! $elem['ajax'] || ! $direct_call) {
                    if ($elem_name === 'settings_general') {
                        spbct_settings__the_settings_tab_draw($elem);
                    } else {
                        spbc_settings__tab_content_draw($elem);
                    }
                } else {
                    echo $elem['preloader'];
                }
                break;
            case 'section':
                $anchor = isset($elem['anchor']) ? 'id="' . $elem['anchor'] . '"' : '';
                $hide_settings = '';
                if (!$spbc->key_is_ok && isset($elem['anchor']) && $elem['anchor'] !== 'apikey') {
                    $hide_settings = '--hide';
                }
                $section_class = 'spbc_tab_fields_group--' . $elem_name;
                $fields = isset($elem['fields']) ? $elem['fields'] : array();
                echo '<div class="spbc_tab_fields_group ' . esc_attr($section_class) . ' ' . $hide_settings . '">';

                echo '<div class="spbc_group_header" ' . $anchor . '>'
                        . (! empty($elem['title']) ? '<h3><a href="#' . $elem['anchor'] . '">' . $elem['title'] . '</a></h3>' : '')
                        . (! empty($elem['description']) ? '<div class="spbc_settings_description">' . $elem['description'] . '</div>' : '')
                        . '</div>';
                spbc_settings__draw_elements($fields, true);
                echo '</div>';
                break;
            case 'section_banner':
                spbc_settings__create_notice_on_tab();
                break;
            case 'field':
                call_user_func($elem['callback'], $elem);
                break;
        }
    }

    if (isset($_POST['security']) && ! $direct_call) {
        die();
    }
}

/**
 * Draw the content of the settings tab.
 * @param array $elem
 * @return void
 */
function spbc_settings__tab_content_draw($elem)
{
    // Output
    if ( ! empty($elem['callback'])) {
        call_user_func($elem['callback']);
    } else {
        spbc_settings__draw_elements($elem['sections'], true);
    }

    // Custom elements on tab
    if (isset($elem['after'])) {
        if (function_exists($elem['after'])) {
            call_user_func($elem['after']);
        } else {
            echo $elem['after'];
        }
    }
}

/**
 * Draw the settings tab of the settings itself with quick navigation bar.
 * @param array $elem
 * @return void
 */
function spbct_settings__the_settings_tab_draw($elem)
{
    // Start output buffering to listen the settings tab echos - we need to do this because spbc_settings__tab_content_draw may call_user_func() with echo.
    ob_start();
    spbc_settings__tab_content_draw($elem);
    $settings_content = ob_get_clean();
    // End buffer

    echo $settings_content;
}

/**
 * Messages output for error block.
 * Since 2.159.1 it returns array of strings.
 *
 * @return string[]
 * @global $spbc
 */
function spbc_settings__error__output()
{
    global $spbc;

    if (empty($spbc->errors)) {
        return [];
    }

    // Types
    $types = array(
        // Common
        'memory_limit_low'          => __('You have less than 25 Mib free PHP memory. Error could occurs while scanning.', 'security-malware-firewall'),
        'php_version'               => __('PHP version is lower than 5.4.0. You are using 10 years old software. We strongly recommend you to update.', 'security-malware-firewall'),
        // Misc
        'apikey'                    => __('Access key validating: ', 'security-malware-firewall'),
        'get_key'                   => __('Getting access key automatically: ', 'security-malware-firewall'),
        'notice_paid_till'          => __('Checking account status: ', 'security-malware-firewall'),
        'access_key_notices'        => __('Checking account status2: ', 'security-malware-firewall'),
        'login_page_rename'         => __('Renaming login URL: ', 'security-malware-firewall'),
        'service_customize'         => __('Service customization: ', 'security-malware-firewall'),
        // Cron
        'cron_scan'                 => __('Scheduled scanning: ', 'security-malware-firewall'),
        'cron'                      => __('Scheduled: ', 'security-malware-firewall'),
        // Misc
        'resend_files_for_analysis' => __('Resending files for analysis: ', 'security-malware-firewall'),
        'scanner_update_signatures' => __('An error occurred while updating the signature table: ', 'security-malware-firewall'),
        'scanner_update_signatures_bad_signatures' => __('Some signatures were not recorded in the database: ', 'security-malware-firewall'),
        'configuration'              => __('Server configuration error: ', 'security-malware-firewall'),
    );

    if ($spbc->moderate == 1) {
        $types['debug']              = __('Debug: ', 'security-malware-firewall');
        $types['send_logs']          = __('Sending security logs: ', 'security-malware-firewall');
        $types['send_firewall_logs'] = __('Sending firewall logs: ', 'security-malware-firewall');
        $types['firewall_update']    = __('Updating firewall: ', 'security-malware-firewall');
        $types['signatures_update']  = __('Updating signatures: ', 'security-malware-firewall');
        $types['send_php_logs']      = __('PHP error log sending: ', 'security-malware-firewall');

        // Subtypes
        $sub_types = array(
            'get_hashes'      => __('Getting hashes: ', 'security-malware-firewall'),
            'get_hashes_plug' => __('Getting plugins hashes: ', 'security-malware-firewall'),
            'clear_table'     => __('Clearing table: ', 'security-malware-firewall'),
            'surface_scan'    => __('Surface scan: ', 'security-malware-firewall'),
            'signature_scan'  => __('Signature scanning: ', 'security-malware-firewall'),
            'heuristic_scan'  => __('Heuristic scanning: ', 'security-malware-firewall'),
            'cure_backup'     => __('Creating a backup: ', 'security-malware-firewall'),
            'cure'            => __('Curing: ', 'security-malware-firewall'),
            'links_scan'      => __('Links scanning: ', 'security-malware-firewall'),
            'send_results'    => __('Sending result: ', 'security-malware-firewall'),
        );
    }

    $errors = $spbc->errors;
    $errors_out = array();

    foreach ($errors as $type => $error) {
        if (empty($error) || !isset($types[$type])) {
            continue;
        }

        if (is_array(current($error))) {
            foreach ($error as $sub_type => $sub_error) {
                $text_time = isset($sub_error['error_time']) ? date('Y-m-d H:i:s', $sub_error['error_time']) . ': ' : '';
                $text_type = $types[ $type ];
                $text_sub_type = isset($sub_types[ $sub_type ]) ? $sub_types[ $sub_type ] : $sub_type . ': ';
                $text_error = json_encode($sub_error['error'], JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
                $errors_out[] = $text_time . $text_type . $text_sub_type . $text_error;
            }
        } else {
            $text_time = isset($error['error_time']) ? date('Y-m-d H:i:s', $error['error_time']) . ': ' : '';
            $text_type = $types[ $type ];
            $text_error = json_encode($error['error'], JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
            $errors_out[] = $text_time . $text_type . $text_error;
        }
    }

    return $errors_out;
}

function spbc_tab__summary()
{
    global $spbc;

    // Template with numbered placeholders for all dynamic parts
    $template = '
        <div class="spbc_wrapper_field">
            <div class="spbc_stats_wrapper">
                <div class="spbc_stats_divider_left">
                    %1$s
                </div>
                <div class="spbc_stats_divider_right">
                    %2$s
                </div>
            </div>
        </div>
        <br>
    ';

    // Prepare all dynamic content parts
    $content_parts = [
        spbc_field_statistics(),
        spbc_field_support(),
    ];

    // Output the formatted template
    printf($template, ...$content_parts);
}

/**
 * Displays a compact block with list of all main security features and their on/off status (traffic light).
 * Grouped by settings block titles. Placed in General Settings tab between the request treatment banner and the Access Key.
 */
function spbc_field_options_overview_traffic_light()
{
    global $spbc;

    $groups = array(
        array(
            'title'   => __('Authentication and Logging In', 'security-malware-firewall'),
            'anchor'  => 'auth',
            'features' => array(
                array('label' => __('Brute Force Protection', 'security-malware-firewall'), 'enabled' => ! empty($spbc->settings['bfp__enabled']), 'anchor' => 'auth'),
                array('label' => __('Two-factor authentication (2FA)', 'security-malware-firewall'), 'enabled' => ! empty($spbc->settings['2fa__enable']), 'anchor' => 'auth'),
                array('label' => __('Checking the user\'s password for information leaks', 'security-malware-firewall'), 'enabled' => ! empty($spbc->settings['check_pass__enable']), 'anchor' => 'auth'),
                array('label' => __('Change address to login script', 'security-malware-firewall'), 'enabled' => ! empty($spbc->settings['login_page_rename__enabled']), 'anchor' => 'auth'),
            ),
        ),
        array(
            'title'   => __('Firewall', 'security-malware-firewall'),
            'anchor'  => 'firewall',
            'features' => array(
                array('label' => __('Traffic Control', 'security-malware-firewall'), 'enabled' => ! empty($spbc->settings['traffic_control__enabled']), 'anchor' => 'firewall'),
                array('label' => __('Security Firewall', 'security-malware-firewall'), 'enabled' => ! empty($spbc->settings['secfw__enabled']), 'anchor' => 'firewall'),
                array('label' => __('Web Application Firewall', 'security-malware-firewall'), 'enabled' => ! empty($spbc->settings['waf__enabled']), 'anchor' => 'firewall'),
            ),
        ),
        array(
            'title'   => __('Malware Scanner', 'security-malware-firewall'),
            'anchor'  => 'scanner_setting',
            'features' => array(
                array('label' => __('Malware Scanner', 'security-malware-firewall'), 'enabled' => ! empty($spbc->scaner_enabled), 'anchor' => 'scanner_setting'),
            ),
        ),
        array(
            'title'   => __('Modules vulnerability detection', 'security-malware-firewall'),
            'anchor'  => 'vulnerability_check',
            'features' => array(
                array('label' => __('Test already installed plugins for known vulnerabilities', 'security-malware-firewall'), 'enabled' => ! empty($spbc->settings['vulnerability_check__enable_cron']), 'anchor' => 'vulnerability_check'),
            ),
        ),
        array(
            'title'   => __('Miscellaneous', 'security-malware-firewall'),
            'anchor'  => 'misc',
            'features' => array(
                array('label' => __('Collect and send PHP logs', 'security-malware-firewall'), 'enabled' => ! empty($spbc->settings['misc__backend_logs_enable']), 'anchor' => 'misc'),
            ),
        ),
    );
    return $groups;
}

function spbc_field_security_logs__prepare_data(&$table)
{
    if ($table->items_count) {
        foreach ($table->rows as $row) {
            $ips_c[] = $row->auth_ip;
        }
        unset($row);
        $ips_c = spbc_get_countries_by_ips(implode(',', $ips_c));

        $time_offset = current_time('timestamp') - time();

        foreach ($table->rows as $row) {
            $ip = IP::reduceIPv6($row->auth_ip);
            $allow_layout = '<a href="#" onclick="return spbcSecLogsAllowIp(\''
                . esc_attr($ip)
                . '\')" class="spbcGreen tbl-row_action--allow" data-ip=' . $ip . '>'
                . esc_html__('Allow', 'security-malware-firewall') . '</a>';
            $ban_layout = '<a href="#" onclick="return spbcSecLogsBanIp(\''
                . esc_attr($ip)
                . '\')" class="spbc---red tbl-row_action--ban" data-ip=' . $ip . '>'
                . esc_html__('Ban', 'security-malware-firewall') . '</a>';

            $user_part = $row->user_login;
            $user = get_user_by('login', $row->user_login);
            $change_role_layout = '<span class="spbcGray">' . __('Change Role', 'security-malware-firewall') . '</span>';
            if ($user) {
                $user_part = sprintf(
                    "<a href=\"%s\">%s</a> <span class='spbc_role_title' title='%s'>(%s)</span>",
                    $user ? (admin_url() . '/user-edit.php?user_id=' . $user->data->ID) : '#',
                    $row->user_login,
                    __('Current role', 'security-malware-firewall'),
                    $user->roles[0] ? $user->roles[0] : __('Not defined', 'security-malware-firewall')
                );
                $change_role_layout = sprintf(
                    '<a href="#" onclick="return spbcSecLogsChangeRole(\'%s\', \'%s\', \'%s\')" class="spbcGray tbl-row_action--change_role">%s</a>',
                    $user->data->ID,
                    $row->role,
                    $row->user_login,
                    __('Change Role', 'security-malware-firewall')
                );
            }
            $user_part .= '<br>' . $allow_layout . ' | ' . $ban_layout;
            if ($user) {
                $user_part .= ' | ' . $change_role_layout;
            }

            $url = Escape::escUrl($row->page);
            if ($url === null) {
                $page = '-';
            } elseif (strlen($url) >= 60) {
                $url = esc_html($url);
                $page = '<div class="spbcShortText">'
                    . '<a href="' . $url . '" target="_blank">' . substr($url, 0, 60) . '...</a>'
                    . '</div>'
                    . '<div class="spbcFullText spbcFullText-right spbc_hide_table_cell_desc">'
                    . '<a href="' . $url . '" target="_blank">' . $url . '</a>'
                    . '</div>';
            } else {
                $page = "<a href='" . $url . "' target='_blank'>" . $url . "</a>";
            }

            $event = spbc_format_security_log_event($row->event, $url, $row->page_time);

            $country_part = spbc_report_country_part($ips_c, $row->auth_ip);

            $ip_blacklist_link = LinkConstructor::buildCleanTalkLink(
                // ?utm_source=wordpress&utm_medium=dashboard&utm_campaign=security_log
                'settings__security_logs_ip_addresses',
                'blacklists/%s'
            );
            $ip_part      = sprintf(
                '<a href="' . $ip_blacklist_link . '" target="_blank">%s<i class="spbc-icon-link-ext"></i></a><br>%s',
                $row->auth_ip,
                IP::reduceIPv6($row->auth_ip),
                $country_part
            );

            $table->items[] = array(
                'cb' => $row->id,
                'user_login' => $user_part,
                'datetime'   => date("M d Y, H:i:s", strtotime($row->datetime) + $time_offset),
                'event'      => $event,
                'page'       => $page,
                'auth_ip'    => $ip_part,
                'url'        => $url,
            );
        }

        // accumulate similar rows
        $result = [];
        foreach ($table->items as $item) {
            $last_item = end($result);

            // Get base events without count for comparison
            $current_base_event = trim(preg_replace('/\s*\(\d+\)$/', '', $item['event']));
            $last_base_event = $last_item ? trim(preg_replace('/\s*\(\d+\)$/', '', $last_item['event'])) : '';

            if ($last_item &&
                $item['user_login'] === $last_item['user_login'] &&
                $current_base_event === $last_base_event &&
                $item['url'] === $last_item['url'] &&
                $item['auth_ip'] === $last_item['auth_ip']
            ) {
                // Extract current count if exists
                if (preg_match('/\((\d+)\)$/', $last_item['event'], $matches)) {
                    $count = (int)$matches[1] + 1;
                } else {
                    $count = 2;
                    // Store first datetime when starting to accumulate
                    $result[count($result) - 1]['datetime'] = $last_item['datetime'] . '<br>' . $item['datetime'];
                }

                // Update last item with new count
                $result[count($result) - 1]['event'] = $current_base_event . ' (' . $count . ')';
                // Update end of datetime range
                $result[count($result) - 1]['datetime'] = preg_replace('/<br>.*$/', '', $result[count($result) - 1]['datetime']) . '<br>' . $item['datetime'];

                continue;
            }

            $result[] = $item;
        }
        $table->items = $result;
    }
}

/**
 * Admin callback function - Displays description of 'main' plugin parameters section
 * @throws Exception
 */

function spbc_field_security_logs()
{
    global $spbc;

    echo spbc_get_widget_timeline_code();

    echo '<div class="spbc_wrapper_field">';

    /**
     * Check if tab is restricted by license, layout according HTML if so.
     */
    $feature_state = $spbc->feature_restrictions->getState($spbc, 'security_log');
    if (false === $feature_state->is_active) {
        echo $feature_state->sanitizedReasonOutput();
        echo '</div>';
        return;
    }

    // HEADER
    $message_about_log = __('This table contains details of all brute-force attacks and security actions made in the past 24 hours.', 'security-malware-firewall');

    if ( ! $spbc->data["wl_mode_enabled"] ) {
        $message_about_log .=  sprintf(
            esc_html__(' Please, use your %sSecurity Control Panel%s to see the full report.', 'security-malware-firewall'),
            '<a target="_blank" href="https://cleantalk.org/my/logs?user_token=' . $spbc->user_token . '">',
            '</a>'
        );
    }

    echo "<p class='spbc_hint spbc_hint-security_logs -display--inline-block'>$message_about_log</p>";

    // OUTPUT
    $table = new ListTable(spbc_list_table__get_args_by_type('security_logs'));

    $table->getData();

    // Send logs button
    // @ToDo need to return the button and fix its behaviour
    /*if ($table->items_total) {
        echo '<p class="spbc_hint spbc_hint-send_security_log spbc_hint--link spbc_hint--top_right">'
             . __('Send logs', 'security-malware-firewall')
             . '</p>';
    }*/

    $table->display();

    $link_proceed_to = '';

    if (( ! empty($spbc->user_token))) {
        /**
         * Attention! This link should be hidden by default untill the limit of show more clicks is reached.
         */
        $link_proceed_to = TextPlateStatic::render(
            '
<div class="spbc__show_more_logs spbc_hide">
    <span class="-display--inline-block">{{proceed_to}} <a target="_blank" href="https://cleantalk.org/my/logs?service={{service}}&user_token={{user_token}}">{{link_text}}</a> {{to_see}}</span>
</div>
            ',
            array(
                    'proceed_to' => __('Proceed to', 'security-malware-firewall'),
                    'service' => esc_html($spbc->service_id),
                    'user_token' => esc_html($spbc->user_token),
                    'link_text' => 'Control Panel',
                    'to_see' => 'to see all events for up to 45 days.',
            )
        );
    }

    $button_show_more_logs = TextPlateStatic::render(
        '
<div id="spbc_show_more_button" class="spbc_manual_link">{{button_caption}}</div>
<img class="spbc_preloader" src="{{spbc_path}}/images/preloader.gif" />
        ',
        array(
                'button_caption' => __('Show more records', 'security-malware-firewall'),
                'spbc_path' => esc_url(SPBC_PATH),
        )
    );

    $section_show_more = TextPlateStatic::render(
        '
<div class="spbc__wrapper--center spbc__wrapper--show_more">
    {{link_proceed_to}}
    {{show_more_button}}
</div>
            ',
        array(
                'link_proceed_to' => $link_proceed_to,
                'show_more_button' => $button_show_more_logs
        )
    );



    // SHOW MORE
    if ($table->items_total > SPBC_LAST_ACTIONS_TO_VIEW) {
            echo $section_show_more;
    }

    echo '</div>';
}


/**
 * Creates a banner with a notification
 * @return void
 */
function spbc_settings__create_notice_on_tab()
{
    global $spbc;

    $flag_text_banner = '';

    if ($spbc->data['display_scanner_warnings']['critical'] > 0 &&
        Cookie::getString('spbct_notice-found_critical_files') != '1') {
        $flag_text_banner = 'found_critical_files';
        $text = __("There's a high probability that your website has been compromised, as critical files show signs of infection. Take action now by ordering malware removal from our experienced security specialists.", 'security-malware-firewall');
    }

    if ( $spbc->data['display_scanner_warnings']['db_triggers'] > 0 &&
         Cookie::getString('spbct_notice-found_db_triggers') != '1') {
        $flag_text_banner = 'found_db_triggers';
        $text = DBTriggerView::getWarningTextForMalwareRemovalBanner();
    }

    if ( $spbc->data['display_scanner_warnings']['oscron'] > 0 &&
         Cookie::getString('spbct_notice-found_oscron') != '1') {
        $flag_text_banner = 'found_oscron';
        $text = Scanner\OSCron\View\OSCronLocale::getInstance()->malware_removal_banner_text;
    }

    if (!empty($text)) {
        $email = spbc_get_admin_email();
        $website = get_home_url();
        $button_text = __('Request Malware removal', 'security-malware-firewall');
        $landing_page_link = LinkConstructor::buildCleanTalkLink(
            'banner_link_for_treatment',
            'wordpress-malware-removal',
            array(
                'email' => esc_attr($email),
                'website' => esc_attr($website),
            ),
            $domain = 'https://cleantalk.org'
        );
        $button_div = '<div style="align-content: center;margin: 0 30px;">';
        $button_div .= '
                    <a class="spbc_manual_link" target="_blank" href="' . $landing_page_link . '">'
                       . '<i class="spbc-icon-link-ext"></i>&nbsp;&nbsp;'
                       . $button_text
                       . '</a>
                    ';
        $button_div .= '</div>';
        $text .= $button_div;

        $template = '
            <div class="spbc_tab_fields_group">
                <div class="spbc_group_header"></div>
                <div class="notice notice-warning spbct_notice spbct_notice-%s" style="scroll-margin-top: 10pc" id="notice_id_%s">
                    <p class="spbc---top"">%s</p>
                    <button type="button" class="notice-dismiss spbct_notice-dismiss">
                        <span class="screen-reader-text">Dismiss this notice.</span>
                    </button>
                </div>
            </div>
        ';
        printf($template, $flag_text_banner, $flag_text_banner, $text);
    }
}

function spbc_field_scanner__prepare_data__files(&$table)
{
    global $wpdb,$spbc;

    if ($table->items_count) {
        $root_path = spbc_get_root_path();

        $signatures = $wpdb->get_results('SELECT * FROM ' . SPBC_TBL_SCAN_SIGNATURES, OBJECT_K);

        foreach ($table->rows as $key => $row) {
            // Filtering row actions
            if ($row->last_sent > $row->mtime || $row->size == 0 || $row->size > 1048570) {
                unset($row->actions['send']);
            }

            if ( !$row->real_full_hash || !$row->source_type ) {
                unset($row->actions['replace']);
                unset($row->actions['compare']);
            }

            if ( ! $row->severity) {
                unset($row->actions['view_bad']);
            }
            if ($row->status === 'quarantined') {
                unset($row->actions['quarantine']);
            }

            if ( $table->type === 'approved' ) {
                 $status = esc_html__('User', 'security-malware-firewall');
            } else {
                $status = __('Not checked by Cloud Analysis or ' . $spbc->data["wl_company_name"] . ' Team yet.', 'security-malware-firewall');
                if ( !empty($row->pscan_status) ) {
                    if ( $row->pscan_status === 'DANGEROUS' ) {
                        $status = '<span class="spbcRed">' . __('File is denied by Cloud analysis', 'security-malware-firewall') . '</span>';
                    }
                }
            }

            if ( $row->status === 'APPROVED_BY_CT' ) {
                $status = esc_html__('CleanTalk Team', 'security-malware-firewall');
            }
            if ( $row->status === 'APPROVED_BY_CLOUD' ) {
                $status = esc_html__('Cloud analysis', 'security-malware-firewall');
            }
            if ( $row->status === 'APPROVED_BY_USER' ) {
                $status = esc_html__('User', 'security-malware-firewall');
            }

            if ( !empty($row->status) ) {
                if ( $row->status === 'DENIED_BY_CT' ) {
                    unset($row->actions['send']);
                    unset($row->actions['view_bad']);
                }
            }

            if ( $table->type === 'suspicious' && in_array($row->fast_hash, spbc_get_list_of_scheduled_suspicious_files_to_send())) {
                $status = __('File will be automatically send for Cloud analysis within 5 minutes.', 'security-malware-firewall');
            }

            if ( $row->source_type === 'CORE' ) {
                unset($row->actions['quarantine']);
                unset($row->actions['delete']);
            }

            // Binary files: hide View Suspicious Code
            $is_binary_file = isset($row->source) && $row->source === 'BINARY';
            if ( $is_binary_file ) {
                unset($row->actions['view_bad']);
                unset($row->actions['cure']);
            }

            // wp-config.php: detect only; content view for Super Admin only; never send to cloud
            if ( Scanner\Helper::isWpConfigPath($row->path) ) {
                unset($row->actions['cure']);
                unset($row->actions['delete']);
                unset($row->actions['quarantine']);
                unset($row->actions['replace']);
                unset($row->actions['send']);
                if ( ! Scanner\Helper::canViewWpConfigContents() ) {
                    unset($row->actions['view']);
                    unset($row->actions['view_bad']);
                }
                if ( ! empty($row->weak_spots) || (isset($row->severity) && $row->severity === 'CRITICAL') ) {
                    $status = '<span class="spbcRed">' . esc_html(Scanner\Helper::getWpConfigManualCleanupMessage()) . '</span>';
                }
            }

            $table->items[] = array(
                'cb'      => $row->fast_hash,
                'uid'     => $row->fast_hash,
                'size'    => substr(number_format($row->size, 2, ',', ' '), 0, - 3),
                'perms'   => $row->perms,
                'mtime'   => date('M d Y H:i:s', $row->mtime + $spbc->data['site_utc_offset_in_seconds']),
                'path'    => strlen($root_path . $row->path) >= 40
                    ? '<div class="spbcShortText">...' . esc_html($row->path) . '</div><div class="spbcFullText spbc_hide_table_cell_desc">' . $root_path . esc_html($row->path) . '</div>'
                    : $root_path . esc_html($row->path),
                'actions' => $row->actions,
                'status' => $status,
            );

            if (isset($row->weak_spots)) {
                $weak_spots = json_decode($row->weak_spots, true);
                $ws_string = '';

                if ($weak_spots) {
                    if ( ! empty($weak_spots['SIGNATURES']) && $signatures) {
                        foreach ($weak_spots['SIGNATURES'] as $_string => $weak_spot_in_string) {
                            foreach ($weak_spot_in_string as $weak_spot) {
                                $attack_type = isset($signatures[ $weak_spot ]) ? $signatures[ $weak_spot ]->attack_type : '';
                                $sig_name = isset($signatures[ $weak_spot ]) ? $signatures[ $weak_spot ]->name : '';
                                $ws_string .= '<span class="spbcRed"><i setting="signatures_' . esc_attr($attack_type) . '" class="spbc_long_description__show spbc-icon-help-circled"></i>' . esc_html($attack_type) . ': </span>'
                                             . (strlen($sig_name) > 30
                                        ? esc_html(substr($sig_name, 0, 30)) . '...'
                                        : esc_html($sig_name));
                            }
                        }
                    }
                    if ( ! empty($weak_spots['CRITICAL'])) {
                        // collecting all kinds of code
                        $all_unique_weak_spots = array();
                        foreach ($weak_spots['CRITICAL'] as $_string => $weak_spot_in_string) {
                            $all_unique_weak_spots[] = $weak_spot_in_string[0];
                        }
                        $all_unique_weak_spots = array_unique($all_unique_weak_spots);
                        foreach ($all_unique_weak_spots as $weak_spot_in_string) {
                            $ws_string .= '<p style="margin: 0;"><span class="spbcRed"><i setting="heuristic_' . esc_attr(str_replace(' ', '_', $weak_spot_in_string)) . '" class="spbc_long_description__show spbc-icon-help-circled"></i> Heuristic: </span>'
                                    . (strlen($weak_spot_in_string) > 30
                                    ? esc_html(substr($weak_spot_in_string, 0, 30)) . '...'
                                    : esc_html($weak_spot_in_string));
                            $ws_string .= '</p>';
                        }
                    }
                    if ( ! empty($weak_spots['SUSPICIOUS'])) {
                        // collecting all kinds of code
                        $all_unique_weak_spots = array();
                        foreach ($weak_spots['SUSPICIOUS'] as $_string => $weak_spot_in_string) {
                            $all_unique_weak_spots[] = $weak_spot_in_string[0];
                        }
                        $all_unique_weak_spots = array_unique($all_unique_weak_spots);
                        foreach ($all_unique_weak_spots as $weak_spot_in_string) {
                            $ws_string .= '<p style="margin: 0;"><span class="spbcRed"><i setting="suspicious_' . esc_attr(str_replace(' ', '_', $weak_spot_in_string)) . '" class="spbc_long_description__show spbc-icon-help-circled"></i> Suspicious: </span>'
                                . (strlen($weak_spot_in_string) > 30
                                ? esc_html(substr($weak_spot_in_string, 0, 30)) . '...'
                                : esc_html($weak_spot_in_string));
                            $ws_string .= '</p>';
                        }
                    }
                    if ( ! empty($weak_spots['DENIED_HASH'])) {
                        // collecting all kinds of code
                        $all_unique_weak_spots = array();
                        foreach ($weak_spots['DENIED_HASH'] as $_string => $weak_spot_in_string) {
                            $all_unique_weak_spots[] = $weak_spot_in_string[0];
                        }
                        $all_unique_weak_spots = array_unique($all_unique_weak_spots);
                        foreach ($all_unique_weak_spots as $weak_spot_in_string) {
                             $ws_string .= '<p style="margin: 0;"><span class="spbcRed"><i setting="hash_' . esc_attr(str_replace(' ', '_', $weak_spot_in_string)) . '" class="spbc_long_description__show spbc-icon-help-circled"></i> Hash: </span>'
                                . 'denied';

                            $ws_string .= '</p>';
                            if ( $table->type !== 'approved' ) {
                                $table->items[ $key ]['status'] = __("Delete, cure or quarantine the file immediately!", 'security-malware-firewall');
                            }
                        }
                    }
                }

                $table->items[ $key ]['weak_spots'] = $ws_string;
            }

            //delete send action if file extension is not in list for unknown files
            if ( $table->type === 'unknown' && !empty($row->path) ) {
                $ext = pathinfo($row->path, PATHINFO_EXTENSION);
                if (
                    empty($ext) ||
                    (
                        !empty($ext) &&
                        !in_array($ext, array('php', 'html', 'htm', 'php2', 'php3', 'php4', 'php5', 'php6', 'php7', 'phtml', 'shtml', 'phar', 'odf'))
                    )
                ) {
                    if ( isset($table->items[$key], $table->items[$key]['actions'], $table->items[$key]['actions']['send']) ) {
                        unset($table->items[$key]['actions']['send']);
                    }
                }
            }

            if ($table->type === 'skipped') {
                $parsed_item_error = '';
                if ( !empty($row->error_msg) && is_string($row->error_msg) ) {
                    $errors = json_decode($row->error_msg, true);
                    if (!empty($errors)) {
                        foreach ($errors as $_key => $_val) {
                            $parsed_item_error .= '<p>' . esc_html($_key) . ': ' . esc_html($_val) . '</p>';
                        }
                    } else {
                        $parsed_item_error = 'Unknown error';
                    }
                }

                unset($table->items[$key]['actions']['view']);

                $table->items[$key]['error_msg'] = $parsed_item_error;
            }

            if (isset($row->size) && (int)($row->size) > 1024 * 1024 * 8) {
                unset($table->items[$key]['actions']['send']);
            }
        }
    }
}

function spbc_field_scanner__prepare_data__analysis_log(&$table)
{
    if ($table->items_count) {
        $root_path = spbc_get_root_path();
        ////should be offset, because $row->last_sent has offset
        $curr_time = current_time('timestamp');
        $table->columns['analysis_comment']  = array('heading' => 'Comment', 'width_percent' => 20);

        foreach ($table->rows as $key => $row) {
            $pscan_status = '-';
            $analysis_comment = '-';
            switch ($row->pscan_processing_status) {
                case 'NEW':
                    $pscan_status = __('Queued for inspection', 'security-malware-firewall');
                    $analysis_comment = __('Processing: new, preparing to queueing..', 'security-malware-firewall');
                    break;
                case 'ERROR':
                    $pscan_status = '<span class="spbcRed">' . __('Checked', 'security-malware-firewall') . '</span>';
                    $analysis_comment = '<span class="spbcRed">' . __('Files cause errors on execution.', 'security-malware-firewall') . '</span>';
                    break;
                case 'IN_SCANER':
                    $pscan_status = __('Queued for inspection', 'security-malware-firewall');
                    $analysis_comment = __('Processing: on the cloud scanner..', 'security-malware-firewall');
                    break;
                case 'IN_SANDBOX':
                case 'NEW_SANDBOX':
                    $pscan_status = __('Queued for inspection', 'security-malware-firewall');
                    $analysis_comment = __('Processing: on the cloud sandbox..', 'security-malware-firewall');
                    break;
                case 'IN_CLOUD':
                case 'NEW_CLOUD':
                    $pscan_status = __('Queued for inspection', 'security-malware-firewall');
                    $analysis_comment = __('Processing: on the cloud analysis system', 'security-malware-firewall');
                    break;
                case 'UNKNOWN':
                    $pscan_status = __('Queued for inspection', 'security-malware-firewall');
                    $analysis_comment = __('Processing: adding to queue..', 'security-malware-firewall');
                    break;
                case 'DONE':
                    if ($row->pscan_status === 'DANGEROUS') {
                        $pscan_status = '<span class="spbcRed">' . $row->pscan_status . '</span>';
                        $analysis_comment = '<span class="spbcRed">' . __('Cloud: file is dangerous', 'security-malware-firewall')  . '</span>';
                    } elseif ($row->pscan_status === 'SAFE') {
                        $pscan_status = '<span class="spbcGreen">' . $row->pscan_status . '</span>';
                        $analysis_comment = '<span class="spbcGreen">' . __('Cloud: file is safe', 'security-malware-firewall')  . '</span>';
                    }
                    break;
                default:
                    $pscan_status = esc_html($row->pscan_processing_status);
                    $analysis_comment = 'Not scanned by Cloud or CleanTalk team.';
            }

            if ( isset($row->status) && $row->status === 'QUARANTINED' ) {
                $pscan_status = esc_html($row->pscan_status);
                $analysis_comment = __('Quarantined by user', 'security-malware-firewall');
            }

            if ( isset($row->status) && $row->status === 'APPROVED_BY_USER' ) {
                $pscan_status = 'APPROVED';
                $analysis_comment = __('Approved by user', 'security-malware-firewall');
            }

            if ($row->pscan_pending_queue == '1') {
                $pscan_status = __('Queued for inspection', 'security-malware-firewall');
                $analysis_comment = __('Processing: queue is full. File will be resent in 5 minutes.', 'security-malware-firewall');
            }

            if ( !is_null($row->pscan_estimated_execution_time) ) {
                $estimated_execution_time = $row->pscan_estimated_execution_time . ' ' . __('second(s)', 'security-malware-firewall');
            } else {
                $estimated_execution_time = $row->pscan_processing_status === 'DONE' ? 'Done' : 'Wait for assessing';
            }

            // Filter actions for approved files
            if ( in_array($row->pscan_status, array('SAFE','DANGEROUS')) || $curr_time - $row->last_sent < 500 ) {
                unset($row->actions['check_analysis_status']);
            }

            if ( empty($row->pscan_status) ) {
                unset($row->actions['delete']);
                unset($table->bulk_actions['delete_from_analysis_log']);
            }

            $table->items[ $key ] = array(
                'cb'               => $row->fast_hash,
                'uid'              => $row->fast_hash,
                'path'             => strlen($root_path . $row->path) >= 40
                    ? '<div class="spbcShortText">...' . esc_html($row->path) . '</div><div class="spbcFullText spbc_hide_table_cell_desc">' . $root_path . esc_html($row->path) . '</div>'
                    : $root_path . esc_html($row->path),
                'detected_at'      => is_numeric($row->detected_at) ? date('M j, Y, H:i:s', $row->detected_at) : null,
                'last_sent'        => is_numeric($row->last_sent) ? date('M j, Y, H:i:s', $row->last_sent) : null,
                'pscan_status'  => $pscan_status,
                'analysis_comment' => $analysis_comment,
                'pscan_estimated_execution_time' => $estimated_execution_time,
                'actions'          => $row->actions,
            );
        }
    }
}

/**
 * Count found in os cron.
 * @return int
 */
function spbc_scanner_oscron_count_found()
{
    return OSCronView::getCountOfTasksScanned();
}

/**
 * Get data for oscron.
 * @return array
 */
function spbc_scanner_oscron_get_scanned()
{
    return OsCronTasksStorage::getAsArray();
}

/**
 * Prepare data for oscron.
 * @param $table
 */
function spbc_scanner_oscron_prepare_data(&$table)
{
    $table = OSCronView::prepareTableData($table);
}

/**
 * Settings function wrapper. Get count found in db trigger.
 * @return int
 */
function spbc_scanner_db_trigger_count_found()
{
    return DBTriggerService::countTriggersStorage();
}

/**
 * Settings function wrapper. Get data for db trigger.
 * @return array
 */
function spbc_scanner_db_trigger_get_scanned()
{
    return DBTriggerService::loadTriggersStorage();
}

/**
 * Settings function wrapper. Modify data in triggers table.
 * @param $table
 */
function spbc_scanner_db_trigger_prepare_data(&$table)
{
    $table = DBTriggerView::prepareTableData($table);
}

function spbc_field_scanner__prepare_data__files_quarantine(&$table)
{
    global $spbc;
    if ($table->items_count) {
        $root_path = spbc_get_root_path();
        foreach ($table->rows as $_key => $row) {
            $table->items[] = array(
                'cb'             => $row->fast_hash,
                'uid'            => $row->fast_hash,
                'actions'        => $row->actions,
                'path'           => strlen($root_path . $row->path) >= 40
                    ? '<div class="spbcShortText">...' . esc_html($row->path) . '</div><div class="spbcFullText spbc_hide_table_cell_desc">' . $root_path . esc_html($row->path) . '</div>'
                    : $root_path . esc_html($row->path),
                'previous_state' => json_decode($row->previous_state)->status,
                'severity'       => $row->severity,
                'perms'   => $row->perms,
                'mtime'   => date('M d Y H:i:s', $row->mtime + $spbc->data['site_utc_offset_in_seconds']),
                'q_time'         => date('M d Y H:i:s', $row->q_time),
                'size'           => substr(number_format($row->size, 2, ',', ' '), 0, - 3),
            );
        }
    }
}

function spbc_field_scanner__prepare_data__domains(&$table)
{
    if ($table->items_count) {
        $num = $table->sql['offset'] + 1;
        foreach ($table->rows as $row) {
            $_text = "<a href={{href}} target='_blank'>{{href_text}}</a>";
            $domain = TextPlateStatic::render(
                $_text,
                [
                    'href' => esc_url($row->domain),
                    'href_text' => esc_html($row->domain),
                ]
            );
            $table->items[] = array(
                'num'         => $num++,
                'uid'         => esc_html($row->domain),
                'domain'      => $domain,
                'spam_active' => isset($row->spam_active) ? ($row->spam_active ? 'Yes' : 'No') : 'Unknown',
                'page_url'    => esc_url($row->page_url),
                'link_count'  => htmlspecialchars($row->link_count),
                'actions'     => $row->actions,
            );
        }
    }
}

function spbc_field_scanner__prepare_data__links(&$table)
{
    if ($table->items_count) {
        foreach ($table->rows as $row) {
            $_text = "<a href={{href}} target='_blank'>{{href_text}}</a>";
            $link = TextPlateStatic::render(
                $_text,
                [
                    'href' => esc_url($row->link),
                    'href_text' => esc_html($row->link),
                ]
            );
            $page_url = TextPlateStatic::render(
                $_text,
                [
                    'href' => esc_url($row->page_url),
                    'href_text' => esc_html($row->page_url),
                ]
            );
            $table->items[] = array(
                'link_id'     => $row->link_id,
                'link'        => $link,
                'page_url'    => $page_url,
                'link_text'   => htmlspecialchars($row->link_text),
            );
        }
    }
}

function spbc_field_scanner__prepare_data__frontend(&$table)
{
    if ($table->items_count) {
        foreach ($table->rows as $row) {
            $table->items[] = array(
                // 'cb' row has no useful matter, but should be kept for checkbox placement
                'cb'             => $row->page_id,
                'url'            => $row->url,
                'uid'            => $row->url,
                'page_id'        => $row->page_id,
                'actions'        => $row->actions,
                'dbd_found'      => $row->dbd_found
                    ? '<span class="spbcRed">' . __('Found', 'security-malware-firewall') . '</span>'
                    : '<span class="spbcGreen">' . __('Not found', 'security-malware-firewall') . '</span>',
                'redirect_found' => $row->redirect_found
                    ? '<span class="spbcRed">' . __('Found', 'security-malware-firewall') . '</span>'
                    : '<span class="spbcGreen">' . __('Not found', 'security-malware-firewall') . '</span>',
                'csrf'           => $row->csrf
                    ? '<span class="spbcRed">' . __('Found', 'security-malware-firewall') . '</span>'
                    : '<span class="spbcGreen">' . __('Not found', 'security-malware-firewall') . '</span>',
                'signature'      => $row->signature
                    ? '<span class="spbcRed">' . __('Found', 'security-malware-firewall') . '</span>'
                    : '<span class="spbcGreen">' . __('Not found', 'security-malware-firewall') . '</span>',
            );
        }
    }
}

/**
 * Get data for frontend scan malware results.
 * @param $offset
 * @param $limit
 * @return array|object|stdClass[]|null
 */
function spbc_field_scanner__get_data__frontend_malware($offset = 1, $limit = 20, $order_direction = "DESC", $order = "page_id")
{
    global $wpdb;

    return $wpdb->get_results($wpdb->prepare(
        'SELECT * FROM ' . SPBC_TBL_SCAN_FRONTEND . '
        WHERE approved IS NULL OR approved <> 1
		ORDER BY %s %s
		LIMIT %d, %d;',
        $order,
        $order_direction,
        $offset,
        $limit
    ));
}

/**
 * Get data for frontend scan approved results.
 * @param $offset
 * @param $limit
 * @return array|object|stdClass[]|null
 */
function spbc_field_scanner__get_data__frontend_approved($offset = 0, $limit = 20)
{
    global $wpdb;

    return $wpdb->get_results($wpdb->prepare(
        'SELECT * FROM ' . SPBC_TBL_SCAN_FRONTEND . '
        WHERE approved = 1
		ORDER BY page_id DESC
		LIMIT %d, %d;',
        $offset,
        $limit
    ));
}

/**
 * Counts amount of accessible URL
 *
 * @return int
 * @psalm-suppress InvalidArrayAccess
 */
function spbc_field_scanner__files_listing__get_total()
{
    global $spbc;

    $accessible_urls = is_array($spbc->scanner_listing) && !empty($spbc->scanner_listing['accessible_urls'])
        ? $spbc->scanner_listing['accessible_urls']
        : array();

    if ($accessible_urls === []) {
        $accessible_urls = is_object($spbc->scanner_listing) &&
            !empty($spbc->scanner_listing['accessible_urls'])
            ? $spbc->scanner_listing['accessible_urls']
            : array();
    }

    if (
        isset($accessible_urls) &&
        (is_array($accessible_urls) || is_object($accessible_urls))
    ) {
        return count($accessible_urls);
    }

    return 0;
}

/**
 * Provides data in the correct format for table
 *
 * @return array of objects
 * @psalm-suppress InvalidArrayAccess
 */
function spbc_field_scanner__files_listing__get_data()
{
    global $spbc;

    $out = array();

    $accessible_urls = is_array($spbc->scanner_listing) && !empty($spbc->scanner_listing['accessible_urls'])
        ? $spbc->scanner_listing['accessible_urls']
        : array();

    if ($accessible_urls === []) {
        $accessible_urls = is_object($spbc->scanner_listing) && !empty($spbc->scanner_listing['accessible_urls'])
            ? $spbc->scanner_listing['accessible_urls']
            : array();
    }

    if (
        isset($accessible_urls) &&
        (is_array($accessible_urls) || is_object($accessible_urls))
    ) {
        foreach ($accessible_urls as $entry) {
            $out[] = (object) $entry;
        }
    }

    return $out;
}

function spbc_field_scanner__files_listing__data_prepare(&$table)
{
    if ($table->items_count) {
        foreach ($table->rows as $row) {
            $table->items[] = array(
                'url'  => "<a href='{$row->url}' target='_blank'>" . get_option('home') . "{$row->url}</a>",
                'type' => ucfirst($row->type)
                          . '<i setting="' . $row->type . '" class="spbc_long_description__show spbc-icon-help-circled"></i>'
                          . '<i setting="' . $row->type . '" class="spbc_long_recommendation__show spbc-icon-info-circled" style="cursor: pointer;"></i>',
            );
        }
    }
}

/**
 * Modify data to the Approved section
 *
 * @return void
 */
function spbc_field_scanner__approved__data_prepare(&$table)
{
    if ($table->items_count) {
        foreach ($table->rows as $row) {
            $table->items[] = array(
                'cb'         => $row->page_id,
                'path'       => $row->path,
                'weak_spots' => $row->weak_spots,
                'size'       => $row->size,
                'perms'      => $row->perms,
                'mtime'      => $row->mtime,
                'status'     => $row->status === 'APPROVED_BY_CT'
                    ? esc_html__('CleanTalk Team', 'security-malware-firewall')
                    : esc_html__('User', 'security-malware-firewall'),
            );
        }
    }
}

function spbc_field_scanner__log()
{
    global $spbc;

    $out = '<h4 class="spbc-scan-log-title spbc---hidden">' . esc_html__('Scan log', 'security-malware-firewall') . '</h4><div class="spbc_log-wrapper spbc---hidden"></div>';

    return $out;
}

/**
 * @throws Exception
 */
function spbc_field_scanner()
{
    global $spbc, $wp_version;

    echo '<div class="spbc_wrapper_field">';

    /**
     * Check if tab is restricted by license, layout according HTML if so.
     */
    $feature_state = $spbc->feature_restrictions->getState($spbc, 'scanner');
    if (false === $feature_state->is_active) {
        echo $feature_state->sanitizedReasonOutput();
        echo '</div>';
        return;
    }

    if (preg_match('/^[\d\.]*$/', $wp_version) !== 1) {
        echo '<p class="spbc_hint" style="text-align: center;">';
        printf(__('Your WordPress version %s is not supported', 'security-malware-firewall'), $wp_version);
        echo '</p>';
        // return;
    }

    echo '<p class="spbc_hint" style="text-align: center;">';
    echo '<span class="spbc_hint__last_scan_title">';
    if (empty($spbc->data['scanner']['last_scan'])) {
        _e('System hasn\'t been scanned yet. Please, perform the scan to secure the website.', 'security-malware-firewall');
        //should be offset because last_scan is offset
    } elseif ($spbc->data['scanner']['last_scan'] < current_time('timestamp') - 86400 * 7) {
        _e('System hasn\'t been scanned for a long time', 'security-malware-firewall');
    } else {
        _e('Look below for scan results.', 'security-malware-firewall');
    }
    echo '</span>';
    echo '</br>';
    if (! $spbc->data["wl_mode_enabled"]) {
        printf(
            __('%sView all scan results for this website%s%s', 'security-malware-firewall'),
            "<a target='blank' href='https://cleantalk.org/my/logs_mscan?service={$spbc->service_id}&user_token={$spbc->user_token}'>",
            '<i class="spbc-icon-link-ext"></i>',
            '</a>, '
        );
    }
    // show save to pdf link
    if ( ! empty($spbc->data['scanner']['last_scan'])) {
        echo ' &nbsp;<a id="spbc_scanner_save_to_pdf" href="" onclick="event.preventDefault()">'
                . __('Export results to PDF', 'security-malware-firewall')
                . '</a>, ';
    }
    //show backups link
    printf(
        __('%sBackups%s', 'security-malware-firewall'),
        '&nbsp;<a href="' . admin_url('options-general.php?page=spbc&spbc_tab=backups') . '">',
        '</a>'
    );
    echo '</p>';
    $scanner_disabled = isset($spbc->errors['configuration']) ? 'disabled="disabled"' : '';
    $scanner_disabled_reason = $scanner_disabled
        ? 'title="' . __('Scanner is disabled. Please, check errors on the top of the settings.', 'security-malware-firewall') . '"'
        : '';
    echo '<div style="text-align: center; margin-top: 1em;">'
         . '<button id="spbc_perform_scan" class="spbc_manual_link_scan" type="button" ' . $scanner_disabled . $scanner_disabled_reason . '>'
         . __('Perform Scan', 'security-malware-firewall')
         . '</button>'
         . '<img  class="spbc_preloader" src="' . SPBC_PATH . '/images/preloader.gif" />'
         . '</div>';

    echo '<p id="spbc_scanner__last_scan_info" class="spbc_hint" style="text-align: center; margin-top: 5px;">';
    echo spbc_scanner__last_scan_info(true);
    echo '</p>';

    // Show link for shuffle salts
    if ($spbc->settings['there_was_signature_treatment']) {
        echo '<div style="text-align: center;" id="spbc_notice_about_shuffle_link">';
        echo '<a href="options-general.php?page=spbc&spbc_tab=settings_general#action-shuffle-salts-wrapper">' . __('We recommend changing your secret authentication keys and salts when curing is done.', 'security-malware-firewall') . '</a>';
        echo '</div>';
    }
    echo '<p class="spbc_hint spbc_hint_warning spbc_hint_warning__long_scan" style="display: none; text-align: center; margin-top: 5px;">';
    _e('A lot of files were found, so it will take time to scan', 'security-malware-firewall');
    echo '</p>';
    echo '<p class="spbc_hint spbc_hint_warning spbc_hint_warning__outdated" style="display: none; text-align: center; margin-top: 5px;">';
    _e('Found outdated plugins or themes. Please, update to latest versions.', 'security-malware-firewall');
    echo '</p>';

    echo
        '<div id="spbc_scaner_progress_overall" class="spbc_hide" style="padding-bottom: 10px; text-align: center;">'
        . '<span class="spbc_overall_scan_status_get_cms_hashes">' . __('Receiving core hashes', 'security-malware-firewall') . '</span> -> '
        . '<span class="spbc_overall_scan_status_get_modules_hashes">' . __('Receiving plugin and theme hashes', 'security-malware-firewall') . '</span> -> '
        . '<span class="spbc_overall_scan_status_clean_results">' . __('Preparing', 'security-malware-firewall') . '</span> -> '
        . '<span class="spbc_overall_scan_status_file_system_analysis">' . __('Scanning for modifications', 'security-malware-firewall') . '</span> -> ';

    if ($spbc->settings['scanner__os_cron_analysis']) {
        echo '<span class="spbc_overall_scan_status_os_cron_analysis">' . __('OS Cron Analysis', 'security-malware-firewall') . '</span> -> ';
    }

    if ($spbc->settings['scanner__db_trigger_analysis']) {
        echo '<span class="spbc_overall_scan_status_db_trigger_analysis">' . __('DB Trigger Analysis', 'security-malware-firewall') . '</span> -> ';
    }

    echo
        '<span class="spbc_overall_scan_status_get_denied_hashes">' . __('Updating statuses for the denied files', 'security-malware-firewall') . '</span> -> '
        . '<span class="spbc_overall_scan_status_get_approved_hashes">' . __('Updating statuses for the approved files', 'security-malware-firewall') . '</span> -> ';

    if ($spbc->settings['scanner__signature_analysis']) {
        echo '<span class="spbc_overall_scan_status_signature_analysis">'
             . __('Signature analysis', 'security-malware-firewall')
             . '</span> -> ';
    }

    if ($spbc->settings['scanner__heuristic_analysis']) {
        echo '<span class="spbc_overall_scan_status_heuristic_analysis">'
             . __('Heuristic analysis', 'security-malware-firewall')
             . '</span> -> ';
    }

    if ($spbc->settings['scanner__binary_analysis']) {
        echo '<span class="spbc_overall_scan_status_binary_analysis">'
                . __('Binary analysis', 'security-malware-firewall')
                . '</span> -> ';
    }

    if ($spbc->settings['scanner__schedule_send_heuristic_suspicious_files']) {
        echo '<span class="spbc_overall_scan_status_schedule_send_heuristic_suspicious_files">'
            . __('Schedule suspicious files sending', 'security-malware-firewall')
            . '</span> -> ';
    }

    if ($spbc->settings['scanner__auto_cure']) {
        echo '<span class="spbc_overall_scan_status_auto_cure_backup">' . __('Creating a backup', 'security-malware-firewall') . '</span> -> ';
        echo '<span class="spbc_overall_scan_status_auto_cure">' . __('Curing', 'security-malware-firewall') . '</span> -> ';
    }



    if ($spbc->settings['scanner__outbound_links']) {
        echo '<span class="spbc_overall_scan_status_outbound_links">' . __('Scanning links', 'security-malware-firewall') . '</span> -> ';
    }

    if ($spbc->settings['scanner__frontend_analysis']) {
        echo '<span class="spbc_overall_scan_status_frontend_analysis">' . __('Scanning public pages', 'security-malware-firewall') . '</span> -> ';
    }

    if ($spbc->settings['scanner__important_files_listing']) {
        echo '<span class="spbc_overall_scan_status_important_files_listing">' . __('Scanning for publicly accessible files', 'security-malware-firewall') . '</span> -> ';
    }

    echo '<span class="spbc_overall_scan_status_send_results">' . __('Sending results', 'security-malware-firewall') . '</span>'

         . '</div>';
    echo '<div id="spbc_scaner_progress_bar" class="spbc_hide" style="height: 22px;"><div class="spbc_progressbar_counter"><span></span></div></div>';

    // Log style output for scanned files

    echo '<div id="spbc_dialog" title="File output" style="overflow: initial;"></div>';


    echo '<div id="spbc_scan_accordion">';
    if ( ! empty($spbc->data['scanner']['last_scan'])) {
        spbc_field_scanner__show_accordion(true);
    }
    echo '</div>';

    echo '<br>';
    echo spbc_field_scanner__log();

    // Scan results log
    if ( ! empty($spbc->data['scanner']['last_scan'])) {
        echo ScanningLogFacade::render();
    }

    echo '<br>';

    echo spbc_bulk_actions_description();

    echo '</div>';
}

add_action('wp_ajax_spbc_analysyis_files_stats__get_html', 'spbc__analysyis_files_stats__get_html');
/**
 * Retrieves HTML code block to layout files counters stats in the analysis accordion.
 * @return string
 */
function spbc__analysyis_files_stats__get_html()
{
    spbc_check_ajax_referer('spbc_secret_nonce', 'security');

    $out = '
    <div id="spbc_analysis_files_stats" style="display: block; padding-bottom: 5px">
        <p>%s</p>
        <p>%s: %d / %d / %d</p>
        %s
    </div>
    ';
    $caption = __('List of files sent for the Cloud analysis, it takes up to 10 minutes to process a file.', 'security-malware-firewall');
    $files_stats_string = __('Files sent/checked/unchecked', 'security-malware-firewall');
    $data = spbc__analysyis_files_stats__get_data();
    $last_updated_chunk = __('Files statuses updates every', 'security-malware-firewall')  . ' ' . SPBC_PSCAN_UPDATE_FILES_STATUS_PERIOD . ' seconds';
    $last_updated_chunk .= '<span id="spbc_last_update_time">';
    $last_updated_chunk .= $data['last_updated'] && is_int($data['last_updated'])
        ? ', ' . __('last update time', 'security-malware-firewall') . ': ' . date("M d Y H:i:s", $data['last_updated'])
        : '.';
    $last_updated_chunk .= '</span>';
    $out = sprintf(
        $out,
        $caption,
        $files_stats_string,
        $data['files_sent_count'],
        $data['files_checked_count'],
        $data['files_unchecked_count'],
        $last_updated_chunk
    );

    if (Post::getString('sub_action') === 'give_me_html') {
        echo $out;
        exit;
    }

    return $out;
}

/**
 * Retrieves the data for analysis stats block.
 * @return array
 */
function spbc__analysyis_files_stats__get_data()
{
    global $wpdb, $spbc;
    $out = array(
            'files_sent_count' => 'N/D',
            'files_checked_count' => 'N/D',
            'files_unchecked_count' => 'N/D',
            'last_updated' => false,
    );
    $files_sent_count = $wpdb->get_var('
        SELECT COUNT(*) from ' . SPBC_TBL_SCAN_FILES . '
        WHERE last_sent IS NOT NULL;
    ');
    $files_checked_count = $wpdb->get_var('
        SELECT COUNT(*) from ' . SPBC_TBL_SCAN_FILES . '
        WHERE last_sent IS NOT NULL AND pscan_processing_status = \'DONE\';
    ');
    $files_unchecked_count = !is_null($files_sent_count) && !is_null($files_checked_count)
        ? (int)$files_sent_count - (int)$files_checked_count
        : false;
    $last_updated = \CleantalkSP\SpbctWP\Cron::getTask('scanner_update_pscan_files_status');
    //next call checking is a trick - the last_call key does not work properly
    $last_updated = $last_updated && !empty($last_updated['next_call'])
        ? $last_updated['next_call'] - $last_updated['period'] + $spbc->data['site_utc_offset_in_seconds']
        : false;
    $out['files_sent_count'] = !is_null($files_sent_count) ? (int)$files_sent_count : $out['files_sent_count'];
    $out['files_checked_count'] = !is_null($files_checked_count) ? (int)$files_checked_count : $out['files_checked_count'];
    $out['files_unchecked_count'] = $files_unchecked_count ? : $out['files_unchecked_count'];
    $out['last_updated'] = $last_updated ? : $out['last_updated'];
    return $out;
}

function spbc_field_scanner__show_accordion($direct_call = false)
{
    if ( ! $direct_call) {
        spbc_check_ajax_referer('spbc_secret_nonce', 'security');
    }

    global $spbc;

    //analysis log description
    $dashboard_link = ! $spbc->data['wl_mode_enabled'] ? sprintf(
        __(' at the %s Security Dashboard %s.', 'security-malware-firewall'),
        '<a href="https://cleantalk.org/my/support/open?subject=Cloud%20Malware%20scanner,%20results%20question" target="_blank">',
        '</a>'
    ) : '';
    $analysis_log_description = spbc__analysyis_files_stats__get_html() .
        '<div id="spbc_notice_cloud_analysis_feedback" class="notice is-dismissible">' .
        '<p>' .
        '<img src="' . SPBC_PATH . '/images/att_triangle.png" alt="attention" style="margin-bottom:-1px">' .
        ' ' .
        __('If you feel that the Cloud verdict is incorrect, please click the link "Copy file info" near the file name and contact us', 'security-malware-firewall') . ' ' .
        $dashboard_link .
        '</p>' .
        '</div>';
    if ($spbc->data['display_scanner_warnings']['analysis'] && !$spbc->data['wl_mode_enabled']) {
        $analysis_log_description .= spbc__get_accordion_tab_info_block_html('analysis');
    }

    //critical description
    $critical_description = __('These files contain known vulnerabilities. Immediately cure, quarantine or delete the files!', 'security-malware-firewall');
    if ($spbc->data['display_scanner_warnings']['critical'] && !$spbc->data['wl_mode_enabled']) {
        $critical_description .= spbc__get_accordion_tab_info_block_html('critical');
    }
    if ($spbc->settings['scanner__schedule_send_heuristic_suspicious_files'] ) {
        $scheduled_count = count(spbc_get_list_of_scheduled_suspicious_files_to_send());
        if ( $scheduled_count > 0 ) {
            $critical_description .= Escape::escKsesPreset(spbct_get_automatic_files_send_notice_html($scheduled_count), 'spbc_settings__notice_autosend');
        }
    }

    $suspicious_description = __('These files may not contain malicious code, but they use very dangerous PHP functions and constructions! Take a look at files code or send it to the cloud for analyzing.', 'security-malware-firewall');

    //unknown files description
    $unknown_files_description = __('These files do not include known malware signatures or dangerous code. In same time these files do not belong to the WordPress core or any plugin, theme which are hosted on wordpress.org.', 'security-malware-firewall')
        . ' '
        . __('To disable this list deactivate the', 'security-malware-firewall')
        . ' <i>'
        . '"' . __('List unknown files', 'security-malware-firewall') . '"'
        . '</i> '
        . __('option', 'security-malware-firewall')
        . ' '
        . '<a href="options-general.php?page=spbc&spbc_tab=settings_general#spbc_setting_scanner__list_unknown">' . __('here', 'security-malware-firewall') . '</a>.';
    $unknown_files_description .= $spbc->data['wl_mode_enabled'] ? '' : spbc__get_accordion_tab_info_block_html('unknown');

    //cure log description
    $cure_log_description = sprintf(
        '<div> %s <a href="/wp-admin/options-general.php?page=spbc&spbc_tab=backups">%s</a><br><ul><li>%s</li><li>%s</li></ul></div>',
        __('These files were automatically cured. You can see backups and restore files on the ', 'security-malware-firewall'),
        __('Backups tab:', 'security-malware-firewall'),
        __('CURED - the file was automatically cured.', 'security-malware-firewall'),
        __('FAILED, PARTIALLY CURED - errors occurred when curing the file; see the \'Threats uncured\' column.', 'security-malware-firewall')
    );

    //set descriptions
    $tables_files = array(
        'critical'     => $critical_description,
        'suspicious'   => $suspicious_description,
        'approved'     => __('Manually approved files list.', 'security-malware-firewall'),
        'quarantined'  => __('Punished files.', 'security-malware-firewall'),
        'analysis_log' => $analysis_log_description,
        'cure_log'     => $cure_log_description,
        'db_trigger'   => DBTriggerView::getScannerTabDescription(),
        'skipped'     => __('List of files that were not checked by the scanner.', 'security-malware-firewall'),
    );

    $tables_files['skipped'] .= spbc__get_accordion_tab_info_block_html('skipped');

    if (!$spbc->data['wl_mode_enabled']) {
        $tables_files['suspicious'] .= spbc__get_accordion_tab_info_block_html('suspicious');
    }

    if ($spbc->settings['scanner__list_unknown']) {
        $tables_files['unknown'] = $unknown_files_description;
    }

    if ($spbc->settings['scanner__os_cron_analysis']) {
        $tables_files['oscron'] = Scanner\OSCron\View\OSCronLocale::getInstance()->settings__accordion_tab_description;
    }

    if ($spbc->settings['scanner__list_approved_by_cleantalk']) {
        $company_name = $spbc->default_data['wl_company_name'];
        if ($spbc->data["wl_mode_enabled"]) {
            $company_name = $spbc->data["wl_company_name"];
        }
        $tables_files['approved_by_cloud'] = __('Approved by ' . $company_name . ' Team or Cloud files list. To disable this list view, please disable the `Show approved by ' . $company_name . ' Cloud` option.', 'security-malware-firewall');
    }

    if ($spbc->settings['scanner__outbound_links']) {
        $tables_files['outbound_links'] = __('Found outgoing links from this website and websites the links are leading to.', 'security-malware-firewall');
        $tables_files['outbound_links'] .= spbc__get_accordion_tab_info_block_html('outbound_links');
    }

    if ($spbc->settings['scanner__frontend_analysis']) {
        $tables_files['frontend_malware'] = __('Malware on public pages found', 'security-malware-firewall');
    }

    if ($spbc->settings['scanner__frontend_analysis']) {
        $tables_files['frontend_scan_results_approved'] = __('Public pages that approved by user', 'security-malware-firewall');
    }

    if ($spbc->settings['scanner__important_files_listing']) {
        $tables_files['files_listing'] = __('Publicly accessible important files found', 'security-malware-firewall');
    }

    if (!empty($spbc->data['unsafe_permissions']['files']) || !empty($spbc->data['unsafe_permissions']['dirs'])) {
        $tables_files['unsafe_permissions'] = __('Permissions for files and directories from the list are unsafe. We recommend change it to 755 for each directory and 644 for each file from the list.', 'security-malware-firewall');
    }

    $accordions_order = array(
        'files' => array(
            'category_description' => __('Files scan results', 'security-malware-firewall'),
            'types' => array(
                'critical',
                'suspicious',
                'approved',
                'approved_by_cloud',
                'quarantined',
                'cure_log',
                'unknown',
                'skipped',
                'analysis_log',
                'unsafe_permissions',
                'files_listing',
            ),
        ),
        'os_cron_analysis' => array(
            'category_description' => __('OS Cron Analysis', 'security-malware-firewall'),
            'types' => array(
                'oscron',
                'oscron_quarantined',
                'oscron_approved',
            ),
            'display' => (bool) $spbc->settings['scanner__os_cron_analysis']
        ),
        'db_trigger_analysis' => array(
            'category_description' => __('DB Trigger Analysis', 'security-malware-firewall'),
            'types' => array(
                'db_trigger',
            ),
            'display' => (bool) $spbc->settings['scanner__db_trigger_analysis']
        ),
        'pages' => array(
            'category_description' => __('Pages scan results', 'security-malware-firewall'),
            'types' => array(
                'outbound_links',
                'frontend_malware',
                'frontend_scan_results_approved',
            ),
            'display' => (bool) $spbc->settings['scanner__frontend_analysis']
        ),
    );

    foreach ($accordions_order as $_category => $data) {
        if ( isset($data['display']) && ! $data['display'] ) {
            continue;
        }
        echo '<div refresh_control_group="' . $_category . '" class="spbc_accordion_category_wrapper">';
        echo '<h4 class="spbc_accordion_category_header">' .  $data['category_description'] . '</h4>';
        foreach ($data['types'] as $type_name) {
            if ( !isset($tables_files[$type_name]) ) {
                continue;
            }
            //todo spbc_scanner_get_files_by_category() has internal SQL error on 'approved' column
            if ( $type_name !== 'frontend_malware' && $type_name !== 'frontend_scan_results_approved' ) {
                if (
                    empty(
                        ScanResultsTableActions::getFilesByCategory($type_name, true)
                    )
                ) {
                    continue;
                }
            }
            $description = $tables_files[$type_name];
            $args = spbc_list_table__get_args_by_type($type_name);

            $args['id'] = 'spbc_tbl__scanner_' . $type_name;
            $args['type'] = $type_name;

            $table = new ListTable($args);

            $table->getData();

            $danger_dot = '';
            if (
                ($type_name === 'critical' && $spbc->data['display_scanner_warnings']['critical'])
                || ($type_name === 'frontend_malware' && $spbc->data['display_scanner_warnings']['frontend'])
                || ($type_name === 'analysis_log' && $spbc->data['display_scanner_warnings']['analysis'])
                || ($type_name === 'oscron' && $spbc->data['display_scanner_warnings']['oscron'])
                || ($type_name === 'db_trigger' && $spbc->data['display_scanner_warnings']['db_triggers'])
            ) {
                $danger_dot = '<span class="red_dot"></span>';
            }

            // Pass output if empty and said to do so
            if ( $args['if_empty_items'] !== false || $table->items_total !== 0 ) {
                echo '<div refresh_control_tab="' . $type_name . '">';
                echo '<h3><a href="#">' . ucwords(str_replace('_', ' ', $type_name))
                    . ' (<span class="spbc_bad_type_count '
                    . $type_name . '_counter">' . $table->items_total . '</span>)</a>'
                    . $danger_dot . '</h3>';
                echo '<div id="spbc_scan_accordion_tab_' . $type_name . '">';

                echo '<p class="spbc_hint">'
                    . $description
                    . '</p>';
                $table->display();
                echo '</div>';
                echo "</div>";
            }
        }
        echo '</div>';
    }

    if ($direct_call) {
        return;
    } else {
        die('');
    }
}

/**
 * Return arguments for ListTable::__constructor()
 *
 * @param string $table_type
 *
 * @return array
 */
function spbc_list_table__get_args_by_type($table_type)
{
    global $spbc;

    // Default arguments for file tables
    $accordion_default_args = array(
        'sql'            => array(
            'add_col'   => array('fast_hash', 'last_sent', 'real_full_hash', 'severity', 'difference', 'status', 'source_type', 'source'),
            'table'     => SPBC_TBL_SCAN_FILES,
            'offset'    => 0,
            'limit'     => SPBC_LAST_ACTIONS_TO_VIEW,
            'get_array' => false,
        ),
        'if_empty_items' => 'NOPE',
        'columns'        => array(
            'cb'    => array('heading' => '<input type=checkbox>', 'class' => 'check-column',),
            'path'  => array('heading' => 'Path', 'primary' => true,),
            'size'  => array('heading' => 'Size, bytes',),
            'perms' => array('heading' => 'Permissions',),
            'mtime' => array('heading' => 'Last Modified',),
        ),
        'actions'        => array(
            'view'   => array('name' => 'View', 'handler' => 'spbcScannerButtonFileViewEvent(this);',),
        ),
        'bulk_actions'   => array(
        ),
        'sortable'       => array('path', 'size', 'perms', 'mtime',),
        'pagination'     => array(
            'page'     => 1,
            'per_page' => SPBC_LAST_ACTIONS_TO_VIEW,
        ),
    );

    switch ($table_type) {
        case 'links':
            $domain = Post::getString('domain');
            $domain = esc_sql($domain);
            $args = array(
                'id'                => 'spbc_tbl__scanner__outbound_links',
                'sql'               => array(
                    'table'     => SPBC_TBL_SCAN_LINKS,
                    'get_array' => false,
                    'where'     => ' WHERE domain = "' . $domain . '"',
                ),
                'order_by'          => array('domain' => 'asc'),
                'html_before'       =>
                    sprintf(__('Links for <b>%s</b> domain.', 'security-malware-firewall'), esc_html($domain)) . ' '
                    . sprintf(__('%sSee all domains%s', 'security-malware-firewall'), '<a href="javascript://" onclick="spbcScannerSwitchTable(this, \'outbound_links\');">', '</a>')
                    . '<br /><br />',
                'func_data_prepare' => 'spbc_field_scanner__prepare_data__links',
                'if_empty_items'    => '<p class="spbc_hint">' . __('No links found.', 'security-malware-firewall') . '</p>',
                'columns'           => array(
                    'link_id'     => array(
                        'heading' => __('Number', 'security-malware-firewall'),
                        'class'   => ' tbl-width--50px',
                        'primary' => true
                    ),
                    'link'        => array('heading' => __('Link', 'security-malware-firewall')),
                    'page_url'    => array('heading' => __('Post Page', 'security-malware-firewall'),),
                    'link_text'   => array('heading' => __('Link Text', 'security-malware-firewall'),),
                ),
                'sortable'        => array('link', 'page_url'),
            );
            break;

        case 'cure_backups':
            $args = array(
                'id'              => 'spbc_tbl__scanner_cure_backups',
                'sql'             => array(
                    'table'     => SPBC_TBL_BACKUPS,
                    'offset'    => 0,
                    'limit'     => SPBC_LAST_ACTIONS_TO_VIEW,
                    'get_array' => false,
                    'where'     => ' RIGHT JOIN ' . SPBC_TBL_BACKUPED_FILES . ' ON ' . SPBC_TBL_BACKUPS . '.backup_id = ' . SPBC_TBL_BACKUPED_FILES . '.backup_id',
                ),
                'func_data_total' => [BackupsActions::class, 'countBackups'],
                'func_data_get'   => 'spbc_field_backups__get_data',
                'if_empty_items'  => '<p class="spbc_hint">' . __('No backups found', 'security-malware-firewall') . '</p>',
                'columns'         => array(
                    'backup_id' => array('heading' => 'Backup ID', 'primary' => true,),
                    'datetime'  => array('heading' => 'Date',),
                    'type'      => array('heading' => 'Type',),
                    'real_path' => array('heading' => 'File',),
                ),
                'actions'         => array(
                    'rollback' => array('name' => 'Rollback', 'handler' => 'spbcActionBackupsRollback(this);',),
                    'delete'   => array('name' => 'Delete', 'handler' => 'spbcActionBackupsDelete(this);',),
                ),
                'sortable'        => array('backup_id', 'datetime',),
                'pagination'      => array(
                    'page'     => 1,
                    'per_page' => SPBC_LAST_ACTIONS_TO_VIEW,
                ),
                'order_by'        => array('datetime' => 'desc'),
            );
            break;

        case 'security_logs':
            $args = array(
                'id'                => 'spbc_tbl__secuirty_logs',
                'sql'               => array(
                    'add_col'   => array('id', 'page_time', 'role'),
                    'table'     => SPBC_TBL_SECURITY_LOG,
                    'where'     => (SPBC_WPMS ? ' WHERE blog_id = ' . get_current_blog_id() : ''),
                    'offset'    => 0,
                    'limit'     => SPBC_LAST_ACTIONS_TO_VIEW,
                    'get_array' => false,
                ),
                'order_by'          => array('datetime' => 'desc'),
                'func_data_prepare' => 'spbc_field_security_logs__prepare_data',
                'if_empty_items'    => '<p class="spbc_hint">' . __("0 brute-force attacks have been made.", 'security-malware-firewall') . '</p>',
                'columns'           => array(
                    'cb'         => array('heading' => '<input type=checkbox>', 'class' => 'check-column',),
                    'user_login' => array('heading' => 'User', 'primary' => true,),
                    'auth_ip'    => array('heading' => 'IP, Location, Hostname',),
                    'datetime'   => array('heading' => 'Date',),
                    'event'      => array('heading' => 'Action (hits)',),
                    'page'       => array('heading' => 'Page',),
                ),
                'sortable'          => array('user_login', 'datetime'),
                'pagination'        => array(
                    'page'     => 1,
                    'per_page' => SPBC_LAST_ACTIONS_TO_VIEW,
                ),
                'bulk_actions'   => array(
                    'allow' => array('name' => 'Allow',),
                    'ban' => array('name' => 'Ban',),
                ),
                'bulk_actions_all' => false,
            );
            break;

        case 'critical':
            $args = array_replace_recursive(
                $accordion_default_args,
                array(
                    'columns'           => array(
                        'cb'         => array('heading' => '<input type=checkbox>', 'class' => 'check-column',  'width_percent' => 2),
                        'path'       => array('heading' => 'Path', 'primary' => true, 'width_percent' => 38),
                        'size'       => array('heading' => 'Size, bytes', 'width_percent' => 7),
                        'perms'      => array('heading' => 'Permissions', 'width_percent' => 7),
                        'weak_spots' => array('heading' => 'Detected', 'width_percent' => 18),
                        'mtime'      => array('heading' => 'Last Modified', 'width_percent' => 10),
                        'status'      => array('heading' => 'Analysis verdict', 'width_percent' => 15),
                    ),
                    'func_data_prepare' => 'spbc_field_scanner__prepare_data__files',
                    'if_empty_items'    => '<p class="spbc_hint">' . __('No threats are found or all the files have been sent for analysis', 'security-malware-firewall') . '</p>',
                    'actions'           => array(
                        'approve'    => array('name' => 'Approve', 'tip' => 'Approved file will not be scanned again'),
                        'quarantine' => array('name' => 'Quarantine', 'tip' => 'Place file to quarantine'),
                        'replace'    => array(
                            'name' => 'Replace with Original',
                            'tip'  => 'Restore the initial state of file'
                        ),
                        'delete'     => array('name' => 'Delete',),
                        'view'       => array(
                            'name'    => 'View',
                            'handler' => 'spbcScannerButtonFileViewEvent(this);',
                        ),
                        'view_bad'   => array(
                            'name'    => 'View Suspicious Code',
                            'handler' => 'spbcScannerButtonFileViewBadEvent(this);',
                        ),
                        'cure'     => array(
                            'name'    => 'Cure',
                            'handler' => 'spbcScannerButtonCureFileAjaxHandler(this);',
                        ),
                    ),
                    'bulk_actions'      => array(
                        'approve'    => array('name' => 'Approve',),
                        'delete'     => array('name' => 'Delete',),
                        'replace'    => array('name' => 'Replace with original',),
                        'quarantine' => array('name' => 'Quarantine',),
                        'cure' => array('name' => 'Cure',),
                    ),
                    'sql'               => array(
                        'where' => Scanner\Helper::getSQLWhereAddictionForTableOfCategory('critical'),
                    ),
                    'order_by'          => array('path' => 'asc'),
                )
            );
            $args['sql']['add_col'][] = 'pscan_status';
            $args['sql']['add_col'][] = 'pscan_pending_queue';
            $args['sql']['add_col'][] = 'full_hash';
            break;

        case 'suspicious':
            $args = array_replace_recursive(
                $accordion_default_args,
                array(
                    'columns'           => array(
                        'cb'         => array('heading' => '<input type=checkbox>', 'class' => 'check-column',  'width_percent' => 2),
                        'path'       => array('heading' => 'Path', 'primary' => true, 'width_percent' => 38),
                        'size'       => array('heading' => 'Size, bytes', 'width_percent' => 7),
                        'perms'      => array('heading' => 'Permissions', 'width_percent' => 7),
                        'weak_spots' => array('heading' => 'Detected', 'width_percent' => 18),
                        'mtime'      => array('heading' => 'Last Modified', 'width_percent' => 10),
                        'status'      => array('heading' => 'Analysis verdict', 'width_percent' => 15),
                    ),
                    'func_data_prepare' => 'spbc_field_scanner__prepare_data__files',
                    'if_empty_items'    => false,
                    'actions'           => array(
                        'send'       => array(
                            'name' => 'Send for Analysis',
                            'tip'  => 'Send file to the CleanTalk Cloud for analysis'
                        ),
                        'approve'    => array('name' => 'Approve', 'tip' => 'Approved file will not be scanned again'),
                        'quarantine' => array('name' => 'Quarantine', 'tip' => 'Place file to quarantine'),
                        'replace'    => array(
                            'name' => 'Replace with Original',
                            'tip'  => 'Restore the initial state of file'
                        ),
                        'delete'     => array('name' => 'Delete',),
                        'view'       => array(
                            'name'    => 'View',
                            'handler' => 'spbcScannerButtonFileViewEvent(this);',
                        ),
                        'view_bad'   => array(
                            'name'    => 'View Suspicious Code',
                            'handler' => 'spbcScannerButtonFileViewBadEvent(this);',
                        ),
                    ),
                    'bulk_actions'      => array(
                        'send'       => array('name' => 'Send for Analysis',),
                        'approve'    => array('name' => 'Approve',),
                        'delete'     => array('name' => 'Delete',),
                        'replace'    => array('name' => 'Replace with original',),
                        'quarantine' => array('name' => 'Quarantine',),
                    ),
                    'sql'               => array(
                        'where' => Scanner\Helper::getSQLWhereAddictionForTableOfCategory('suspicious'),
                    ),
                    'order_by'          => array('path' => 'asc'),
                )
            );
            break;

        case 'analysis_log':
            $args                 = array_replace_recursive(
                $accordion_default_args,
                array(
                    'func_data_prepare' => 'spbc_field_scanner__prepare_data__analysis_log',
                    'if_empty_items'    => false,
                    'sql'               => array(
                        'add_col' => array(
                            'pscan_processing_status',
                            'fast_hash',
                            'pscan_status',
                            'pscan_pending_queue',
                            'pscan_estimated_execution_time'
                        ),
                        'where' => Scanner\Helper::getSQLWhereAddictionForTableOfCategory('analysis_log'),
                    ),
                    'order_by'          => array('pscan_status' => 'desc'),
                    'sortable'          => array('path', 'last_sent', 'pscan_status', 'pscan_estimated_execution_time'),
                )
            );

            $args['columns']      = array(
                'cb'                => array('heading' => '<input type=checkbox>', 'class' => 'check-column', 'width_percent' => 2),
                'path'              => array('heading' => 'Path', 'primary' => true, 'width_percent' => 28),
                'detected_at'       => array('heading' => 'Detected at', 'width_percent' => 15),
                'last_sent'         => array('heading' => 'Sent for analysis at', 'width_percent' => 15),
                'pscan_status'      => array('heading' => 'Cloud verdict', 'width_percent' => 10),
                'pscan_estimated_execution_time' => array('heading' => 'Estimated time', 'width_percent' => 10),
                //'analysis_comment'  => array('heading' => 'Comment', 'width_percent' => 20),
            );

            $args['actions']      = array(
                'check_analysis_status' => array('name' => 'Refresh the analysis status'),
                'copy_file_info' => array('name' => 'Copy file info'),
                'view'       => array(
                    'name'    => 'View',
                    'handler' => 'spbcScannerButtonFileViewEvent(this);',
                ),
                'delete' => array('name' => 'Delete from log', 'handler' => 'spbcScannerAnalysisLogDeleteFromLog(this);'),
            );
            $args['bulk_actions'] = array(
                'check_analysis_status' => array('name' => 'Refresh the analysis status',),
                'delete_from_analysis_log' => array('name' => 'Delete from log', 'handler' => 'spbcScannerAnalysisLogDeleteFromLog(this);'),
            );
            break;

        case 'unknown':
            $args = array_replace_recursive(
                $accordion_default_args,
                array(
                    'func_data_prepare' => 'spbc_field_scanner__prepare_data__files',
                    'columns'           => array(
                        'cb'         => array('heading' => '<input type=checkbox>', 'class' => 'check-column',  'width_percent' => 2),
                        'path'       => array('heading' => 'Path', 'primary' => true, 'width_percent' => 39),
                        'size'       => array('heading' => 'Size, bytes', 'width_percent' => 13),
                        'perms'      => array('heading' => 'Permissions', 'width_percent' => 13),
                        'mtime'      => array('heading' => 'Last Modified', 'width_percent' => 13),
                    ),
                    'if_empty_items'    => false,
                    'actions'           => array(
                        'delete' => array('name' => 'Delete',),
                        'approve' => array('name' => 'Approve',),
                        'view'    => array('name' => 'View',),
                    ),
                    'bulk_actions'      => array(
                        'delete'  => array('name' => 'Delete',),
                        'approve' => array('name' => 'Approve',),
                    ),
                    'sql'               => array(
                        'where' => Scanner\Helper::getSQLWhereAddictionForTableOfCategory('unknown'),
                    ),
                    'order_by'          => array('path' => 'asc'),
                )
            );
            $args['actions']['send'] = array('name' => 'Send for Analysis',);
            break;

        case 'oscron':
            $args = array(
                'func_data_total'   => 'spbc_scanner_oscron_count_found',
                'func_data_get'     => 'spbc_scanner_oscron_get_scanned',
                'func_data_prepare' => 'spbc_scanner_oscron_prepare_data',
                'if_empty_items'    => '<div class="notice notice-info spbc-icon-info" style="padding: 10px; margin: 10px 0px;">'
                                       . __('Crontab not found in the server environment or is unavailable to read/write.', 'security-malware-firewall')
                                       . '</div>',
                'columns'           => array(
                    'id'            => array( 'heading' => 'id', 'width_percent' => 10 ),
                    'status'        => array( 'heading' => 'Status', 'primary' => true, 'width_percent' => 15 ),
                    'detected'      => array( 'heading' => 'Detected', 'width_percent' => 15 ),
                    'verdict'       => array( 'heading' => 'Verdict', 'width_percent' => 15 ),
                    'command'       => array( 'heading' => 'Command', 'width_percent' => 35 ),
                    'repeats'       => array( 'heading' => 'Repeats on', 'width_percent' => 5 ),
                    'line_number'   => array( 'heading' => 'Line number', 'width_percent' => 5 ),
                ),
                'actions'           => array(
                    'disable_oscron_task' => array( 'name' => Scanner\OSCron\View\OSCronLocale::getInstance()->action__disable_task, ),
                    'enable_oscron_task'  => array( 'name' => Scanner\OSCron\View\OSCronLocale::getInstance()->action__enable_task, ),
                ),
            );
            break;

        case 'db_trigger':
            $args = array(
                'func_data_total'   => 'spbc_scanner_db_trigger_count_found',
                'func_data_get'     => 'spbc_scanner_db_trigger_get_scanned',
                'func_data_prepare' => 'spbc_scanner_db_trigger_prepare_data',
                'if_empty_items'    => '<div class="notice notice-info spbc-icon-info" style="padding: 10px; margin: 10px 0px;">'
                                       . __('DB Trigger not found in the server environment or is unavailable to read/write.', 'security-malware-firewall')
                                       . '</div>',
                'columns'           => array(
                    'cb'         => array('heading' => '<input type=checkbox>', 'class' => 'check-column',  'width_percent' => 2),
                    'about_trigger'   => array( 'heading' => 'About trigger', 'width_percent' => 30 ), // name, table, time, action
                    'code'            => array( 'heading' => 'Code', 'width_percent' => 43 ),
                    'signature'       => array( 'heading' => 'Signature', 'width_percent' => 10 ),
                    'analysis_status' => array( 'heading' => 'Verdict', 'width_percent' => 15 ),
                ),
                'actions'           => array(
                    'delete'   => array('name' => 'Delete',),
                ),
                'bulk_actions'      => array(
                    'delete'  => array('name' => 'Delete',),
                ),
            );
            break;

        case 'approved':
            $args = array_replace_recursive(
                $accordion_default_args,
                array(
                    'func_data_prepare' => 'spbc_field_scanner__prepare_data__files',
                    'columns'           => array(
                        'cb'         => array('heading' => '<input type=checkbox>', 'class' => 'check-column',  'width_percent' => 2),
                        'path'       => array('heading' => 'Path', 'primary' => true, 'width_percent' => 40),
                        'weak_spots' => array('heading' => 'Detected', 'width_percent' => 20),
                        'size'       => array('heading' => 'Size, bytes', 'width_percent' => 7),
                        'perms'      => array('heading' => 'Permissions', 'width_percent' => 7),
                        'mtime'      => array('heading' => 'Last Modified', 'width_percent' => 7),
                        'status'     => array('heading' => 'Approved by', 'width_percent' => 15),
                    ),
                    'if_empty_items'    => false,
                    'actions'           => array(
                        'disapprove' => array('name' => 'Disapprove',),
                    ),
                    'bulk_actions'      => array(
                        'disapprove' => array('name' => 'Disapprove',),
                    ),
                    'sql'               => array(
                        'where' => Scanner\Helper::getSQLWhereAddictionForTableOfCategory('approved'),
                    ),
                    'order_by'          => array('path' => 'asc'),
                )
            );
            break;

        case 'approved_by_cloud':
            $args = array_replace_recursive(
                $accordion_default_args,
                array(
                    'func_data_prepare' => 'spbc_field_scanner__prepare_data__files',
                    'columns'           => array(
                        'cb'         => array('heading' => '<input type=checkbox>', 'class' => 'check-column',  'width_percent' => 2),
                        'path'       => array('heading' => 'Path', 'primary' => true, 'width_percent' => 40),
                        'size'       => array('heading' => 'Size, bytes', 'width_percent' => 7),
                        'perms'      => array('heading' => 'Permissions', 'width_percent' => 7),
                        'mtime'      => array('heading' => 'Last Modified', 'width_percent' => 7),
                        'status'     => array('heading' => 'Approved by', 'width_percent' => 15),
                    ),
                    'if_empty_items' => false,
                    'sql'            => array(
                        'where' => Scanner\Helper::getSQLWhereAddictionForTableOfCategory('approved_by_cloud'),
                    ),
                    'order_by'       => array('path' => 'asc'),
                )
            );
            break;

        case 'quarantined':
            $args = array_replace_recursive(
                $accordion_default_args,
                array(
                    'func_data_prepare' => 'spbc_field_scanner__prepare_data__files_quarantine',
                    'columns'           => array(
                        'cb'             => array('heading' => '<input type=checkbox>', 'class' => 'check-column',),
                        'path'           => array('heading' => 'Path', 'primary' => true,),
                        'previous_state' => array('heading' => 'Status',),
                        'severity'       => array('heading' => 'Severity',),
                        'q_time'         => array('heading' => 'Quarantine time',),
                        'size'           => array('heading' => 'Size',),
                    ),
                    'if_empty_items'    => false,
                    'actions'           => array(
                        'restore'  => array('name' => 'Restore',),
                        'delete_quarantine' => array('name' => 'Delete from quarantine',),
                        'view'     => array(
                            'name'    => 'View',
                            'handler' => 'spbcScannerButtonFileViewEvent(this);',
                        ),
                        'download' => array(
                            'name'   => 'Download',
                            'type'   => 'link',
                            'local'  => true,
                            'uid'    => true,
                            'target' => '_blank',
                            'href'   => '?plugin_name=security&spbc_remote_call_token=' . md5($spbc->settings['spbc_key']) . '&spbc_remote_call_action=download__quarantine_file&file_id=',
                        ),
                    ),
                    'bulk_actions'      => array(
                        'restore' => array('name' => 'Restore',),
                        'delete_quarantine' => array('name' => 'Delete from quarantine',),
                    ),
                    'sql'               => array(
                        'add_col' => array_merge($accordion_default_args['sql']['add_col'], array(
                            'previous_state',
                            'q_path',
                            'q_time',
                        )),
                        'where'   => Scanner\Helper::getSQLWhereAddictionForTableOfCategory('quarantined'),
                    ),
                    'sortable'          => array('path', 'previous_state', 'severity', 'q_time', 'size',),
                    'order_by'          => array('path' => 'asc'),
                )
            );
            break;

        case 'outbound_links':
            $args = array(
                'id'                => 'spbc_tbl__scanner__outbound_links',
                'actions'           => array(
                    'edit_post'  => array(
                        'name'           => 'Edit',
                        'type'           => 'link',
                        'local'          => true,
                        'edit_post_link' => true,
                        'target'         => '_blank',
                    ),
                    'show_links' => array(
                        'name'    => 'Show links',
                        'handler' => 'spbcScannerSwitchTable(this, "links");'
                    ),
                ),
                'order_by'          => array('domain' => 'desc'),
                'func_data_total'   => [LinksActions::class, 'countScannedDomains'],
                'func_data_get'     => [LinksActions::class, 'getScannedDomains'],
                'func_data_prepare' => 'spbc_field_scanner__prepare_data__domains',
                'if_empty_items'    => '<p class="spbc_hint">' . __('No links found.', 'security-malware-firewall') . '</p>',
                'columns'           => array(
                    'num'         => array(
                        'heading' => __('Number', 'security-malware-firewall'),
                        'class'   => ' tbl-width--50px',
                        'primary' => true
                    ),
                    'domain'      => array('heading' => __('Domain', 'security-malware-firewall')),
                    'link_count'  => array(
                        'heading' => __('Links of domain', 'security-malware-firewall'),
                        'hint'    => __('Number of found links to the domain on site.', 'security-malware-firewall'),
                    ),
                ),
                'sortable'          => array('domain', 'link_count'),
                'pagination'        => array(
                    'page'     => 1,
                    'per_page' => SPBC_LAST_ACTIONS_TO_VIEW,
                ),
            );
            break;

        case 'frontend_malware':
            $args = array(
                'id'                => 'spbc_tbl__scanner_frontend_malware',
                'actions'           => array(
                    'view'     => array('name' => 'View', 'handler' => 'spbcScannerButtonPageViewEvent(this);',),
                    'approve_page'  => array('name' => 'Approve Page'),
                ),
                'bulk_actions'      => array(
                    'approve_page' => array('name' => 'Approve',),
                ),
                'sql'               => array(
                    'table'     => SPBC_TBL_SCAN_FRONTEND,
                    'offset'    => 0,
                    'limit'     => 20,
                    'get_array' => false,
                    'where'     => Scanner\Helper::getSQLWhereAddictionForTableOfCategory('frontend_malware'),
                ),
                'func_data_prepare' => 'spbc_field_scanner__prepare_data__frontend',
                'func_data_get' => 'spbc_field_scanner__get_data__frontend_malware',
                'if_empty_items'    => __('No malware found', 'security-malware-firewall'),
                'columns'           => array(
                    'cb'            => array('heading' => '<input type=checkbox>', 'class' => 'check-column',  'width_percent' => 2),
                    'url'            => array('heading' => 'Page', 'primary' => true,   'width_percent' => 38, 'no_code_header' => 'Page'),
                    'dbd_found'      => array('heading' => '<i setting="dbd_found" class="spbc_long_description__show spbc-icon-help-circled"></i>Drive by Download', 'width_percent' => 15, 'no_code_header' => 'Drive by Download'),
                    'redirect_found' => array('heading' => '<i setting="redirect_found" class="spbc_long_description__show spbc-icon-help-circled"></i>Redirects', 'width_percent' => 15, 'no_code_header' => 'Redirects'),
                    'csrf'           => array('heading' => '<i setting="csrf" class="spbc_long_description__show spbc-icon-help-circled"></i>CSRF', 'width_percent' => 15, 'no_code_header' => 'CSRF'),
                    'signature'      => array('heading' => '<i setting="signature" class="spbc_long_description__show spbc-icon-help-circled"></i>Signatures', 'width_percent' => 15, 'no_code_header' => 'Signatures'),
                ),
                'order_by'          => array('url' => 'asc'),
                'sortable'          => array('url', 'dbd_found', 'redirect_found', 'signature', 'csrf'),
                'pagination'        => array(
                    'page'     => 1,
                    'per_page' => SPBC_LAST_ACTIONS_TO_VIEW,
                ),
            );
            break;

        case 'frontend_scan_results_approved':
            $args = array(
                'id'                => 'spbc_tbl__scanner_frontend_malware',
                'actions'           => array(
                    'view'     => array('name' => 'View', 'handler' => 'spbcScannerButtonPageViewEvent(this);',),
                    'disapprove_page'  => array('name' => 'Disapprove page'),
                ),
                'bulk_actions'      => array(
                    'disapprove_page' => array('name' => 'Disapprove',),
                ),
                'sql'               => array(
                    'table'     => SPBC_TBL_SCAN_FRONTEND,
                    'offset'    => 0,
                    'limit'     => 20,
                    'get_array' => false,
                    'where'     => Scanner\Helper::getSQLWhereAddictionForTableOfCategory('frontend_scan_results_approved'),
                ),
                'func_data_prepare' => 'spbc_field_scanner__prepare_data__frontend',
                'func_data_get' => 'spbc_field_scanner__get_data__frontend_approved',
                'if_empty_items'    => false,
                'columns'           => array(
                    'cb'            => array('heading' => '<input type=checkbox>', 'class' => 'check-column',),
                    'url'            => array('heading' => 'Page', 'primary' => true,),
                    'dbd_found'      => array('heading' => '<i setting="dbd_found" class="spbc_long_description__show spbc-icon-help-circled"></i>Drive by Download',),
                    'redirect_found' => array('heading' => '<i setting="redirect_found" class="spbc_long_description__show spbc-icon-help-circled"></i>Redirects',),
                    'csrf'           => array('heading' => '<i setting="csrf" class="spbc_long_description__show spbc-icon-help-circled"></i>CSRF',),
                    'signature'      => array('heading' => '<i setting="signature" class="spbc_long_description__show spbc-icon-help-circled"></i>Signatures',),
                ),
                'order_by'          => array('url' => 'asc'),
                'sortable'          => array('url', 'dbd_found', 'redirect_found', 'signature', 'csrf'),
                'pagination'        => array(
                    'page'     => 1,
                    'per_page' => SPBC_LAST_ACTIONS_TO_VIEW,
                ),
            );
            break;

        case 'files_listing':
            $args = array(
                'id'                => 'spbc_tbl__scanner__files_listing',
                'func_data_total'   => 'spbc_field_scanner__files_listing__get_total',
                'func_data_get'     => 'spbc_field_scanner__files_listing__get_data',
                'func_data_prepare' => 'spbc_field_scanner__files_listing__data_prepare',
                'if_empty_items'    => '<p class="spbc_hint">' . __('No threads are found', 'security-malware-firewall') . '</p>',
                'columns'           => array(
                    'url'  => array('heading' => __('URL', 'security-malware-firewall'), 'primary' => true,),
                    'type' => array('heading' => __('Type', 'security-malware-firewall'),),
                ),
                'pagination'        => array(
                    'page'     => 1,
                    'per_page' => SPBC_LAST_ACTIONS_TO_VIEW,
                ),
                'order_by'          => array('path' => 'asc'),
                'display' => (bool) $spbc->settings['scanner__important_files_listing']
            );
            break;

        case 'unsafe_permissions':
            $args = array(
                'id' => 'spbc_tbl__scanner_scan_unsafe_permissions',
                'actions' => array (),
                'func_data_total'   => 'spbc_scanner__unsafe_permissions_count',
                'func_data_get'     => 'spbc_scanner_unsafe_permissions_data',
                'if_empty_items' => __('All files and directories have the safe permission levels', 'security-malware-firewall'),
                'columns' => array(
                    'path'           => array('heading' => 'Path','primary' => true,),
                    'perms'     => array('heading' => 'Permission',),
                ),
                'order_by'  => array('path' => 'asc'),
                'pagination' => array(
                    'page'     => 1,
                    'per_page' => 20,
                ),
            );
            break;

        case 'cure_log':
            $args = array(
                'id' => 'spbc_tbl__scanner_scan_cure_log',
                'actions' => array (
                    'view'     => array(
                        'name'    => 'View',
                        'handler' => 'spbcScannerButtonFileViewEvent(this);',
                    ),
                    'cure'     => array(
                        'name'    => 'Cure',
                        'handler' => 'spbcScannerButtonCureFileAjaxHandler(this);',
                    ),
                    'restore'     => array(
                        'name'    => 'Restore',
                        'handler' => 'spbcScannerButtonRestoreFromBackupAjaxHandler(this);',
                    ),
                ),
                'bulk_actions'      => array(
                    'cure' => array('name' => 'Cure',),
                    'restore' => array('name' => 'Restore',),
                ),
                'func_data_total'   => 'spbc_scanner__cure_log_get_count_total',
                'func_data_get'     => 'spbc_scanner__get_cure_log_data',
                'func_data_prepare'     => 'spbc_scanner__cure_log_data_prepare',
                'if_empty_items' => __('There are no automatically cured files.', 'security-malware-firewall'),
                'columns' => array(
                    'cb'             => array('heading' => '<input type=checkbox>', 'class' => 'check-column',  'width_percent' => 2),
                    'real_path'      => array('heading' => 'Path','primary' => true, 'width_percent' => 27),
                    'last_cure_date' => array('heading' => 'Cure date', 'width_percent' => 10),
                    'cure_status'          => array('heading' => 'Status', 'width_percent' => 13),
                    'weak_spots_cured'      => array('heading' => 'Threats cured', 'width_percent' => 24),
                    'weak_spots_uncured'      => array('heading' => 'Threats uncured', 'width_percent' => 24),
                ),
                'order_by'  => array('real_path' => 'asc'),
                'pagination' => array(
                    'page'     => 1,
                    'per_page' => 20,
                ),
            );

            $cure_log = new Scanner\CureLog\CureLog();
            if ( !$cure_log->hasFailedCureTries() ) {
                unset($args['columns']['weak_spots_uncured']);
            }
            break;
        case 'skipped':
            $args = array_replace_recursive(
                $accordion_default_args,
                array(
                    'func_data_prepare' => 'spbc_field_scanner__prepare_data__files',
                    'if_empty_items'    => false,
                    'bulk_actions'      => false,
                    'actions'           => array(),
                    'sql'               => array(
                        'where' => Scanner\Helper::getSQLWhereAddictionForTableOfCategory('skipped'),
                    ),
                    'order_by'          => array('path' => 'asc'),
                )
            );
            $args['columns'] = array(
                'path'       => array('heading' => 'Path', 'primary' => true, 'width_percent' => 39),
                'size'       => array('heading' => 'Size, bytes', 'width_percent' => 10),
                'perms'      => array('heading' => 'Permissions', 'width_percent' => 10),
                'mtime'      => array('heading' => 'Last Modified', 'width_percent' => 13),
                'error_msg'  => array('heading' => 'Reason', 'width_percent' => 28),
            );
            $args['sql']['add_col'][] = 'error_msg';
            break;
        default:
            $args = $accordion_default_args;
    }

    $args['type'] = $table_type;

    return $args;
}

function spbc_field_backups__get_data($offset = 0, $limit = 20)
{
    global $wpdb;

    return $wpdb->get_results($wpdb->prepare(
        'SELECT ' . SPBC_TBL_BACKUPS . '.backup_id, ' . SPBC_TBL_BACKUPS . '.datetime, ' . SPBC_TBL_BACKUPS . '.type, ' . SPBC_TBL_BACKUPED_FILES . '.real_path
		FROM ' . SPBC_TBL_BACKUPS . '
		RIGHT JOIN ' . SPBC_TBL_BACKUPED_FILES . ' ON ' . SPBC_TBL_BACKUPS . '.backup_id = ' . SPBC_TBL_BACKUPED_FILES . '.backup_id
		ORDER BY DATETIME DESC
		LIMIT %d, %d;',
        $offset,
        $limit
    ));
}

function spbc_field_backups()
{
    global $spbc;

    echo '<div class="spbc_wrapper_field">';

    $feature_state = $spbc->feature_restrictions->getState($spbc, 'backups');
    if (false === $feature_state->is_active) {
        echo $feature_state->sanitizedReasonOutput();
        echo '</div>';
        return;
    }

    echo '<div id="spbc_scan_accordion">';

    $table = new ListTable(spbc_list_table__get_args_by_type('cure_backups'));
    $table->getData();

    // Pass output if empty and said to do so
    if ($table->items_total !== 0) {
        echo '<h3>'
             . '<a href="#">'
             . ucwords(str_replace('_', ' ', 'cure_backups'))
             . ' <span class="spbc_bad_type_count ' . 'cure_backups' . '_counter">'
             . $table->items_total
             . '</span>'
             . '</a>'
             . '</h3>';
        echo '<div id="spbc_scan_accordion_tab_' . 'cure_backups' . '">';

        echo '<p class="spbc_hint">'
             . __('All backups that were made during the automatic curing procedure', 'security-malware-firewall')
             . '</p>';

        $table->display();

        echo "</div>";
    } else {
        // This is necessary to display 'No backups found'
        $table->display();
    }

    echo '</div>';

    echo '</div>';
}

function spbc_field_debug_drop()
{
    echo '<div class="spbc_wrapper_field">'
         . '<br>'
         . '<input form="debug_drop" type="submit" name="spbc_debug__drop" value="Drop debug data" />'
         . '<div class="spbc_settings_description">If you don\'t what is this just push the button =)</div>'
         . '</div>';
}

add_action('wp_ajax_spbc_debug_users_pass_check_run', 'spbc_debug_users_pass_check_run');
function spbc_debug_users_pass_check_run()
{
    spbc_check_ajax_referer('spbc_secret_nonce', 'security');

    global $wpdb;

    // Reset all records so the worker processes them fresh
    $wpdb->query("UPDATE {$wpdb->base_prefix}spbc_users_pass SET checked = 0");

    // Run the worker directly (synchronous, no cron delay)
    \CleantalkSP\SpbctWP\UsersPassCheckModule\UsersPassCheckCron::worker();

    // Collect stats after the run
    $total   = (int) $wpdb->get_var("SELECT COUNT(*) FROM {$wpdb->base_prefix}spbc_users_pass");
    $checked = (int) $wpdb->get_var("SELECT COALESCE(SUM(checked), 0) FROM {$wpdb->base_prefix}spbc_users_pass");
    $leaked  = (int) $wpdb->get_var("SELECT COALESCE(SUM(leaked), 0) FROM {$wpdb->base_prefix}spbc_users_pass");

    if ($total > 0 && $checked === 0) {
        wp_send_json_error('pwnedpasswords.com API unavailable or no pass hashes in table');
    }

    wp_send_json_success(array(
        'total'   => $total,
        'checked' => $checked,
        'leaked'  => $leaked,
    ));
}

function spbc_field_debug_user_pass_check()
{
    ?>
    <div class="spbc_wrapper_field">
        <br>
        <button type="button" id="spbc_debug__run_pass_check_btn">Run UsersPassCheck now (reset &amp; check all)</button>
        &nbsp;<span id="spbc_debug__pass_check_status" style="font-style:italic;"></span>
    </div>
    <?php
}

function spbc_field_debug__check_connection()
{
    echo '<div class="spbc_wrapper_field">'
         . '<br>'
         . '<input form="debug_check_connection" type="submit" name="spbc_debug__check_connection" value="Check connection to servers" />'
         . '</div>';
}

function spbc_field_debug__set_fw_update_cron()
{
    global $spbc;

    echo '<div class="spbc_wrapper_field">'
        . '<br>'
        . '<form id="debug__cron_set_set_fw_update">'
        . '<input type="hidden" name="plugin_name"             value="security" />'
        . '<input type="hidden" name="spbc_remote_call_action" value="cron_update_task" />'
        . '<input type="hidden" name="spbc_remote_call_token"  value="' . md5($spbc->api_key) . '" />'
        . '<input type="hidden" name="task"                    value="firewall_update" />'
        . '<input type="hidden" name="handler"                 value="spbc_security_firewall_update__init" />'
        . '<input type="hidden" name="period"                  value="86400" />'
        . '<input type="hidden" name="first_call"              value="' . (time() + 60) . '" />'
        . '<input type="submit" name="spbc_debug__fw_update_cron_10_seconds" value="Set FW update to 60 seconds from now" />'
        . '</form>'
        . '</div>';
}

function spbc_field_debug__set_scan_cron()
{
    global $spbc;

    echo '<div class="spbc_wrapper_field">'
        . '<br>'
        . '<form id="debug__cron_set_set_scan_cron">'
        . '<input type="hidden" name="spbc_remote_call_action" value="cron_update_task" />'
        . '<input type="hidden" name="plugin_name"             value="security" />'
        . '<input type="hidden" name="spbc_remote_call_token"  value="' . md5($spbc->api_key) . '" />'
        . '<input type="hidden" name="task"                    value="scanner__launch" />'
        . '<input type="hidden" name="handler"                 value="spbc_scanner__launch" />'
        . '<input type="hidden" name="period"                  value="86400" />'
        . '<input type="hidden" name="first_call"              value="' . (time() + 60) . '" />'
        . '<input type="submit" name="spbc_debug__scan_cron_60_seconds" value="Schedule scan 60 seconds from now" />'
        . '</form>'
        . '</div>';
}

function spbc_field_debug__set_check_vulnerabilities_cron()
{
    global $spbc;

    echo '<div class="spbc_wrapper_field">'
        . '<br>'
        . '<form id="debug__cron_set_set_check_vulnerabilities">'
        . '<input type="hidden" name="spbc_remote_call_action" value="cron_update_task" />'
        . '<input type="hidden" name="plugin_name"             value="security" />'
        . '<input type="hidden" name="spbc_remote_call_token"  value="' . md5($spbc->api_key) . '" />'
        . '<input type="hidden" name="task"                    value="check_vulnerabilities" />'
        . '<input type="hidden" name="handler"                 value="spbc_security_check_vulnerabilities" />'
        . '<input type="hidden" name="period"                  value="86400" />'
        . '<input type="hidden" name="first_call"              value="' . (time() + 60) . '" />'
        . '<input type="submit"'
        . 'name="spbc_debug__check_vulnerabilities_cron_60_seconds"'
        . 'value="Schedule check vulnerabilities in 60 seconds from now" />'
        . '</form>'
        . '</div>';
}

function spbc_field_debug()
{
    global $spbc;

    $nonce_field = wp_nonce_field('spbc_secret_nonce');

    echo '<form id="debug_drop" method="POST">' . $nonce_field . '</form>'
         . '<form id="debug_check_connection" method="POST">' . $nonce_field . '</form>'
         . '<form id="debug__cron_set" method="POST">' . $nonce_field . '</form>';

    if ($spbc->debug) {
        $debug  = get_option(SPBC_DEBUG);
        $output = print_r($debug, true);
        $output = str_replace("\n", "<br>", $output);
        $output = preg_replace("/[^\S]{4}/", "&nbsp;&nbsp;&nbsp;&nbsp;", $output);
        $spbc->dev_log; // This is the lazy load the `dev_log` property into the State
        if ( ! empty($spbc->dev_log) ) {
            echo 'Dev log:' . "<br>";
            $dev_log  = $spbc->dev_log;
            $dev_log_output = print_r($dev_log, true);
            $dev_log_output = str_replace("\n", "<br>", $dev_log_output);
            $dev_log_output = preg_replace("/[^\S]{4}/", "&nbsp;&nbsp;&nbsp;&nbsp;", $dev_log_output);
            $output .= $dev_log_output;
        }
        echo "<div class='spbc_wrapper_field'>";
        echo $output
             . "<label for=''>" .

             "</label>" .
             "<div class='spbc_settings_description'>" .

             "</div>";
        echo "</div>";
    }
}

/**
 * Admin callback function - Sanitize settings
 *
 * @param array $settings raw settings array
 *
 * @return array sanitized settings
 */
function spbc_sanitize_settings($settings)
{
    global $spbc;

    // Set missing settings.
    foreach ($spbc->default_settings as $setting => $value) {
        if ( ! isset($settings[ $setting ])) {
            $settings[ $setting ] = null;
            settype($settings[ $setting ], gettype($value));
        }
    }
    unset($setting, $value);

    //Sanitizing traffic_control__autoblock_amount setting
    if (isset($settings['traffic_control__autoblock_amount'])) {
        $settings['traffic_control__autoblock_amount'] = floor(intval($settings['traffic_control__autoblock_amount']));
        $settings['traffic_control__autoblock_amount'] = ($settings['traffic_control__autoblock_amount'] == 0 ? 1000 : $settings['traffic_control__autoblock_amount']);
        $settings['traffic_control__autoblock_amount'] = ($settings['traffic_control__autoblock_amount'] < 20 ? 20 : $settings['traffic_control__autoblock_amount']);
    }

    //Sanitizing bfp__allowed_wrong_auths setting
    if (isset($settings['bfp__allowed_wrong_auths'])) {
        $settings['bfp__allowed_wrong_auths'] = (int) $settings['bfp__allowed_wrong_auths'];
        if ($settings['bfp__allowed_wrong_auths'] < 3) {
            $settings['bfp__allowed_wrong_auths'] = $spbc->default_settings['bfp__allowed_wrong_auths'];
        }
        if ($settings['bfp__allowed_wrong_auths'] > 20) {
            $settings['bfp__allowed_wrong_auths'] = 20;
        }
    }

    // XSS: sanitize options
    foreach ($settings as &$setting) {
        if (is_scalar($setting)) {
            $setting = preg_replace('/[<"\'>]/', '', trim((string)$setting));
        }
    }

    // Sanitize URLs for redirect login page
    $settings['login_page_rename__name'] = preg_match('@^[a-zA-Z0-9-/]+$@', (string)$settings['login_page_rename__name']) &&
                                           ! in_array(
                                               $settings['login_page_rename__name'],
                                               \CleantalkSP\SpbctWP\RenameLoginPage::getForbiddenSlugs(),
                                               true
                                           )
        ? (string)$settings['login_page_rename__name']
        : 'login';
    $settings['login_page_rename__name'] = trim($settings['login_page_rename__name'], '/');

    $settings['login_page_rename__redirect'] =
        preg_match('@^[a-zA-Z0-9-=/]+$@', $settings['login_page_rename__redirect'])
        || $settings['login_page_rename__redirect'] === ''
        ? (string)$settings['login_page_rename__redirect']
        : '';
    $settings['login_page_rename__redirect'] = trim($settings['login_page_rename__redirect'], '/');

    // Sanitize URLs for technical support link
    $settings['edit_tech_support_url__link'] = preg_match('@^[a-zA-Z0-9-/]+$@', (string)$settings['edit_tech_support_url__link'])
        ? (string)$settings['edit_tech_support_url__link']
        : '';
    $settings['edit_tech_support_url__link'] = trim($settings['edit_tech_support_url__link'], '/');

    // Clearing the link if the edit_tech_support_url__remove flag is set
    if ($settings['edit_tech_support_url__remove']) {
        $settings['edit_tech_support_url__link'] = '';
    }

    // Send email notification to admin if about changing login URL
    if (
        empty($spbc->settings['login_page_rename__enabled']) &&
        $settings['login_page_rename__enabled'] &&
        ($settings['login_page_rename__send_email_notification'] && current_user_can('activate_plugins'))
    ) {
        $mail = wp_mail(
            spbc_get_admin_email(),
            $spbc->data["wl_brandname"] . esc_html__(': New login URL', 'security-malware-firewall'),
            sprintf(
                esc_html__('New login URL is: %s', 'security-malware-firewall'),
                \CleantalkSP\SpbctWP\RenameLoginPage::getURL($settings['login_page_rename__name'])
            )
            . "\n\n"
            . esc_html__('Please, make sure that you will not forget the URL!', 'security-malware-firewall')
        );

        // If email is not sent, disabling the feature
        if ( !$mail ) {
            $spbc->error_add(
                'login_page_rename',
                __('Can not send notification email to the admin address. New login URL was not sent. Changes aborted.', 'security-malware-firewall')
            );
            $settings['login_page_rename__enabled'] = '0';
        } else {
            $spbc->error_delete('login_page_rename');
        }
    }

    if (!$settings['login_page_rename__send_email_notification']) {
        $spbc->error_delete('login_page_rename', true);
    }

    // Send logs for 2 previous days
    if ($settings['misc__backend_logs_enable'] && ! $spbc->settings['misc__backend_logs_enable']) {
        //neither we show this in the UI, method spbc_PHP_logs__collect use time() value to collect logs correct
        $spbc->data['last_php_log_sent'] = time() - 86400 * 2;
        $spbc->save('data');
    }

    // Scanner custom start time logic
    if (
            empty($spbc->errors['configuration']) &&
            $settings['scanner__auto_start_manual_time'] &&
            $settings['scanner__auto_start_manual_time'] != $spbc->settings['scanner__auto_start_manual_time']
    ) {
    //if ( empty($spbc->errors['configuration']) ) {
        $scanner_launch_data = spbc_get_custom_scanner_launch_data(false, $settings);
        \CleantalkSP\SpbctWP\Cron::updateTask(
            'scanner__launch',
            'spbc_scanner__launch',
            $scanner_launch_data['period'],
            $scanner_launch_data['start_time']
        );
    }

    // Sanitizing website mirrors
    if ($settings['scanner__outbound_links_mirrors']) {
        if (preg_match('/^[\sa-zA-Z0-9,_\.\-\~]+$/', $settings['scanner__outbound_links_mirrors'])) {
            $tmp     = explode(',', $settings['scanner__outbound_links_mirrors']);
            $mirrors = array();
            foreach ($tmp as $key => $value) {
                $value = trim($value);
                if ( ! empty($value)) {
                    $mirrors[ $key ] = trim($value);
                }
            }
            unset($key, $value);
            $settings['scanner__outbound_links_mirrors'] = implode(', ', $mirrors);
        }
    }

    // Sanitizing scanner dirs exceptions
    if ($settings['scanner__path_exclusions_view']) {
        $pathExclusion = new FilesScanPathExclusion();

        $settings['scanner__path_exclusions_view'] = $pathExclusion->pathExclusionsView($settings['scanner__path_exclusions_view']);
        $settings['scanner__path_exclusions'] = $pathExclusion->pathExclusions($settings['scanner__path_exclusions_view']);
    } else {
        update_option('spbc_upload_dirs_stat', array());
    }

    // Sanitizing frontend scanner URL exclusions
    if ($settings['scanner__frontend_analysis__domains_exclusions_view']) {
        $domainExclusion = new \CleantalkSP\SpbctWP\Settings\FrontendScanDomainExclusion();

        $domainExclusionView = $domainExclusion->frontendScanDomainExclusionsView($settings['scanner__frontend_analysis__domains_exclusions_view']);
        $settings['scanner__frontend_analysis__domains_exclusions_view'] = $domainExclusionView;

        $domainExclusionSets = $domainExclusion->domainExclusions($settings['scanner__frontend_analysis__domains_exclusions_view']);
        $settings['scanner__frontend_analysis__domains_exclusions'] = $domainExclusionSets;

        $domainExclusion->resetScannerFrontendResult($settings);
    }

    if ($settings['scanner__path_exclusions_view'] || $settings['scanner__frontend_analysis__domains_exclusions_view']) {
        SpbcCron::updateTask(
            'update_scan_settings_exclusions',
            'spbc_update_scan_settings_exclusions',
            FilesScanPathExclusion::EXTERNAL_SOURCE_UPDATE_PERIOD
        );
    }

    if ($settings['scanner__fs_watcher__snapshots_period']) {
        SpbcCron::updateTask('fswatcher_do_work', 'spbc_cron__fs_watcher_do_work', $settings['scanner__fs_watcher__snapshots_period']);
    }

    // Sanitizing API key
    $settings['spbc_key']      = trim($settings['spbc_key']);
    $settings['spbc_key']      = preg_match('/^[a-z\d]*$/', $settings['spbc_key']) ? $settings['spbc_key'] : $spbc->settings['spbc_key']; // Check key format a-z\d
    $settings['spbc_key']      = is_main_site() || $spbc->ms__work_mode != 2 ? $settings['spbc_key'] : $spbc->network_settings['spbc_key'];
    $spbc->data['key_changed'] = $settings['spbc_key'] !== $spbc->settings['spbc_key'];
    $spbc->data['key_is_ok']   = spbc_api_key__is_correct($settings['spbc_key']);

    if ($settings['spbc_key'] === '' && $spbc->data['key_changed']) {
        $spbc = spbc_drop_to_defaults_on_key_clearance($spbc);
        \CleantalkSP\SpbctWP\Cron::removeAllTasks();
    }

    $spbc->save('data');

    if ($spbc->is_network && $spbc->is_mainsite) {
        // @todo Should check unset settings because some hook is saving settings twice
        $spbc->network_settings['spbc_key'] = $settings['spbc_key'];

        if (isset($settings['ms__hoster_api_key'])) {
            $spbc->network_settings['ms__hoster_api_key'] = $settings['ms__hoster_api_key'];
            unset($settings['ms__hoster_api_key']);
        }

        if (isset($settings['ms__work_mode'])) {
            $spbc->network_settings['ms__work_mode'] = $settings['ms__work_mode'];
            unset($settings['ms__work_mode']);
        }

        $spbc->save('network_settings');

        $spbc->network_data = array(
            'key_is_ok'  => $spbc->data['key_is_ok'],
            'user_token' => isset($spbc->data['user_token']) ? $spbc->data['user_token'] : '',
            'service_id' => isset($spbc->data['service_id']) ? $spbc->data['service_id'] : '',
            'moderate'   => $spbc->data['moderate'],
        );
        $spbc->save('network_data');
    }

    if (isset($settings['2fa__enable'])) {
        if ($settings['2fa__enable'] == 1 || $settings['2fa__enable'] == -1) {
            $code2fa = get_site_option('spbc_confirmation_code');
            if (isset($code2fa['verified']) && $code2fa['verified'] === false) {
                $settings['2fa__enable'] = 0;
            }
        }

        if ($settings['2fa__enable'] == 0) {
            delete_site_option('spbc_confirmation_code');
        }
    }

    if (
        isset($settings['check_pass__enable']) &&
        $settings['check_pass__enable'] == '0' &&
        empty($settings['check_pass__roles']) &&
        ! empty($spbc->settings['check_pass__roles'])
    ) {
        $settings['check_pass__roles'] = $spbc->settings['check_pass__roles'];
    }

    /**
     * Triggered before returning the settings
     */
    do_action('spbc_before_returning_settings', $settings);

    // Try to add|remove content to .htaccess file
    if ($settings['wp__upload_dir_prevent_php_execution'] !== $spbc->settings['wp__upload_dir_prevent_php_execution']) {
        SpbcCron::updateTask('upload_dir_prevent_php_execution', 'spbc_upload_dir_prevent_php_execution', 86400, time() + 60);
    }

    return $settings;
}

/**
 * Whether the current AJAX request comes from the React signup wizard.
 *
 * @return bool
 */
function spbc_settings__is_wizard_ajax_request()
{
    return Post::getInt('spbct_wizard_request') === 1;
}

//Get auto key button
function spbc_get_key_auto($direct_call = false)
{
    if ( ! $direct_call) {
        spbc_check_ajax_referer('spbc_secret_nonce', 'security');
    }

    global $spbc;

    $website        = parse_url(get_option('home'), PHP_URL_HOST) . parse_url(get_option('home'), PHP_URL_PATH);
    $platform       = 'wordpress';
    $user_ip        = \CleantalkSP\SpbctWP\Helpers\IP::get();
    $timezone       = Post::getString('ct_admin_timezone');
    $language       = \CleantalkSP\Variables\Server::getString('HTTP_ACCEPT_LANGUAGE');
    /** @psalm-suppress RedundantCondition */
    $wpms           = SPBC_WPMS && defined('SUBDOMAIN_INSTALL') && ! SUBDOMAIN_INSTALL;
    $white_label    = false;
    $hoster_api_key = $spbc->ms__hoster_api_key;
    $admin_email    =  Post::getString('email') ? Post::getString('email') : spbc_get_admin_email();

    /**
     * Filters the email to get API key
     *
     * @param string email to get API key
     */
    $filtered_admin_email = apply_filters('spbc_get_api_key_email', $admin_email);
    $filtered_admin_email = filter_var($filtered_admin_email, FILTER_VALIDATE_EMAIL);

    // Mark the registrations made by the Connect button of the setup wizard
    $lead_source = spbc_settings__is_wizard_ajax_request() ? 'spbct_wizard_auto' : '';

    $result = API::method__get_api_key(
        'security',
        $filtered_admin_email,
        $website,
        $platform,
        $timezone,
        $language,
        $user_ip,
        $wpms,
        $white_label,
        $hoster_api_key,
        $lead_source
    );

    if ( ! empty($result['error'])) {
        $spbc->data['key_is_ok'] = false;
        $spbc->error_add('get_key', $result);

        $out = array(
            'success' => false,
            'msg'     => isset($result['error_message'])
                ? esc_html($result['error_message'])
                : $result['error']
        );
    } elseif (isset($result['error_no']) && $result['error_no'] == '403') {
        $out = array(
            'success' => false,
            'error' => isset($result['error_message'])
                ? esc_html($result['error_message'])
                : esc_html('Our service is not available in your region.'),
        );
    } elseif ( ! isset($result['auth_key'])) {
        $out = array(
            'success' => false,
            'msg'     => sprintf(
                __('Please, get the Access Key from %s CleanTalk Control Panel %s and insert it in the Access Key field', 'cleantalk-spam-protect'),
                '<a href="https://cleantalk.org/my/?cp_mode=security" target="_blank">',
                '</a>'
            )
        );
    } else {
        $user_token = ! empty($result['user_token']) ? $result['user_token'] : '';
        spbc_save_key($result['auth_key'], $user_token, true);

        $templates = \CleantalkSP\SpbctWP\CleantalkSettingsTemplates::get_options_template($result['auth_key']);

        if ( ! empty($templates)) {
            $templatesObj = new \CleantalkSP\SpbctWP\CleantalkSettingsTemplates($result['auth_key']);
            $out          = array(
                'success'      => true,
                'getTemplates' => $templatesObj->getHtmlContent(true),
            );
        } else {
            $out = array(
                'success' => true
            );
        }
    }

    if ($direct_call) {
        return $result;
    }

    die(json_encode($out));
}

function spbc_save_key($apikey, $user_token = '', $direct_call = false)
{
    global $spbc;

    // if ajax - run full check with notice_paid_till
    if ( ! $direct_call ) {
        $nonce_check  = spbc_check_ajax_referer('spbc_secret_nonce', 'security', false);
        try {
            $apikey = trim(Post::getString('apiKey'));
            $apikey = spbc_validate_ajax_access_key($apikey, $nonce_check);
        } catch (\Exception $e) {
            die(
                json_encode(
                    [
                        'success' => false,
                        'msg' => sprintf(
                            __('Access Key validation error: %s', 'security-malware-firewall'),
                            sanitize_text_field($e->getMessage())
                        )
                    ]
                )
            );
        }
    }

    //run main saving flow
    $settings['spbc_key'] = trim($apikey);
    $settings['spbc_key'] = preg_match('/^[a-z\d]*$/', $settings['spbc_key']) ? $settings['spbc_key'] : $spbc->settings['spbc_key']; // Check key format a-z\d
    $settings['spbc_key'] = is_main_site() || $spbc->ms__work_mode != 2 ? $settings['spbc_key'] : $spbc->network_settings['spbc_key'];

    $spbc->settings['spbc_key'] = $settings['spbc_key'];
    $spbc->save('settings');

    $spbc->data['user_token']  = ! empty($user_token) ? $user_token : '';
    $spbc->data['key_is_ok']   = spbc_api_key__is_correct($settings['spbc_key']);
    $spbc->data['key_changed'] = true;
    $spbc->save('data');

    //die if ajax
    if ( ! $direct_call ) {
        die(
            json_encode(
                ['success' => true]
            )
        );
    }
}

/**
 * Validation function for AJAX inserted apikey.
 * Runs provided nonce state check, rate limit check, and main check flow via notice_paid_till.
 * @param string $apikey
 * @param bool $nonce_check
 * @return string provided apikey if no errors
 * @throws Exception on any step failed, the message contains a reason.
 */
function spbc_validate_ajax_access_key($apikey, $nonce_check)
{
    //validate nonce
    if (!$nonce_check) {
        $error_msg = __('invalid nonce or permissions.', 'security-malware-firewall');
        throw new \Exception($error_msg);
    }

    //run rate limiter check
    $config = new RateLimiterConfig('ajax_key_validation_call', 3, 30);
    $rate_limiter = new SpbcRateLimiter($config);
    $rate_passed = $rate_limiter->checkPassed();
    if (!$rate_passed) {
        $error_msg = __('you have made too many requests. Please, try again later.', 'security-malware-firewall');
        throw new \Exception($error_msg);
    }

    //run notice paid till and other checks
    $key_full_validation = spbc_validate_access_key($apikey);
    $validation_errors = !empty($key_full_validation['errors'])
        ? $key_full_validation['errors']
        : [];
    if (!empty($validation_errors)) {
        $error_msg = __('unknown error.', 'security-malware-firewall');
        if (is_array($validation_errors)) {
            $error_msg = implode(', ', $validation_errors);
        }
        throw new \Exception($error_msg);
    }
    return $apikey;
}

function spbc_settings__spbc_create_support_user($direct_call = false)
{
    if ( ! $direct_call) {
        spbc_check_ajax_referer('spbc_secret_nonce', 'security');
    }

    $support_user = new \CleantalkSP\SpbctWP\SupportUser();
    $result = $support_user->ajaxProcess();
    wp_send_json($result);
}

function spbc_show_more_security_logs_callback()
{
    spbc_check_ajax_referer('spbc_secret_nonce', 'security');

    // PREPROCESS INPUT
    $args                 = spbc_list_table__get_args_by_type('security_logs');
    $args['sql']['limit_force'] = Post::getInt('amount') ?: SPBC_LAST_ACTIONS_TO_VIEW;

    // OUTPUT
    $table = new ListTable($args);
    $table->getData();

    die(
        json_encode(
            array(
                'html' => $table->displayRows(null, 'return'),
                'size' => $table->items_count,
                'total' => $table->items_total,
            )
        )
    );
}

function spbc_get_hostnames_by_ips_callback()
{
    global $wpdb;

    spbc_check_ajax_referer('spbc_secret_nonce', 'security');

    if ( ! function_exists('gethostbyaddr') ) {
        die(
            json_encode(
                array(
                    'success' => false,
                    'hostnames' => [],
                )
            )
        );
    }

    // validate ips — table stores IPv4 as unsigned int; IPv6 cannot use ip2long()
    $ips = Post::getArray('ips');
    if (empty($ips)) {
        wp_send_json_error('IPs are required.');
    }
    $ips = array_map(function ($ip) {
        return trim($ip);
    }, $ips);
    $ips = array_unique($ips);
    $ips = array_filter($ips, function ($ip) {
        return IP::validate($ip) === 'v4';
    });
    $ips = array_map(function ($ip) {
        return (int) sprintf('%u', ip2long($ip));
    }, $ips);
    $ips = array_values(array_unique(array_filter($ips)));

    if (!empty($ips)) {
        // check in db
        $hostnames = $wpdb->get_results(
            $wpdb->prepare(
                "SELECT network, hostname FROM " . SPBC_TBL_SECURITY_LOG_HOSTNAMES . " WHERE network IN (" . implode(',', array_fill(0, count($ips), '%d')) . ")",
                $ips
            ),
            ARRAY_A
        );
    } else {
        die(
            json_encode(
                array(
                    'success' => false,
                    'hostnames' => [],
                )
            )
        );
    }

    // separate new ips (cast DB values to int for strict comparison)
    $known_networks = array_map('intval', array_column($hostnames, 'network'));
    $new_ips = array_values(array_diff($ips, $known_networks));

    // Get hostname by IPs using gethostbyaddr
    if ( ! empty($new_ips) ) {
        $new_hostnames = array();
        foreach ($new_ips as $ip) {
            $new_ip = long2ip($ip);
            $new_hostname = gethostbyaddr($new_ip);
            if ($new_hostname == $new_ip) {
                $new_hostname = '';
            }
            $new_hostnames[] = array(
                'network' => $ip,
                'hostname' => $new_hostname
            );
        }

        // write to db new hostnames (IGNORE races / already-cached rows)
        $values = array();
        foreach ($new_hostnames as $row) {
            $values[] = $wpdb->prepare("(%d, %s)", $row['network'], $row['hostname']);
        }
        // @psalm-suppress WpdbUnsafeMethodsIssue
        $wpdb->query(
            "INSERT IGNORE INTO " . SPBC_TBL_SECURITY_LOG_HOSTNAMES . " (network, hostname) VALUES " . implode(',', $values)
        );

        for ($i = 0; $i < count($new_hostnames); $i++) {
            $new_hostnames[$i]['network'] = long2ip($new_hostnames[$i]['network']);
        }
    }

    if (isset($new_hostnames) && is_array($new_hostnames)) {
        $hostnames = array_merge($hostnames, $new_hostnames);
    }

    foreach ($hostnames as &$hostname) {
        if (is_numeric($hostname['network'])) {
            $hostname['network'] = long2ip($hostname['network']);
        }
    }
    unset($hostname);

    die(
        json_encode(
            array(
                'success' => true,
                'hostnames' => $hostnames,
            )
        )
    );
}

function spbc_tc__filter_ip()
{
    global $spbc;

    spbc_check_ajax_referer('spbc_secret_nonce', 'security');

    $ip = Post::getString('ip'); // validation is done next line
    $status = Post::getString('status');

    if ( IP::validate($ip) === false ) {
        wp_send_json_error('IP is not correct.');
    }

    if ( $status !== 'allow' && $status !== 'deny' ) {
        wp_send_json_error('Status is not correct.');
    }

    // Add to the personal lists to the Cloud
    $res_cloud = API::method__private_list_add($spbc->user_token, $ip, $spbc->data['service_id'], ['status' => $status]);
    if ( isset($res_cloud['records']) && is_array($res_cloud['records']) ) {
        foreach ( $res_cloud['records'] as $record ) {
            if ( $record['operation_status'] === 'FAILED' ) {
                wp_send_json_error('API: adding IP ' . $record['record'] . ' failed: ' . $record['operation_message']);
            }
        }
    } else {
        wp_send_json_error('API wrong answer.');
    }

    // Add to the local database
    $status_for_db = $status === 'allow' ? '1' : '0';
    $version = IP::validate($ip);
    if ( $version === 'v4' ) {
        $data[] = ip2long($ip) . ',' . ip2long('255.255.255.255') . ',' . $status_for_db;
    } elseif ( $version === 'v6' ) {
        $data[] = $ip . ',' . '128' . ',' . $status_for_db;
    } else {
        wp_send_json_error('Local database: adding IP ' . $ip . ' failed: ip does not look like a valid IP address');
    }

    try {
        $res_local = spbct_sfw_private_records_handler('add', json_encode($data, JSON_FORCE_OBJECT));
        wp_send_json_success($res_local);
    } catch (\Exception $e) {
        wp_send_json_error('Local database: adding IP ' . $ip . ' failed: ' . $e->getMessage());
    }
}

/**
 * @return void
 */
function spbc_settings__get_description()
{
    global $spbc;

    spbc_check_ajax_referer('spbc_secret_nonce', 'security');

    if (!isset($_POST['setting_id'])) {
        return;
    }

    $setting_id = str_replace(' ', '_', $_POST['setting_id']);

    $tc_learn_more_link = ! $spbc->data["wl_mode_enabled"]
        ? '<p><a class="spbc_long_desc__link" href="https://blog.cleantalk.org/wordpress-ddos-protection-how-to-mitigate-ddos-attacks/" target="_blank">'
         . __('Learn more', 'security-malware-firewall')
         . '</a></p>'
        : '';

    $logins_collecting_learn_mode_links = ! $spbc->data["wl_mode_enabled"]
        ? '<p><a class="spbc_long_desc__link" href="https://blog.cleantalk.org/hiding-your-wordpress-username-from-bad-bots/" target="_blank">'
          . __('Learn more', 'security-malware-firewall')
          . '</a></p>'
        : '';

    $two_fa_learn_more_link = ! $spbc->data["wl_mode_enabled"]
        ? '<p><a class="spbc_long_desc__link" href="https://cleantalk.org/help/two-factor-auth" target="_blank">'
          . __('Use this guide', 'security-malware-firewall')
          . '</a>'
          . ' ' . __('to see more details.', 'security-malware-firewall')
          . '</p>'
        : '';

    $descriptions = array(
        'secfw__enabled'              => array(
            'title' => __('Security FireWall', 'security-malware-firewall'),
            'desc'  => __('Security FireWall is a part of the security service and blocks a malicious active before the site pages load.', 'security-malware-firewall')
        ),
        'waf__xss_check'              => array(
            'title' => __('XSS check', 'security-malware-firewall'),
            'desc'  => __('Cross-Site Scripting (XSS) — prevents malicious code to be executed/sent to any user. As a result malicious scripts can not get access to the cookie files, session tokens and any other confidential information browsers use and store. Such scripts can even overwrite content of HTML pages. ' . $spbc->data["wl_company_name"] . ' WAF monitors for patterns of these parameters and block them.', 'security-malware-firewall')
        ),
        'waf__sql_check'              => array(
            'title' => __('SQL-injection check', 'security-malware-firewall'),
            'desc'  => __('SQL Injection — one of the most popular ways to hack websites and programs that work with databases. It is based on injection of a custom SQL code into database queries. It could transmit data through GET, POST requests or cookie files in an SQL code. If a website is vulnerable and execute such injections then it would allow attackers to apply changes to the website\'s MySQL database.', 'security-malware-firewall')
        ),
        'upload_checker__file_check'             => array(
            'title' => __('Check uploaded files', 'security-malware-firewall'),
            'desc'  => __('The option checks each uploaded file to a website for malicious code. If it\'s possible for visitors to upload files to a website, for instance a work resume, then attackers could abuse it and upload an infected file to execute it later and get access to your website.', 'security-malware-firewall')
        ),
        'traffic_control__enabled'    => array(
            'title' => __('Traffic Control', 'security-malware-firewall'),
            'desc'  => __('It analyzes quantity of requests towards website from any IP address for a certain period of time. For example, for an ordinary visitor it\'s impossible to generate 2000 requests within 1 hour. Big amount of requests towards website from the same IP address indicates that there is a high chance of presence of a malicious program.', 'security-malware-firewall')
                . $tc_learn_more_link
        ),
        'scanner__outbound_links'     => array(
            'title' => __('Scan links', 'security-malware-firewall'),
            'desc'  => __('This option allows you to know the number of outgoing links on your website and website addresses they lead to. These websites addresses will be checked with the ' . $spbc->data["wl_company_name"] . ' Database and the results will show if they were used in spam messages. The option\'s purpose is to check your website and find hidden, forgotten and spam links. You should always remember if you have links to other websites which have a bad reputation, it could affect your visitors\' trust and your SEO.', 'security-malware-firewall')
        ),
        'scanner__heuristic_analysis' => array(
            'title' => __('Heuristic analysis', 'security-malware-firewall'),
            'desc'  => __('Often, authors of malicious code disguise their code which makes it difficult to identify it by their signatures. The malicious code itself can be placed anywhere on the site, for example the obfuscated PHP-code in the "logo.png" file, and the code itself is called by one inconspicuous line in "index.php". Therefore, the usage of plugins to search for malicious code is preferable. Heuristic analysis can indicate suspicious PHP constructions in a file that you should pay attention to.', 'security-malware-firewall')
        ),
        'scanner__schedule_send_heuristic_suspicious_files' => array(
            'title' => __('Auto-send suspicious files for analysis', 'security-malware-firewall'),
            'desc'  => __('Automatic schedule suspicious files to send for analysis. Make note, if the file contains a malware signature, the file will not be sent, because this case is definitely a malware.', 'security-malware-firewall')
        ),
        'scanner__signature_analysis' => array(
            'title' => __('Signature analysis', 'security-malware-firewall'),
            'desc'  => __('Code signatures — it\'s a code sequence a malicious program consists of. Signatures are being added to the database after analysis of the infected files. Search for such malicious code sequences is performed in scanning by signatures. If any part of code matches a virus code from the database, such files would be marked as critical.', 'security-malware-firewall')
        ),
        'scanner__binary_analysis' => array(
            'title' => __('Binary analysis', 'security-malware-firewall'),
            'desc'  => __('Search for suspicious binary patterns in files.', 'security-malware-firewall')
        ),
        'scanner__auto_cure'          => array(
            'title' => __('Cure malware', 'security-malware-firewall'),
            'desc'  => __('It cures infected files automatically if the scanner knows cure methods for these specific cases. If the option is disabled then when the scanning process ends you will be presented with several actions you can do to the found files: Cure. Malicious code will be removed from the file. Replace. The file will be replaced with the original file. Delete. The file will be put in quarantine. Do nothing. Before any action is chosen, backups of the files will be created and if the cure is unsuccessful it\'s possible to restore each file.', 'security-malware-firewall')
        ),
        'misc__backend_logs_enable'   => array(
            'title' => __('Collect and send PHP logs', 'security-malware-firewall'),
            'desc'  => __('To control appearing errors you have to check log file of your hosting account regularly. It\'s inconvenient and just a few webmasters pay attention to it. Also, errors could appear for a short period of time and only when one specific function is running, they can\'t be spotted in other circumstances so it\'s hard to catch them. PHP errors tell you that some of your website functionality doesn\'t work correctly, furthermore hackers may use these errors to get access to your website. The ' . $spbc->data["wl_company_name"] . ' Scanner will check your website backend once per hour. Statistics of errors is available in your ' . $spbc->data["wl_company_name"] . ' Dashboard.', 'security-malware-firewall')
        ),
        'vulnerability_check__enable_cron'    => array(
            'title' => __('Test installed plugins for known vulnerabilities', 'security-malware-firewall'),
            'desc'  => __('All the data about vulnerability statuses will be saved. If a known vulnerability found plugin informs you in Dashboard about details and gives instructions. Also, if appropriated setting below is enabled, you will be informed about the status on the modules page', 'security-malware-firewall')
        ),
        'vulnerability_check__test_before_install'    => array(
            'title' => __('Test plugins for known vulnerabilities before install them', 'security-malware-firewall'),
            'desc'  => __('The plugin will request the vulnerability check over research.cleantalk.org for all the plugins listed on the installation page. If the appropriated data received, you will be earned about the result.', 'security-malware-firewall')
        ),
        'misc__prevent_logins_collecting'    => array(
            'title' => __('Prevent collecting of authors logins', 'security-malware-firewall'),
            'desc'  => __('The option helps to hide the name of the author of articles on the site pages. This helps protect against login parsing, spam, and brute force.', 'security-malware-firewall')
                . $logins_collecting_learn_mode_links
        ),
        'misc__prevent_logins_collecting_on_password_reset'    => array(
            'title' => __('Prevent collecting of login on password reset error', 'security-malware-firewall'),
            'desc'  => __('The option exclude the info about the login existing on password change error. Error message will be replaced with followed text:', 'security-malware-firewall')
                . '<p>'
                . '"' . LoginCollectingProtector::changeConfirmationErrorMessage() . '"'
                . '</p>'

        ),
        'data__set_cookies'           => array(
            'title' => __('Set cookies', 'security-malware-firewall'),
            'desc'  => __('Part of the CleanTalk FireWall functions depend on cookie files, so disabling this option could lead to deceleration of the firewall work. It will affect user identification who are logged in right now. Traffic Control will not be able to determine authorized users and they could be blocked when the request limit is reached. We do not recommend to disable this option without serious reasons. However, you should disable this option is you\'re using Varnish.', 'security-malware-firewall')
        ),
        '2fa__enable'                 => array(
            'title' => __('Two factor authentication for administrators', 'security-malware-firewall'),
            'desc'  => __('Two-Factor Authentication for WordPress admin accounts will improve your website security and make it safer, if not impossible, for hackers to breach your WordPress account. Two-Factor Authentication works via e-mail. Authentication code will be sent to your admin email. When authorizing, a one-time code will be sent to your email. While entering the code, make sure that it does not contain spaces. With your first authorization, the ' . $spbc->data["wl_company_name"] . ' Security plugin remembers your browser and you won’t have to input your authorization code every time anymore. However, if you started to use a new device or a new browser then you are required to input your authorization code. The plugin will remember your browser for 30 days.', 'security-malware-firewall')
                . $two_fa_learn_more_link
        ),
        'check_pass__enable'                 => array(
            'title' => __('Checking the user\'s password for information leaks', 'security-malware-firewall'),
            'desc'  => __("This feature helps enhance your website's security by continuously monitoring users' passwords for any potential exposure in known data breaches. With the increasing number of leaked password databases available on the dark web, it's critical to ensure that no user accounts are compromised. By enabling this feature, the CleanTalk Security plugin will automatically cross-check all user passwords against a regularly updated list of compromised credentials.  Each time a password leak is detected, the plugin will alert the administrator and recommend a password reset for the affected user. This ensures that any compromised credentials are immediately addressed, reducing the risk of unauthorized access to your WordPress site.  The feature works in the background, requiring no action from users unless a leak is detected. This periodic check is fully automated, and the plugin will ensure that users are notified only when necessary. It's a proactive measure to prevent potential attacks and to keep your website safe from the consequences of data breaches.  By enabling this feature, you're taking an extra step in protecting your WordPress site from potential threats caused by compromised user passwords.", 'security-malware-firewall')
        ),
        'data__additional_headers'    => array(
            'title' => __('Additional Headers', 'security-malware-firewall'),
            'desc'  => __('"X-Content-Type-Options" improves the security of your site (and your users) against some types of drive-by-downloads. <br> "X-XSS-Protection" header improves the security of your site against some types of XSS (cross-site scripting) attacks.', 'security-malware-firewall') .
                       '<br>' . esc_html__('"Strict-Transport-Security" response header (often abbreviated as HSTS) informs browsers that the site should only be accessed using HTTPS, and that any future attempts to access it using HTTP should automatically be converted to HTTPS.', 'security-malware-firewall') .
                       '<br>' . esc_html__('"Referrer-Policy" make the `Referer` http-header transferring more strictly.', 'security-malware-firewall')
        ),
        'misc_disable_file_editor'    => array(
            'title' => __('Disable File Editor', 'security-malware-firewall'),
            'desc'  => __('By prohibiting file editing, you protect the site from malicious attacks that may try to change the code and gain access to the site or steal confidential information', 'security-malware-firewall')
        ),
        'wp__disable_xmlrpc'          => array(
            'title' => __('Disable XML-RPC', 'security-malware-firewall'),
            'desc'  => __('XML-RPC is an out-of-date technology that can compromise websites. It is still enabled by default in WordPress for the purpose of reverse compatibility for some parts of information systems like old apps on phones and tablets. Please, make sure that you don\'t use such obsolete systems. If you don\'t know anything about it it\'s a good practice to enable this option and disable the XML-RPC.<br><br>Enabled XML-RPC could give hackers a possibility to brute-force your website credentials and access your website.', 'security-malware-firewall')
        ),
        'ms__work_mode'               => array(
            'title' => __('WordPress Multisite Work Mode', 'security-malware-firewall'),
            'desc'  => __(
                '<h4>Mutual Account, Individual Access Keys</h4>'
                . '<span>Each blog uses a separate key from the network administrator account. Each blog has its own separate security log, settings, personal lists. Key will be provided automatically to each blog once it is created or during the plugin activation process. The key could be changed only by the network administrator.</span>'
                . '<h4>Mutual Account, Mutual Access Key</h4>'
                . '<span>All blogs use one mutual key. They also share security logs, settings and personal lists with each other. Network administrator holds the key.</span>'
                . '<h4>Individual accounts, individual Access keys</h4>'
                . '<span>Each blog uses its own account and its own key. Separate security logs, settings, personal lists. Blog administrator can change the key on his own.</span>',
                'security-malware-firewall'
            )
        ),
        'ms__hoster_api_key'          => array(
            'title' => __('Hoster access key', 'security-malware-firewall'),
            'desc'  => __('You could find it here:<br><a href ="https://cleantalk-screenshots.s3.amazonaws.com/help/hosting-antispam/hapi-en.png"><img src="https://cleantalk-screenshots.s3.amazonaws.com/help/hosting-antispam/hapi-en.png"></a><br>Press on the screenshot to zoom.', 'security-malware-firewall')
        ),
        'listing'                     => array(
            'title' => __('Directory can be listed from the Internet', 'security-malware-firewall'),
            'desc'  => __('The listing of a directory allows an attacker to see the files inside the folder and the very existence of the folder. So if he sees ".git" folder is open for the listing, he can assume that you are using GIT technology and could exploit the known security issues to hack the website.', 'security-malware-firewall')
        ),
        'accessible'                  => array(
            'title' => __('File is accessible from the Internet', 'security-malware-firewall'),
            'desc'  => __('Anyone who knows the location of the file could download its content. This could sound pretty harmless, but in fact if this file is an error log, the attacker could identify the modules and plugins you are using and get some additional info about his hack attempts.', 'security-malware-firewall')
        ),
        'action_shuffle_salts'        => array(
            'title' => 'Shuffle Salts',
            'desc'  => __('WordPress secret keys and salts are a random set of symbols that are being used in encrypting the 
                    usernames and passwords that are being stored in the browser cookies. If the site has been hacked, 
                    all data on the site can be considered compromised. One of the first important recommendations is 
                    to change all passwords and security keys. If hackers have the security keys, they can regain 
                    access to the site even if the passwords have been changed. It is very important to change each 
                    security key along with the passwords when the malicious code is removed.', 'security-malware-firewall')
        ),
        'dbd_found' => array(
            'title' => 'Drive by Download',
            'desc'  => __('Unintentional loading of data from an external source is possible', 'security-malware-firewall')
        ),
        'redirect_found' => array(
            'title' => 'Redirects',
            'desc'  => __('An unexpected redirect to another resource is possible', 'security-malware-firewall')
        ),
        'csrf' => array(
            'title' => 'CSRF',
            'desc'  => __('Code found that can be used for csrf attacks', 'security-malware-firewall')
        ),
        'signature' => array(
            'title' => 'Signatures',
            'desc'  => __('Search for malicious code using the Cleantalk signature database', 'security-malware-firewall')
        ),
        'signatures_XSS' => array(
            'title' => 'XSS attack',
            'desc'  => __('Cross-Site Scripting (XSS) attacks are a type of injection, in which malicious scripts are 
                    injected into otherwise benign and trusted websites.', 'security-malware-firewall')
        ),
        'signatures_SQL_INJECTION' => array(
            'title' => 'SQL injection',
            'desc'  => __('SQL injection is a code injection technique that might destroy your database.', 'security-malware-firewall')
        ),
        'signatures_EXPLOIT' => array(
            'title' => 'Exploit',
            'desc'  => __('An exploit is a piece of software, a chunk of data, or a sequence of commands that takes 
                    advantage of a bug or vulnerability to cause unintended or unanticipated behavior to occur on 
                    computer software, hardware, or something electronic (usually computerized).', 'security-malware-firewall')
        ),
        'signatures_SUSPICIOUS' => array(
            'title' => 'Suspicious',
            'desc'  => __('The code looks suspicious. Make sure it is safe.', 'security-malware-firewall')
        ),
        'signatures_MALWARE' => array(
            'title' => 'Malware',
            'desc'  => __('Malware has been found during the signature analysis.', 'security-malware-firewall')
        ),
        'heuristic_assert' => array(
            'title' => 'assert()',
            'desc'  => __('Using the function in production is not recommended', 'security-malware-firewall')
        ),
        'heuristic_eval' => array(
            'title' => 'eval()',
            'desc'  => __('The eval() language construct is very dangerous because it allows execution of arbitrary PHP code. Its use thus is discouraged.', 'security-malware-firewall')
        ),
        'heuristic_create_function' => array(
            'title' => 'create_function()',
            'desc'  => __('This function internally performs an eval() and as such has the same security issues as eval().', 'security-malware-firewall')
        ),
        'heuristic_system' => array(
            'title' => 'system()',
            'desc'  => __('Execute an external program and display the output', 'security-malware-firewall')
        ),
        'heuristic_passthru' => array(
            'title' => 'passthru()',
            'desc'  => __('Execute an external program and display raw output', 'security-malware-firewall')
        ),
        'heuristic_proc_open' => array(
            'title' => 'proc_open()',
            'desc'  => __('Execute a command and open file pointers for input/output', 'security-malware-firewall')
        ),
        'heuristic_exec' => array(
            'title' => 'exec()',
            'desc'  => __('Execute an external program', 'security-malware-firewall')
        ),
        'heuristic_pcntl_exec' => array(
            'title' => 'pcntl_exec()',
            'desc'  => __('Executes specified program in current process space', 'security-malware-firewall')
        ),
        'heuristic_popen' => array(
            'title' => 'popen()',
            'desc'  => __('Opens process file pointer', 'security-malware-firewall')
        ),
        'heuristic_shell_exec' => array(
            'title' => 'shell_exec()',
            'desc'  => __('Execute command via shell and return the complete output as a string', 'security-malware-firewall')
        ),
        'heuristic_str_rot13' => array(
            'title' => 'str_rot13()',
            'desc'  => __('Perform the rot13 transform on a string', 'security-malware-firewall')
        ),
        'heuristic_syslog' => array(
            'title' => 'syslog()',
            'desc'  => __('Generate a system log message', 'security-malware-firewall')
        ),
        'heuristic_global_variables_in_a_sys_command' => array(
            'title' => 'Super global in system command',
            'desc'  => __('Found direct request to super global variables in the system commands functions.', 'security-malware-firewall')
        ),
        'heuristic_base64_decode' => array(
            'title' => 'base64_decode()',
            'desc'  => __('Suspicious base64_decode usage.', 'security-malware-firewall')
        ),
        'heuristic_the_function_contains_suspicious_arguments' => array(
            'title' => '',
            'desc'  => __('The function contains suspicious arguments', 'security-malware-firewall')
        ),
        'suspicious_assert' => array(
            'title' => 'assert()',
            'desc'  => __('Using the function in production is not recommended', 'security-malware-firewall')
        ),
        'suspicious_eval' => array(
            'title' => 'eval()',
            'desc'  => __('The eval() language construct is very dangerous because it allows execution of arbitrary PHP code. Its use thus is discouraged.', 'security-malware-firewall')
        ),
        'suspicious_create_function' => array(
            'title' => 'create_function()',
            'desc'  => __('This function internally performs an eval() and as such has the same security issues as eval().', 'security-malware-firewall')
        ),
        'suspicious_system' => array(
            'title' => 'system()',
            'desc'  => __('Execute an external program and display the output', 'security-malware-firewall')
        ),
        'suspicious_passthru' => array(
            'title' => 'passthru()',
            'desc'  => __('Execute an external program and display raw output', 'security-malware-firewall')
        ),
        'suspicious_proc_open' => array(
            'title' => 'proc_open()',
            'desc'  => __('Execute a command and open file pointers for input/output', 'security-malware-firewall')
        ),
        'suspicious_exec' => array(
            'title' => 'exec()',
            'desc'  => __('Execute an external program', 'security-malware-firewall')
        ),
        'suspicious_pcntl_exec' => array(
            'title' => 'pcntl_exec()',
            'desc'  => __('Executes specified program in current process space', 'security-malware-firewall')
        ),
        'suspicious_popen' => array(
            'title' => 'popen()',
            'desc'  => __('Opens process file pointer', 'security-malware-firewall')
        ),
        'suspicious_shell_exec' => array(
            'title' => 'shell_exec()',
            'desc'  => __('Execute command via shell and return the complete output as a string', 'security-malware-firewall')
        ),
        'suspicious_str_rot13' => array(
            'title' => 'str_rot13()',
            'desc'  => __('Perform the rot13 transform on a string', 'security-malware-firewall')
        ),
        'suspicious_syslog' => array(
            'title' => 'syslog()',
            'desc'  => __('Generate a system log message', 'security-malware-firewall')
        ),
        'suspicious_global_variables_in_a_sys_command' => array(
            'title' => 'Super global in system command',
            'desc'  => __('Found direct request to super global variables in the system commands functions.', 'security-malware-firewall')
        ),
        'suspicious_base64_decode' => array(
            'title' => 'base64_decode()',
            'desc'  => __('Suspicious base64_decode usage.', 'security-malware-firewall')
        ),
        'login_page_rename__send_email_notification' => array(
            'title' => 'Send email with new login URL',
            'desc'  => __('If enabled, the plugin will necessarily send the notification to the admin email before login URL is changed.
            If email could not be sent, all the changes will be reverted.
            Disable this option if you have mail connection issues or SMTP service is not configured on this WordPress instance.
            Please note that only user that has permissions to activate plugins can disable this option.', 'security-malware-firewall'),
        ),
        'scanner__path_exclusions_view' => array(
            'title' => __('Exclusions ruleset for files and directories', 'security-malware-firewall'),
            'desc'  => __('These rules will exclude files or directories (and all subdirectories) matching the specified path. Any type of directory separator is acceptable. Example: wp-content/themes/yourtheme/skipthisdir or wp-content/uploads/file.txt', 'security-malware-firewall')
                . '<p><a class="spbc_long_desc__link" href="'
                . LinkConstructor::buildSimpleLink(
                    'https://cleantalk.org',
                    'help/exclude-files-and-folders-from-malware-scanning'
                )
                . '" target="_blank">'
                . __('Learn more', 'security-malware-firewall')
                . '</a></p>',
        ),
        'hash_denied_hash' => array(
            'title' => 'denied_hash',
            'desc'  => __('The file hash is in denied list. It means that the Security analysts have marked this file
             as critically dangerous early.', 'security-malware-firewall')
        ),
        'secfw__get_ip' => array(
            'title' => IP::getOptionLongDescriptionArray()['title'],
            'desc'  => IP::getOptionLongDescriptionArray()['desc'],
        ),
        'sending_for_analysis_rules' => array(
            'title' => 'Sending for cloud analysis',
            'desc'  => spbc__get_accordion_tab_info_block_html('sending_for_analysis_rules'),
        ),
        'wp__disable_rest_api' => array(
                'title' => 'Disable WordPress REST API',
                'desc'  => __('This option restricts access for non-authenticated users only.<br><br>Modes:<br><br>Disable endpoint "users" - only /wp-json/wp/v2/users and /wp-json/wp/v2/users/"id_user" will be restricted.<br><br>Disable all endpoints - any REST routes will be restricted.', 'security-malware-firewall'),
        ),
        //  Note: this long desc called form user interface
        'spbc_pass_check' => array(
                'title' => 'User password leaks check',
                'desc'  => \CleantalkSP\SpbctWP\UsersPassCheckModule\UserPassCheckView::getLongDescription(),
        ),
        'no_description' => array(
            'title' => esc_html($setting_id),
            'desc'  => __('No description provided yet for this item. We are sorry about this. Please, contact support@cleantalk.org for further help.', 'security-malware-firewall'),
        ),
    );

    $out = isset($descriptions[ $setting_id ]) ? $descriptions[ $setting_id ] : $descriptions['no_description'];

    wp_send_json($out);
}

/**
 * @return void
 */
function spbc_settings__get_recommendation()
{
    global $spbc;

    spbc_check_ajax_referer('spbc_secret_nonce', 'security');

    if (!isset($_POST['setting_id'])) {
        return;
    }

    $setting_id = str_replace(' ', '_', $_POST['setting_id']);

    $recomendations = array(
        'listing' => array(
            'title' => __('Directory can be listed from the Internet', 'security-malware-firewall'),
            'desc'  => __('The listing of a directory allows an attacker to see the files inside the folder and the very existence of the folder. So if he sees ".git" folder is open for the listing, he can assume that you are using GIT technology and could exploit the known security issues to hack the website.', 'security-malware-firewall')
        ),
        'accessible' => array(
            'title' => __('File is accessible from the Internet', 'security-malware-firewall'),
            'desc'  => __('To solve this issue rename or move the debug.log')
                . '<br><br>'
                . '<a href="https://wordpress.org/support/article/debugging-in-wordpress/#wp_debug_log" target="_blank" class="spbc_manual_link">'
                . __('More info', 'security-malware-firewall')
                . '</a>'
        ),
        'unsafe_permissions' => array(
            'title' => __('You likely do need to modify file permissions', 'security-malware-firewall'),
            'desc'  => __('Do it via FTP or hosting control panel. Set 644 for files and 755 for folders. If you are not sure, contact your hosting provider.', 'security-malware-firewall')
                . '<br><br>'
                . '<a href="https://wordpress.org/documentation/article/changing-file-permissions/" target="_blank" class="spbc_manual_link">'
                . __('More info', 'security-malware-firewall')
                . '</a>'
        ),
    );

    if (!isset($recomendations[ $setting_id ])) {
        return;
    }

    wp_send_json($recomendations[ $setting_id ]);
}

// Ajax handler: send 2FA confirmation code (React settings UI)
function spbctGenerateAndSendConfirmationCode()
{
    global $spbc;

    spbc_check_ajax_referer('spbc_secret_nonce', 'security');

    $user = wp_get_current_user();
    if (isset($user->ID) && $user->ID > 0) {
        $email = $user->user_email;
    } else {
        $email = spbc_get_admin_email();
    }

    spbc_check_ajax_referer('spbc_secret_nonce', 'security');

    $confirmation_code = get_site_option('spbc_confirmation_code', false);
    $save_code         = true;

    // Code is outdated. Generate a new code
    if ( ! isset($confirmation_code['generate_time']) || $confirmation_code['generate_time'] + 10 * 60 < time()) {
        $confirmation_code = array(
            'code'          => rand(10000000, 99999999),
            'generate_time' => time(),
            'verified'      => false,
        );

        $save_code = update_site_option('spbc_confirmation_code', $confirmation_code);
    }

    if (isset($confirmation_code['code'])) {
        if ($save_code === true) {
            $mail_result = wp_mail(
                $email,
                $spbc->data["wl_brandname"] . esc_html__(' confirmation code ', 'security-malware-firewall') . get_home_url(),
                sprintf(
                    $spbc->data["wl_brandname"] . esc_html__('. Two-Factor Authentication Code on %s - %s', 'security-malware-firewall'),
                    get_home_url(),
                    $confirmation_code['code']
                )
            );
            if ($mail_result) {
                wp_send_json_success();
            } else {
                wp_send_json_error(__('Confirmation code not sent!', 'security-malware-firewall'));
            }
        } else {
            wp_send_json_error(__('Confirmation code not saved!', 'security-malware-firewall'));
        }
    } else {
        wp_send_json_error(__('Confirmation code generation error!', 'security-malware-firewall'));
    }
}

// Ajax handler: verify 2FA confirmation code (React settings UI)
function spbctCheckConfirmationCode()
{
    spbc_check_ajax_referer('spbc_secret_nonce', 'security');

    if ( ! isset($_POST['code'])) {
        wp_send_json_error('Confirmation code not provided!');
    }

    $code = filter_input(INPUT_POST, 'code', FILTER_SANITIZE_NUMBER_INT);

    $get_code = get_site_option('spbc_confirmation_code');

    if ($get_code && array_key_exists('code', $get_code) && array_key_exists('generate_time', $get_code)) {
        if ($get_code['code'] == $code && $get_code['generate_time'] + 10 * 60 > time()) { //Code is live for 10 minutes
            if (isset($get_code['verified']) && $get_code['verified'] === false) {
                $get_code['verified'] = true;
                update_site_option('spbc_confirmation_code', $get_code);
            }
            wp_send_json_success($get_code);
        } else {
            wp_send_json_error('Confirmation code is wrong or outdated!');
        }
    } else {
        wp_send_json_error('Could not check confirmation code!');
    }
}

/**
 * Ajax handler for checking renew banner
 */
function spbc_settings__check_renew_banner()
{
    spbc_check_ajax_referer('spbc_secret_nonce', 'security');
    global $spbc;
    wp_send_json(array(
        'close_renew_banner' => $spbc->data['notice_show'] == 0
            ? true
            : false
    ));
}

/**
 * Descriptions for scanner results actions.
 * @return string
 */
function spbc_bulk_actions_description()
{
    global $spbc;

    $guide_link = LinkConstructor::buildSimpleLink('https://research.cleantalk.org', 'major-signs-of-malware-on-an-infected-wordpress-site');
    $guide_text = sprintf(
        esc_html__('Check %s this guide %s out, it helps to identify a malware.', 'security-malware-firewall'),
        '<a href="' . esc_url($guide_link) . '" target=_blank>',
        '</a>'
    );

    $actions = ListTable::getRowActionsElementNamingData();

    $description = '<div id="spbcscan-scanner-caption">';
    $description .= '<div class="column">';
    $description .= '<ul>';
    $description .= '<h4>' . esc_html__('Available actions on the found files:', 'security-malware-firewall') . '</h4>';

    $action_description = array();
    foreach ($actions as $action) {
        // @todo description with tooltips
        // $action_description[] =
        // ' <u>' . $action['title'] . '</u>'
        // . ' <i class="spbc_popup_tip--spbc-icon---show spbc-icon-help-circled" spbc_tip_title="' . ucfirst( $action['title'] ) . '" spbc_tip_text="' . $action['tip'] . '"></i>';
        $action_description[] =
            '<li><strong>' . ucfirst($action['title']) . ':</strong> ' . $action['tip'] . '</li>';
    }

    $description .= implode('', $action_description);
    $description .= '</u>';
    $description .= __('The actions are available only after scanning your website.', 'security-malware-firewall');

    $description .= '</div>';
    $description .= '<div class="column">';
    $description .= '<div id="spbcscan-results-log-caption">';
    $description .= '<h4>' . esc_html__('File Scan Results:', 'security-malware-firewall') . '</h4>';
    $description .= '<p><b>OK</b> - ' . esc_html__('file is fine.', 'security-malware-firewall') . '</p>';
    $description .= '<p><b>APPROVED</b> - ' . esc_html__('file is approved by the user.', 'security-malware-firewall') . '</p>';
    $description .= '<p><b>APPROVED_BY_CT</b> - ' . esc_html__('file is approved by ' . $spbc->data["wl_brandname"] . '.', 'security-malware-firewall') . '</p>';
    $description .= '<p><b>MODIFIED</b> - ' . esc_html__('file is different from the original one.', 'security-malware-firewall') . '</p>';
    $description .= '<p><b>INFECTED</b> - ' . esc_html__('file is infected.', 'security-malware-firewall') . '</p>';
    $description .= '<p><b>QUARANTINED</b> - ' . esc_html__('file has been quarantined.', 'security-malware-firewall') . '</p>';
    $description .= '<p><b>UNKNOWN</b> - ' . esc_html__('file of unknown origin.', 'security-malware-firewall') . '</p>';
    $description .= '</div>';
    $description .= '</div>';
    $description .= '</div>';

    $description .= $guide_text;
    $description .= '<br><br>';
    $description .= '*<br>';
    $description .= esc_html__('Website total files - only executable files (*.php, *.html, *.htm, *.phtml, *.shtml, *.phar, *.odf), excluding quarantined files, zero-size files, and files over the 2 MB size limit.', 'security-malware-firewall');
    $description .= '<br>';
    $description .= '**<br>';
    $description .= esc_html__('Files scanned – the number of files checked. The scanner may include additional files if deemed necessary.', 'security-malware-firewall');

    return $description;
}

/**
 * Implementation of service_update_local_settings functionality
 */
add_action('spbc_before_returning_settings', 'spbc__send_local_settings_to_api');

/**
 * Hook action to untrack users outside UsersPassCheck roles array.
 */
add_action('spbc_before_returning_settings', array(\CleantalkSP\SpbctWP\UsersPassCheckModule\UsersPassCheckModel::class, 'hookUntrackUsers'));

function spbc__send_local_settings_to_api($settings)
{
    $api_key  = $settings['spbc_key'] ?: '';
    $settings = json_encode($settings);
    $hostname = preg_replace('/^(https?:)?(\/\/)?(www\.)?/', '', get_site_url());

    API::methodSendLocalSettings($api_key, $hostname, $settings);
}

add_action('spbc_before_returning_settings', 'spbc_cdn_checker__run_check_on_settings_change');

function spbc_cdn_checker__run_check_on_settings_change($settings)
{
    if ( isset($settings['secfw__get_ip__enable_cdn_auto_self_check']) && $settings['secfw__get_ip__enable_cdn_auto_self_check'] != 0) {
        SpbcCron::updateTask('cdn_check', 'spbc_cdn_checker__send_request', 86400, time() + 60);
    }
}

function spbc_settings_field__secfw__get_ip__get_labels()
{
    $options          = array();
    $options[]        = array('value' => 1, 'label' => __('Auto', 'security-malware-firewall'),);

    foreach (IP::$known_headers_collection as $key => $header ) {
        IP::get($header['slug'], [], true);
        $option_value = $header['name'];
        $option_value .= isset(IP::getInstance()->ips_stored[$header['slug']])
            ? ' (' . IP::getInstance()->ips_stored[$header['slug']] . ')'
            : ' (not provided)';
        $options[]    = array('value' => $key, 'label' => $option_value);
    }

    return $options;
}

/**
 * @return int|void
 */
function spbc_scanner__unsafe_permissions_count()
{
    global $spbc;
    $unsafe_permission = new Scanner\UnsafePermissionsModule\UnsafePermissionFunctions($spbc);

    return $unsafe_permission->getCountData();
}

/**
 * @return array
 */
function spbc_scanner_unsafe_permissions_data()
{
    global $spbc;
    $unsafe_permission = new Scanner\UnsafePermissionsModule\UnsafePermissionFunctions($spbc);

    return $unsafe_permission->getDataToAccordion();
}

/**
 * Wrapper for Cure log files counter.
 * @return int
 */
function spbc_scanner__cure_log_get_count_total()
{
    $cure_log = new Scanner\CureLog\CureLog();
    return $cure_log->getCountData();
}

/**
 * @return array|object
 */
function spbc_scanner__get_cure_log_data()
{
    $offset = 0;
    $amount = 20;
    if (isset($_POST['page'])) {
        $offset = ((int)$_POST['page'] - 1) * $amount;
    }
    $cure_log = new Scanner\CureLog\CureLog();
    return $cure_log->getDataToAccordion($offset, $amount);
}

/**
 * Prepare cure log table
 * @param $table
 * @return void
 */
function spbc_scanner__cure_log_data_prepare(&$table)
{
    if ($table->items_count) {
        foreach ($table->rows as $_key => $row) {
            // Add Cure action if file was not cure
            if ($row->cure_status === 'CURED') {
                unset($row->actions['cure']);
            }

            // rewrite on restored
            if ($row->cure_status === 'RESTORED' || $row->cure_status === 'FAILED') {
                unset($row->actions['restore']);
            }

            $cure_status_string = $row->cure_status === 'CURED'
                ? '<span class="spbc---green">' . $row->cure_status . '</span>'
                : '<span class="spbc---red">' . $row->cure_status . '</span>';
            // rewrite on restored
            if ($row->is_restored === '1') {
                $cure_status_string = '<span class="spbc---gray">' . $row->cure_status . '</span>';
            }

            $table->items[] = array(
                'cb'             => $row->fast_hash,
                'uid'            => $row->fast_hash,
                'actions'        => $row->actions,
                'real_path' => $row->real_path,
                'last_cure_date'       => $row->last_cure_date,
                'cure_status'          => $cure_status_string,
                'weak_spots_cured'   => $row->weak_spots_cured,
                'weak_spots_uncured'   => $row->weak_spots_uncured,
            );
        }
    }
}

function spbc_scanner__last_scan_info($direct_call = false)
{
    global $spbc;

    if ( ! $direct_call ) {
        spbc_check_ajax_referer('spbc_secret_nonce', 'security');
    }

    if ( ! empty($spbc->data['scanner']['last_scan'])) {
        $output = sprintf(
            __('The last scan of this website was on %s, website total files*: %d, files scanned**: %d.', 'security-malware-firewall'),
            date('M d, Y H:i:s', $spbc->data['scanner']['last_scan']),
            isset($spbc->data['scanner']['files_total']) ? $spbc->data['scanner']['files_total'] : $spbc->data['scanner']['last_scan_amount'],
            isset($spbc->data['scanner']['scanned_total']) ? $spbc->data['scanner']['scanned_total'] : null
        );
        if ($spbc->settings['scanner__outbound_links']) {
            $count_outbound_links = (string)spbc__get_count_outbound_links();
            $output .= sprintf(' ' . __('Outbound links found: %s.', 'security-malware-firewall'), $count_outbound_links);
        }
    } else {
        $output = __('Website hasn\'t been scanned yet.', 'security-malware-firewall');
    }

    if ( ! $direct_call ) {
        wp_send_json_success($output . spbc_get_next_scan_launch_time_text());
    }

    return $output . spbc_get_next_scan_launch_time_text();
}

/**
 * Get the string with next scan time description.
 * - "The next automatic scan is scheduled on %s."
 * @return string
 */
function spbc_get_next_scan_launch_time_text()
{
    $next_scan_time = spbc_get_next_scan_launch_time();
    return $next_scan_time
        ? sprintf(
            ' ' . __('The next automatic scan is scheduled on %s.', 'security-malware-firewall'),
            $next_scan_time
        )
        : '';
}

/**
 * Get the next scan launch time.
 * @return string
 */
function spbc_get_next_scan_launch_time()
{
    global $spbc;

    $task = \CleantalkSP\SpbctWP\Cron::getTask('scanner__launch');
    if ($spbc->settings['scanner__auto_start']
        && isset($task['next_call'])
    ) {
        return
            date('M d, Y H:i:s', $task['next_call'] + ((float)get_option('gmt_offset') * 3600)) . ' ' .
            spbc_wp_timezone_string();
    }
    return '';
}

/**
 * Generate HTML code for accordions to suggest user manual audit services.
 * @param $for string destination accordion name
 * @return string html
 */
function spbc__get_accordion_tab_info_block_html($for)
{
    global $spbc;

    $button_div = '';
    $show_exclaim_triangle = false;
    $email = spbc_get_admin_email();
    $website = get_home_url();
    $landing_page_link = LinkConstructor::buildCleanTalkLink(
        'banner_link_for_treatment',
        'wordpress-malware-removal',
        array(
            'email' => esc_attr($email),
            'website' => esc_attr($website),
        ),
        $domain = 'https://cleantalk.org'
    );

    $company_name = $spbc->default_data['wl_company_name'];
    if ($spbc->data["wl_mode_enabled"]) {
        $company_name = $spbc->data["wl_company_name"];
    }

    switch ($for) {
        case 'critical':
            //critical files accordion
            $info_block_out = __('With a high degree of probability, your site has been infected. If you need professional help 
        from security specialists, feel free to order', 'security-malware-firewall');

            $classes = 'notice notice-warning';
            $show_exclaim_triangle = true;

            //generate button
            $button_text = __('Request Malware removal', 'security-malware-firewall');
            $button_div = '<div style="text-align: center; padding: 10px">';
            $button_div .= '
                <a class="spbc_manual_link" target="_blank" href="' . $landing_page_link . '">'
                . '<i class="spbc-icon-link-ext"></i>&nbsp;&nbsp;'
                . $button_text
                . '</a>
                ';
            $button_div .= '</div>';
            break;
        case 'critical-for-widget':
            //banner for widget if critical files exist
            $info_block_out = __('With a high degree of probability, your site has been infected. If you need professional help 
        from security specialists, feel free to order', 'security-malware-firewall');

            $classes = 'spbc_widget_notice_critical';
            $show_exclaim_triangle = true;

            //generate button
            $button_text = __('Request Malware removal', 'security-malware-firewall');
            $button_div = '<div style="">';
            $button_div .= '
                <a class="spbc_manual_link" target="_blank" href="' . $landing_page_link . '">'
                . '<i class="spbc-icon-link-ext"></i>&nbsp;&nbsp;'
                . $button_text
                . '</a>
                ';
            $button_div .= '</div>';
            break;
        case 'suspicious':
            $info_block_out = '<p>' . __('If you are not sure of the results and cannot assess for yourself whether these files are dangerous or not, then we recommend sending these files to the cloud for analysis. Select suspicious files and click "Send for Analysis".', 'security-malware-firewall') . '</p>';
            $info_block_out .= '<p>' . __('Please, note, the size of file to send is restricted with 1024 Kb.', 'security-malware-firewall') . '</p>';
            if ( (int) $spbc->settings['scanner__schedule_send_heuristic_suspicious_files'] === 2 ) {
                $info_block_out .= '<p>'
                    . sprintf(
                        'Suspicious files are sent to the CleanTalk cloud to be analyzed by Cloud Malware scanner. If you do not want to send it to the cloud, turn this option off in the plugin %s settings %s',
                        '<a href="options-general.php?page=spbc&spbc_tab=settings_general#spbc_setting_scanner__heuristic_analysis">',
                        '</a>'
                    )
                    . '</p>';
            }
            $classes = 'notice notice-info';
            break;
        case 'analysis':
            // todo this was the same output as suspicious - removed for now
            return '';
        case 'unknown':
            $template = '
            <div>
                %MAIN_TEXT%
                <ul style="list-style-type: circle; padding-left: 2%">
                    <li>%OPTION_1%</li>
                    <li>%OPTION_2%<i setting="sending_for_analysis_rules" class="spbc_long_description__show spbc-icon-help-circled"></i></li>
                </ul>
            </div>
            ';
            $main_text = __('If you are not sure about these files, you have two options,', 'security-malware-firewall');
            $guide_link = LinkConstructor::buildSimpleLink('https://research.cleantalk.org', 'major-signs-of-malware-on-an-infected-wordpress-site');
            $option1 = sprintf(
                esc_html__('Check %s this guide %s out, it helps to identify a malware.', 'security-malware-firewall'),
                '<a href="' . esc_url($guide_link) . '" target=_blank>',
                '</a>'
            );
            $option2 = __('Send it to the cloud where files will be passed through additional tests (Send for Analysis).', 'security-malware-firewall');
            $template = str_replace('%MAIN_TEXT%', $main_text, $template);
            $template = str_replace('%OPTION_1%', $option1, $template);
            $template = str_replace('%OPTION_2%', $option2, $template);
            $info_block_out = $template;
            $classes = 'notice notice-info';
            break;
        case 'sending_for_analysis_rules':
            $template = '
            <div>
                <p>%MAIN_TEXT_1%</p>
                <p>%MAIN_TEXT_2%</p>
                <ul style="list-style-type: disc; padding-left: 5%">
                    <li>%OPTION_1%</li>
                    <li>%OPTION_2%</li>
                    <li>%OPTION_3%</li>
                </ul>
                <p>%MAIN_TEXT_3%</p>
                <ul style="list-style-type: disc; padding-left: 5%">
                    <li>%OPTION_4%</li>
                </ul>
                <p>%MAIN_TEXT_4%</p>
            </div>
            ';
            $main_text_1 = __('Send the file for cloud analysis. After the file is sent, the file is available in the tab "Analysis log". Read more about analysis results in the appropriate tab. ', 'security-malware-firewall');
            $main_text_2 = __('The file sent for analysis must meet the following requirements:', 'security-malware-firewall');
            $main_text_3 = __('For "Unknown" files category, file extension should be from the list of allowed extensions:', 'security-malware-firewall');
            $main_text_4 = __('If any of requirements are not met, the action for file is not available.', 'security-malware-firewall');
            $option1 = __('the file was not ever denied or approved by ' . $company_name . ' team', 'security-malware-firewall');
            $option2 = __('the file sending is not already scheduled during common scan process', 'security-malware-firewall');
            $option3 = __('the file size is larger than zero and less than 1Mb', 'security-malware-firewall');
            $option4 = __('.php*, .html, .htm, .phtml, shtml, .phar, .odf', 'security-malware-firewall');
            $template = str_replace('%MAIN_TEXT_1%', $main_text_1, $template);
            $template = str_replace('%MAIN_TEXT_2%', $main_text_2, $template);
            $template = str_replace('%MAIN_TEXT_3%', $main_text_3, $template);
            $template = str_replace('%MAIN_TEXT_4%', $main_text_4, $template);
            $template = str_replace('%OPTION_1%', $option1, $template);
            $template = str_replace('%OPTION_2%', $option2, $template);
            $template = str_replace('%OPTION_3%', $option3, $template);
            $template = str_replace('%OPTION_4%', $option4, $template);
            $info_block_out = Escape::escKsesPreset($template, 'spbc_settings__sending_for_analysis_rules');
            $classes = '';
            break;
        case 'skipped':
            $template = '
                <div>
                    <p>%HEADER_P%:</p>
                    <ul style="list-style-type: circle; padding-left: 2%">
                        <li>%NOT_EMPTY%</li>
                        <li>%SIGN_RESTRICT%</li>
                        <li>%HEUR_RESTRICT%</li>
                    </ul>
                    <p>%SUGGEST_FM%</p>
                    <p>%SUGGEST_CONTACT% <a href="%LINK%">%LINK%</a></p>
                </div>
            ';
            $header_p = __('Please, note the restrictions', 'security-malware-firewall');
            $empty_info = __('Scanner does not check and report about empty files (file size is 0).', 'security-malware-firewall');
            $signatures_restrict = __('Signatures module does not check files with size larger then %d Kb.', 'security-malware-firewall');
            $value = \CleantalkSP\Common\Scanner\SignaturesAnalyser\Controller::SIGNATURES_SCAN_MAX_FILE_SIZE / 1024;
            $signatures_restrict = sprintf($signatures_restrict, $value);
            $heuristic_restrict = __('Heuristic module does not check files with size larger then %d Kb. ', 'security-malware-firewall');
            $value = \CleantalkSP\Common\Scanner\HeuristicAnalyser\HeuristicAnalyser::HEURISTIC_SCAN_MAX_FILE_SIZE / 1024;
            $heuristic_restrict = sprintf($heuristic_restrict, $value);

            $suggest_file_manger = __('You can use a file manager to manage files in the list.', 'security-malware-firewall');
            $suggest_contact = __('If you are sure that a file should be checked please let us know', 'security-malware-firewall');
            $link = 'https://wordpress.org/support/plugin/security-malware-firewall/';
            $template = str_replace('%HEADER_P%', $header_p, $template);
            $template = str_replace('%SIGN_RESTRICT%', $signatures_restrict, $template);
            $template = str_replace('%HEUR_RESTRICT%', $heuristic_restrict, $template);
            $template = str_replace('%NOT_EMPTY%', $empty_info, $template);
            $template = str_replace('%SUGGEST_FM%', $suggest_file_manger, $template);
            $template = str_replace('%SUGGEST_CONTACT%', $suggest_contact, $template);
            $template = str_replace('%LINK%', $link, $template);
            $info_block_out = $template;
            $classes = 'notice notice-info';
            break;
        case 'outbound_links':
            $info_block_out = '<p>'
                . __('Viruses post links to lead site visitors to compromised and fishing sites. It is a good idea to check links that you have not seen before. To manage the option go to the', 'security-malware-firewall')
                . ' '
                . '<a href="options-general.php?page=spbc&spbc_tab=settings_general#scanner_setting">'
                . __('scanner settings', 'security-malware-firewall')
                . '</a></p>';
            $classes = 'notice notice-info';
            break;
        default:
            return '';
    }

    $out = '<div id="spbc_notice_cloud_analysis_feedback" class="' . $classes . '" style="margin-left: 0px; margin-right: 0px;">';
    $out .= '<p>';
    // show triangle
    $out .= $show_exclaim_triangle
        ? '<img src="' . SPBC_PATH . '/images/att_triangle.png" alt="attention" style="margin-bottom:-1px">&nbsp'
        : '';
    // complete the suggestion text
    $out .= $info_block_out;
    $out .= '</p>';
    // add button if needs
    $out .= $button_div;

    $out .= '</div>';

    return $out;
}

/**
 * Returns notice HTML about automatic sending is enabled.
 * @param int $scheduled_count count of files scheduled to send
 * @return string HTML
 */
function spbct_get_automatic_files_send_notice_html($scheduled_count)
{
    $html = '<div class="notice notice-info">';
    $html .= '<p>';
    $html .= '<img src="' . SPBC_PATH . '/images/att_triangle.png" alt="attention" style="margin-bottom:-1px"> ';
    $html .= "The automatic sending files for Cloud analysis is enabled in the plugin settings. Files count: $scheduled_count";
    $html .= '</p>';
    $html .= '</div>';
    return $html;
}

/**
 * Returns list of files scheduled to send for analysis due scan process.
 * Uses state->data if available, wpdb query if not.
 * @return array Array of fast_hash
 */
function spbc_get_list_of_scheduled_suspicious_files_to_send()
{
    global $wpdb, $spbc;

    if ( !isset($spbc->data['scheduled_suspicious_files_to_send']) ) {
        $query = 'SELECT fast_hash FROM ' . SPBC_TBL_SCAN_FILES . ' WHERE pscan_pending_queue = 1';
        $result = array_keys($wpdb->get_results($query, OBJECT_K));
        $spbc->data['scheduled_suspicious_files_to_send'] = $result;
    }

    return (array)$spbc->data['scheduled_suspicious_files_to_send'];
}

function spbc__get_count_outbound_links()
{
    global $wpdb;

    return $wpdb->get_var(
        "SELECT COUNT(*) FROM " . SPBC_TBL_SCAN_LINKS . ";"
    );
}

/**
 * Drop current data to defaults from state->default_data.
 * On exceptions roll back to old current state.
 * Attention! This function does not save the state to options,
 * only the current state object will be handled.
 * @param \CleantalkSP\SpbctWP\State $spbc current state
 * @return \CleantalkSP\SpbctWP\State state dropped
 */
function spbc_drop_to_defaults_on_key_clearance(\CleantalkSP\SpbctWP\State $spbc)
{
    $old_data = $spbc->data;
    try {
        $keep_data_keys = array(
            'scanner',
            'display_scanner_warnings',
            'errors'
        );
        $spbc->error_delete_all(true);
        foreach ( $spbc->default_data as $key => $value) {
            if (!in_array($key, $keep_data_keys)) {
                $spbc->data[$key] = $value;
            }
        }
    } catch (Exception $e) {
        $spbc->data = $old_data;
    }

    return $spbc;
}

/**
 * Get HTML content for change role template
 * @return void *echo
 */
function spbc_change_role_template()
{
    spbc_check_ajax_referer('spbc_secret_nonce', 'security');

    $user_login = sanitize_text_field($_POST['data']['user_login']);
    $role = sanitize_text_field($_POST['data']['role']);

    $template = sprintf(
        '<div> <p>%s <strong>%s</strong></p> <p>%s</p> %s <br> <button id="spbc_change_role_button" class="button-primary">%s</button> </div>',
        __('Change role for', 'security-malware-firewall'),
        $user_login,
        __('It will be applied immediately, be careful with this action.', 'security-malware-firewall'),
        '%ROLE_SELECT%',
        __('Change Role', 'security-malware-firewall')
    );

    $role_select = '<select name="role" id="spbc_role_select">';
    $all_roles = wp_roles()->role_names;
    foreach ($all_roles as $key => $value) {
        if ($key == $role) {
            $role_select .= '<option value="' . $key . '" selected>' . $value . '</option>';
        } else {
            $role_select .= '<option value="' . $key . '">' . $value . '</option>';
        }
    }
    $role_select .= '</select>';
    $role_select .= '<div id="spbc-role-capabilities-list" style="margin-top:10px;"></div>';
    $template = str_replace('%ROLE_SELECT%', $role_select, $template);

    echo $template;

    die();
}

/**
 * Change role for user
 * @param bool $direct_call if true, do not check nonce
 * @return array|void
 */
function spbc_change_role($direct_call = false)
{
    if ( ! $direct_call ) {
        spbc_check_ajax_referer('spbc_secret_nonce', 'security');
    }

    if ( ! current_user_can('promote_users') ) {
        if ( ! $direct_call ) {
            wp_send_json_error(array('message' => __('You are not allowed to change role for this user.', 'security-malware-firewall')));
        } else {
            return array('success' => false, 'message' => __('You are not allowed to change role for this user.', 'security-malware-firewall'));
        }
    }

    $user_id = sanitize_text_field($_POST['user_id']);
    $new_role = sanitize_text_field($_POST['new_role']);

    // check that new role exists
    if (!isset(wp_roles()->role_names[$new_role])) {
        if ( ! $direct_call ) {
            wp_send_json_error(array('message' => __('Role not found.', 'security-malware-firewall')));
        } else {
            return array('success' => false, 'message' => __('Role not found.', 'security-malware-firewall'));
        }
    }

    $user = get_user_by('id', $user_id);
    if ( ! $user ) {
        if ( ! $direct_call ) {
            wp_send_json_error(array('message' => __('User not found.', 'security-malware-firewall')));
        } else {
            return array('success' => false, 'message' => __('User not found.', 'security-malware-firewall'));
        }
    }

    $user->set_role($new_role);

    if ( ! $direct_call ) {
        wp_send_json_success(array('message' => __('Role changed successfully.', 'security-malware-firewall')));
    } else {
        return array('success' => true, 'message' => __('Role changed successfully.', 'security-malware-firewall'));
    }
}

add_action('wp_ajax_spbc_get_role_capabilities', 'spbc_get_role_capabilities_callback');

/**
 * Get translations for capabilities.
 * @return array
 */
function spbc_get_capabilities_translations()
{
    return array(
        // Common
        'read'                   => __('Read', 'security-malware-firewall'),
        'edit_posts'             => __('Edit posts', 'security-malware-firewall'),
        'delete_posts'           => __('Delete posts', 'security-malware-firewall'),
        'publish_posts'          => __('Publish posts', 'security-malware-firewall'),
        'upload_files'           => __('Upload files', 'security-malware-firewall'),
        'edit_published_posts'   => __('Edit published posts', 'security-malware-firewall'),
        'delete_published_posts' => __('Delete published posts', 'security-malware-firewall'),
        'edit_others_posts'      => __('Edit others\' posts', 'security-malware-firewall'),
        'delete_others_posts'    => __('Delete others\' posts', 'security-malware-firewall'),
        'manage_categories'      => __('Manage categories', 'security-malware-firewall'),
        'edit_private_posts'     => __('Edit private posts', 'security-malware-firewall'),
        'delete_private_posts'   => __('Delete private posts', 'security-malware-firewall'),
        'read_private_posts'     => __('Read private posts', 'security-malware-firewall'),

        // Pages
        'edit_pages'             => __('Edit pages', 'security-malware-firewall'),
        'delete_pages'           => __('Delete pages', 'security-malware-firewall'),
        'publish_pages'          => __('Publish pages', 'security-malware-firewall'),
        'edit_published_pages'   => __('Edit published pages', 'security-malware-firewall'),
        'delete_published_pages' => __('Delete published pages', 'security-malware-firewall'),
        'edit_others_pages'      => __('Edit others\' pages', 'security-malware-firewall'),
        'delete_others_pages'    => __('Delete others\' pages', 'security-malware-firewall'),
        'edit_private_pages'     => __('Edit private pages', 'security-malware-firewall'),
        'delete_private_pages'   => __('Delete private pages', 'security-malware-firewall'),
        'read_private_pages'     => __('Read private pages', 'security-malware-firewall'),

        // Users
        'list_users'             => __('List users', 'security-malware-firewall'),
        'edit_users'             => __('Edit users', 'security-malware-firewall'),
        'create_users'           => __('Create users', 'security-malware-firewall'),
        'delete_users'           => __('Delete users', 'security-malware-firewall'),
        'promote_users'          => __('Promote users', 'security-malware-firewall'),
        'add_users'              => __('Add users', 'security-malware-firewall'),
        'remove_users'           => __('Remove users', 'security-malware-firewall'),

        // Plugins and themes
        'install_plugins'        => __('Install plugins', 'security-malware-firewall'),
        'update_plugins'         => __('Update plugins', 'security-malware-firewall'),
        'delete_plugins'         => __('Delete plugins', 'security-malware-firewall'),
        'activate_plugins'       => __('Activate plugins', 'security-malware-firewall'),
        'deactivate_plugins'       => __('Deactivate plugins', 'security-malware-firewall'),
        'edit_plugins'           => __('Edit plugins', 'security-malware-firewall'),
        'install_themes'         => __('Install themes', 'security-malware-firewall'),
        'update_themes'          => __('Update themes', 'security-malware-firewall'),
        'delete_themes'          => __('Delete themes', 'security-malware-firewall'),
        'switch_themes'          => __('Switch themes', 'security-malware-firewall'),
        'edit_themes'            => __('Edit themes', 'security-malware-firewall'),

        // Option and settings management
        'manage_options'         => __('Manage options', 'security-malware-firewall'),
        'edit_theme_options'     => __('Edit theme options', 'security-malware-firewall'),
        'customize'              => __('Customize site', 'security-malware-firewall'),
        'edit_dashboard'         => __('Edit dashboard', 'security-malware-firewall'),
        'manage_links'           => __('Manage links', 'security-malware-firewall'),
        'unfiltered_html'        => __('Unfiltered HTML', 'security-malware-firewall'),
        'edit_files'             => __('Edit files', 'security-malware-firewall'),

        // Export/import
        'export'                 => __('Export', 'security-malware-firewall'),
        'import'                 => __('Import', 'security-malware-firewall'),

        // Media
        'edit_attachments'       => __('Edit attachments', 'security-malware-firewall'),
        'delete_attachments'     => __('Delete attachments', 'security-malware-firewall'),
        'unfiltered_upload'     => __('Unfiltered upload', 'security-malware-firewall'),

        // Comments
        'moderate_comments'      => __('Moderate comments', 'security-malware-firewall'),
        'edit_comment'           => __('Edit comment', 'security-malware-firewall'),
        'edit_comments'          => __('Edit comments', 'security-malware-firewall'),
        'delete_comment'         => __('Delete comment', 'security-malware-firewall'),
        'delete_comments'        => __('Delete comments', 'security-malware-firewall'),

        // Other capabilities
        'manage_privacy_options' => __('Manage privacy options', 'security-malware-firewall'),
        'edit_css'               => __('Edit CSS', 'security-malware-firewall'),
        'edit_custom_css'        => __('Edit custom CSS', 'security-malware-firewall'),
        'edit_snippets'          => __('Edit code snippets', 'security-malware-firewall'),
        'update_core'          => __('Update Core', 'security-malware-firewall'),

        // WooCommerce
        'manage_woocommerce'     => __('Manage WooCommerce', 'security-malware-firewall'),
        'view_woocommerce_reports' => __('View WooCommerce reports', 'security-malware-firewall'),
        'edit_products'          => __('Edit products', 'security-malware-firewall'),
        'publish_products'       => __('Publish products', 'security-malware-firewall'),
        'delete_products'        => __('Delete products', 'security-malware-firewall'),
        'edit_shop_orders'       => __('Edit shop orders', 'security-malware-firewall'),
        'edit_shop_coupons'      => __('Edit shop coupons', 'security-malware-firewall'),
        'edit_shop_webhooks'     => __('Edit shop webhooks', 'security-malware-firewall'),
    );
}

/**
 * AJAX: Get role capabilities
 */
function spbc_get_role_capabilities_callback()
{
    spbc_check_ajax_referer('spbc_secret_nonce', 'security');

    if ( ! current_user_can('promote_users') ) {
        wp_send_json_error(__('You are not allowed to view capabilities.', 'security-malware-firewall'));
    }

    $role = isset($_POST['role']) ? sanitize_text_field($_POST['role']) : '';
    if ( ! $role ) {
        wp_send_json_error(__('Role is required.', 'security-malware-firewall'));
    }

    global $wp_roles;
    if ( ! isset($wp_roles) ) {
        $wp_roles = new WP_Roles();
    }
    $roles = $wp_roles->get_names();
    if ( ! isset($roles[$role]) ) {
        wp_send_json_error(__('Role not found.', 'security-malware-firewall'));
    }

    $capabilities = isset($wp_roles->roles[$role]['capabilities']) ? $wp_roles->roles[$role]['capabilities'] : array();

    $capabilities = array_filter(
        $capabilities,
        function ($_enabled, $cap) {
            return !preg_match('/^level_\d+$/', $cap);
        },
        ARRAY_FILTER_USE_BOTH
    );

    $translations = spbc_get_capabilities_translations();
    $core_caps_list = [];
    $other_caps_list = [];
    foreach ($capabilities as $cap => $enabled) {
        if (isset($translations[$cap])) {
            $core_caps_list[] = [
                'cap' => $cap,
                'enabled' => (bool)$enabled,
                'label' => $translations[$cap],
            ];
        } else {
            $other_caps_list[] = [
                'cap' => $cap,
                'enabled' => (bool)$enabled,
                'label' => $cap,
            ];
        }
    }

    wp_send_json_success([
        'core' => $core_caps_list,
        'other' => $other_caps_list,
    ]);
}
