plugin = $plugin; } public function init() { $plugin_basename = plugin_basename(A360_LOGIN_AVATAR_PANEL_FILE); add_action('admin_menu', array($this, 'add_settings_page')); add_action('admin_init', array($this, 'register_settings')); add_action('admin_enqueue_scripts', array($this, 'enqueue_admin_assets')); add_action('admin_footer-plugins.php', array($this, 'render_plugin_details_modal')); add_action('admin_post_a360_login_avatar_reset_counter', array($this, 'reset_login_counter')); add_action('wp_dashboard_setup', array($this, 'add_dashboard_widget')); add_filter('plugin_action_links_' . $plugin_basename, array($this, 'add_plugin_action_links')); add_filter('plugin_row_meta', array($this, 'add_plugin_row_meta'), 10, 2); } public function add_settings_page() { add_options_page( __('Login Avatar Panel', 'a360-login-avatar-panel'), __('Login Avatar Panel', 'a360-login-avatar-panel'), 'manage_options', 'a360-login-avatar-panel', array($this, 'render_settings_page') ); } public function register_settings() { register_setting( 'a360_login_avatar_panel', A360_Login_Avatar_Panel::OPTION_NAME, array( 'type' => 'array', 'sanitize_callback' => 'a360_login_avatar_panel_sanitize_settings', 'default' => $this->plugin->defaults(), ) ); } public function enqueue_admin_assets($hook_suffix) { $should_load_styles = in_array($hook_suffix, array('plugins.php', 'index.php', 'settings_page_a360-login-avatar-panel'), true); if ($should_load_styles) { wp_enqueue_style( 'a360-login-avatar-panel-admin', plugins_url('assets/admin.css', A360_LOGIN_AVATAR_PANEL_FILE), array(), A360_Login_Avatar_Panel::VERSION ); } if ('plugins.php' === $hook_suffix) { add_thickbox(); wp_enqueue_script( 'a360-login-avatar-panel-admin', plugins_url('assets/admin.js', A360_LOGIN_AVATAR_PANEL_FILE), array(), A360_Login_Avatar_Panel::VERSION, true ); } if ('settings_page_a360-login-avatar-panel' === $hook_suffix) { wp_enqueue_media(); wp_enqueue_script( 'a360-login-avatar-panel-admin', plugins_url('assets/admin.js', A360_LOGIN_AVATAR_PANEL_FILE), array(), A360_Login_Avatar_Panel::VERSION, true ); wp_localize_script( 'a360-login-avatar-panel-admin', 'a360LoginAvatarPanelAdmin', array( 'noPanel' => __('No panel', 'a360-login-avatar-panel'), 'chooseLoginBackground' => __('Choose login background', 'a360-login-avatar-panel'), 'useThisImage' => __('Use this image', 'a360-login-avatar-panel'), ) ); } } public function add_plugin_action_links($links) { $settings_url = menu_page_url('a360-login-avatar-panel', false); $security_url = add_query_arg('tab', 'security', $settings_url); $plugin_links = array( 'settings' => sprintf( '%s', esc_url($settings_url), esc_html__('Settings', 'a360-login-avatar-panel') ), 'security' => sprintf( '%s', esc_url($security_url), esc_html__('Security', 'a360-login-avatar-panel') ), ); return array_merge($plugin_links, $links); } public function add_plugin_row_meta($links, $file) { if (plugin_basename(A360_LOGIN_AVATAR_PANEL_FILE) !== $file) { return $links; } $details_url = '#TB_inline?width=760&height=620&inlineId=a360-login-avatar-panel-details'; $security_url = add_query_arg('tab', 'security', menu_page_url('a360-login-avatar-panel', false)); $links[] = sprintf( '%s', esc_url($details_url), esc_attr__('View A360 Login Avatar Panel details', 'a360-login-avatar-panel'), esc_html__('View details', 'a360-login-avatar-panel') ); $links[] = sprintf( '%s', esc_url($security_url), esc_html__('Security report', 'a360-login-avatar-panel') ); $links[] = sprintf( '%s', esc_url('https://www.gnu.org/licenses/gpl-2.0.html'), esc_html__('GPLv2 license', 'a360-login-avatar-panel') ); return $links; } public function render_plugin_details_modal() { ?>
render_failed_logins_report('dashboard'); } public function reset_login_counter() { if (! current_user_can('manage_options')) { wp_die(esc_html__('You do not have permission to perform this action.', 'a360-login-avatar-panel')); } check_admin_referer('a360_login_avatar_reset_counter'); $this->plugin->reset_lockout_counter(); wp_safe_redirect( add_query_arg( array( 'page' => 'a360-login-avatar-panel', 'tab' => 'security', 'counter-reset' => '1', '_wpnonce' => wp_create_nonce('a360_login_avatar_counter_reset_notice'), ), admin_url('options-general.php') ) ); exit; } private function render_failed_logins_report($context) { $failed_events = $this->plugin->get_failed_login_events(); $successful_events = $this->plugin->get_successful_login_events(); $failed_last_24h = $this->plugin->count_failed_logins(DAY_IN_SECONDS); $successful_last_24h = $this->plugin->count_successful_logins(DAY_IN_SECONDS); $risk = $this->get_failed_login_risk($failed_last_24h); $chart_points = $this->get_login_chart_points($failed_events, $successful_events); $max_count = max(1, max(wp_list_pluck($chart_points, 'failed')), max(wp_list_pluck($chart_points, 'successful'))); $failed_polyline = $this->build_dashboard_chart_polyline($chart_points, $max_count, 'failed'); $successful_polyline = $this->build_dashboard_chart_polyline($chart_points, $max_count, 'successful'); $title_id = 'a360-login-risk-chart-title-' . sanitize_html_class($context); $failed_24h_label = sprintf( /* translators: %d: number of failed login attempts in the past 24 hours. */ __('%d failed login attempts in the past 24 hours', 'a360-login-avatar-panel'), $failed_last_24h ); $failed_heading = sprintf( /* translators: %s: formatted number of failed login attempts in the past 24 hours. */ _n('%s failed login attempt in the past 24 hours', '%s failed login attempts in the past 24 hours', $failed_last_24h, 'a360-login-avatar-panel'), number_format_i18n($failed_last_24h) ); ?> = 200) { return array( 'key' => 'high', 'message' => __('Your site is currently at high risk for brute force activity.', 'a360-login-avatar-panel'), ); } if ($count >= 25) { return array( 'key' => 'medium', 'message' => __('Your site is currently at medium risk for brute force activity.', 'a360-login-avatar-panel'), ); } return array( 'key' => 'low', 'message' => __('Your site currently has a low level of failed login attempts.', 'a360-login-avatar-panel'), ); } private function get_login_chart_points($failed_events, $successful_events) { $points = array(); for ($day_offset = 3; $day_offset >= 0; $day_offset--) { $timestamp = strtotime('-' . $day_offset . ' days', time()); $key = wp_date('Y-m-d', $timestamp); $points[$key] = array( 'label' => wp_date('m-d', $timestamp), 'failed' => 0, 'successful' => 0, ); } foreach ($failed_events as $event) { $key = wp_date('Y-m-d', absint($event)); if (isset($points[$key])) { $points[$key]['failed']++; } } foreach ($successful_events as $event) { $key = wp_date('Y-m-d', absint($event)); if (isset($points[$key])) { $points[$key]['successful']++; } } return array_values($points); } private function build_dashboard_chart_polyline($points, $max_count, $metric) { $coordinates = array(); $count = count($points); foreach ($points as $index => $point) { $x = 44 + ($index * (692 / max(1, $count - 1))); $y = 240 - (($point[$metric] / max(1, $max_count)) * 220); $coordinates[] = round($x, 2) . ',' . round($y, 2); } return implode(' ', $coordinates); } private function render_login_customization_settings($settings) { $option_name = A360_Login_Avatar_Panel::OPTION_NAME; $background_color = sanitize_hex_color($settings['background_color'] ?? '') ?: '#111119'; $background_image = ! empty($settings['background_image']) ? esc_url($settings['background_image']) : ''; ?>