| Current Path : /home/flapst5/ekmtoronto.com/wp-content/plugins/wp-defender/src/controller/ |
| Current File : /home/flapst5/ekmtoronto.com/wp-content/plugins/wp-defender/src/controller/class-dashboard.php |
<?php
/**
* Handles the main admin page.
*
* @package WP_Defender\Controller
*/
namespace WP_Defender\Controller;
use WP_Defender\Component\Audit;
use WP_Defender\Event;
use Calotes\Helper\HTTP;
use Calotes\Helper\Route;
use WP_Defender\Model\Setting\Login_Lockout;
use WP_Defender\Model\Setting\Notfound_Lockout;
use WP_Defender\Model\Setting\User_Agent_Lockout;
use WP_Defender\Traits\Defender_Dashboard_Client;
use WP_Defender\Traits\IO;
use Calotes\Component\Request;
use Calotes\Component\Response;
use WP_Defender\Traits\Formats;
use WP_Defender\Behavior\WPMUDEV;
use WP_Defender\Component\Feature_Modal;
use WP_Defender\Component\Hub_Connector as Hub_Connector_Component;
use WP_Defender\Model\Setting\Audit_Logging as Audit_Logging_Settings;
use WP_Defender\Model\Audit_Log;
use WP_Defender\Model\Setting\Global_Ip_Lockout;
use WP_Defender\Component\Config\Config_Hub_Helper;
use WP_Defender\Component\IP\Global_IP as Global_IP_Component;
use WP_Defender\Model\Setting\Session_Protection;
use WP_Defender\Controller\Session_Protection as Session_Protection_Controller;
/**
* Handles the main admin page.
*/
class Dashboard extends Event {
use IO;
use Formats;
use Defender_Dashboard_Client;
/**
* The slug identifier for this controller.
*
* @var string
*/
public $slug = 'wp-defender';
/**
* Site option key for the one-time report-schedule upgrade notice.
*/
public const REPORT_SCHEDULE_NOTICE_OPTION = 'wd_show_report_schedule_notice';
/**
* Site-wide dismissal key for the Dashboard plugin required modal.
*/
public const DASHBOARD_REQUIRED_NOTICE_OPTION = 'wpdef_dashboard_required_notice_dismissed';
/**
* Initializes the model and service, registers routes, and sets up scheduled events if the model is active.
*/
public function __construct() {
$this->attach_behavior( WPMUDEV::class, WPMUDEV::class );
$this->add_main_page();
$this->register_routes();
add_action( 'defender_enqueue_assets', array( $this, 'enqueue_assets' ) );
add_filter( 'custom_menu_order', '__return_true' );
add_filter( 'menu_order', array( $this, 'menu_order' ) );
add_filter( 'plugins_api', array( $this, 'filter_dashboard_plugin_info' ), 101, 3 );
add_action( 'admin_init', array( $this, 'maybe_redirect_notification_request' ), 99 );
}
/**
* Because we move the notifications on separate modules, so links from HUB should be redirected to correct URL.
*
* @return void
*/
public function maybe_redirect_notification_request(): void {
$page = HTTP::get( 'page' );
if ( ! in_array( $page, array( 'wdf-scan', 'wdf-ip-lockout', 'wdf-hardener', 'wdf-logging' ), true ) ) {
return;
}
$view = HTTP::get( 'view' );
if ( in_array( $view, array( 'reporting', 'notification', 'report' ), true ) ) {
wp_safe_redirect( network_admin_url( 'admin.php?page=wdf-notification' ) );
exit;
}
}
/**
* Filter out the defender menu for changing text.
*
* @param array $menu_order The current menu order.
*
* @return array
*/
public function menu_order( $menu_order ) {
global $submenu;
if ( isset( $submenu['wp-defender'] ) ) {
$defender_menu = $submenu['wp-defender'];
$defender_menu[0][0] = esc_html__( 'Dashboard', 'wpdef' );
$defender_menu = array_values( $defender_menu );
// Change the global $submenu variable, because otherwise the menu name/order will not change.
$submenu['wp-defender'] = $defender_menu; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
}
global $menu;
// Get the total scanning active issues.
$count = wd_di()->get( \WP_Defender\Component\Scan::class )->indicator_issue_count();
$indicator = $count > 0
? ' <span class="update-plugins wd-issue-indicator-sidebar"></span>'
: null;
foreach ( $menu as $k => $item ) {
if ( 'wp-defender' === $item[2] ) {
// Add a badge next to the "Defender" menu item in the global $menu variable.
$menu[ $k ][0] .= $indicator; // phpcs:ignore WordPress.WP.GlobalVariablesOverride.Prohibited
}
}
return $menu_order;
}
/**
* Registers the main page in the WordPress admin menu.
*/
protected function add_main_page() {
$this->register_page(
$this->get_page_title(),
$this->parent_slug,
array( $this, 'main_view' ),
null,
$this->get_menu_icon(),
$this->get_menu_title()
);
}
/**
* Renders the main view for this page.
*/
public function main_view() {
$this->render( 'main' );
}
/**
* Page-specific data for a concrete controller.
*
* @return array
*/
protected function get_page_data(): array {
$security_tweaks = wd_di()->get( \WP_Defender\Controller\Security_Tweaks::class );
$security_tweaks->refresh_tweaks_status();
$security_tweaks_data = $security_tweaks->dashboard_widget();
$audit_model = wd_di()->get( Audit_Logging_Settings::class );
$enabled_audit = $audit_model->is_active();
// Audit summary.
$audit_events_logged = 0;
$last_event_time = esc_html__( '-', 'wpdef' );
if ( $enabled_audit ) {
$audit_events_logged = Audit::get_audit_events_per_week();
$audit_last = Audit_Log::get_last();
if ( is_object( $audit_last ) ) {
$last_event_time = $this->get_date( $audit_last->timestamp );
}
}
// Firewall summary.
$firewall = wd_di()->get( Firewall::class )->get_summary();
// Different lockout types.
$enabled_login = wd_di()->get( Login_Lockout::class )->enabled;
$enabled_nf = wd_di()->get( Notfound_Lockout::class )->enabled;
$enabled_ua = wd_di()->get( User_Agent_Lockout::class )->enabled;
return array(
'defenderSetupNonce' => wp_create_nonce( 'defender_quick_setup' ),
'securityTweaks' => $security_tweaks_data['summary']['issues_count'],
'scanData' => array(
'numberIssues' => wd_di()->get( \WP_Defender\Component\Scan::class )->indicator_issue_count(),
'settings' => wd_di()->get( \WP_Defender\Model\Setting\Scan::class )->export(),
// Scan routes & nonces are set above.
),
'firewallData' => array(
'enabledLocalFirewall' => $enabled_login || $enabled_nf || $enabled_ua,
'enabledLogin' => $enabled_login,
'enabledNotFound' => $enabled_nf,
'enabledUserAgent' => $enabled_ua,
'loginLockoutMonth' => $firewall['lockout_login_this_month'],
'nfLockoutMonth' => $firewall['lockout_404_this_month'],
'uaLockoutMonth' => $firewall['lockout_ua_this_month'],
'antibot' => wd_di()->get( Antibot_Global_Firewall::class )->data_frontend(),
),
'site_id' => wd_di()->get( WPMUDEV::class )->get_site_id(),
'auditData' => array(
'enabled' => $enabled_audit,
'eventsLogged' => $audit_events_logged,
'lastEvent' => $last_event_time,
),
'sessionProtection' => wd_di()->get( Session_Protection::class )->export(),
'showReportScheduleNotice' => ! defender_is_wp_org_version()
&& (bool) get_site_option( self::REPORT_SCHEDULE_NOTICE_OPTION, false ),
);
}
/**
* Enqueues scripts and styles for this page.
* Only enqueues assets if the page is active.
*/
public function enqueue_assets() {
if ( ! $this->is_page_active() ) {
return;
}
$wizard_action = HTTP::get( 'wizard_action' );
$wizard_source = HTTP::get( 'source' );
// Fallback completion marker for setup wizard integrations rendered on dashboard.
// This prevents onboarding loops if client-side completion request fails.
if (
'setup_wizard' === $wizard_source
&& in_array( $wizard_action, array( 'view_results', 'close_wizard', 'finish_wizard' ), true )
&& current_user_can( 'manage_options' )
) {
update_site_option( 'wp_defender_shown_activator', true );
}
$show_onboarding = \WP_Defender\Model\Onboard::maybe_show_onboarding();
$api_error = HTTP::get( 'api_error' );
$hub_connection_source = HTTP::get( 'hub_connection_source' );
$hub_connect_recovery = ! $show_onboarding
&& is_string( $api_error )
&& '' !== $api_error
&& 'profile_menu' !== $hub_connection_source
&& ! Hub_Connector_Component::is_logged_in()
&& ! Hub_Connector_Component::is_wpmudev_dashboard_connected();
if ( $show_onboarding || $hub_connect_recovery ) {
add_filter( 'admin_body_class', array( $this, 'admin_body_class' ) );
}
$handle = 'defender-ui-dashboard';
wp_enqueue_script(
$handle,
WP_DEFENDER_BASE_URL . 'assets/js/dashboard-ui.js',
array( 'def-vue', 'def-manifest', 'def-core-ui', 'defender', 'wp-i18n' ),
DEFENDER_VERSION,
true
);
wp_set_script_translations( $handle, 'wpdef' );
$setup_wizard_data = wd_di()->get( Setup_Wizard::class )->data_frontend();
$tracking_data = wd_di()->get( Data_Tracking::class )->get_dashboard_notice_data();
$dashboard_data = $this->dump_routes_and_nonces();
$firewall_data = wd_di()->get( Firewall::class )->dump_routes_and_nonces();
$scan_data = wd_di()->get( \WP_Defender\Controller\Scan::class )->dump_routes_and_nonces();
$routes = array_merge(
$setup_wizard_data['routes'] ?? array(),
$tracking_data['routes'] ?? array(),
$dashboard_data['routes'] ?? array(),
$firewall_data['routes'] ?? array(),
$scan_data['routes'] ?? array(),
);
$nonces = array_merge(
$setup_wizard_data['nonces'] ?? array(),
$tracking_data['nonces'] ?? array(),
$dashboard_data['nonces'] ?? array(),
$firewall_data['nonces'] ?? array(),
$scan_data['nonces'] ?? array(),
);
unset( $setup_wizard_data['routes'], $setup_wizard_data['nonces'] );
unset( $tracking_data['routes'], $tracking_data['nonces'] );
unset( $firewall_data['routes'], $firewall_data['nonces'] );
wp_localize_script(
$handle,
'defenderUIData',
array_merge(
$this->get_shared_data(),
$this->get_page_data(),
// Welcome modal's details.
wd_di()->get( Feature_Modal::class )->get_dashboard_modals(),
// Specific data.
array(
'showOnboarding' => $show_onboarding,
'dashboardRequiredNotice' => $this->get_dashboard_required_notice_data(),
'routes' => $routes,
'nonces' => $nonces,
),
$setup_wizard_data,
$tracking_data,
wd_di()->get( \WP_Defender\Controller\Scan::class )->get_initial_scan_data()
)
);
wp_enqueue_style(
$handle,
WP_DEFENDER_BASE_URL . 'assets/css/showcase.css',
array(),
DEFENDER_VERSION
);
$this->enqueue_main_assets();
}
/**
* Get the state and action URLs for the Dashboard plugin required modal.
*
* @return array
*/
private function get_dashboard_required_notice_data(): array {
return array(
'isProPlugin' => WP_DEFENDER_PRO_PATH === DEFENDER_PLUGIN_BASENAME,
'dashboardActive' => $this->is_dash_activated(),
'dashboardInstalled' => $this->is_dash_installed(),
'dashboardPageUrl' => network_admin_url( 'admin.php?page=wpmudev' ),
'activateUrl' => add_query_arg(
array(
'_wpnonce' => wp_create_nonce( 'activate-plugin_wpmudev-updates/update-notifications.php' ),
'action' => 'activate',
'plugin' => 'wpmudev-updates/update-notifications.php',
),
network_admin_url( 'plugins.php' )
),
'installUrl' => add_query_arg(
array(
'_wpnonce' => wp_create_nonce( 'install-plugin_install_wpmudev_dash' ),
'action' => 'install-plugin',
'plugin' => 'install_wpmudev_dash',
),
network_admin_url( 'update.php' )
),
'dismissed' => (bool) get_site_option( self::DASHBOARD_REQUIRED_NOTICE_OPTION, false ),
);
}
/**
* Supply WordPress with the WPMU DEV Dashboard package details.
*
* @param mixed $result Existing Plugins API result.
* @param string $action Requested Plugins API action.
* @param object $args Requested plugin arguments.
*
* @return mixed
*/
public function filter_dashboard_plugin_info( $result, $action, $args ) {
if (
'plugin_information' !== $action
|| ! is_object( $args )
|| ! isset( $args->slug )
|| '' === trim( (string) $args->slug )
|| false === strpos( $args->slug, 'install_wpmudev_dash' )
) {
return $result;
}
$plugin = new \stdClass();
$plugin->name = 'WPMU DEV Dashboard';
$plugin->slug = 'wpmu-dev-dashboard';
$plugin->version = '';
$plugin->rating = 100;
$plugin->homepage = 'https://wpmudev.com/project/wpmu-dev-dashboard/';
$plugin->download_link = 'https://wpmudev.com/api/dashboard/v1/download-dashboard';
$plugin->tested = get_bloginfo( 'version' );
return $plugin;
}
/**
* Adds onboarding body classes on dashboard when onboarding wizard is active.
*
* @param string $classes Existing admin body classes.
*
* @return string
*/
public function admin_body_class( $classes ): string {
$classes .= ' wdf-onboarding-active ';
return $classes;
}
/**
* Returns the current hardening (security tweaks) issue count via AJAX.
*
* @return Response
* @defender_route
* @defender_redirect
*/
public function get_hardening_count(): Response {
$security_tweaks = wd_di()->get( \WP_Defender\Controller\Security_Tweaks::class )->dashboard_widget();
return new Response(
true,
array(
'count' => (int) ( $security_tweaks['summary']['issues_count'] ?? 0 ),
)
);
}
/**
* Handles the request to hide new features modal.
*
* @param Request $request The request object containing data.
*
* @return Response The response object indicating success or failure.
* @defender_route
*/
public function hide_new_features( Request $request ): Response {
$data = $request->get_data(
array(
'intention' => array(
'type' => 'string',
'sanitize' => 'sanitize_text_field',
),
)
);
$intention = $data['intention'] ?? false;
if ( 'welcome_modal' === $intention ) {
Feature_Modal::delete_modal_key();
}
return new Response( true, array() );
}
/**
* Activate Global IP submodule with the enabled Auto sync option.
*
* @return Response
* @defender_route
*/
public function activate_global_ip(): Response {
// Changes for Global IP.
$model = wd_di()->get( Global_Ip_Lockout::class );
$model->enabled = true;
$model->blocklist_autosync = true;
$model->save();
// Clear Global IP reminder.
wd_di()->get( Global_IP_Component::class )->delete_dashboard_notice_reminder();
// Changes for Hub.
Config_Hub_Helper::set_clear_active_flag();
return new Response(
true,
array(
'redirect' => network_admin_url( 'admin.php?page=wdf-ip-lockout&view=global-ip' ),
'interval' => 1,
)
);
}
/**
* Activate Session Protection submodule.
*
* @return Response
* @defender_route
*/
public function activate_session_protection(): Response {
$model = wd_di()->get( Session_Protection::class );
$model->enabled = true;
$model->save();
// Changes for Hub.
Config_Hub_Helper::set_clear_active_flag();
return new Response(
true,
array(
'redirect' => network_admin_url( 'admin.php?page=wdf-advanced-tools&view=session-protection' ),
'interval' => 1,
)
);
}
/**
* Remove Global IP notice reminder.
*
* @return Response
* @defender_route
*/
public function remove_global_ip_notice_reminder(): Response {
wd_di()->get( Global_IP_Component::class )->delete_dashboard_notice_reminder();
return new Response( true, array() );
}
/**
* Dismiss the one-time report-schedule notice set during the 6.1.0 upgrade.
*
* @return Response
* @defender_route
*/
public function dismiss_report_schedule_notice(): Response {
delete_site_option( self::REPORT_SCHEDULE_NOTICE_OPTION );
return new Response( true, array() );
}
/**
* Permanently dismiss the Dashboard plugin required modal for this site.
*
* @return Response
* @defender_route
*/
public function dismiss_dashboard_required_notice(): Response {
update_site_option( self::DASHBOARD_REQUIRED_NOTICE_OPTION, true );
return new Response( true, array() );
}
/**
* Toggle a dashboard feature by feature key.
*
* @param Request $request The current request data.
*
* @return Response
* @defender_route
*/
public function toggle_feature( Request $request ): Response {
$data = $request->get_data(
array(
'feature' => array(
'type' => 'string',
'sanitize' => 'sanitize_text_field',
),
)
);
$feature = $data['feature'] ?? '';
$feature_controllers = array(
'antibot' => Antibot_Global_Firewall::class,
'audit' => Audit_Logging::class,
'session_protection' => Session_Protection_Controller::class,
);
if ( isset( $feature_controllers[ $feature ] ) ) {
return wd_di()->get( $feature_controllers[ $feature ] )->save_settings( $request );
}
return new Response(
false,
array(
'message' => esc_html__( 'Unsupported feature toggle request.', 'wpdef' ),
)
);
}
/**
* Removes settings for all submodules.
*/
public function remove_settings() {
wd_di()->get( Feature_Modal::class )->upgrade_site_options();
delete_site_option( self::REPORT_SCHEDULE_NOTICE_OPTION );
}
/**
* Delete all the data & the cache.
*/
public function remove_data() {
delete_site_option( self::DASHBOARD_REQUIRED_NOTICE_OPTION );
}
/**
* Provides data for the frontend.
*
* @return array An array of data for the frontend.
*/
public function data_frontend(): array {
[ $endpoints, $nonces ] = Route::export_routes( 'dashboard' );
$firewall = wd_di()->get( Firewall::class );
return array_merge(
wd_di()->get( Feature_Modal::class )->get_dashboard_modals(),
array(
'scan' => wd_di()->get( Scan::class )->data_frontend(),
'firewall' => $firewall->data_frontend(),
'waf' => wd_di()->get( WAF::class )->data_frontend(),
'audit' => wd_di()->get( Audit_Logging::class )->data_frontend(),
'blocklist_monitor' => wd_di()->get( Blocklist_Monitor::class )->data_frontend(),
'blacklist' => array(
'nonces' => $nonces,
'endpoints' => $endpoints,
),
'two_fa' => wd_di()->get( Two_Factor::class )->data_frontend(),
'advanced_tools' => array(
'mask_login' => wd_di()->get( Mask_Login::class )->dashboard_widget(),
'security_headers' => wd_di()->get( Security_Headers::class )->dashboard_widget(),
'pwned_passwords' => wd_di()->get( Password_Protection::class )->dashboard_widget(),
'captcha' => wd_di()->get( Captcha::class )->dashboard_widget(),
'strong_passwords' => wd_di()->get( Strong_Password::class )->dashboard_widget(),
),
'security_tweaks' => wd_di()->get( Security_Tweaks::class )->dashboard_widget(),
'notifications' => wd_di()->get( Notification::class )->data_frontend(),
'settings' => wd_di()->get( Main_Setting::class )->data_frontend(),
'countries' => $firewall->dashboard_widget(),
'global_ip' => wd_di()->get( Global_Ip::class )->data_frontend(),
'hub_connector' => wd_di()->get( Hub_Connector::class )->data_frontend(),
'antibot' => wd_di()->get( Antibot_Global_Firewall::class )->data_frontend(),
)
);
}
/**
* Converts the current object state to an array.
*
* @return array The array representation of the object.
*/
public function to_array(): array {
return array();
}
/**
* Imports data into the model.
*
* @param array $data Data to be imported into the model.
*/
public function import_data( array $data ) {
}
/**
* Exports strings.
*
* @return array An array of strings.
*/
public function export_strings(): array {
return array();
}
}