AlkantarClanX12
Your IP : 216.73.217.24
<?php
/*
Copyright (C) 2015-26 CERBER TECH INC., https://wpcerber.com
Licensed under the GNU GPL.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
*========================================================================*
| |
| ATTENTION! Do not change or edit this file! |
| |
*========================================================================*
*/
if ( ! defined( 'WPINC' ) ) {
exit;
}
const CRB_ADMIN_ICONS = array(
'settings' => 'bx-slider',
'activity' => 'bx-pulse',
'know_more' => 'bx-idea',
);
if ( ! is_multisite() ) {
add_action( 'admin_menu', 'cerber_admin_menu' );
define( 'CRB_ADMIN_CAP', 'manage_options' );
}
else {
add_action( 'network_admin_menu', 'cerber_admin_menu' ); // only network wide menu allowed in multisite mode
define( 'CRB_ADMIN_CAP', 'manage_network' );
}
function cerber_admin_menu() {
$position = 100;
if ( cerber_is_admin_page() ) {
if ( crb_get_settings( 'top_admin_menu' ) ) {
$position = 1;
}
}
$hook = add_menu_page( 'WP Cerber Security', 'WP Cerber', CRB_ADMIN_CAP, 'cerber-security', 'cerber_render_admin_page', 'dashicons-shield', $position );
add_action( 'load-' . $hook, 'crb_admin_screen_options' );
add_submenu_page( 'cerber-security', __( 'WP Cerber Dashboard', 'wp-cerber' ), __( 'Dashboard', 'wp-cerber' ), CRB_ADMIN_CAP, 'cerber-security', 'cerber_render_admin_page' );
$hook = add_submenu_page( 'cerber-security', __( 'WP Cerber: Traffic Inspector', 'wp-cerber' ), __( 'Traffic Inspector', 'wp-cerber' ), CRB_ADMIN_CAP, 'cerber-traffic', 'cerber_render_admin_page' );
add_action( 'load-' . $hook, 'crb_admin_screen_options' );
if ( lab_lab() ) {
add_submenu_page( 'cerber-security', __( 'WP Cerber: Data Shield Policies', 'wp-cerber' ), __( 'Data Shield', 'wp-cerber' ), CRB_ADMIN_CAP, 'cerber-shield', 'cerber_render_admin_page' );
//add_submenu_page( 'cerber-security', __( 'Cerber Security Rules', 'wp-cerber' ), __( 'Security Rules', 'wp-cerber' ), CRB_ADMIN_CAP, 'cerber-rules', 'cerber_render_admin_page' );
}
add_submenu_page( 'cerber-security', __( 'WP Cerber: Security Rules', 'wp-cerber' ), __( 'Security Rules', 'wp-cerber' ), CRB_ADMIN_CAP, 'cerber-rules', 'cerber_render_admin_page' );
add_submenu_page( 'cerber-security', __( 'WP Cerber: User Security', 'wp-cerber' ), __( 'User Policies', 'wp-cerber' ), CRB_ADMIN_CAP, 'cerber-users', 'cerber_render_admin_page' );
if ( cerber_get_upload_dir_mu() ) {
$hook = add_submenu_page( 'cerber-security', 'WP Cerber: Site Integrity', __( 'Site Integrity', 'wp-cerber' ), CRB_ADMIN_CAP, 'cerber-integrity', 'cerber_render_admin_page' );
add_action( 'load-' . $hook, 'crb_admin_screen_options' );
}
add_submenu_page( 'cerber-security', __( 'WP Cerber: Anti-spam Settings', 'wp-cerber' ), __( 'Anti-spam', 'wp-cerber' ), CRB_ADMIN_CAP, 'cerber-recaptcha', 'cerber_render_admin_page' );
$hook = add_submenu_page( 'cerber-security', 'Cerber.Hub', 'Cerber.Hub', CRB_ADMIN_CAP, 'cerber-nexus', 'nexus_admin_page' );
if ( nexus_is_main() ) {
add_action( 'load-' . $hook, 'nexus_master_screen' );
}
if ( ! CRB_Addons::none() ) {
add_submenu_page( 'cerber-security', __( 'WP Cerber: Add-ons', 'wp-cerber' ), __( 'Add-ons', 'wp-cerber' ), CRB_ADMIN_CAP, CRB_ADDON_PAGE, 'cerber_render_admin_page' );
}
add_submenu_page( 'cerber-security', __( 'WP Cerber: Tools', 'wp-cerber' ), __( 'Tools', 'wp-cerber' ), CRB_ADMIN_CAP, 'cerber-tools', 'cerber_render_admin_page' );
/* We use an ugly hack to make a link in the admin menu */
add_submenu_page( 'users.php', __( 'User Sessions', 'wp-cerber' ), __( 'User Sessions', 'wp-cerber' ), 'manage_options', 'wp-cerber-user-sessions', 'show_mass_page' );
global $submenu;
if ( ! empty( $submenu['users.php'] ) ) {
foreach ( $submenu['users.php'] as &$item ) {
if ( $item[2] == 'wp-cerber-user-sessions' ) {
$item[2] = cerber_admin_link( 'sessions' );
break;
}
}
}
/* End of ugly hack */
}
add_action( 'admin_bar_menu', 'cerber_admin_bar' );
function cerber_admin_bar( $wp_admin_bar ) {
if ( ! is_multisite() ) {
return;
}
$args = array(
'parent' => 'network-admin',
'id' => 'cerber_admin',
'title' => 'WP Cerber',
'href' => cerber_admin_link(),
);
$wp_admin_bar->add_node( $args );
}
/**
* Outputs WP Cerber admin headers
*
* @return void
*
* @since 9.1.1
*/
function crb_admin_headers() {
if ( ! cerber_is_admin_page()
|| cerber_is_wp_ajax() ) {
return;
}
//header( "Content-Security-Policy: base-uri 'self'; default-src 'self' 'unsafe-inline'; img-src https: data:; font-src https: data:; connect-src 'self'; object-src 'none';" );
header( "Content-Security-Policy: base-uri 'self'; default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src https: data:; font-src https: data:; connect-src 'self'; form-action 'self'; frame-ancestors 'none'; object-src 'none';" );
}
/**
* Wrapper for all admin pages
*
* @param $title string
* @param $tabs array
* @param $active_tab string
* @param $renderer callable
*/
function cerber_show_admin_page( $title, $tabs = array(), $active_tab = null, $renderer = null ) {
if ( ! $active_tab ) {
$active_tab = crb_admin_get_tab( $tabs );
}
if ( nexus_is_valid_request() ) {
$title .= nexus_request_data()->at_site;
}
if ( ! cerber_is_dashboard() ) {
CRB_Issue_Monitor::monitor();
}
CRB_Wisdom::schedule_updating();
?>
<div id="crb-admin" class="wrap">
<h1><?php echo $title; ?></h1>
<?php
cerber_show_admin_messages();
cerber_show_tabs( $active_tab, $tabs );
echo '<div id="crb-admin-content">';
echo '<div id="crb-main" class="crb-tab-' . $active_tab . '">';
if ( $active_tab == 'help' ) {
cerber_render_help_page();
}
elseif ( is_callable( $renderer ) ) {
$tab_data = crb_array_get( $tabs[ $active_tab ], 'tab_data' );
call_user_func( $renderer, $active_tab, $tab_data );
}
echo '</div>';
cerber_show_aside( $active_tab );
echo '</div>';
?>
</div>
<?php
}
/**
* Displays lockouts in the Dashboard
*
* @param array $args
* @param bool $echo
*
* @return string|void
*/
function cerber_show_lockouts_page( $args = array(), $echo = true ) {
//$wp_cerber->deleteGarbage();
$per_page = ( ! empty( $args['per_page'] ) ) ? $args['per_page'] : crb_admin_get_per_page();
$limit = crb_build_sql_limit( $per_page );
$controls = empty( $args['compact_view'] );
if ( $rows = cerber_db_get_results( 'SELECT * FROM ' . CRB_LOCKOUT_TABLE . ' ORDER BY block_until DESC ' . $limit, MYSQL_FETCH_OBJECT ) ) {
if ( $controls ) {
$total = crb_get_locked_out_num();
}
$table_rows = array();
$base_url = cerber_admin_link( 'activity' );
$remove_base = cerber_admin_link( crb_admin_get_tab(), [], true );
foreach ( $rows as $row ) {
$ip = '<a href="' . $base_url . '&filter_ip=' . $row->ip . '">' . $row->ip . '</a>';
$closing = crb_get_icon( 'activity' ) . '<a href="' . $base_url . '&filter_set=1&filter_ip=' . $row->ip . '">' . __( 'View the log of suspicious and malicious activity from this IP address', 'wp-cerber' ) . '</a>';
if ( ! $reason = CRB_Explainer::create_popup( 10, $row->reason_id, 0, '', '', $row->reason, $closing ) ) {
$reason = '<a href="' . $base_url . '&filter_set=1&filter_ip=' . $row->ip . '">' . $row->reason . '</a>';
}
$ip_info = cerber_get_ip_info( $row->ip );
if ( isset( $ip_info['hostname_html'] ) ) {
$hostname = $ip_info['hostname_html'];
}
else {
$ip_id = cerber_get_id_ip( $row->ip );
$hostname = crb_get_ajax_placeholder( 'hostname', $ip_id );
}
if ( lab_lab() ) {
$single_ip = str_replace( '*', '1', $row->ip );
$country = '</td><td>' . crb_country_html( null, $single_ip );
}
else {
$country = '';
}
$the_row = '<td>' . $ip . '</td><td>' . $hostname . $country . '</td><td>' . cerber_date( $row->block_until ) . '</td><td>' . $reason . '</td>';
if ( $controls ) {
/* translators: Control button label. Unlock means lifting the active lockout for an IP address. */
$the_row .= '<td><a href="' . $remove_base . '&cerber_admin_do=unlockip&ip=' . esc_attr( $row->ip ) . '">' . __( 'Unlock', 'wp-cerber' ) . '</a></td>';
}
$table_rows[] = $the_row;
}
$heading = array(
__( 'IP Address', 'wp-cerber' ),
__( 'Hostname', 'wp-cerber' ),
__( 'Country', 'wp-cerber' ),
__( 'Expires', 'wp-cerber' ),
__( 'Reason', 'wp-cerber' ),
__( 'Action', 'wp-cerber' ),
);
if ( ! lab_lab() ) {
unset( $heading[2] );
}
if ( $controls ) {
$navi = cerber_page_navi( $total, $per_page );
$hint = '<div class="cerber-margin"><p>' . __( 'Click the IP address to see its activity', 'wp-cerber' ) . '</p></div>';
}
else {
$navi = '';
$hint = '';
unset( $heading[5] );
}
$titles = '<tr><th>' . implode( '</th><th>', $heading ) . '</th></tr>';
$table = '<table id="crb-locked-out" class="widefat crb-table"><thead>' . $titles . '</thead><tfoot>' . $titles . '</tfoot>' . implode( '</tr><tr>', $table_rows ) . '</tr></table>';
$ret = $table . $navi . $hint;
}
else {
$ret = crb_ui_renderer()->render_element( crb_ui_message_box( array( __( 'No lockouts at the moment. The sky is clear.', 'wp-cerber' ) ) ) );
}
if ( $echo ) {
echo $ret;
return;
}
return $ret;
}
/**
* @param string $ip
*
* @return int
*/
function crb_delete_lockout( $ip ) {
$result = 0;
if ( cerber_db_query( 'DELETE FROM ' . CRB_LOCKOUT_TABLE . ' WHERE ip = "' . cerber_db_real_escape( $ip ) . '"' ) ) {
$result = cerber_db_get_affected();
}
crb_event_handler( 'ip_event', array(
'e_type' => 'unlocked',
'ip' => $ip,
'result' => $result
) );
return $result;
}
/*
AJAX admin requests are landing here
*/
add_action( 'wp_ajax_cerber_ajax', 'cerber_admin_ajax' );
function cerber_admin_ajax() {
$go = cerber_check_ajax_permissions( false );
if ( $go === false ) {
return false;
}
$admin = $go === true;
$response = array();
$request = crb_get_request_fields();
if ( $admin && ( $ip = crb_array_get( $request, 'acl_delete' ) ) ) {
if ( cerber_acl_remove( $ip, crb_array_get( $request, 'slice' ) ) ) {
$response['deleted_ip'] = $ip;
}
else {
$response['error'] = 'Unable to delete the entry';
}
}
elseif ( ( $slug = crb_array_get( $request, 'crb_ajax_slug' ) )
&& $list = crb_array_get( $request, 'crb_ajax_list' ) ) {
$response['slug'] = $slug;
$list = array_unique( $list );
$response_data = array();
/*
$list = array_map(function ($ip_id){
return cerber_get_ip_id( $ip_id );
}, $list);
$list = array_filter( $list, function ( $ip ) {
if (filter_var( $ip, FILTER_VALIDATE_IP )){
return true;
}
});*/
if ( $slug == 'hostname' || $slug == 'country' ) {
$ip_list = array();
foreach ( $list as $ip_id ) {
if ( $ip = filter_var( cerber_get_ip_id( $ip_id ), FILTER_VALIDATE_IP ) ) {
$ip_list[ $ip_id ] = $ip;
$response_data[ $ip_id ] = '';
}
else {
$response_data[ $ip_id ] = '-';
}
}
}
switch ( $slug ) {
case 'hostname':
foreach ( $ip_list as $ip_id => $ip ) {
$ip_info = cerber_get_ip_info( $ip, false );
$response_data[ $ip_id ] = $ip_info['hostname_html'];
}
break;
case 'country':
if ( $country_list = lab_get_country( $ip_list, false ) ) {
foreach ( $country_list as $ip_id => $country ) {
if ( $country ) {
$response_data[ $ip_id ] = crb_get_flag_html( $country, crb_get_country_name( $country ) );
}
else {
$response_data[ $ip_id ] = __( 'Unknown', 'wp-cerber' );
}
}
}
break;
case 'cbcc':
foreach ( $list as $user_id ) {
$response_data[ $user_id ] = 0;
$base = admin_url( 'edit-comments.php' );
// to get this working we added filter 'preprocess_comment'
if ( $com = get_comments( array( 'author__in' => $user_id ) ) ) {
$response_data[ $user_id ] = '<a href="' . $base . '?user_id=' . $user_id . '" target="_blank">' . count( $com ) . '</a>';
}
}
break;
case 'cbla':
$base = cerber_activity_link( array( CRB_EV_LIN ) );
foreach ( $list as $user_id ) {
$user_id = crb_absint( $user_id );
if ( $last_log = crb_get_last_user_login( $user_id ) ) {
$ip = $last_log['ip'];
$time = $last_log['ts'];
$date = ( $time ) ? cerber_date( $time ) : __( 'Unknown', 'wp-cerber' );
if ( $ip
&& ( $country = crb_country_html( $last_log['cn'], $ip, false ) ) ) {
$country = '<br />' . $country;
}
else {
$country = '';
}
$val = '<a href="' . $base . '&filter_user=' . $user_id . '">' . $date . '</a>' . $country;
}
else {
$val = __( 'Never', 'wp-cerber' );
}
$response_data[ $user_id ] = $val;
}
break;
case 'cbfl':
$base = cerber_activity_link( array( CRB_EV_LFL ) );
foreach ( $list as $user_id ) {
$u = crb_get_userdata( $user_id );
$val = 0;
$failed = crb_q_cache_get( 'SELECT COUNT(user_id) FROM ' . CERBER_LOG_TABLE . ' WHERE ( user_login = "' . $u->user_login . '" OR user_login = "' . $u->user_email . '" ) AND activity = ' . CRB_EV_LFL . ' AND stamp > ' . ( time() - 7 * 24 * 3600 ), CERBER_LOG_TABLE );
if ( ! empty( $failed[0][0] ) ) {
$val = '<a href="' . $base . '&filter_login=' . $u->user_email . '|' . $u->user_login . '">' . $failed[0][0] . '</a>';
}
$response_data[ $user_id ] = $val;
}
break;
}
$response['data'] = $response_data;
}
elseif ( $admin && isset( $request['crb_dismiss_info'] ) ) {
$context = $request['crb_context'] ?? '';
$target_id = $request['crb_target_id'] ?? '';
$diss_msg = 'OK';
switch ( $context ) {
case 'delete_single_issue':
$result = CRB_Issues::delete_by_id( $target_id );
if ( is_wp_error( $result ) ) {
$diss_msg = $result->get_error_message();
}
break;
case 'new_release_announcement':
delete_site_option( CRB_ADMIN_ANNOUNCE );
break;
case 'lab_action':
if ( in_array( $target_id, array( 'lab_ok', 'lab_no' ) ) ) {
lab_user_opt_in( $target_id );
}
else {
$diss_msg = 'Error. Unknown target.';
}
break;
default:
$diss_msg = 'Error. Unknown context.';
}
$response ['diss_result_msg'] = $diss_msg;
}
elseif ( $admin && ( $ajax_route = crb_sanitize_id( crb_array_get( $request, 'ajax_route' ) ) ) ) {
$ref_params = crb_get_referrer_params();
$response = array( 'html' => '', 'error' => '' );
$result = '';
switch ( $ajax_route ) {
case 'dashboard_analytics':
cerber_widgets_init();
$result = CRB_Widgets::render_widget( $request['ds_widget'] ?? '', true );
break;
case 'scanner_analytics':
$result = cerber_generate_insights( $request['itype'] );
break;
case 'user_activity_analytics':
$result = crb_generate_user_insights( absint( $request['user_id'] ), $ref_params['tab'] ?? '' );
break;
case 'ip_extra_info':
$result = cerber_ip_extra_view_ajax( $request, $ref_params );
break;
case 'ip_quick_analytics':
$result = crb_generate_ip_insights( $request['ip_address'], $ref_params['tab'] );
break;
case 'diagnostic_tools':
$result = cerber_db_diag();
break;
default:
$response['error'] = 'Error: Unknown WP Cerber AJAX route (' . $ajax_route . ')';
}
if ( crb_is_wp_error( $result ) ) {
$response ['error'] = $result->get_error_message();
}
else {
$response ['html'] = $result;
}
}
elseif ( $admin && ( $usearch = crb_array_get( $request, 'user_search' ) ) ) {
$users = get_users( array( 'search' => '*' . esc_attr( $usearch ) . '*', ) );
if ( $users ) {
foreach ( $users as $user ) {
$data = crb_get_userdata( $user->ID );
$response[] = array(
'id' => $user->ID,
'text' => crb_format_user_name( $data )
);
}
}
}
elseif ( $admin && $action = $request['cerber_ajax_action'] ?? false ) {
$msg = '*';
$result = false;
switch ( $action ) {
case 'dashboard_save_sortable':
$order = array_map( function ( $id ) {
return crb_sanitize_id( $id );
}, $request['dash_order'] );
$result = CRB_Widgets::save_order( $order );
break;
}
if ( crb_is_wp_error( $result ) ) {
$response ['error'] = $result->get_error_message();
}
else {
$msg = 'OK';
}
$response ['message'] = $msg;
}
if ( empty( $response ) ) {
$response['error'] = 'Unknown AJAX request. Check your code, it\'s time to show your debugging skills!';
}
echo json_encode( $response );
if ( ! nexus_is_valid_request() ) {
wp_die();
}
}
add_action( 'wp_ajax_cerber_local_ajax', function () {
check_ajax_referer( 'crb-ajax-admin', 'ajax_nonce' );
if ( ! is_super_admin() ) {
wp_die( 'Oops! Access denied.' );
}
$done = array();
switch ( crb_array_get( $_REQUEST, 'crb_ajax_do' ) ) {
case 'bg_tasks_run':
$done = CRB_Deferred_Tasks::launcher( crb_array_get( $_REQUEST, 'run_tasks', array() ) );
break;
}
echo json_encode( array( 'done' => $done ) );
exit;
} );
/**
* @param string $group
* @param string $item_id
*
* @return string
*/
function crb_get_ajax_placeholder( $group, $item_id ) {
return '<img class="crb-ajax-load" data-ajax_group="' . $group . '" data-item_id="' . $item_id . '" src="' . CRB_Globals::assets_url( 'ajax-loader.gif' ) . '" />';
}
/*
* Retrieve extended IP information
* @since 2.2
*
*/
function cerber_get_ip_info( $ip, $cache_only = true ) {
$ip_id = 'info_' . cerber_get_id_ip( $ip );
$ip_info = cerber_get_set( $ip_id );
if ( $cache_only ) {
return $ip_info;
}
if ( empty( $ip_info['hostname_html'] ) ) {
$ip_info = array();
if ( ! $hostname = @gethostbyaddr( $ip ) ) {
$hostname = __( 'unknown', 'wp-cerber' );
}
$ip_info['hostname'] = $hostname;
if ( ! filter_var( $hostname, FILTER_VALIDATE_IP ) ) {
$hostname = str_replace( '.', '.<wbr>', $hostname );
}
$ip_info['hostname_html'] = $hostname;
cerber_update_set( $ip_id, $ip_info, null, true, time() + 24 * 3600 );
}
return $ip_info;
}
/*
Admin dashboard actions
*/
add_action( 'wp_loaded', // 'wp_loaded' @since 5.6
function () {
if ( ! is_admin() ) {
return;
}
crb_process_admin_request();
}, 0 );
/**
* @param bool $is_post
*
* @return array|false|void
*
*/
function crb_process_admin_request( $is_post = false ) {
global $wpdb;
if ( ! nexus_is_valid_request()
&& ! cerber_user_can_manage() ) {
return;
}
$get = crb_get_query_params();
if ( ( ! $nonce = crb_array_get( $get, 'cerber_nonce' ) )
&& ( ! $nonce = crb_get_post_fields( 'cerber_nonce' ) ) ) {
return;
}
if ( ! wp_verify_nonce( $nonce, 'control' ) ) {
// TODO: Investigate why is this fires while managing a remote website via Cerber.Hub
//cerber_admin_notice( 'Nonce verification failed.' );
return;
}
$post = crb_get_post_fields();
//$q = crb_admin_parse_query( array( 'cerber_admin_do', 'ip' ) );
$remove_args = array();
if ( cerber_is_http_get() ) {
if ( ( $do = crb_array_get( $get, 'cerber_admin_do' ) ) ) {
switch ( $do ) {
case 'unlockip':
$err = '';
$ip = crb_array_get( $get, 'ip' );
if ( cerber_is_ip_or_net( $ip ) ) {
if ( crb_delete_lockout( $ip ) ) {
/* translators: %s is the IP address. */
cerber_admin_message( sprintf( __( 'Lockout for %s was removed', 'wp-cerber' ), $ip ) );
}
else {
$err = 'No lockout for the specified IP address ' . $ip . ' found.';
}
}
else {
$err = 'An invalid IP address has been specified.';
}
if ( $err ) {
cerber_admin_notice( 'Request failed. ' . $err );
}
$remove_args[] = 'ip';
break;
case 'testnotify':
$test_type = crb_array_get_validated( $get, 'type', '', '\w+' );
$msg = array();
if ( $test_type == 'plugin_updates' ) {
if ( false === crb_plugin_update_notifier( true, true, $sent, $msg ) ) {
$sent = false;
}
}
else {
$test_chan = crb_array_get_validated( $get, 'channel', '', '\w+' );
$period = crb_array_get_validated( $get, 'test_period', 'one_week', '\w+' );
$channels = array();
if ( $test_chan && ( $test_chan != 'email' || lab_lab() ) ) {
$channels = array_fill_keys( array_keys( CRB_CHANNELS ), 0 );
$channels = array_merge( $channels, array( $test_chan => 1 ) );
}
$sent = CRB_Messaging::send( $test_type, array( 'subj' => ' *** ' . __( 'TEST MESSAGE', 'wp-cerber' ) . ' *** ' ), $channels, true, array( 'report_id' => $period ) );
}
if ( $sent !== false ) {
$to = ' ' . implode( ', ', $sent );
/* translators: Here %s is the name of a mobile device or/and email addresses. */
cerber_admin_message( sprintf( __( 'A message has been sent to %s', 'wp-cerber' ), $to ) );
}
elseif ( ! $msg ) {
cerber_admin_notice( __( 'Unable to send a message', 'wp-cerber' ) );
}
else {
cerber_admin_notice( $msg );
}
$remove_args[] = 'type';
$remove_args[] = 'channel';
break;
case 'manage_alert':
if ( 'create_alert' == crb_array_get( $get, 'mode' ) ) {
CRB_Activity_Alerts::create_alert();
}
else {
CRB_Activity_Alerts::delete_alert();
}
$remove_args = array_merge( $remove_args, CRB_Activity_Alerts::CRB_NON_EVENT_PARAMS );
$remove_args[] = 'manage_alert';
$remove_args[] = 'mode';
break;
case 'nexus_set_role':
nexus_enable_role();
crb_safe_redirect( cerber_admin_link( '', array( 'page' => 'cerber-nexus' ) ) );
exit();
break;
case 'nexus_delete_slave':
crb_safe_redirect( cerber_admin_link( 'nexus_sites' ) );
exit();
break;
case 'nexus_site_table':
if ( cerber_get_bulk_action() ) {
nexus_do_bulk();
}
break;
case 'scan_tegrity':
$adm = crb_array_get( $get, 'crb_scan_adm' );
$file = crb_array_get( $get, 'crb_file_id' );
if ( in_array( $adm, array( 'delete', 'restore' ) ) ) {
cerber_quarantine_do( $adm, crb_array_get( $get, 'crb_scan_id' ), crb_array_get( $get, 'crb_file_id' ) );
}
elseif ( $adm == 'remove_ignore' ) {
if ( crb_remove_ignore( $file ) ) {
cerber_admin_message( 'The file has been removed from the list' );
}
}
$remove_args = array( 'crb_scan_adm', 'crb_scan_id', 'crb_file_id' );
break;
case 'manage_diag_log':
cerber_manage_diag_log( crb_array_get( $get, 'do_this' ) );
$remove_args[] = 'do_this';
break;
case 'terminate_session':
crb_sessions_kill( crb_array_get_validated( $get, 'id', false, '\w+' ), crb_array_get_validated( $get, 'user_id', false, '\d+' ) );
$remove_args = array( 'user_id', 'id' );
break;
case 'crb_manage_sessions':
if ( cerber_get_bulk_action() == 'bulk_session_terminate' ) {
crb_sessions_kill( crb_array_get_validated( $get, 'ids', array(), '\w+' ) );
}
elseif ( cerber_get_bulk_action() == 'bulk_block_user' ) {
if ( ( $sids = crb_array_get_validated( $get, 'ids', array(), '\w+' ) )
&& ( $users = cerber_db_get_col( 'SELECT user_id FROM ' . cerber_get_db_prefix() . CERBER_USS_TABLE . ' WHERE wp_session_token IN ("' . implode( '","', $sids ) . '")' ) ) ) {
array_walk( $users, 'cerber_block_user' );
}
}
break;
case 'export':
if ( nexus_is_valid_request() ) {
return crb_admin_get_tokenized_link();
}
crb_admin_download_file( crb_array_get( $get, 'type' ) );
break;
case 'load_defaults':
cerber_load_defaults();
cerber_admin_message( __( 'Default settings have been loaded', 'wp-cerber' ) );
break;
case 'clear_cache':
CRB_Cache::reset();
break;
default:
return;
}
if ( nexus_is_valid_request() ) {
return array( 'redirect' => true, 'remove_args' => $remove_args );
}
else {
cerber_safe_redirect( $remove_args );
}
}
// TODO: move to the switch above
if ( cerber_get_get( 'citadel_do' ) == 'deactivate' ) {
cerber_disable_citadel();
}
elseif ( isset( $_GET['force_repair_db'] ) ) {
cerber_create_db();
cerber_upgrade_db( true );
cerber_admin_message( 'Cerber\'s database tables have been repaired and upgraded' );
cerber_safe_redirect( 'force_repair_db' );
}
elseif ( $table = cerber_get_get( 'truncate', '[a-z_]+' ) ) {
if ( 0 === strpos( $table, 'cerber_' ) ) {
if ( $wpdb->query( 'TRUNCATE TABLE ' . $table ) ) {
if ( $table == CERBER_LOG_TABLE ) {
CRB_Activity::clear_status();
}
cerber_admin_message( 'Table ' . $table . ' has been truncated' );
}
else {
cerber_admin_notice( $wpdb->last_error );
}
}
cerber_safe_redirect( 'truncate' );
}
elseif ( isset( $_GET['force_check_nodes'] ) ) {
$best = lab_check_nodes( true );
cerber_admin_message( 'Connectivity to Cerber Security Cloud has been checked. The closest node: ' . $best );
cerber_safe_redirect( 'force_check_nodes' );
}
elseif ( isset( $_GET['clear_up_lab_cache'] ) ) {
lab_cleanup_cache();
cerber_admin_message( 'The Cerber Security Cloud cache has been cleared' );
cerber_safe_redirect( 'clear_up_lab_cache' );
}
elseif ( isset( $_GET['clear_up_the_cache'] ) ) {
lab_cleanup_cache();
CRB_Cache::reset();
CRB_Wisdom::clear_cache( true );
cerber_delete_expired_set( true );
CRB_Issues::delete_all();
cerber_admin_message( 'The plugin cache has been cleared' );
cerber_safe_redirect( 'clear_up_the_cache' );
}
}
if ( cerber_is_http_post() ) {
$redirect = false;
if ( ( $do = crb_array_get( $post, 'cerber_admin_do' ) ) ) {
switch ( $do ) {
case 'save_widget_list':
cerber_widgets_init();
CRB_Widgets::save_list( $post );
$redirect = true;
break;
case 'update_role_policies':
crb_settings_update_role_policies( $post );
$redirect = true;
break;
case 'update_geo_rules':
crb_settings_update_geo_rules( $post );
break;
case 'add2acl':
crb_acl_form_process( $post );
break;
case 'add_slave':
nexus_add_client( crb_array_get( $post, 'new_slave_token' ) );
break;
case 'install_key':
// Sanitize: license keys are ASCII alphanumeric only.
$lic = preg_replace( '/[^A-Z0-9]/i', '', crb_array_get( $post, 'cerber_license' ) );
$lic_length = strlen( $lic );
$redirect = true;
// Case 1: empty submission removes the currently installed key.
if ( $lic_length === 0 ) {
lab_cleanup_cache();
cerber_delete_expired_set( true );
lab_update_key( '' );
cerber_admin_message( __( 'The license key has been removed.', 'wp-cerber' ) );
break;
}
// Case 2: wrong length - reject early and do not touch the stored key.
if ( $lic_length !== LAB_KEY_LENGTH ) {
cerber_admin_notice( sprintf(
__( 'The license key you entered is not valid. A license key must be exactly %d characters long.', 'wp-cerber' ),
LAB_KEY_LENGTH
) );
break;
}
// Case 3: correct length - validate BEFORE persisting so a bad key cannot overwrite a working one.
lab_cleanup_cache();
cerber_delete_expired_set( true );
lab_get_site_meta();
$validate_msg = '';
$site_ip = '';
$is_valid = lab_validate_lic( $lic, $validate_msg, $site_ip );
// lab_validate_lic() already emits its own admin notice on network errors;
// do not contradict it with an "invalid key" notice in that case.
$is_network_error = ( strpos( $validate_msg, '(2)' ) !== false );
if ( $is_valid ) {
$msg = '<b>' . __( 'Great! Your license key is valid.', 'wp-cerber' ) . '</b>';
$msg .= '<p>' . __( 'Now, whenever you see a green shield icon in the top right-hand corner of any WP Cerber admin page, it means the professional version works as intended, and your website is protected by WP Cerber Security Cloud.', 'wp-cerber' ) . '</p>';
/* translators: %s is the URL to the client portal. */
$msg .= '<p>' . sprintf( __( 'Please use our client portal to manage your subscription, license keys and get support at %s', 'wp-cerber' ), '<a target="_blank" href="https://my.wpcerber.com">https://my.wpcerber.com</a>' ) . '</p>';
$msg .= '<p>' . __( 'Thank you for being our client.', 'wp-cerber' ) . '</p>';
cerber_admin_message( $msg );
}
elseif ( ! $is_network_error ) {
cerber_admin_notice( __( 'Error! You have entered an invalid or expired license key.', 'wp-cerber' ) );
}
break;
}
if ( $redirect ) {
if ( nexus_is_valid_request() ) {
// No redirection is needed so far, we use a second 'get_page' request
//return array( 'redirect' => true, 'remove_args' => $remove_args );
}
else {
cerber_safe_redirect( $remove_args );
}
}
}
}
}
/**
* Redirects safely to the current URL while removing specified query parameters.
*
* This function performs a safe local redirect using wp_safe_redirect() after removing the specified query
* parameters from the current URL. It ensures that commonly used temporary query parameters are also removed.
*
* @param string|string[] $rem_args A single query parameter or an array of query parameters to be removed.
* If empty, only predefined, single-use parameters are removed.
*
* @return void
*/
function cerber_safe_redirect( $rem_args ) {
if ( empty( $rem_args ) ) {
$rem_args = array();
}
elseif ( ! is_array( $rem_args ) ) {
$rem_args = array( (string) $rem_args );
}
// These are most used "temporary" query parameters
$rem_args = array_merge( $rem_args, array(
'_wp_http_referer',
'_wpnonce',
'cerber_nonce',
'ids',
'cerber_admin_do',
'action',
'action2'
) );
$url = remove_query_arg( $rem_args );
crb_safe_redirect( $url );
exit();
}
function crb_admin_get_tokenized_link() {
if ( empty( nexus_request_data()->get_params ) ) {
return false;
}
$key = crb_random_string( 26 );
if ( cerber_update_set( '_the_key_' . $key, array( 'query' => nexus_request_data()->get_params ), 1, true, time() + 60 ) ) {
return array( 'redirect' => true, 'redirect_url' => home_url( '?cerber_magic_key=' . $key ) );
}
cerber_admin_notice( 'Unable to generate a tokenized URL' );
return false;
}
/**
* Returns HTML code of a set of navigation links to be used for quick filtering logs.
*
* @param string $context Defines what links are included in the set
*
* @return string Escaped HTML code
*/
function crb_admin_activity_nav_links( $context = '' ) {
$links = array();
if ( ! $context ) {
$links[] = array( array(), __( 'View all', 'wp-cerber' ) );
$labels = cerber_get_labels( 'activity' );
$set = array( CRB_EV_LIN );
foreach ( $set as $item ) {
$links[] = array( array( 'filter_activity' => $item ), $labels[ $item ] );
}
}
if ( $context == 'users' ) {
$links[] = array( array( 'filter_user' => '*' ), __( 'View all', 'wp-cerber' ) );
}
if ( $context != 'suspicious' ) {
$links[] = array( array( 'filter_activity' => array( 1, 2 ) ), __( 'New users', 'wp-cerber' ) );
$links[] = array( array( 'filter_set' => 2 ), __( 'Login issues', 'wp-cerber' ) );
}
if ( $context != 'users' ) {
if ( ! $context ) {
$txt = __( 'Suspicious activity', 'wp-cerber' );
}
else {
$txt = __( 'View all', 'wp-cerber' );
}
$links[] = array( array( 'filter_set' => 1 ), $txt );
}
if ( ! $context ) {
$links[] = array( array( 'filter_status' => array( CRB_STS_11, CRB_STS_532, 706 ) ), __( 'Spam Events', 'wp-cerber' ) );
$links[] = array( array( 'filter_activity' => array( CRB_EV_IPL, CRB_EV_NEL ) ), __( 'IP locked out', 'wp-cerber' ) );
$links[] = array( array( 'filter_user' => '*' ), __( 'Users', 'wp-cerber' ) );
$links[] = array( array( 'filter_user' => 0 ), __( 'Non-authenticated', 'wp-cerber' ) );
if ( ! nexus_is_valid_request() ) {
$links[] = array( array( 'filter_user' => get_current_user_id() ), __( 'My activity', 'wp-cerber' ) );
$links[] = array( array( 'filter_ip' => cerber_get_remote_ip() ), __( 'My IP', 'wp-cerber' ) );
}
}
return crb_build_quick_nav_block( $links, 'activity' );
}
/**
* Generates a quick navigation block and highlights the link matching the current query parameters.
*
* @param array $link_list A list of UI navigation link definitions.
* @param string $tab Admin page tab to generate links for.
* @param string $class CSS class for the link wrapper.
*
* @return string Escaped HTML.
*
* @since 8.8.3.2
*/
function crb_build_quick_nav_block( $link_list, $tab = 'activity', $class = '' ) {
$current_params = crb_get_referrer_params();
unset( $current_params['page'], $current_params['tab'], $current_params['pagen'] );
$html = '';
foreach ( $link_list as $link_config ) {
$query_string = '';
$selected = false;
$remaining_params = $current_params;
if ( $parameters = $link_config[0] ?? '' ) {
$query_string .= '&' . http_build_query( $parameters );
$selected = true;
foreach ( $parameters as $name => $expected_value ) {
if ( ! is_array( $expected_value ) ) {
$actual_value = crb_array_get( $current_params, $name );
// Treat a single-item query array as equivalent to its scalar form.
if ( is_array( $actual_value ) && count( $actual_value ) === 1 ) {
$actual_value = reset( $actual_value );
}
if ( (string) $actual_value !== (string) $expected_value ) {
$selected = false;
}
else {
unset( $remaining_params[ $name ] );
}
}
else {
foreach ( $expected_value as $key => $expected_item_value ) {
if ( crb_array_get( $current_params, array( $name, $key ) ) !== (string) $expected_item_value ) {
$selected = false;
}
else {
unset( $remaining_params[ $name ][ $key ] );
}
}
}
}
}
$remaining_params = array_filter( $remaining_params, function ( $val ) {
return $val === 0 || ! empty( $val );
} );
$anchor = crb_escape_html( $link_config[1] );
if ( $selected & empty( $remaining_params ) ) {
$html .= '<span class="crb_selected">' . $anchor . '</span> ';
}
else {
$html .= '<a class="crb-nav-link" href="' . crb_escape_url( cerber_admin_link( $tab ) . $query_string ) . '">' . $anchor . '</a> ';
}
}
return '<div class="crb-tag-buttons ' . crb_sanitize_alphanum( $class ) . '">' . $html . '</div>';
}
/**
* Renders the admin Activity log view as an HTML fragment.
*
* Reads activity rows through CRB_Activity::fetch() using either the current request query string
* or the explicit overrides supplied in $args.
*
* Rendering options include per_page, compact_view, no_export, no_details, date, hide_columns, and context.
*
* In full mode (compact_view = false), the returned markup includes the quick filters, search form,
* pagination, export link, alert controls, and contextual user or IP information
* when a corresponding filter resolves to a known user or IP address.
*
* In compact mode (compact_view = true), only the table or no-results message is rendered, making it suitable for
* dashboard widgets.
*
* The function performs database reads, may resolve WordPress users, may enqueue
* lazy hostname placeholders, and emits HTML directly when $echo is true.
*
* @param array<string,mixed> $args Optional Activity query filters and rendering options.
* @param bool $echo When true, echoes the generated HTML and returns null. When false,
* returns the generated HTML string.
*
* @return string Generated HTML when $echo is false, otherwise null.
*/
function crb_render_activity_view( $args = array(), $echo = true ): string {
$status_labels = cerber_get_labels( 'status' ) + crb_get_lockout_reason();
$base_url = cerber_activity_link();
$is_log_empty = CRB_Activity::is_empty();
$export_link = '';
$toolbar = '';
$result_html = '';
$fetched = CRB_Activity::fetch( $args );
if ( $fetched->has_errors() ) {
$result_html .= crb_ui_renderer()->render_element( crb_ui_log_fetch_error_box( $fetched ) );
if ( ! $echo ) {
return $result_html;
}
echo $result_html;
return '';
}
$log_rows = $fetched->get_element( 'rows', array() );
$total = (int) $fetched->get_element( 'total', 0 );
$filter_values = $fetched->get_element( 'filters', array() );
$per_page = $filter_values['per_page'];
$filter_ip = $filter_values['ip'];
$filter_login = $filter_values['login'];
$user_id = $filter_values['user_id'];
$search = $filter_values['search'];
$sid = $filter_values['sid'];
$in_url = $filter_values['in_url'];
$sname = '';
$extra_view = '';
$_find = array();
if ( $filter_login ) {
$login = explode( '|', $filter_login );
if ( is_email( $login[0] ) ) {
$_find[] = array( 'email', $login[0] );
}
else {
$_find[] = array( 'login', $login[0] );
}
if ( isset( $login[1] ) ) {
if ( is_email( $login[1] ) ) {
$_find[] = array( 'email', $login[1] );
}
else {
$_find[] = array( 'login', $login[1] );
}
}
}
if ( $search ) {
if ( cerber_is_ip( $search ) ) {
$filter_ip = $search;
}
else {
if ( is_email( $search ) ) {
$_find[] = array( 'email', $search );
}
else {
$_find[] = array( 'login', $search );
}
}
}
if ( ! $user_id && $_find ) {
foreach ( $_find as $item ) {
if ( $user = get_user_by( $item[0], $item[1] ) ) {
$user_id = $user->ID;
break;
}
}
}
if ( $user_id ) {
$sname = crb_format_user_name( $user_id );
$extra_view .= cerber_user_extra_view( $user_id );
}
if ( $filter_ip ) {
$extra_view .= cerber_ip_extra_view( $filter_ip );
}
if ( $log_rows ) {
$no_results = false;
$hidden = array_flip( $args['hide_columns'] ?? array() );
if ( ! lab_lab() ) {
$hidden['crb_country_col'] = true;
}
$tbody = '';
if ( ! defined( 'CERBER_FULL_URI' ) || ! CERBER_FULL_URI ) {
$short = true;
$site_url = cerber_get_home_url();
$site_url = substr( $site_url, strpos( $site_url, '://' ) + 3 );
$start = strlen( rtrim( $site_url, '/' ) );
}
else {
$short = false;
$start = 0;
}
foreach ( $log_rows as $row ) {
$country = '';
$user_cell = '';
$username_cell = '';
$svg = '';
$act = (int) $row->activity;
$ip_id = cerber_get_id_ip( $row->ip );
$activity = '<span class="crb-activity-single actv' . $act . '" title="' . $act . '">' . crb_get_activity_label( $act, $row->user_id, $row->ac_by_user ) . '</span>';
if ( empty( $args['no_details'] ) ) {
$status = (int) $row->ac_status;
if ( $sts_label = crb_array_get( $status_labels, $status ) ) {
$activity .= ' <span class = "crb-log-status crb-status-' . $status . '" title="' . $status . '">' . $sts_label . '</span>';
}
$details = explode( '|', $row->details );
$uri = crb_array_get( $details, 4 );
$svg = CRB_Explainer::create_popup( $row->activity, $row->ac_status, $row->user_id, crb_array_get( $details, 0, '' ), $row->ip );
if ( $uri
&& ( $act < CRB_EV_IPL || $act > 12 ) ) {
if ( $short && 0 === strpos( $uri, $site_url ) ) {
$ac_uri = substr( $uri, $start );
}
else {
$ac_uri = $uri;
}
$activity .= '<div class="crb_act_url" title="' . crb_attr_escape( $uri ) . '">' . str_replace( array( '-', '/' ), array(
'<wbr>-',
'<wbr>/'
), crb_escape_html( urldecode( $ac_uri ) ) ) . '</div>';
}
}
$activity = '<div class="' . crb_get_act_style( $act ) . '">' . $activity . '</div>';
if ( ! isset( $hidden['crb_user_col'] ) ) {
$user_cell = '<td>' . crb_admin_get_user_cell( $row->user_id, $base_url ) . '</td>';
}
if ( ! isset( $hidden['crb_username_col'] ) ) {
if ( $row->user_login ) {
$login_used = crb_escape_html( $row->user_login );
$username_cell = '<a href="' . $base_url . '&filter_login=' . $login_used . '">' . $login_used . '</a>';
}
$username_cell = '<td>' . $username_cell . '</td>';
}
$ip_info = cerber_get_ip_info( $row->ip );
$hostname = $ip_info['hostname_html'] ?? crb_get_ajax_placeholder( 'hostname', $ip_id );
if ( ! empty( $args['date'] ) && $args['date'] == 'ago' ) {
$date = cerber_ago_time( $row->stamp );
}
else {
$date = '<span title="' . $row->stamp . ' / ' . $row->session_id . ' / ' . $act . '">' . cerber_date( $row->stamp ) . '</span>';
}
if ( ! isset( $hidden['crb_country_col'] ) ) {
$country = '<td>' . crb_country_html( $row->country, $row->ip ) . '</td>';
}
$tbody .= '<tr><td>' . crb_admin_ip_cell( $row->ip, $base_url . '&filter_ip=' . $row->ip ) . '</td><td class="crb-hostname">' . $hostname . '</td>' . $country . '<td>' . $date . '</td><td class="crb-act-info"><div class="crb-act-holder">' . $activity . $svg . '</div></td>' . $user_cell . $username_cell . '</tr>';
}
$heading = array(
'crb_ip_col' => '<div class="crb_act_icon"></div>' . __( 'IP Address', 'wp-cerber' ),
'crb_hostname_col' => __( 'Hostname', 'wp-cerber' ),
'crb_country_col' => __( 'Country', 'wp-cerber' ),
'crb_date_col' => __( 'Date', 'wp-cerber' ),
'crb_event_col' => __( 'Event', 'wp-cerber' ),
'crb_user_col' => __( 'Local User', 'wp-cerber' ),
'crb_username_col' => __( 'Username', 'wp-cerber' )
);
if ( $hidden ) {
$heading = array_diff_key( $heading, $hidden );
}
$table_header = '';
$table_footer = '';
foreach ( $heading as $id => $title ) {
$table_header .= '<th id="' . $id . '" class="' . $id . '-class">' . $title . '</th>';
$table_footer .= '<th class="' . $id . '-class">' . $title . '</th>';
}
$class = ' widefat crb-table cerber-margin crb-' . count( $heading ) . '-columns';
if ( $context = $args['context'] ?? '' ) {
$class .= ' crb-activity--' . crb_sanitize_id( $context );
}
$result_html = '<table id="crb-activity" class="' . $class . '"><thead><tr>' . $table_header . '</tr></thead><tfoot><tr>' . $table_footer . '</tr></tfoot><tbody>' . $tbody . '</tbody></table>';
if ( empty( $args['compact_view'] ) ) {
$result_html .= cerber_page_navi( $total, $per_page );
}
if ( empty( $args['no_export'] ) ) {
$export_link .= '<a download class="button button-secondary cerber-button" href="' .
cerber_admin_link_add( array(
'cerber_admin_do' => 'export',
'type' => 'activity',
), true ) . '"><i class="crb-icon crb-icon-bx-download"></i> ' . __( 'Export', 'wp-cerber' ) . '</a>';
}
}
else {
// No results found -------------------------------------------------------------
$no_results = true;
// Is it a search (filter) request?
$is_search = ( array_intersect_key( CRB_Activity::EVENT_FILTERING_PARAMS, crb_get_query_params() ) );
$hints = array();
if ( ! $is_search ) {
$hints[] = __( 'No activity has been logged yet.', 'wp-cerber' );
}
else {
$hints[] = __( 'No events found using the given search criteria', 'wp-cerber' );
if ( CRB_Activity_Alerts::alert_exists() ) {
$hints[] = crb_ui_element( 'p', array(), array(
__( 'You will be notified when such an event occurs', 'wp-cerber' ) . ' [ ',
crb_ui_raw_html( crb_admin_alert_dialog( false, '', __( 'Delete', 'wp-cerber' ) ) ),
' ]',
) );
}
elseif ( $alert_link = crb_admin_alert_dialog( false, __( 'Get me notified when such an event occurs', 'wp-cerber' ) ) ) {
$hints[] = crb_ui_element( 'p', array(), crb_ui_raw_html( $alert_link ) );
}
if ( ! $is_log_empty ) {
$hints[] = crb_ui_element( 'p', array(), crb_ui_link( $base_url, __( 'View all logged events', 'wp-cerber' ) ) );
}
$trf_params = array();
if ( cerber_is_ip_or_net( $search ) ) {
$trf_params['filter_ip'] = $search;
}
if ( $trf_params ) {
$hints[] = crb_ui_element( 'p', array( 'class' => 'cerber-margin' ), crb_ui_link( cerber_admin_link( 'traffic', $trf_params ), __( 'Check for requests from the IP address', 'wp-cerber' ) . ' ' . $trf_params['filter_ip'] ) );
}
}
// Let's render a info panel
$result_html = crb_ui_renderer()->render_element( crb_ui_message_box( $hints ) );
}
if ( empty( $args['compact_view'] ) ) {
$display_full = ( $sid || $in_url ) ? '' : 'display: none;';
$act_filter_form = '<form action="' . cerber_activity_link() . '"><div id="crb-activity-fields"><div>'
. crb_get_activity_dd()
. cerber_select( 'filter_user', ( $user_id ) ? array( $user_id => $sname ) : array(), $user_id, 'crb-select2-ajax', '', false, esc_html__( 'Filter by registered user', 'wp-cerber' ), array( 'min_symbols' => 3 ) )
. '<input type="text" value="' . $search . '" name="search_activity" placeholder="' . esc_html__( 'Search for IP or username', 'wp-cerber' ) . '">
<div id="crb-more-activity-fields" style="' . $display_full . '">
<input type="text" value="' . $sid . '" name="filter_sid" placeholder="' . esc_html__( 'Request ID', 'wp-cerber' ) . '">
<input type="text" value="' . $in_url . '" name="search_url" placeholder="' . esc_html__( 'Search in URL', 'wp-cerber' ) . '">
</div>
</div>
<div class="crb-act-controls">
<button type="submit" class="cerber-button button button-primary">
' . __( 'Filter', 'wp-cerber' ) . '
</button>
</div>
<div class="crb-act-controls">
[ <a href="#" class="crb-opener" data-target="crb-more-activity-fields">More</a> ]
</div>
</div>
<input type="hidden" name="page" value="cerber-security" >
<input type="hidden" name="tab" value="activity">
</form>';
$right_links = ! $no_results ? crb_admin_alert_dialog() : '';
$toolbar_secondary = '<div id="crb-admin-toolbar-secondary-row"><div>' . $act_filter_form . '</div><div>' . $right_links . $export_link . '</div></div><br style="clear: both;">';
$quick_nav = ! $is_log_empty ? crb_admin_activity_nav_links() : '';
$toolbar_primary = '<div id="crb-admin-toolbar-primary-row"><div class="crb-quick-nav">' . $quick_nav . '</div></div>';
$toolbar = '<div id="crb-admin-toolbar">' . $toolbar_primary . $toolbar_secondary . $extra_view . '</div>';
}
$return = $toolbar . $result_html;
if ( ! $echo ) {
return $return;
}
echo $return;
return '';
}
/**
* Generates activity breakdown report
*
* @param bool $just_check If true, will return true if the activity log contains data for generating the report
*
* @return string HTML code of the report table
*
* @since 9.6.4.1
*/
function cerber_activity_summary( $just_check = false ) {
$stamp = time() - 24 * 3600;
$rows = array();
$not_in = ' activity NOT IN (' . crb_get_activity_set( 'blocked', true ) . ')';
$logged = (bool) cerber_db_get_var( 'SELECT ip FROM ' . CERBER_LOG_TABLE . ' WHERE stamp >= ' . $stamp . ' AND ' . $not_in . ' LIMIT 1' );
if ( ! $logged ) {
return '';
}
if ( $just_check ) {
return true;
}
$events = cerber_db_get_results( 'SELECT activity, COUNT(activity) cnt FROM ' . CERBER_LOG_TABLE . ' WHERE stamp >= ' . $stamp . ' AND ' . $not_in . ' GROUP by activity ORDER BY cnt DESC', MYSQL_FETCH_OBJECT );
$events = array_slice( $events, 0, 10 );
$events_not = cerber_db_get_results( 'SELECT activity, COUNT(activity) cnt FROM ' . CERBER_LOG_TABLE . ' WHERE stamp >= ' . $stamp . ' AND user_id = 0 GROUP by activity', MYSQL_FETCH_OBJECT_K );
$events_aut = cerber_db_get_results( 'SELECT activity, COUNT(activity) cnt FROM ' . CERBER_LOG_TABLE . ' WHERE stamp >= ' . $stamp . ' AND user_id > 0 GROUP by activity', MYSQL_FETCH_OBJECT_K );
$lables = cerber_get_labels();
$ac_base = cerber_admin_link( 'activity' );
$ac_base_time = $ac_base . '&filter_time_begin=' . $stamp;
foreach ( $events as $a ) {
if ( $a->cnt < 1 ) {
break;
}
$act = absint( $a->activity );
$no_user = 0;
if ( $r = crb_array_get( $events_not, $a->activity, 0 ) ) {
$no_user = '<a href="' . $ac_base_time . '&filter_activity=' . $act . '&filter_user=0">' . $r->cnt . '</a>';
}
$yes_user = 0;
if ( $r = crb_array_get( $events_aut, $a->activity, 0 ) ) {
$yes_user = '<a href="' . $ac_base_time . '&filter_activity=' . $act . '&filter_user=*">' . $r->cnt . '</a>';
}
$rows[] = array(
'<a href="' . $ac_base . '&filter_activity=' . $act . '">' . $lables[ $act ] . '</a>',
$yes_user,
$no_user,
'<a href="' . $ac_base_time . '&filter_activity=' . $act . '">' . $a->cnt . '</a>'
);
}
return cerber_make_table( $rows, array( __( 'Event', 'wp-cerber' ), __( 'Known Users', 'wp-cerber' ), __( 'Unauthenticated Users', 'w-cerber' ), __( 'Total', 'w-cerber' ) ), '', 'crb-table-align-right-2', '', '', false );
}
/**
* Generates a report of the top 10 malicious IP addresses detected in the past 24 hours.
*
* The report includes the total number of incidents, spam activities, and blocked actions
* for each IP address. Each entry also provides additional metadata such as hostname and
* country (if available). The results are returned as an HTML table.
*
* @return string|array
*
* @since 9.6.4.7
*/
function crb_top_malicious_ip_report() {
$stamp = time() - 24 * 3600;
$in = crb_get_activity_set( 'suspicious', true );
$in_spam = crb_get_activity_set( 'spam', true );
$results = cerber_db_get_results(
'SELECT ip, country, COUNT(*) AS total,
SUM(activity IN (' . CRB_EV_IPL . ', ' . CRB_EV_NEL . ')) AS locked_out_sum,
SUM(activity IN (' . $in_spam . ')) AS spam_sum
FROM ' . CERBER_LOG_TABLE . '
WHERE stamp >= ' . $stamp . ' AND activity IN (' . $in . ')
GROUP BY ip
ORDER BY total DESC
LIMIT 10',
MYSQL_FETCH_OBJECT
);
if ( empty( $results ) ) {
return array( CRB_Widgets::NO_DATA => __( 'No malicious activity detected in the past 24 hours.', 'wp-cerber' ) );
}
$rows = [];
$base_url = cerber_activity_link();
$base_url_time = $base_url . '&filter_time_begin=' . $stamp;
$show_country = false;
foreach ( $results as $row ) {
$ip_id = cerber_get_id_ip( $row->ip );
$ip_info = cerber_get_ip_info( $row->ip );
$hostname = $ip_info['hostname_html'] ?? crb_get_ajax_placeholder( 'hostname', $ip_id );
$country = crb_country_html( $row->country, $row->ip );
$cells = [
crb_admin_ip_cell( $row->ip, $base_url . '&filter_ip=' . $row->ip ),
array( 'cell' => $hostname, 'class' => 'crb-hostname' ),
];
if ( $country ) {
$cells[] = $country;
$show_country = true;
}
$link_base = $base_url_time . '&filter_ip=' . $row->ip;
$cells[] = '<a href="' . $link_base . '&filter_set=1">' . $row->total . '</a>';
$cells[] = $row->spam_sum ? '<a href="' . $link_base . '&filter_set=3">' . $row->spam_sum . '</a>' : $row->spam_sum;
$cells[] = $row->locked_out_sum ? '<a href="' . $link_base . '&filter_activity[0]=' . CRB_EV_IPL . '&filter_activity[1]=' . CRB_EV_NEL . '">' . $row->locked_out_sum . '</a>' : $row->locked_out_sum;
$rows[] = $cells;
}
$heading = [ '<div class="crb_act_icon"></div>' . __( 'IP Address', 'wp-cerber' ), __( 'Hostname', 'wp-cerber' ) ];
if ( $show_country ) {
$heading[] = __( 'Country', 'wp-cerber' );
}
$heading[] = __( 'Incidents', 'wp-cerber' );
$heading[] = __( 'Spam', 'wp-cerber' );
$heading[] = __( 'Locked Out', 'wp-cerber' );
return cerber_make_table(
$rows,
$heading,
'',
'crb-activity--dashboard crb-table-align-right-4',
'',
'',
false
);
}
/**
* @param int $ac Activity ID
*
* @return string CSS class name
*
* @since 9.5.3.1
*/
function crb_get_act_style( $ac ) {
static $green = array( CRB_EV_LIN );
$ac = crb_absint( $ac );
$class = '';
if ( in_array( $ac, $green ) ) {
$class = 'crb-act-granted';
}
elseif ( in_array( $ac, crb_get_activity_set( 'denied_by_crb' ) ) ) {
$class = 'crb-act-denied';
}
return 'crb-act-' . $ac . ' ' . $class;
}
/**
* Generates an HTML markup for a given IP address to be shown on the plugin admin pages
*
* @param string $ip Valid IP address
* @param string $ip_link A link to wrap the IP address
* @param string $html Any escaped HTML code or plain text
*
* @return string A formatted HTML view of a given IP address
*/
function crb_admin_ip_cell( $ip, $ip_link = '', $html = '' ) {
static $cache = array();
if ( isset( $cache[ $ip ] ) ) {
return $cache[ $ip ];
}
$acl = cerber_acl_check( $ip, '', 0, $row );
$tip = '';
if ( ! empty( $row ) ) {
$tip = $row->comments;
}
else {
if ( $acl == 'W' ) {
$tip = __( 'Allowed IP Access List', 'wp-cerber' );
}
elseif ( $acl == 'B' ) {
$tip = __( 'Blocked IP Access List', 'wp-cerber' );
}
}
if ( $b = cerber_block_check( $ip ) ) {
$block = ' crb_color_blocked ';
$tip = ( $tip ) ? ' / ' . $b->reason : $b->reason;
}
else {
$block = '';
}
if ( $ip_link ) {
$ip = '<a href="' . crb_escape_url( $ip_link ) . '">' . $ip . '</a>';
}
$cache[ $ip ] = '<div class="crb_css_table"><div><span class="crb_act_icon crb_ip_acl' . $acl . ' ' . $block . '" title="' . crb_attr_escape( $tip ) . '"></span></div><div>' . $ip . $html . '</div></div>';
return $cache[ $ip ];
}
/*
* Additional information about IP address
*/
function cerber_ip_extra_view( $ip, $context = 'activity' ) {
if ( ! $ip || ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
return '';
}
$html = crb_generate_ip_extra_view( $ip, $context, true );
$class = 'crb-extra-info';
$ajax = '';
if ( ! $html ) {
$ajax = ' data-ajax_route="ip_extra_info" data-ip="' . $ip . '"';
$class .= ' crb_async_content';
$html = UIS_LOADER_HTML;
}
return '<div id="crb-extra-ip-info" class="' . $class . '" ' . $ajax . '>' . $html . '</div>';
}
/**
* @param array $request
* @param array $ref_params
*
* @return array
*/
function cerber_ip_extra_view_ajax( $request, $ref_params ) {
$ip = filter_var( $request['ip'], FILTER_VALIDATE_IP );
$tab = $ref_params['tab'] ?? '';
return crb_generate_ip_extra_view( $ip, $tab );
}
function crb_generate_ip_extra_view( $ip, $context = 'activity', $cache_only = false ) {
if ( ! $ip || ! filter_var( $ip, FILTER_VALIDATE_IP ) ) {
return '';
}
$ip_navs = '';
if ( $context == 'activity' ) {
$ip_navs .= crb_get_traffic_link( array( 'filter_ip' => $ip ) );
}
else {
$ip_navs .= ' <a class="crb-button-tiny" href="' . cerber_admin_link( 'activity', array( 'filter_ip' => $ip ) ) . '">' . __( 'Check for activities', 'wp-cerber' ) . '</a>';
}
$acl = cerber_acl_check( $ip, '', 0, $row );
$comments = ( $row ) ? crb_attr_escape( $row->comments ) : '';
$ip_status = '';
if ( $acl == 'W' ) {
$ip_status .= '<span title="' . $comments . '" class="crb_color_green crb_ip_info_label">' . __( 'Allowed IP Access List', 'wp-cerber' ) . '</span>';
}
elseif ( $acl == 'B' ) {
$ip_status .= '<span title="' . $comments . '" class="crb_color_black crb_ip_info_label">' . __( 'Blocked IP Access List', 'wp-cerber' ) . '</span>';
}
$key_cache = '_cache_ipx_' . $ip . $context . (string) $acl;
if ( $block = cerber_block_check( $ip ) ) {
$ip_status .= '<span class="crb_color_blocked crb_ip_info_label">' . __( 'Locked out', 'wp-cerber' ) . '</span><span class="crb-log-status crb-status-' . $block->reason_id . '">' . $block->reason . '</span>';
$key_cache .= '_blocked';
}
if ( $cache = cerber_get_set( $key_cache, null, false, true ) ) {
return $cache;
}
elseif ( $cache_only ) {
return '';
}
$rdap_info = '';
$country = '';
$abuse = '';
$network = '';
$network_info = '';
$network_navs = '';
$to_cache = true;
if ( crb_get_settings( 'ip_extra' ) ) {
$ip_rdap = CRB_RDAP_Client::get_parsed_ip_info( $ip );
if ( crb_is_wp_error( $ip_rdap ) ) {
$rdap_info = $ip_rdap->get_error_message();
$to_cache = false;
}
else {
$rdap_info = CRB_RDAP_Client::render_html_view( $ip_rdap );
if ( $country = $ip_rdap->country ) {
$country = crb_get_flag_html( $country, '<b>' . crb_get_country_name( $country ) . ' (' . $country . ')</b>' );
}
if ( $network = $ip_rdap->cidr ) {
$range = cerber_any2range( $network );
/* translators: %s is the network CIDR. */
$network_info = sprintf( __( 'Network: %s', 'wp-cerber' ), $network );
$network_navs = '<a class="crb-button-tiny" href="' . cerber_admin_link( 'activity', array( 'filter_ip' => $range['range'] ) ) . '">' . __( 'Check for activities', 'wp-cerber' ) . '</a> ' . crb_get_traffic_link( array( 'filter_ip' => $range['range'] ) );
}
}
}
$form = '';
if ( ! cerber_is_myip( $ip ) && ! cerber_acl_check( $ip ) ) {
if ( $network ) {
$net_button = '<button type="submit" value="' . $network . '" name="add_acl" class="button button-secondary cerber-button">';
}
else {
$net_button = '<button disabled="disabled" class="button button-secondary cerber-button">';
}
$net_button .= __( 'Block subnet', 'wp-cerber' ) . '</button> ';
$form = '<form id="add_acl_black" action="" method="post">
<input type="hidden" name="cerber_admin_do" value="add2acl">
<input type="hidden" name="add_acl_comment" value="">
<input type="hidden" name="acl_tag" value="B">
<button type="submit" value="' . $ip . '" name="add_acl" class="crb-button-tiny">' . __( 'Block IP address', 'wp-cerber' ) . '</button>' .
'<!-- <p>' . $net_button . '</p>-->' .
cerber_nonce_field( 'control' ) .
'</form>';
}
$ip_info = '<span id="crb-ip-address">' . $ip . '</span><span id="crb-ip-country">' . $country . '</span>';
$ip_insights = crb_generate_ip_insights( $ip, $context );
$ret = '
<div class="crb_extra_info_row">
<div id="crb_the_summary">
<div><div style="white-space: nowrap;">' . $ip_info . '</div><div class="crb-nav-row">' . $ip_status . ' ' . $ip_navs . ' ' . $form . '</div></div>
<div><div style="white-space: nowrap;">' . $network_info . '</div><div class="crb-nav-row">' . $network_navs . '</div></div>' . $abuse . '
</div>
<div class="crb_async_content" data-ajax_route="ip_quick_analytics" data-ip_address="' . $ip . '" data-the_active_tab="' . $context . '">' . $ip_insights . '
</div>
</div>
<div id="crb-whois-data">' . $rdap_info . '</div>
';
if ( $to_cache ) {
cerber_update_set( $key_cache, $ret, 0, false, time() + 7200, true );
}
return $ret;
}
/**
* @param string $ip
* @param string $tab
* @param false $cache_only
*
* @return string|false
*/
function crb_generate_ip_insights( $ip, $tab = 'activity', $cache_only = false ) {
if ( $tab != 'activity' ) {
$tab = 'traffic';
}
$result = '';
$links = array();
$labels = cerber_get_labels();
if ( $data = crb_q_cache_get( 'SELECT COUNT(activity) as cnt, activity FROM ' . CERBER_LOG_TABLE . ' WHERE ip = "' . $ip . '" GROUP BY activity', CERBER_LOG_TABLE, $cache_only ) ) {
$links[] = array( array( 'filter_ip' => $ip ), __( 'All' ) );
foreach ( $data as $item ) {
$links[] = array( array( 'filter_activity' => $item[1], 'filter_ip' => $ip ), crb_array_get( $labels, $item[1], 'Unknown' ) . ' - ' . $item[0] );
}
}
if ( $links ) {
$result = '<div id="crb-quick-insights">' . crb_build_quick_nav_block( $links, $tab ) . '</div>';
}
if ( ! $result ) {
if ( $cache_only ) {
return false;
}
$result = __( 'No activity has been logged yet.', 'wp-cerber' );
}
return $result;
}
/**
* Prepare a short report (extra information) about a given user
*
* @param int $user_id
* @param string $context
*
* @return string
*/
function cerber_user_extra_view( int $user_id, string $context = 'activity' ): string {
global $wpdb;
if ( ! $user = crb_get_userdata( $user_id ) ) {
return '';
}
$ret = '';
$class = '';
$user_profile = '';
$user_summary = array();
if ( $avatar = (string) get_avatar( $user_id, 96 ) ) {
$user_profile = '<div style="margin-bottom: 1em;">' . $avatar . '</div>';
}
if ( ! is_multisite() && $user->roles ) {
$roles = array();
$wp_roles = wp_roles();
foreach ( $user->roles as $role ) {
$roles[] = $wp_roles->roles[ $role ]['name'];
}
$roles = '<span class="crb_act_role">' . implode( ', ', $roles ) . '</span>';
}
else {
$roles = '';
}
if ( ! nexus_is_valid_request() ) {
$name = '<a href="' . get_edit_user_link( $user_id ) . '" target="_blank">' . $user->display_name . '</a>';
}
else {
$name = $user->display_name;
}
$name = '<span class="crb-user-name">' . $name . '</span><p>' . $roles . '</p>';
$user_profile .= '<div>' . $name . '</div>';
// Last seen
$seen = array();
$seen[0] = $wpdb->get_row( 'SELECT stamp,ip FROM ' . CERBER_TRAF_TABLE . ' WHERE user_id = ' . $user_id . ' ORDER BY stamp DESC LIMIT 1' );
$seen[1] = $wpdb->get_row( 'SELECT stamp,ip FROM ' . CERBER_LOG_TABLE . ' WHERE user_id = ' . $user_id . ' AND ac_by_user = 0 ORDER BY stamp DESC LIMIT 1' );
$seen[2] = $wpdb->get_row( 'SELECT stamp,ip FROM ' . CERBER_LOG_TABLE . ' WHERE ac_by_user = ' . $user_id . ' ORDER BY stamp DESC LIMIT 1' );
$tmp = array();
foreach ( $seen as $key => $log_row ) {
$tmp[ $key ] = ( $log_row ) ? $log_row->stamp : 0;
}
if ( $max = max( $tmp ) ) {
$max_keys = array_keys( $tmp, $max );
$last_log = $seen[ $max_keys[0] ];
$last_seen = '<span title="' . cerber_date( $last_log->stamp, false ) . '">' . cerber_ago_time( $last_log->stamp ) . '</span>';
$country = crb_country_html( null, $last_log->ip );
$user_summary[] = array( __( 'Last seen', 'wp-cerber' ), $last_seen, $country );
}
// Last login
$last_login = crb_get_last_user_login( $user_id );
if ( ! empty( $last_login ) ) {
$country = crb_country_html( $last_login['cn'], $last_login['ip'] );
$user_summary[] = array( __( 'Last login', 'wp-cerber' ), cerber_date( $last_login['ts'], false ), $country );
}
// Registered (created)
if ( $time = strtotime( cerber_db_get_var( "SELECT user_registered FROM {$wpdb->users} WHERE id = " . $user_id ) ) ) {
$reg_date = cerber_auto_date( $time, false );
$reg_event = __( 'Registered', 'wp-cerber' );
$country = '';
if ( $reg_meta = get_user_meta( $user_id, '_crb_reg_', true ) ) {
if ( $reg_meta['IP'] ?? false ) {
$country = crb_country_html( null, $reg_meta['IP'] );
}
if ( $reg_meta['user'] ?? false ) {
$reg_event = crb_get_activity_label( 1, $user_id, $reg_meta['user'] );
}
}
$user_summary[] = array( $reg_event, $reg_date, $country );
}
// Activated - BuddyPress
if ( $log = CRB_Activity::get_log( array( 200 ), array( 'id' => $user_id ) ) ) {
$acted = $log[0];
$activated = cerber_auto_date( $acted->stamp );
$country = crb_country_html( null, $acted->ip );
$user_summary[] = array( __( 'Activated', 'wp-cerber' ), $activated, $country );
}
$usn = crb_sessions_get_num( $user_id );
$sess_link = $usn ? '<a href="' . cerber_admin_link( 'sessions', array( 'filter_user' => $user_id ) ) . '">' . $usn . '</a>' : '0';
$user_summary[] = array(
'<div style="white-space: nowrap;">' . __( 'Active sessions', 'wp-cerber' ) . '</div>',
$sess_link
);
// Make a link to switch to other log
if ( $context == 'activity' ) {
$link = crb_get_traffic_link( array( 'filter_user' => $user_id ), false );
}
else {
$link = ' <a href="' . cerber_admin_link( 'activity', array( 'filter_user' => $user_id ) ) . '">' . __( 'Check for activities', 'wp-cerber' ) . '</a>';
}
$user_summary[] = array( $link );
$summary = '';
foreach ( $user_summary as $row ) {
$row = array_pad( $row, 3, '' );
$summary .= '<tr><td>' . implode( '</td><td>', $row ) . '</td></tr>';
}
if ( $us_sts = crb_get_user_auth_status( $user ) ) {
$summary .= '<tr><td colspan="3"><div>' . crb_format_user_status( $us_sts ) . '</div></td></tr>';
}
$summary = '<table id="crb-user-summary">' . $summary . '</table>';
$ins_class = '';
if ( ! $ins = crb_generate_user_insights( $user_id, $context, true ) ) {
$ins_class = 'crb_async_content';
}
$ret .= '<div class="crb_extra_info_row"><div>' . $user_profile . '</div><div id="crb_the_summary">' . $summary . '</div><div class="' . $ins_class . '" data-ajax_route="user_activity_analytics" data-user_id="' . $user_id . '" data-the_active_tab="' . $context . '">' . $ins . '</div></div>';
return '<div class="crb-extra-info ' . $class . '" id="crb-user-extra-info">' . $ret . '</div>';
}
// Users -------------------------------------------------------------------------------------
add_filter( 'users_list_table_query_args', function ( $args ) {
if ( crb_get_settings( 'usersort' ) && empty( $args['orderby'] ) ) {
$args['orderby'] = 'user_registered';
$args['order'] = 'desc';
}
return $args;
} );
/*
Add custom columns to the Users admin screen
*/
add_filter( 'manage_users_columns', function ( $columns ) {
return array_merge( $columns,
array(
'cbcc' => __( 'Comments', 'wp-cerber' ),
'cbla' => __( 'Last login', 'wp-cerber' ),
'cbfl' => '<span title="In last 24 hours">' . __( 'Failed login attempts', 'wp-cerber' ) . '</span>',
'cbdr' => __( 'Registered', 'wp-cerber' )
) );
} );
add_filter( 'manage_users_sortable_columns', function ( $sortable_columns ) {
$sortable_columns['cbdr'] = 'user_registered';
return $sortable_columns;
} );
/*
Display custom columns on the Users screen
*/
add_filter( 'manage_users_custom_column', function ( $value, $column, $user_id ) {
global $wpdb, $user_ID;
$ret = $value;
switch ( $column ) {
case 'cbcc' :
case 'cbla' :
case 'cbfl' :
$ret = crb_get_ajax_placeholder( $column, $user_id );
break;
case 'cbdr' :
$time = strtotime( cerber_db_get_var( "SELECT user_registered FROM $wpdb->users WHERE id = " . $user_id ) );
$ret = cerber_auto_date( $time );
if ( $reg_data = get_user_meta( $user_id, '_crb_reg_', true ) ) {
if ( $ip = filter_var( $reg_data['IP'], FILTER_VALIDATE_IP ) ) {
$act_link = cerber_admin_link( 'activity', array( 'filter_ip' => $ip ) );
$ret .= '<br /><a href="' . $act_link . '">' . $ip . '</a>';
if ( $country = crb_country_html( null, $ip ) ) {
$ret .= '<br />' . $country;
}
}
if ( $uid = absint( $reg_data['user'] ) ) {
$name = cerber_db_get_var( 'SELECT meta_value FROM ' . $wpdb->usermeta . ' WHERE user_id = ' . $uid . ' AND meta_key = "nickname"' );
if ( ! $user_ID ) {
$user_ID = get_current_user_id();
}
if ( $user_ID == $uid ) {
$name .= ' (' . __( 'You', 'wp-cerber' ) . ')';
}
$ret .= '<br />' . $name;
}
}
break;
}
return $ret;
}, 10, 3 );
/*
Registering WordPress Dashboard widgets
*/
if ( ! is_multisite() ) {
add_action( 'wp_dashboard_setup', 'cerber_widgets' );
}
else {
add_action( 'wp_network_dashboard_setup', 'cerber_widgets' );
}
function cerber_widgets() {
if ( cerber_user_can_manage() ) {
wp_add_dashboard_widget( 'cerber_quick', __( 'Cerber Quick View', 'wp-cerber' ), 'cerber_quick_w' );
}
}
/*
Cerber Quick View widget - WordPress Dashboard
*/
function cerber_quick_w() {
$dash = cerber_admin_link();
$act = cerber_admin_link( 'activity' );
$traf = cerber_admin_link( 'traffic' );
$scanner = cerber_admin_link( 'scan_main' );
$acl = cerber_admin_link( 'acl' );
$sess = cerber_admin_link( 'sessions' );
$locks = cerber_admin_link( 'lockouts' );
$s_count = cerber_db_get_var( 'SELECT COUNT(DISTINCT user_id) FROM ' . cerber_get_db_prefix() . CERBER_USS_TABLE );
$failed = cerber_db_get_var( 'SELECT count(ip) FROM ' . CERBER_LOG_TABLE . ' WHERE activity IN (' . CRB_EV_LFL . ') AND stamp > ' . ( time() - 24 * 3600 ) );
$failed_prev = cerber_db_get_var( 'SELECT count(ip) FROM ' . CERBER_LOG_TABLE . ' WHERE activity IN (' . CRB_EV_LFL . ') AND stamp > ' . ( time() - 48 * 3600 ) . ' AND stamp < ' . ( time() - 24 * 3600 ) );
$failed_ch = cerber_percent( $failed_prev, $failed );
$locked = cerber_db_get_var( 'SELECT count(ip) FROM ' . CERBER_LOG_TABLE . ' WHERE activity IN (' . CRB_EV_IPL . ',' . CRB_EV_NEL . ') AND stamp > ' . ( time() - 24 * 3600 ) );
$locked_prev = cerber_db_get_var( 'SELECT count(ip) FROM ' . CERBER_LOG_TABLE . ' WHERE activity IN (' . CRB_EV_IPL . ',' . CRB_EV_NEL . ') AND stamp > ' . ( time() - 48 * 3600 ) . ' AND stamp < ' . ( time() - 24 * 3600 ) );
$locked_ch = cerber_percent( $locked_prev, $locked );
$lockouts = crb_get_locked_out_num();
if ( $last = cerber_db_get_var( 'SELECT MAX(stamp) FROM ' . CERBER_LOG_TABLE . ' WHERE activity IN (' . CRB_EV_IPL . ',' . CRB_EV_NEL . ')' ) ) {
$last = cerber_ago_time( $last );
}
else {
$last = __( 'Never', 'wp-cerber' );
}
$w_count = cerber_db_get_var( 'SELECT count(ip) FROM ' . CERBER_ACL_TABLE . ' WHERE tag ="W"' );
$b_count = cerber_db_get_var( 'SELECT count(ip) FROM ' . CERBER_ACL_TABLE . ' WHERE tag ="B"' );
if ( cerber_is_citadel() ) {
$citadel = '<span style="color:#FF0000;">' . __( 'Active', 'wp-cerber' ) . '</span> (<a href="' . cerber_admin_link_add( [ 'citadel_do' => 'deactivate' ] ) . '">' . __( 'deactivate', 'wp-cerber' ) . '</a>)';
}
else {
if ( crb_get_settings( 'citadel_on' ) && crb_get_settings( 'ciperiod' ) ) {
$citadel = __( 'Inactive', 'wp-cerber' );
}
else {
$citadel = __( 'Disabled', 'wp-cerber' );
}
}
echo '<div class="cerber-widget">';
echo '<table style="width:100%;"><tr><td style="width:50%; vertical-align:top;"><table><tr><td class="bigdig">' . $failed . '</td><td class="per">' . $failed_ch . '</td></tr></table><p>' . __( 'failed attempts in the last 24 hours', 'wp-cerber' ) . '<br/>(<a href="' . $act . '&filter_activity=' . CRB_EV_LFL . '">' . __( 'view all', 'wp-cerber' ) . '</a>)</p></td>';
echo '<td style="width:50%; vertical-align:top;"><table><tr><td class="bigdig">' . $locked . '</td><td class="per">' . $locked_ch . '</td></tr></table><p>' . __( 'lockouts in the last 24 hours', 'wp-cerber' ) . '<br/>(<a href="' . $act . '&filter_activity[]=' . CRB_EV_IPL . '&filter_activity[]=' . CRB_EV_NEL . '">' . __( 'view all', 'wp-cerber' ) . '</a>)</p></td></tr></table>';
echo '<table id="quick-info"><tr><td>' . __( 'Active lockouts', 'wp-cerber' ) . '</td><td><b><a href="' . $locks . '">' . $lockouts . '</a></b></td></tr>';
echo '<tr><td>' . __( 'Last lockout', 'wp-cerber' ) . '</td><td>' . $last . '</td></tr>';
echo '<tr class="with-padding"><td>' . __( 'Logged-in users', 'wp-cerber' ) . '</td><td><b><a href="' . $sess . '">' . $s_count . ' ' . _n( 'user', 'users', $s_count, 'wp-cerber' ) . '</a></b></td></tr>';
echo '<tr class="with-padding"><td>' . __( 'Allowed IP Access List', 'wp-cerber' ) . '</td><td><b><a href="' . $acl . '">' . $w_count . ' ' . _n( 'entry', 'entries', $w_count, 'wp-cerber' ) . '</a></b></td></tr>';
echo '<tr><td>' . __( 'Blocked IP Access List', 'wp-cerber' ) . '</td><td><b><a href="' . $acl . '">' . $b_count . ' ' . _n( 'entry', 'entries', $b_count, 'wp-cerber' ) . '</a></b></td></tr>';
echo '<tr class="with-padding"><td>' . __( 'Citadel mode', 'wp-cerber' ) . '</td><td><b>' . $citadel . '</b></td></tr>';
$status = ( ! crb_get_settings( 'tienabled' ) ) ? '<span style="color: red;">' . __( 'Disabled', 'wp-cerber' ) . '</span>' : __( 'Enabled', 'wp-cerber' );
echo '<tr class="with-padding"><td>' . __( 'Traffic Inspector', 'wp-cerber' ) . '</td><td><b>' . $status . '</b></td></tr>';
$lab = lab_lab();
if ( $lab ) {
$status = ( ! lab_is_cloud_ok() ) ? '<span style="color: red;">' . __( 'No connection', 'wp-cerber' ) . '</span>' : __( 'Active', 'wp-cerber' );
echo '<tr><td>Cloud Protection</td><td><b>' . $status . '</b></td></tr>';
}
$s = '';
$scan = cerber_get_scan();
if ( ! $scan || ( WEEK_IN_SECONDS < ( time() - $scan['finished'] ) ) ) {
$s = 'style="color: red;"';
}
if ( $scan ) {
$lms = $scan['mode_h'] . ' ' . cerber_auto_date( $scan['started'] );
}
else {
$lms = __( 'Never', 'wp-cerber' );
}
echo '<tr ' . $s . '><td>' . _x( 'Last malware scan', 'Example: Last malware scan: 23 Jan 2018', 'wp-cerber' ) . '</td><td><a href="' . $scanner . '">' . $lms . '</a></td></tr>';
$link = cerber_admin_link( 'scan_schedule' );
$quick = ( ! $lab || ! $q = absint( crb_get_settings( 'scan_aquick' ) ) ) ? __( 'Disabled', 'wp-cerber' ) : cerber_get_qs( $q );
echo '<tr><td>' . __( 'Quick Scan', 'wp-cerber' ) . '</td><td><a href="' . $link . '">' . $quick . '</a></td></tr>';
$f = ( ! $lab || ! crb_get_settings( 'scan_afull-enabled' ) ) ? __( 'Disabled', 'wp-cerber' ) : crb_get_settings( 'scan_afull' );
echo '<tr><td>' . __( 'Full Scan', 'wp-cerber' ) . '</td><td><a href="' . $link . '">' . $f . '</a></td></tr>';
/*
$dev = crb_get_settings('pbdevice');
if (!$dev || $dev == 'N') echo '<tr><td style="padding-top:15px;">'.__('Push notifications','wp-cerber').'</td><td style="padding-top:15px;"><b>not configured</b></td></tr>';
*/
echo '</table></div>';
echo '<div class="wilinks">
<a href="' . $dash . '"><i class="crb-icon crb-icon-bxs-dashboard"></i> ' . __( 'Dashboard', 'wp-cerber' ) . '</a> |
<a href="' . $act . '"><i class="crb-icon crb-icon-bx-pulse"></i> ' . __( 'Activity', 'wp-cerber' ) . '</a> |
<a href="' . $traf . '"><i class="crb-icon crb-icon-bx-show"></i> ' . __( 'Traffic', 'wp-cerber' ) . '</a> |
<a href="' . $scanner . '"><i class="crb-icon crb-icon-bx-radar"></i> ' . __( 'Integrity', 'wp-cerber' ) . '</a>
</div>';
if ( cerber_check_new_version() ) {
echo '<div class="up-cerber">' . __( 'A new version is available', 'wp-cerber' ) . '</div>';
}
}
/**
*
* WP Cerber Dashboard
*
*
*/
function cerber_show_dashboard() {
cerber_widgets_init();
$all_widgets = CRB_Widgets::get_titles( true );
echo '<div id="crb-dashboard-container">';
foreach ( $all_widgets as $widget_id => $titles ) {
$title = $titles[0];
$subtitle = $titles[1] ? '<span class="crb-widget-subtitle"> ── ' . $titles[1] . '</span>' : '';
$widget = CRB_Widgets::render_widget( $widget_id, false );
$show_control = true;
if ( crb_is_wp_error( $widget ) ) {
$body = $widget->get_error_message();
}
elseif ( is_array( $widget ) ) {
$body = $widget[0] ?? '';
$show_control = $widget[1] ?? true;
}
else {
$body = $widget;
}
if ( ! $body || ! is_string( $body ) ) {
continue;
}
$controls = CRB_Widgets::get_controls( $widget_id );
$visibility = $show_control ? '' : ' style="visibility: hidden;" ';
$heading = ( ! CRB_Widgets::hide_header( $widget_id ) && $title ) ? '<div class="crb-widget-heading"><div class="crb-widget-title-cell"><h2 class="crb-widget-title crb-dash-padding">' . $title . '</h2>' . $subtitle . '</div><div class="crb-widget-controls" ' . $visibility . '>' . $controls . '</div></div>' : '';
echo '<div class="crb-dashboard-element" data-widget_id="' . $widget_id . '">' . $heading . '<div class="crb-widget-content" data-from_cache="' . ( CRB_Widgets::$from_cache ? 1 : 0 ) . '">' . $body . '</div></div>';
}
echo '</div>';
echo '<div style="text-align: right">' . crb_admin_screen_dialog() . '</div>';
}
/**
* Initializing and registering dashboard widgets code.
*
* @return void
*
* @since 9.6.4.2
*/
function cerber_widgets_init() {
CRB_Widgets::register( 'wgt_ac_kpi',
__( 'Security Status Monitor', 'wp-cerber' ),
'',
'',
function () {
$kpi_list = cerber_calculate_kpi( time() - 24 * 3600, time() );
$kpi_show = '';
foreach ( $kpi_list as $kpi ) {
$kpi_show .= '<td>' . $kpi[1] . '</td><td><span style="z-index: 10;">' . $kpi[0] . '</span></td>';
}
$kpi_show = '<table id="crb-kpi" class="" title="In the last 24 hours"><tr>' . $kpi_show . '</tr></table>';
// TODO: add link "send daily report to my email"
// $kpi_show .= '<p style="text-align: right; margin: 0;">' . __( 'in the last 24 hours', 'wp-cerber' ) . '</p>';
return $kpi_show;
},
[ CRB_Activity::LOG_TABLE_STATUS ],
[ 'no_heading' => true ] );
CRB_Widgets::register( 'wgt_ac_breakdown',
__( 'Activity Breakdown', 'wp-cerber' ),
__( 'most frequent incidents and events in the last 24 hours', 'wp-cerber' ),
'',
function ( $ajax = false ) {
return cerber_activity_summary();
/* AJAX variant
if ( $ajax ) {
return cerber_activity_summary();
}
if ( cerber_activity_summary( true ) ) {
return array( '', false, true );
}
return ''; */
},
array( CRB_Activity::LOG_TABLE_STATUS ) );
CRB_Widgets::register( 'wgt_mali_top',
__( 'Top Offending IP Addresses', 'wp-cerber' ),
__( 'most active in the last 24 hours', 'wp-cerber' ),
'',
function ( $ajax = false ) {
return crb_top_malicious_ip_report();
},
array( CRB_Activity::LOG_TABLE_STATUS ) );
CRB_Widgets::register( 'wgt_new_users',
__( 'New Users', 'wp-cerber' ),
__( 'most recent user registrations', 'wp-cerber' ),
crb_build_quick_nav_block( array( array( array( 'filter_activity' => array( 1, 2 ) ), __( 'View all', 'wp-cerber' ) ) ), 'activity' ),
function () {
$logged = cerber_db_get_var( 'SELECT ip FROM ' . CERBER_LOG_TABLE . ' WHERE activity IN (1,2) LIMIT 1' );
if ( ! $logged ) {
return array( CRB_Widgets::NO_DATA => __( 'No activity has been logged yet.', 'wp-cerber' ) );
}
return crb_render_activity_view( array(
'filter_activity' => array( 1, 2 ),
'per_page' => 10,
'compact_view' => true,
'no_export' => true,
'no_details' => true,
'date' => 'ago',
'hide_columns' => array( 'crb_username_col' ),
'context' => 'dashboard',
), false );
},
array( CRB_Activity::LOG_TABLE_STATUS, 60 ) );
CRB_Widgets::register( 'wgt_login_issues',
__( 'Login Issues', 'wp-cerber' ),
__( 'most recent authentication issues and requests', 'wp-cerber' ),
crb_build_quick_nav_block( array( array( array( 'filter_set' => '2' ), __( 'View all', 'wp-cerber' ) ) ), 'activity' ),
function () {
$in = crb_get_activity_set( 'login_issues', true );
$logged = cerber_db_get_var( 'SELECT ip FROM ' . CERBER_LOG_TABLE . ' WHERE activity IN (' . $in . ') LIMIT 1' );
if ( ! $logged ) {
return array( CRB_Widgets::NO_DATA => __( 'No activity has been logged yet.', 'wp-cerber' ) );
}
return crb_render_activity_view( array(
'filter_set' => 2,
'per_page' => 10,
'compact_view' => true,
'no_export' => true,
'no_details' => true,
'date' => 'ago',
'hide_columns' => array( 'crb_user_col' ),
'context' => 'dashboard',
), false );
},
array( CRB_Activity::LOG_TABLE_STATUS, 60 ) );
CRB_Widgets::register(
'wgt_user_activity',
__( "Users' Activity", 'wp-cerber' ),
__( 'most recent user events', 'wp-cerber' ),
crb_admin_activity_nav_links( 'users' ),
function () {
$user_logged = warp_run_db( static function ( CRB_Database $db ): Revalt {
return $db->table( CERBER_LOG_TABLE )
->select( 'ip' )
->where( 'user_id', '<>', 0 )
->limit( 1 )
->get_value();
} );
if ( $user_logged->has_errors() ) {
return array(
CRB_Widgets::NO_DATA => sprintf(
__( 'An error occurred while retrieving data from the database table %s.', 'wp-cerber' ),
'<code>' . CERBER_LOG_TABLE . '</code>'
),
);
}
if ( ! $user_logged->get_results() ) {
return array( CRB_Widgets::NO_DATA => __( 'No user activity has been logged yet.', 'wp-cerber' ) );
}
return crb_render_activity_view( array(
'filter_user' => '*',
'per_page' => 10,
'compact_view' => true,
'no_export' => true,
'no_details' => true,
'date' => 'ago',
'hide_columns' => array( 'crb_username_col' ),
'context' => 'dashboard',
), false );
},
array( CRB_Activity::LOG_TABLE_STATUS, 60 )
);
CRB_Widgets::register( 'wgt_mali_activity',
__( 'Malicious Activity', 'wp-cerber' ),
__( 'most recent violations of security rules', 'wp-cerber' ),
crb_admin_activity_nav_links( 'suspicious' ),
function () {
$in = crb_get_activity_set( 'suspicious', true );
$bad_logged = cerber_db_get_var( 'SELECT ip FROM ' . CERBER_LOG_TABLE . ' WHERE activity IN (' . $in . ') LIMIT 1' );
if ( ! $bad_logged ) {
return array( CRB_Widgets::NO_DATA => __( 'No activity has been logged yet.', 'wp-cerber' ) );
}
return crb_render_activity_view( array(
'filter_set' => 1,
'per_page' => 10,
'compact_view' => true,
'no_export' => true,
'no_details' => true,
'date' => 'ago',
'context' => 'dashboard',
), false );
},
array( CRB_Activity::LOG_TABLE_STATUS, 60 ) );
CRB_Widgets::register( 'wgt_blocked_ips',
__( 'Recently locked out IP addresses', 'wp-cerber' ),
'',
crb_build_quick_nav_block( array( array( array(), __( 'View all', 'wp-cerber' ) ) ), 'lockouts' ),
function () {
$locked = crb_get_locked_out_num();
if ( ! $locked ) {
return array( CRB_Widgets::NO_DATA => __( 'No lockouts at the moment. The sky is clear.', 'wp-cerber' ) );
}
return cerber_show_lockouts_page( array(
'per_page' => 10,
'compact_view' => true
), false );
} );
CRB_Widgets::register(
'wgt_system_readiness',
__( 'System Readiness', 'wp-cerber' ),
__( 'key factors affecting security and stability', 'wp-cerber' ),
'',
function () {
return crb_build_readiness_widget();
},
[],
[ 'hideable' => false ]
);
}
/**
* Retrieve issues and generate the widget HTML.
*
* @return string|array HTML content string or an array with 'no_issues' message.
*
* @since 9.6.16
*/
function crb_build_readiness_widget() {
$all_issues = CRB_Issue_Monitor::build_summary();
if ( empty( $all_issues ) || ! is_array( $all_issues ) ) {
return array( CRB_Widgets::NO_DATA => __( 'Your system configuration and environment appear to be in a ready state. No issues affecting security or stability were detected.', 'wp-cerber' ) );
}
$list_items = '';
$allowed_html = array(
'b' => array(),
'strong' => array(),
'i' => array(),
'code' => array(),
'samp' => array(),
'br' => array(),
'p' => array(),
'a' => array(
'href' => true,
'target' => true,
'rel' => true,
),
);
// Iterate through the list of issues
foreach ( $all_issues as $issue_data ) {
$message = $issue_data['message'] ?? '';
if ( '' === $message ) {
continue;
}
// 1. Build diagnostic details (optional)
$diagnostic_output = '';
$details = $issue_data['diagnostic_details'] ?? array();
if ( ! empty( $details ) && is_array( $details ) ) {
$sanitized_lines = array_map( function ( $line ) {
$line = str_replace( array( '…', '…' ), '', $line );
return crb_escape_html( crb_strip_tags( $line ) );
}, $details );
$diagnostic_content = implode( '<br>', $sanitized_lines );
$diagnostic_output = '<div class="crb-issue-diagnostic-details">' . $diagnostic_content . '</div>';
}
// 2. Build date and controls blocks
$controls_content = '';
if ( isset( $issue_data['type'] )
&& CRB_Issues::TYPE_EVENT === $issue_data['type'] ) {
$timestamp = $issue_data['last_occurred_at'] ?? 0;
if ( $timestamp ) {
/* translators: %s is the date and time when the issue occurred last time. */
$controls_content .= '<div class="crb-issue-date">' . sprintf( __( 'Last occurrence: %s', 'wp-cerber' ), cerber_auto_date( $timestamp ) ) . '</div>';
}
}
if ( ! empty( $issue_data['dismissable'] ) && ! empty( $issue_data['id'] ) ) {
$controls_content .= '<div class="crb-issue-dismiss">[ <a href="#" class="crb-dismiss-trigger" data-crb-context="delete_single_issue" data-crb-dismiss-id="' . crb_attr_escape( $issue_data['id'] ) . '">' . __( 'Dismiss', 'wp-cerber' ) . '</a> ]</div>';
}
$controls_output = '';
if ( $controls_content ) {
$controls_output = '<div class="crb-issue-controls">' . $controls_content . '</div>';
}
// 3. Build context links
$setting_link = crb_get_setting_link( (string) $issue_data['setting_id'] ?? '' );
$documentation_link = '';
$doc_page_url = $issue_data['doc_page'] ?? '';
if ( $doc_page_url ) {
$documentation_link = ' [ <a href="' . esc_url( $doc_page_url ) . '" target="_blank" rel="noopener noreferrer">' . __( 'Documentation', 'wp-cerber' ) . '</a> ]';
}
// 4. Build CSS classes
$severity = $issue_data[ CRB_Issues::KEY_SEVERITY ] ?? '';
$item_classes = 'crb-readiness-item crb-dismiss-scope';
if ( $severity ) {
$item_classes .= ' crb-severity-' . crb_attr_escape( $severity );
}
// 5. Build single item HTML
$list_items .= '<div class="' . $item_classes . '"><div class="crb-issue-message">' . wp_kses( $message, $allowed_html ) . $setting_link . $documentation_link . '</div>' . $diagnostic_output . $controls_output . '</div>';
}
if ( '' === $list_items ) {
return array( CRB_Widgets::NO_DATA => __( 'Your system configuration and environment appear to be in a ready state. No issues affecting security or stability were detected.', 'wp-cerber' ) );
}
return '<div class="crb-readiness-list">' . $list_items . '</div>';
}
/*
Admin aside bar
*/
function cerber_show_aside( $tab ) {
if ( in_array( $tab, array( 'nexus_sites', 'activity', 'lockouts', 'traffic' ) ) ) {
return;
}
$aside = array();
if ( $tab != 'scan_main' && ! lab_lab() && crb_was_activated( 1 * DAY_IN_SECONDS ) ) {
/*$aside[] =
'<a class="crb-button-one" style="background-color: #1DA1F2;" href="https://twitter.com/wpcerber" target="_blank"><span class="dashicons dashicons-twitter"></span> Follow Cerber on Twitter</a>';
*/
// <a class="crb-button-one" href="https://wpcerber.com/subscribe-newsletter/" target="_blank"><span class="dashicons dashicons-email-alt"></span> Subscribe to Cerber\'s newsletter</a>
// <a class="crb-button-one" style="background-color: #3B5998;" href="https://www.facebook.com/wpcerber/" target="_blank"><span class="dashicons dashicons-facebook"></span> Follow Cerber on Facebook</a>
//';
$banners = array(
array( 'bn4ra.png', 'https://wpcerber.com/pro/' ),
array( 'bn5ra.png', 'https://wpcerber.com/pro/' ),
array( 'talk_b1.png', 'https://talk.wpcerber.com' ),
);
if ( ! crb_admin_get_tab() ) {
$n = 2;
}
else {
$d = (int) date( 'z' );
$n = ( $d & 1 ) ? 1 : 0;
}
$banner = $banners[ $n ];
$banner_image = CRB_Globals::assets_url( $banner[0] );
$aside[] = '<a href="' . $banner[1] . '" target="_blank"><img src="' . $banner_image . '" class="crb-full-width" /></a>';
}
$context_links = '';
$docs = crb_get_doc_links();
foreach ( $docs as $doc ) {
$context_links .= '<p><a href="' . $doc[0] . '" target="_blank">' . $doc[1] . '</a></p>';
}
$aside[] = '<div class="crb-box" id="crb-blog"><div class="crb-box-inner">
<h3>Documentation & How To</h3>
' . $context_links . '</div></div>';
if ( ! lab_lab()
&& crb_was_activated( 2 * WEEK_IN_SECONDS ) ) {
$r = array();
$r[0] = crb_get_review_url( 'tpilot' );
$r[1] = crb_get_review_url( 'cap' );
$r[2] = crb_get_review_url( 'tradius' );
shuffle( $r );
$aside[] = '<a href="' . $r[0] . '" target="_blank"><img class="crb-full-width" src="' . CRB_Globals::assets_url( 'fb2b.png' ) . '" /></a>';
}
echo '<div id="crb-aside">' . implode( ' ', $aside ) . '</div>';
}
/**
* Displaying admin messages (errors, notices, etc.) in the WP Dashboard
*
* @return void
*/
function cerber_show_admin_messages() {
$msg_shown = false;
if ( nexus_is_remote_context() ) {
return;
}
if ( cerber_is_citadel() && cerber_user_can_manage() ) {
$citadel_notice_element = crb_ui_element(
'div',
[
'class' => 'update-nag',
'style' => 'display: block; margin-top: 0; background-color: #fff; border-left: 6px solid #ff0000;',
],
[
crb_ui_element(
'p',
[],
[
__( 'Logins are currently blocked. Citadel Mode was activated by WP Cerber in response to a traffic anomaly detected on your site. Only IP addresses on the Allowed IP Access List can log in.', 'wp-cerber' ) . ' ',
crb_ui_link(
_crb_admin_link_add( [ 'citadel_do' => 'deactivate' ] ),
__( 'Deactivate', 'wp-cerber' )
),
' | ',
crb_ui_link(
cerber_admin_link( 'activity' ),
__( 'View Activity', 'wp-cerber' )
),
]
),
]
);
echo crb_ui_renderer()->render_element( $citadel_notice_element );
}
// Check for proper context or admin page
if ( ! ( nexus_is_valid_request()
|| cerber_is_admin_page( null, 'plugins' ) ) ) {
return;
}
$all_messages = array();
if ( $notice = cerber_get_set( CRB_ADMIN_NOTICE ) ) {
if ( is_array( $notice ) ) {
$notice = '<p>' . implode( '</p><p>', $notice ) . '</p>';
}
$all_messages[] = array( $notice, 'crb-admin-warning' ); // red color
cerber_update_set( CRB_ADMIN_NOTICE, array() );
}
if ( $message = cerber_get_set( CRB_ADMIN_MESSAGE ) ) {
if ( is_array( $message ) ) {
$message = '<p>' . implode( '</p><p>', $message ) . '</p>';
}
$all_messages[] = array( $message, 'updated' ); // green color
cerber_update_set( CRB_ADMIN_MESSAGE, array() );
}
if ( $all_messages ) {
foreach ( $all_messages as $item ) {
$filtered = crb_purify_message( (string) $item[0] );
$msg_element = crb_ui_element(
'div',
[
'class' => $item[1] . ' notice is-dismissible crb-admin-message crb-dismiss-scope',
],
[
crb_ui_raw_html( $filtered ),
crb_ui_raw_html(
'<button type="button" class="notice-dismiss crb-dismiss-trigger">' .
'<span class="screen-reader-text">Dismiss this notice.</span>' .
'</button>'
),
]
);
echo crb_ui_renderer()->render_element( $msg_element );
}
$msg_shown = true;
}
// Prominent announcement
if ( $announcement = cerber_get_set( CRB_ADMIN_WIDE, null, false ) ) {
$filtered = crb_purify_message( (string) $announcement );
crb_show_admin_announcement( $filtered, false );
cerber_update_set( CRB_ADMIN_WIDE, '' );
$msg_shown = true;
}
if ( $msg_shown || ! cerber_is_admin_page() ) {
return;
}
// Big announcement
if ( $news = get_site_option( CRB_ADMIN_ANNOUNCE ) ) {
$filtered = crb_purify_message( (string) $news );
crb_show_admin_announcement( $filtered );
return;
}
// Opt-in banner
lab_show_opt_in_message();
}
/**
* Sanitizes an admin message by allowing only specific HTML elements and attributes.
*
* This function uses wp_kses() to sanitize the provided message, ensuring
* that only tags and attributes defined in the allow list are retained.
*
* Allowed tags reflect the actual markup used in:
* - cerber_admin_message()
* - cerber_admin_notice()
* - CRB_ADMIN_WIDE and CRB_ADMIN_ANNOUNCE announcements
*
* @param string $string Admin message HTML to sanitize.
*
* @return string Sanitized HTML containing only allowed tags and attributes.
*/
function crb_purify_message( string $string = '' ): string {
$allowed_tags = array(
'h1' => array(),
'h2' => array(
'style' => true,
),
'p' => array(
'style' => true,
),
'br' => array(),
'div' => array(
'id' => true,
'style' => true,
),
'li' => array(),
'ul' => array(),
'ol' => array(),
'table' => array(
'style' => true,
),
'tr' => array(),
'td' => array(
'style' => true,
),
'b' => array(),
'strong' => array(),
'i' => array(
'class' => true,
),
'span' => array(
'class' => true,
),
'a' => array(
'href' => true,
'target' => true,
'rel' => true,
),
'input' => array(
'type' => true,
'class' => true,
'value' => true,
'data-crb-context' => true,
'data-crb-dismiss-id' => true,
),
);
return wp_kses( $string, $allowed_tags );
}
/**
* Generates a link/button to display a dialog for creating an alert on the currently displaying Activity page
*
* @return string HTML code for using in the Dashboard HTML
*
* @since 8.9.6
*/
function crb_admin_alert_dialog( $button = true, $create_txt = null, $delete_txt = null ) {
$alert_params = CRB_Activity_Alerts::get_alert_params();
// Check if query parameters that are used in alerts are set and not empty.
// All activities, without any filter are not allowed
$empty = array_filter( array_values( $alert_params ) );
if ( empty( $empty ) ) {
return '';
}
$alerts = CRB_Activity_Alerts::get_alerts_list();
// Limit to the number of subscriptions
if ( $alerts && count( $alerts ) > 50 ) {
return '';
}
$mode = ( CRB_Activity_Alerts::alert_exists( $alert_params ) ) ? 'delete_alert' : 'create_alert';
if ( $mode == 'create_alert' ) {
$label = $create_txt ?? __( 'Create Alert', 'wp-cerber' );
$icon = ( $button ) ? 'crb-icon-bx-bell' : '';
}
else {
$label = $delete_txt ?? __( 'Delete Alert', 'wp-cerber' );
$icon = ( $button ) ? 'crb-icon-bx-bell-off' : '';
$link = cerber_admin_link_add( array( 'cerber_admin_do' => 'manage_alert', 'mode' => 'delete_alert' ), true );
if ( $button ) {
return '<a class="button button-secondary cerber-button" href="' . $link . '" style="margin-right: 0.5em;"><i class="crb-icon ' . $icon . '"></i> ' . $label . '</a>';
}
return '<a href="' . $link . '">' . $label . '</a>';
}
// Create the alert dialog
$em_list = crb_build_email_recipients();
$mob_name = cerber_pb_get_active();
if ( lab_lab() ) {
$atts = array( 'type' => 'checkbox', 'data-validation_group' => 'required_min_one', 'checked' => true );
$channels = array(
'title' => __( 'Channels to send alerts', 'wp-cerber' ),
'visible' =>
array(
'al_send_emails' => array(
/* translators: Here %s is the email address(es). */
'label' => sprintf( __( 'Send email alerts to %s', 'wp-cerber' ), implode( ', ', $em_list ) ),
'value' => '1',
'atts' => $atts,
),
'al_send_me' => array(
'label' => sprintf( __( 'Send email alerts to my email', 'wp-cerber' ), $mob_name ),
'value' => get_current_user_id(),
'atts' => $atts,
),
),
'error_messages' => array( 'required_min_one' => __( 'Please select at least one channel', 'wp-cerber' ) )
);
if ( $mob_name ) {
$channels['visible']['al_send_pushbullet'] = array(
/* translators: Here %s is the name of a mobile device. */
'label' => sprintf( __( 'Send mobile alerts to %s', 'wp-cerber' ), $mob_name ),
'value' => '1',
'atts' => $atts,
);
}
}
else {
if ( $mob_name ) {
/* translators: Here %s is the name of a mobile device. */
$mobile = sprintf( __( 'Mobile alerts will be sent to %s', 'wp-cerber' ), $mob_name );
}
else {
$mobile = __( 'Mobile alerts are not configured', 'wp-cerber' );
}
$recipients = ( 1 < count( $em_list ) ) ? __( 'Email alerts will be sent to these emails:', 'wp-cerber' ) : __( 'Email alerts will be sent to this email:', 'wp-cerber' );
$recipients .= ' ' . implode( ', ', $em_list );
$recipients = '<p>' . $recipients . '</p><p>' . $mobile . '</p>';
$recipients .= '<p><a href="https://wpcerber.com/wordpress-notifications-made-easy/" target="_blank">' . __( 'Documentation', 'wp-cerber' ) . '</a></p>';
$channels = array(
'html' => $recipients,
);
}
// Extract values of the alert fields
$alert_fields = array_intersect_key( $alert_params, $_GET );
$limits = array(
'title' => __( 'Optional alert limits', 'wp-cerber' ),
'hidden' => array_merge( $alert_fields, array(
'cerber_admin_do' => 'manage_alert',
'mode' => $mode
) ),
'visible' => array(
'al_limit' => array(
'label' => __( 'Maximum number of alerts to send', 'wp-cerber' ),
'value' => '',
'atts' => array(
'type' => 'text',
'pattern' => '\d+',
'placeholder' => __( 'No limit', 'wp-cerber' ),
),
),
'al_expires' => array(
'label' => __( 'Do not send alerts after this date', 'wp-cerber' ),
'value' => '',
'atts' => array( 'type' => 'date', 'min' => date( 'Y-m-d' ) ),
),
'al_ignore_rate' => array(
'label' => __( 'Ignore global rate limits', 'wp-cerber' ),
'value' => '1',
'atts' => array( 'type' => 'checkbox' ),
),
),
);
return crb_create_popup_form( $limits, $channels, [ 'icon' => $icon, 'label' => $label ] );
}
/**
* Deletes an alert using an alert hash from the $_GET parameter
*
*/
function crb_delete_alert() {
if ( ( $hash = cerber_get_get( 'unsubscribeme', '[a-z\d]+' ) )
&& cerber_user_can_manage() ) {
CRB_Activity_Alerts::delete_alert( $hash );
crb_safe_redirect( remove_query_arg( 'unsubscribeme' ) );
exit;
}
}
/**
* Generates pagination links
*
* @param int $total
* @param int $per_page
*
* @return string
*/
function cerber_page_navi( $total, $per_page = 25 ) {
$max_links = 10;
$total = max( 0, intval( $total ) );
$per_page = intval( $per_page );
if ( $per_page <= 0 ) {
$per_page = 25;
}
$page = crb_get_page_num();
$base_url = crb_escape_url( remove_query_arg( 'pagen', add_query_arg( crb_get_query_params(), cerber_admin_link() ) ) );
$last_page = ceil( $total / $per_page );
if ( $last_page <= 1 ) {
return '';
}
$links = array();
$start = 1 + $max_links * intval( ( $page - 1 ) / $max_links );
$end = $start + $max_links - 1;
if ( $end > $last_page ) {
$end = $last_page;
}
if ( $start > $max_links ) {
$links[] = '<a href="' . $base_url . '&pagen=' . ( $start - 1 ) . '" class="arrows"><b>«</b></a>';
}
for ( $i = $start; $i <= $end; $i ++ ) {
if ( $page != $i ) {
$links[] = '<a href="' . $base_url . '&pagen=' . $i . '" >' . $i . '</a>';
}
else {
$links[] = '<a class="active" style="font-size: 16px;">' . $i . '</a> ';
}
}
if ( $end < $last_page ) {
$links[] = '<a href="' . $base_url . '&pagen=' . $i . '" class="arrows">»</a>'; // ➝
}
return '<table class="cerber-margin" style="margin-top:1em; border-collapse: collapse;"><tr><td><div class="pagination">' . implode( ' ', $links ) . '</div></td><td><span style="margin-left:2em;"><b>' . $total . ' ' . _n( 'entry', 'entries', $total, 'wp-cerber' ) . '</b></span></td></tr></table>';
}
/**
* Currently displaying page - for pagination purpose
*
* @return int
*/
function crb_get_page_num(): int {
$q = crb_admin_parse_query( array( 'pagen' ) );
return ( ! $q->pagen ) ? 1 : absint( $q->pagen );
}
/**
* Return safe LIMIT clause
*
* @param integer $per_page
*
* @return string
*/
function crb_build_sql_limit( $per_page = 25 ) {
$per_page = crb_absint( $per_page );
return ' LIMIT ' . ( crb_get_page_num() - 1 ) * $per_page . ',' . $per_page;
}
/*
Plugins admin page links
*/
add_filter( 'plugin_action_links_' . CERBER_PLUGIN_ID, 'cerber_action_links' );
add_filter( 'network_admin_plugin_action_links_' . CERBER_PLUGIN_ID, 'cerber_action_links' );
function cerber_action_links( $actions ) {
$links = array();
$links[] = '<a href="' . cerber_admin_link() . '">' . __( 'Dashboard', 'wp-cerber' ) . '</a>';
if ( lab_lab() ) {
$links[] = '<a href="https://my.wpcerber.com" target="_blank">' . __( 'Support', 'wp-cerber' ) . '</a>';
}
else {
$links[] = '<a href="https://talk.wpcerber.com" target="_blank">' . __( 'Support Forum', 'wp-cerber' ) . '</a>';
}
return array_merge( $links, $actions );
}
add_filter( 'plugin_row_meta', function ( $plugin_meta, $plugin_file, $plugin_data, $status ) {
if ( $plugin_file == CERBER_PLUGIN_ID
|| $plugin_file == 'aaa-wp-cerber.php' ) {
// Fix broken link pointing to a non-existing wordpress.org repository plugin profile
foreach ( $plugin_meta as &$link ) {
if ( strpos( $link, 'class="thickbox open-plugin-details-modal"' ) ) {
$ver = esc_attr( str_replace( '.', '-', $plugin_data['Version'] ) );
$link = preg_replace( '/^<a href="[^"]+"/u', '<a href="https://wpcerber.com/wp-cerber-security-' . $ver . '/?TB_iframe=true&width=600&height=550"', $link );
}
}
// -----------------------------------------
if ( lab_lab() ) {
$plugin_meta[] = '<a href="https://my.wpcerber.com" target="_blank">' . __( 'Support', 'wp-cerber' ) . '</a>';
}
else {
$plugin_meta[] = '<a href="https://talk.wpcerber.com" target="_blank">' . __( 'Support Forum', 'wp-cerber' ) . '</a>';
}
$plugin_meta[] = '<a href="https://wpcerber.com/toc/" target="_blank">' . __( 'Documentaion', 'wp-cerber' ) . '</a>';
}
return $plugin_meta;
}, 10, 4 );
/*
function add_some_pointers() {
?>
<script id="wp-cerber-js-<?php echo crb_sanitize_id( __FUNCTION__ . '_' . __LINE__ ); ?>">
jQuery( function( $ ) {
var options = {'content':'<h3>Info</h3><p>Cerber will request WHOIS database for extra information when you click on IP.</p>','position':{'edge':'right','align':'center'}};
if ( ! options ) return;
options = $.extend( options, {
close: function() {
//to do
}
});
//$("#ip_extra").click(function(){
// $(this).pointer( options ).pointer('open');
//});
$('#subscribe-me').pointer( options ).pointer('open');
});
</script>
<?php
}
add_action('admin_enqueue_scripts', 'cerber_admin_enqueue');
function cerber_admin_enqueue($hook) {
wp_enqueue_style( 'wp-pointer' );
wp_enqueue_script( 'wp-pointer' );
}
*/
add_action( 'admin_enqueue_scripts', 'cerber_admin_assets', 9999 );
function cerber_admin_assets() {
$crb_assets_url = CRB_Globals::assets_url();
if ( cerber_is_admin_page() ) {
wp_register_style( 'crb_multi_css', $crb_assets_url . 'multi/multi.css', null, CERBER_VER );
wp_enqueue_style( 'crb_multi_css' );
wp_enqueue_script( 'crb_multi_js', $crb_assets_url . 'multi/multi.min.js', array(), CERBER_VER );
wp_register_style( 'crb_tpicker_css', $crb_assets_url . 'tpicker/jquery.timepicker.min.css', null, CERBER_VER );
wp_enqueue_style( 'crb_tpicker_css' );
wp_enqueue_script( 'crb_tpicker', $crb_assets_url . 'tpicker/jquery.timepicker.min.js', array(), CERBER_VER );
// Select2
wp_register_style( 'select2css', $crb_assets_url . 'select2/dist/css/select2.min.css', null, null );
wp_enqueue_style( 'select2css' );
wp_enqueue_script( 'select2js', $crb_assets_url . 'select2/dist/js/select2.min.js', null, null, true );
wp_register_style( 'crb_magnific_css', $crb_assets_url . 'magnific/magnific-popup.css', null, CERBER_VER );
wp_enqueue_style( 'crb_magnific_css' );
wp_enqueue_script( 'crb_magnific_js', $crb_assets_url . 'magnific/jquery.magnific-popup.min.js', null, CERBER_VER );
wp_register_style( 'nexus_css', $crb_assets_url . 'nexus.css', null, CERBER_VER );
wp_enqueue_style( 'nexus_css' );
add_thickbox();
wp_enqueue_script( 'jquery-ui-core' );
wp_enqueue_script( 'jquery-effects-bounce' );
wp_enqueue_script( 'jquery-ui-sortable' );
}
if ( ! defined( 'CERBER_BETA' ) ) {
wp_register_style( 'ui_stack_css', $crb_assets_url . 'ui-stack.css', null, CERBER_VER );
wp_enqueue_style( 'ui_stack_css' );
wp_enqueue_script( 'ui_stack_js', $crb_assets_url . 'ui-stack.js', array( 'jquery' ), CERBER_VER, true );
wp_enqueue_script( 'cerber_js', $crb_assets_url . 'admin.js', array( 'jquery' ), CERBER_VER, true );
if ( cerber_is_admin_page( array( 'page' => 'cerber-integrity' ) ) ) {
wp_enqueue_script( 'cerber_scan', $crb_assets_url . 'scanner.js', array( 'jquery' ), CERBER_VER, true );
}
}
wp_register_style( 'cerber_css_variables', $crb_assets_url . 'cerber-variables.css', null, CERBER_VER );
wp_enqueue_style( 'cerber_css_variables' );
wp_register_style( 'crb_icons_css', $crb_assets_url . 'icons/style.css', null, CERBER_VER );
wp_enqueue_style( 'crb_icons_css' );
wp_register_style( 'cerber_css', $crb_assets_url . 'admin.css', null, CERBER_VER );
wp_enqueue_style( 'cerber_css' );
}
/*
* JS & CSS for admin head
*
*/
add_action( 'admin_head', 'cerber_admin_head' );
add_action( 'customize_controls_print_scripts', 'cerber_admin_head' ); // @since 5.8.1
function cerber_admin_head() {
if ( cerber_is_admin_page() ) {
remove_all_actions( 'admin_notices' ); // Remove aliens' notices on WP Cerber's page
if ( crb_get_settings( 'top_admin_menu' ) ) {
add_filter( 'admin_body_class', function ( $classes ) {
return $classes . ' crb-top_admin_menu_enabled ';
} );
}
}
else {
add_action( 'admin_notices', 'cerber_show_admin_messages', 999 );
add_action( 'network_admin_notices', 'cerber_show_admin_messages', 999 );
}
?>
<script id="wp-cerber-js-<?php echo crb_sanitize_id( __FUNCTION__ . '_' . __LINE__ ); ?>">
var crb_admin_url = '<?php echo admin_url(); ?>';
var crb_ajax_nonce = '<?php echo wp_create_nonce( 'crb-ajax-admin' ); ?>';
var crb_ajax_loader = '<?php echo CRB_Globals::assets_url( 'ajax-loader.gif' ) ?>';
var crb_lab_available = <?php echo lab_lab() ? 'true' : 'false'; ?>;
</script>
<style>
#crb-admin-2fa-pins {
border-left: solid 3px rgba(0, 121, 219, 0.63);
}
#crb-admin-2fa-pins table {
border-collapse: collapse;
}
#crb-admin-2fa-pins table tr:first-child td {
font-weight: bold;
}
#crb-admin-2fa-pins table td {
padding: 6px 12px;
background-color: rgba(255, 255, 255, 0.5);
}
</style>
<?php
global $pagenow;
if ( 'users.php' === $pagenow ) :
?>
<style>
/*.crb-user-blocked .crb-user-name,*/
.crb-user-blocked td.column-username,
.crb-user-prohibited td.column-username {
color: red;
}
/* .crb-user-blocked .crb-user-name::after, */
.crb-user-blocked td.column-username strong::after {
content: ' <?php _e('BLOCKED','wp-cerber'); ?>';
}
.crb-user-prohibited td.column-username strong::after {
content: ' <?php _e('PROHIBITED','wp-cerber'); ?>';
}
</style>
<?php
endif;
if ( defined( 'CERBER_BETA' ) ) :
?>
<style id="wp-cerber-css-<?php echo crb_sanitize_id( __FUNCTION__ . '_' . __LINE__ ); ?>">
<?php
readfile( cerber_assets_dir().'/admin.css');
readfile( cerber_assets_dir().'/ui-stack.css');
?>
</style>
<?php
endif;
if ( ! cerber_is_admin_page() ) {
return;
}
?>
<meta http-equiv="x-dns-prefetch-control" content="on"/>
<link rel='dns-prefetch' href='//wpcerber.com'/>
<script id="wp-cerber-js-<?php echo crb_sanitize_id( __FUNCTION__ . '_' . __LINE__ ); ?>">
<?php
echo 'var crb_admin_page = "' . crb_admin_get_page() . '";' . "\n";
echo 'var crb_admin_tab = "' . crb_admin_get_tab() . '";' . "\n";
echo 'var crb_user_locale = "' . substr( get_user_locale(), 0, 6 ) . '";' . "\n";
?>
</script>
<style>
/* Thickbox styles */
#TB_title {
background-color: var(--wp-admin-theme-color) !important;
color: #fff;
}
.tb-close-icon {
color: #fff !important;
}
#TB_window {
font-family: 'Roboto', sans-serif;
}
/* Hide aliens' messages on WP Cerber's admin pages */
.update-nag,
#update-nag,
#setting-error-tgmpa,
.pms-cross-promo,
.vc_license-activation-notice,
#wordfenceConfigWarning,
#crb-admin div#message.updated {
display: none;
}
</style>
<?php
}
/*
* JS & CSS for admin footer
*
*/
add_action( 'admin_footer', 'cerber_admin_footer' );
function cerber_admin_footer() {
//add_some_pointers();
crb_load_wp_js();
// Add buttons to the user profile page
$uid = 0;
if ( defined( 'IS_PROFILE_PAGE' ) && IS_PROFILE_PAGE ) {
$uid = get_current_user_id();
}
elseif ( ! empty( $_GET['user_id'] ) && is_admin_user_edit() ) {
$uid = absint( $_GET['user_id'] );
}
if ( $uid && cerber_user_can_manage() ) {
if ( $user = crb_get_userdata( $uid ) ) {
if ( crb_is_user_blocked( $uid ) ) {
?>
<script id="wp-cerber-js-<?php echo crb_sanitize_id( __FUNCTION__ . '_' . __LINE__ ); ?>">
document.querySelector('#profile-page hr.wp-header-end').insertAdjacentHTML('beforebegin', '<span class="page-title-action" style="border: none; border-radius: 0; color: white; background-color: #e4383d; cursor: auto;"><?php echo __( 'User is blocked', 'wp-cerber' ); ?></span>');
</script>
<?php
}
if ( crb_is_username_prohibited( $user->user_login ) ) {
?>
<script id="wp-cerber-js-<?php echo crb_sanitize_id( __FUNCTION__ . '_' . __LINE__ ); ?>">
document.querySelector('#profile-page hr.wp-header-end').insertAdjacentHTML('beforebegin', '<span class="page-title-action" style="border: none; border-radius: 0; color: white; background-color: #e4383d; cursor: auto;"><?php echo __( 'Username is prohibited', 'wp-cerber' ); ?></span>');
</script>
<?php
}
}
// Link to the user's activity log
$user_links = '<a href="' . cerber_admin_link( 'activity', array( 'filter_user' => $uid ) ) . '" class="page-title-action crb-title-button">' . __( 'View Activity', 'wp-cerber' ) . '</a>';
// Link to the user's sessions, if any
if ( $uss = crb_sessions_get_num( $uid ) ) {
$user_links .= '<a href="' . cerber_admin_link( 'sessions', array( 'filter_user' => $uid ) ) . '" class="page-title-action crb-title-button">' . __( 'Sessions', 'wp-cerber' ) . ' ' . $uss . '</a>';
}
?>
<script id="wp-cerber-js-<?php echo crb_sanitize_id( __FUNCTION__ . '_' . __LINE__ ); ?>">
document.querySelector('#profile-page h1.wp-heading-inline').insertAdjacentHTML('afterend', '<?php echo $user_links; ?>');
</script>
<?php
}
if ( cerber_user_can_manage() ) {
// Link to the Sessions page - all users
?>
<script id="wp-cerber-js-<?php echo crb_sanitize_id( __FUNCTION__ . '_' . __LINE__ ); ?>">
let html_target = document.querySelector('body.users-php #wpbody-content h1.wp-heading-inline')
if (html_target) {
html_target.insertAdjacentHTML('afterend', '<?php echo '<a href="' . cerber_admin_link( 'sessions' ) . '" class="page-title-action crb-title-button">' . __( 'Sessions', 'wp-cerber' ) . '</a>'; ?>');
}
</script>
<?php
}
// Check for blocked users
$blocked = array();
$prohibited = array();
if ( $uids = crb_users_on_the_page() ) {
foreach ( $uids as $uid => $login ) {
if ( crb_is_user_blocked( $uid ) ) {
$blocked[] = $uid;
}
elseif ( crb_is_username_prohibited( $login ) ) {
$prohibited[] = $uid;
}
}
}
?>
<script id="wp-cerber-js-<?php echo crb_sanitize_id( __FUNCTION__ . '_' . __LINE__ ); ?>">
// Highlight blocked users
let crb_blocked_users = <?php echo '[' . implode( ',', $blocked ) . ']'; ?>;
let crb_prohibited_users = <?php echo '[' . implode( ',', $prohibited ) . ']'; ?>;
for (let uid of crb_blocked_users) {
document.getElementById('user-' + uid)?.classList.add("crb-user-blocked");
}
for (let uid of crb_prohibited_users) {
document.getElementById('user-' + uid)?.classList.add("crb-user-prohibited");
}
// Switching inputs on the profile page
jQuery(function ($) {
// Old (in use)
let mrow = $('#profile-page .crb_blocked_txt');
$("#crb_user_blocked").on('change', function () {
if ($(this).is(':checked')) {
mrow.show();
}
else {
mrow.hide();
}
});
// Conditional form fields (new)
let crb_user_edit_fields = $('#crb-wp-user-edit');
crb_user_edit_fields.find('input,select').on('change', function () {
let enabler_id = $(this).attr('id');
let enabler_val;
if ('checkbox' === $(this).attr('type')) {
enabler_val = !!$(this).is(':checked');
}
else {
enabler_val = $(this).val();
}
crb_user_edit_fields.find('[data-input_parent="' + enabler_id + '"]').each(function () {
let input_data = $(this).data();
let method = 'hide';
if (typeof input_data['input_parent_value'] !== "undefined") {
let target = input_data['input_parent_value'];
if (Array.isArray(target)) {
for (let i = 0; i < target.length; i++) {
if (String(enabler_val) === String(target[i])) {
method = 'show';
break;
}
}
}
else {
if (String(enabler_val) === String(input_data['input_parent_value'])) {
method = 'show';
}
}
}
else {
if (enabler_val) {
method = 'show';
}
}
let input_wrapper = $(this).closest('tr');
if (method === 'show') {
input_wrapper.fadeIn(500);
}
else if (method === 'hide') {
input_wrapper.fadeOut();
}
});
});
});
</script>
<?php
/* Background tasks */
$list = CRB_Deferred_Tasks::get_all();
if ( $list ) {
$list = array_slice( $list, 0, 20 );
$list = array_keys( $list );
?>
<script id="wp-cerber-js-<?php echo crb_sanitize_id( __FUNCTION__ . '_' . __LINE__ ); ?>">
let crb_bg_tasks = <?php echo '["' . implode( '","', $list ) . '"]'; ?>;
jQuery(function ($) {
if (crb_bg_tasks.length) {
console.log('Background tasks: ' + crb_bg_tasks.length);
$.post(ajaxurl, {
ajax_nonce: crb_ajax_nonce,
action: 'cerber_local_ajax',
crb_ajax_do: 'bg_tasks_run',
run_tasks: $(crb_bg_tasks).toArray(),
}
);
}
});
</script>
<?php
}
// Inter-Page Loader
?>
<div id="ui_page_overlay" style="display: none;"></div>
<script>
document.addEventListener('DOMContentLoaded', function () {
const containers = [
document.getElementById('crb-admin'),
document.getElementById('toplevel_page_cerber-security'),
document.getElementById('cerber_quick'),
document.querySelector('body.users-php #wpbody-content'),
document.querySelector('#wpbody-content #profile-page'),
document.querySelector('#adminmenu #menu-users'),
];
containers.forEach(container => {
if (container) {
const links = container.querySelectorAll('a');
links.forEach(link => {
link.classList.add('cerber-page-loader-candidate');
});
}
});
// Helper function to parse query parameters
const getQueryParams = (url) => {
const queryString = url.split('?')[1] || '';
return Object.fromEntries(new URLSearchParams(queryString));
};
const excludedSchemes = ['mailto:', 'tel:', 'sms:', 'ftp:', 'geo:', 'javascript:'];
const overlay = document.getElementById('ui_page_overlay');
document.addEventListener('click', function (event) {
let link;
if (event.ctrlKey || event.metaKey) {
return;
}
if (event.target.tagName === 'A') {
link = event.target;
}
else {
link = event.target.closest('a');
}
if (!link || !link.classList.contains('cerber-page-loader-candidate')) {
return;
}
const href = link.getAttribute('href') || '';
const hasOnclick = link.hasAttribute('onclick');
const target = link.getAttribute('target') || '';
// Check for exceptions
if (
!href ||
hasOnclick ||
href.startsWith('#') ||
target ||
excludedSchemes.some(scheme => href.startsWith(scheme))
) {
return;
}
// The link looks valid, let's check it for specific locations (URLs) we will activate the overlay loader for
const params = getQueryParams(href);
// Conditions for overlay activation
const validPages = ['cerber-security', 'cerber-traffic'];
const validTabs = ['dashboard', 'activity', 'sessions', 'traffic'];
if (!link.classList.contains('crb-page-loader-force')) {
if (params.cerber_nonce
|| !params.page
|| !validPages.includes(params.page)
|| (params.tab && !validTabs.includes(params.tab))) {
return;
}
}
event.preventDefault();
overlay.style.display = 'flex';
// Trigger delayed navigation with delay to avoid "flashing effect"
setTimeout(() => {
window.location.href = href;
}, 200);
});
});
</script>
<?php
// ------------------------------------------------------
if ( ! cerber_is_admin_page() ) {
return;
}
if ( defined( 'CERBER_BETA' ) ) :
?>
<script id="wp-cerber-js-<?php echo crb_sanitize_id( __FUNCTION__ . '_' . __LINE__ ); ?>">
<?php
readfile( cerber_assets_dir() . '/admin.js' );
readfile( cerber_assets_dir() . '/ui-stack.js' );
if ( cerber_is_admin_page( array( 'page' => 'cerber-integrity' ) ) ) {
readfile( cerber_assets_dir() . '/scanner.js' );
}
?>
</script>
<?php
endif;
// Time pickers
$format = 'H:i';
if ( cerber_is_ampm() ) {
$format = 'h:i A';
}
?>
<script id="wp-cerber-js-<?php echo crb_sanitize_id( __FUNCTION__ . '_' . __LINE__ ); ?>">
jQuery(function ($) {
$('.crb-tpicker').timepicker({
'step': 20,
'forceRoundTime': true,
'timeFormat': '<?php echo $format; ?>'
});
});
</script>
<?php
if ( ! lab_lab()
&& cerber_is_admin_page( array( 'tab' => array( 'scan_schedule', 'scan_policy' ) ) ) ) :
?>
<script id="wp-cerber-js-<?php echo crb_sanitize_id( __FUNCTION__ . '_' . __LINE__ ); ?>">
jQuery(function ($) {
$('input').attr('disabled', 'disabled');
$('select').attr('disabled', 'disabled');
$('.crb-slider').css('background-color', '#888');
$('th').add('td').add('h2').css('color', '#888');
$('#crb-main h2').first().before('<?php echo str_replace( array(
"\n",
"\r"
), '', crb_admin_cool_features() ); ?>');
});
</script>
<?php
endif;
?>
<script id="wp-cerber-js-<?php echo crb_sanitize_id( __FUNCTION__ . '_' . __LINE__ ); ?>">
<?php
if ( cerber_is_admin_page( array( 'page' => 'cerber-integrity' ) ) ) {
echo crb_generate_safe_js_var( 'crb_scan_top_issues', crb_get_top_issue_ids() );
echo crb_generate_safe_js_var( 'crb_scan_msg_steps', cerber_get_stage_titles_all() );
echo crb_generate_safe_js_var( 'crb_scan_msg_issues', cerber_get_issue_title() );
echo crb_generate_safe_js_var( 'crb_scan_msg_labels', cerber_get_issue_labels() );
echo crb_generate_safe_js_var( 'crb_scan_msg_risks', cerber_get_risk_labels() );
echo crb_generate_safe_js_var( 'crb_scan_msg_misc', crb_scan_messages() );
echo crb_generate_safe_js_var( 'crb_txt_strings', cerber_get_strings() );
}
echo crb_generate_safe_js_var( 'crb_admin_messages', array( 'are_you_sure' => __( 'Are you sure?', 'wp-cerber' ) ) );
// Copy to clipboard code for the Diagnostic page
?>
document.addEventListener('click', function (event) {
if (event.target.matches('.crb-copy-to-clipboard')) {
event.preventDefault();
//event.stopImmediatePropagation();
const copy_control = event.target;
const targetClass = copy_control.dataset?.copy_clipboard_class;
if (!targetClass) {
console.error("Required attribute 'data-copy_clipboard_class' not found.");
return;
}
const elements = document.querySelectorAll(`.${targetClass}`);
if (!elements.length) {
console.error("No elements to copy. Check the class of the elements.");
return;
}
const isPlainText = copy_control.dataset?.plain_text === '1';
const textToCopy = Array.from(elements)
.map(el => {
if (isPlainText) {
return el.textContent.trim();
}
else {
const tempDiv = document.createElement('div');
tempDiv.innerHTML = el.innerHTML.replace(/<br\s*\/?>/gi, '\n');
return tempDiv.textContent.trim();
}
})
.join('\n');
if (!textToCopy) {
console.warn("Nothing to copy.");
return;
}
navigator.clipboard.writeText(textToCopy)
.then(() => {
const originalText = copy_control.textContent;
copy_control.textContent = 'Copied!';
setTimeout(() => {
copy_control.textContent = originalText;
}, 2000);
})
.catch(err => console.error('Unable to copy to clipboard:', err));
}
});
</script>
<?php
echo CRB_Globals::$admin_footer_html;
}
function crb_load_wp_js() {
echo '<script src="' . CRB_Globals::assets_url( 'wp-admin.js' ) . '"></script>';
}
add_action( 'admin_enqueue_scripts', 'cerber_dequeue_unwanted_scripts', PHP_INT_MAX );
/**
* Prevents loading of enemy JS files on WP Cerber admin pages
*
* @return void
*
* @since 9.6.1.5
*/
function cerber_dequeue_unwanted_scripts() {
global $wp_scripts;
if ( ! cerber_is_admin_page() ) {
return;
}
$current_queue = $wp_scripts->queue;
foreach ( $current_queue as $script_handle ) {
if ( ( $script = $wp_scripts->registered[ $script_handle ] ?? false )
&& isset( $script->src )
&& crb_is_enemy_plugin_script( $script->src ) ) {
wp_dequeue_script( $script_handle );
}
}
}
/**
* Checks for enemy JS scripts
*
* @param string $source The source of a JS script
*
* @return bool True if the source may not be included as an asset
*
* @since 9.6.1.5
*/
function crb_is_enemy_plugin_script( $source ) {
static $dir_url, $dir, $allowed = array( 'wp-cerber', 'query-monitor' );
if ( ! $source ) {
return false;
}
if ( ! $dir_url ) {
$dir_url = plugin_dir_url( '' );
}
if ( ! $dir ) {
$dir = cerber_get_plugins_dir() . DIRECTORY_SEPARATOR;
}
// Type one: a URL to a file
if ( 0 === strpos( $source, 'https://' )
|| 0 === strpos( $source, 'http://' ) ) {
if ( 0 === strpos( $source, $dir_url ) ) {
foreach ( $allowed as $slug ) {
if ( 0 === strpos( $source, $dir_url . $slug . '/' ) ) {
return false;
}
}
return true;
}
return false;
}
// Type 2: an absolute path to a file
if ( 0 === strpos( $source, $dir ) ) {
foreach ( $allowed as $slug ) {
if ( 0 === strpos( $source, $dir . $slug . DIRECTORY_SEPARATOR ) ) {
return false;
}
}
return true;
}
// Type 3: a relative path to a file
$source = ABSPATH . ltrim( $source, '/\\' );
if ( 0 === strpos( $source, $dir ) ) {
foreach ( $allowed as $slug ) {
if ( 0 === strpos( $source, $dir . $slug . DIRECTORY_SEPARATOR ) ) {
return false;
}
}
return true;
}
return false;
}
add_filter( 'admin_footer_text', function ( $text ) {
if ( ! cerber_is_admin_page() ) {
return $text;
}
return 'If you like <strong>WP Cerber</strong>, <a target="_blank" href="' . crb_get_review_url( 'tradius' ) . '">please give it a ★★★★★ review</a>. Thanks!';
}, PHP_INT_MAX );
add_filter( 'update_footer', function ( $text ) {
if ( ! cerber_is_admin_page() ) {
return $text;
}
$pr = '';
$pro = false;
$mode = '';
if ( $remote = nexus_get_context() ) {
$ver = $remote->plugin_v;
if ( ( $data = nexus_get_remote_data() )
&& $data['extra']['versions'][6] ?? '' ) {
$pro = true;
}
}
else {
$ver = CERBER_VER;
$pro = lab_lab();
$mode = ( 1 == cerber_get_mode() ) ? 'in the Standard mode' : 'in the Legacy mode';
}
if ( $pro ) {
$pr = 'PRO';
$support = '| <a target="_blank" href="https://my.wpcerber.com">Get Support</a>';
}
else {
$support = '| <a target="_blank" href="https://talk.wpcerber.com">Support Forum</a>';
}
return 'WP Cerber Security ' . $pr . ' ' . $ver . '. ' . $mode . ' ' . $support;
}, PHP_INT_MAX );
/*
* Add per screen user settings
*/
function crb_admin_screen_options() {
if ( ! $id = crb_get_configurable_screen() ) {
return;
}
add_filter( 'screen_settings', function ( $form, $screen ) {
// Fixing an invalid WP redirection implemented using wp_get_raw_referer().
// Instead of using the "Referer" header sent by a user browser, we use the proper value we need.
// A browser may strip $_GET parameters from the header.
$form .= '<input id="crb-fix-referer" type="hidden" name="_wp_http_referer" value="' . crb_attr_escape( $_SERVER['REQUEST_URI'] ) . '" />';
return $form;
}, 0, 2 );
add_screen_option( 'per_page', array(
//'label' => __( 'Number of items per page:' ),
'default' => 25,
'option' => 'cerber_screen_' . $id . '_page', // '_page' is mandatory since WP 5.4.2
) );
}
/*
* Enables saving screen options to the user's meta
*/
add_filter( 'set-screen-option', function ( $status, $option, $value ) {
if ( $id = crb_get_configurable_screen() ) {
if ( 'cerber_screen_' . $id . '_page' == $option ) {
return $value;
}
}
return $status;
}, 10, 3 );
/*
* Returns false if the admin page has no screen options to configure
*
* @return false|string
*/
function crb_get_configurable_screen() {
if ( ! $id = crb_admin_get_tab() ) {
$id = crb_admin_get_page();
}
if ( $id == 'cerber-traffic' ) {
$id = 'traffic';
}
if ( ( $id == 'cerber-nexus' ) && nexus_is_main() ) {
return 'nexus_sites';
}
$ids = array( 'sessions', 'lockouts', 'activity', 'traffic', 'scan_quarantine', 'scan_ignore', 'nexus_sites' );
if ( ! in_array( $id, $ids ) ) {
return false;
}
return $id;
}
/*
* Retrieve settings for the current screen
*
*/
function crb_admin_get_per_page() {
if ( is_multisite() ) {
return 50; // temporary workaround
}
if ( nexus_is_valid_request() ) {
$per_page = nexus_request_data()->screen['per_page'];
}
elseif ( function_exists( 'get_current_screen' ) ) {
set_current_screen();
$screen = get_current_screen();
if ( $screen_option = $screen->get_option( 'per_page', 'option' ) ) {
$per_page = absint( get_user_meta( get_current_user_id(), $screen_option, true ) );
if ( empty ( $per_page ) ) {
$per_page = absint( $screen->get_option( 'per_page', 'default' ) );
}
}
}
if ( empty ( $per_page ) ) {
$per_page = 25;
}
return absint( $per_page );
}
/**
* Renders HTML code for a vertical tabbed UI layout within a form.
*
* @param array<string, array{title: string, content: string, desc?: string, callback?: string}> $tabs_config Tab configurations.
* @param string $submit Submit button text. Defaults to 'Save Changes' if empty.
* @param array<string, string|int> $hidden Hidden form fields as key-value pairs.
* @param string $form_id ID attribute of the form element.
*
* @return void
*/
function crb_admin_render_vtabs( $tabs_config, $submit = '', $hidden = array(), $form_id = 'cerber_tabs' ) {
$tablinks = '';
$tabs = '';
foreach ( $tabs_config as $tab_id => $tab ) {
$tab_id = str_replace( '-', '_', $tab_id );
$tablinks .= '<div class="tablinks" data-tab-id="' . $tab_id . '" data-callback="' . crb_array_get( $tab, 'callback', '' ) . '">' . $tab['title'] . '<br/><span>' . crb_array_get( $tab, 'desc', '' ) . '</span></div>';
$tabs .= '<div id="tab-' . $tab_id . '" class="vtabcontent">' . $tab['content'] . '</div>';
}
echo '<form id="' . $form_id . '" method="post" action="" class="crb-settings-form">';
if ( ! $submit ) {
$submit = __( 'Save Changes', 'wp-cerber' );
}
echo '<table class="vtable" id="crb_vtabs_container" style="width: 100%; border-collapse: collapse;"><tr><td id="crb-vtabs"><div class="vtabs">' . $tablinks . '</div></td><td id="crb-vtabcontent">' . $tabs . '
</td></tr><tr><td></td><td><div style="padding-left: 3em;">' . crb_admin_submit_button( $submit ) . '</div></td></tr></table>';
cerber_nonce_field( 'control', true );
if ( $hidden ) {
foreach ( $hidden as $name => $val ) {
echo '<input type="hidden" name="' . $name . '" value="' . $val . '">';
}
}
echo '</form>';
}
function crb_admin_show_geo_rules() {
$rules = cerber_geo_rule_set();
$tabs_config = array();
foreach ( $rules as $rule_id => $rule ) {
list( $desc, $content ) = crb_admin_geo_selector( $rule_id, $rule );
if ( isset( $rule['multi_set'] ) ) {
$names = array( '---first' => $rule['multi_top'] . ' — ' . $desc );
foreach ( $rule['multi_set'] as $item_id => $item_title ) {
$id = $rule_id . '_' . $item_id;
list( $d, $c ) = crb_admin_geo_selector( $id, $rule, 'crb-display-none' );
$content .= $c;
if ( cerber_get_geo_rules( $id ) ) {
$desc = __( 'Role-based rules are configured', 'wp-cerber' );
$names[ $item_id ] = $item_title . ' — ' . $d;
}
else {
$names[ $item_id ] = $item_title;
}
}
$content = '<p>' . cerber_select( 'sw_' . $rule_id, $names, null, 'crb-geo-switcher', null, null, null, array( 'rule-id' => $rule_id ) ) . '</p>' . $content;
}
$tabs_config[ $rule_id ] = array(
'title' => $rule['name'],
'desc' => $desc,
'content' => '<div style="padding-top: 22px;">' . $content . '</div>',
'callback' => 'geo_rules_activator',
);
}
?>
<script id="wp-cerber-js-<?php echo crb_sanitize_id( __FUNCTION__ . '_' . __LINE__ ); ?>">
window.geo_rules_activator = function (rule_id) {
if (typeof search_fields === 'undefined') {
var search_fields = jQuery('form#crb-geo-rules .multi-wrapper input:text');
}
search_fields.each(function () {
if (jQuery(this).val()) {
jQuery(this).val(''); // Reset "Search" field
if (typeof wrapper === 'undefined') {
var wrapper = jQuery('#crb-geo-rules .multi-wrapper');
}
if (typeof select === 'undefined') {
var select = jQuery('#crb-geo-rules .crb-mega-select');
}
wrapper.remove();
select.removeAttr('data-multijs');
//document.querySelector('#crb-geo-rules .crb-mega-select').removeAttribute('data-multijs');
//document.querySelectorAll('#crb-geo-rules .crb-mega-select').removeAttribute('data-multijs');
}
});
if (typeof geo_selectors === 'undefined') {
var geo_selectors = jQuery('form#crb-geo-rules select.crb-mega-select');
}
geo_selectors.multi({'search_placeholder': '<?php echo crb_escape_string_for_js_in_html( __( 'Start typing here to find a country', 'wp-cerber' ) ); ?>'});
};
</script>
<?php
crb_admin_render_vtabs( $tabs_config, __( 'Save all rules', 'wp-cerber' ), array( 'cerber_admin_do' => 'update_geo_rules' ), 'crb-geo-rules' );
}
function crb_admin_geo_selector( $rule_id, $rule, $class = '' ) {
$geo_config = cerber_get_geo_rules( $rule_id );
$selector = crb_geo_country_selector( $geo_config, $rule_id, $rule );
$opt = crb_get_settings();
if ( ! empty( $geo_config['list'] ) ) {
$num = count( $geo_config['list'] );
$the_country = reset( $geo_config['list'] );
if ( $geo_config['type'] == 'W' ) {
if ( 1 === $num ) {
/* translators: %s is the name of the country. */
$info = sprintf( __( 'Permitted for one country (%s)', 'wp-cerber' ), $the_country );
}
else {
/* translators: %d is the number of countries. */
$info = sprintf( _n( 'Permitted for %d country', 'Permitted for %d countries', $num, 'wp-cerber' ), $num );
}
}
else {
if ( 1 === $num ) {
/* translators: %s is the name of the country. */
$info = sprintf( __( 'Not permitted for one country (%s)', 'wp-cerber' ), $the_country );
}
else {
/* translators: %d is the number of countries. */
$info = sprintf( _n( 'Not permitted for %d country', 'Not permitted for %d countries', $num, 'wp-cerber' ), $num );
}
}
}
else {
$info = __( 'Any country is permitted', 'wp-cerber' );
}
$note = '';
switch ( $rule_id ) {
case 'geo_register':
if ( ! get_option( 'users_can_register' ) ) {
$note = __( 'User registration is disabled in the General WordPress Settings.', 'wp-cerber' );
}
break;
case 'geo_restapi':
if ( $opt['norest'] ) {
$note = __( 'Access to REST API is restricted in the WP Cerber settings.', 'wp-cerber' );
}
break;
case 'geo_xmlrpc':
if ( $opt['xmlrpc'] ) {
$note = __( 'XML-RPC is disabled in the Hardening settings of WP Cerber.', 'wp-cerber' );
}
break;
}
if ( $note ) {
$note = '<p><span class="dashicons-before dashicons-warning"></span> ' . __( 'Heads up!', 'wp-cerber' ) . ' ' . $note . '</p>';
}
return array(
$info,
$note . '<div class="crb-geo-wrapper ' . $class . '" id="crb-geo-wrap_' . $rule_id . '">' . $selector . '</div>'
);
}
/**
* Generates GEO rule selector
*
* @param array $config Rule configuration
* @param string $rule_id
* @param array $rule
*
* @return string HTML code of form
*/
function crb_geo_country_selector( $config = array(), $rule_id = '', $rule = array() ) {
$ret = '<div class="crb-super-select"><select id="countries-' . $rule_id . '" name="crb-' . $rule_id . '-list[]" class="crb-mega-select" style="display: none;" multiple="multiple">';
if ( ! empty( $config['list'] ) ) {
$selected = $config['list'];
}
else {
$selected = null;
}
foreach ( crb_get_country_list() as $code => $country ) {
if ( $selected && in_array( $code, $selected ) ) {
$sel = 'selected';
}
else {
$sel = '';
}
$ret .= '<option ' . $sel . ' value="' . $code . '">' . $country . '</option>';
}
if ( ! empty( $config['type'] ) && $config['type'] == 'B' ) {
$w = '';
$b = 'checked="checked"';
}
else {
$w = 'checked="checked"';
$b = '';
}
if ( ! empty( $rule['desc'] ) ) {
$desc = $rule['desc'];
}
else {
$desc = '<span style="text-transform: lowercase;">' . $rule['name'] . '</span>';
}
$ret .= '
</select>
<div class="crb-super-details">
<p><span style="opacity: 0.7;">' . __( 'Click on a country name to add it to the list of selected countries', 'wp-cerber' ) . '</span></p>
<p style="margin-top: 2em;">
<input type="radio" value="W" name="crb-' . $rule_id . '-type" id="geo-type-' . $rule_id . '-W" ' . $w . '>'
. '<label for="geo-type-' . $rule_id . '-W">'
/* translators: 'selected countries' refers to a list of countries. 'to' is the marker of an infinitive verb, e.g., "to use it". Placeholder %s will be replaced by an action/operation, such as "Submit forms". */
. sprintf( _x( 'Only selected countries are permitted to %s. All others are not.', 'to is a marker of infinitive, e.g. "to use it"', 'wp-cerber' ), $desc )
. '</label>
<p>
<input type="radio" value="B" name="crb-' . $rule_id . '-type" id="geo-type-' . $rule_id . '-B" ' . $b . '>
<label for="geo-type-' . $rule_id . '-B">'
/* translators: 'selected countries' refers to a list of countries. 'to' is the marker of an infinitive verb, e.g., "to use it". Placeholder %s will be replaced by an action/operation, such as "Submit forms". */
. sprintf( _x( 'Selected countries are not permitted to %s. All others are allowed to.', 'to is a marker of infinitive, e.g. "to use it"', 'wp-cerber' ), $desc )
. '</label>
</p>
</div>
</div>';
return $ret;
}
/**
* A list of available GEO rules
*
* @return array
*/
function cerber_geo_rule_set() {
$set = wp_roles()->role_names;
$rules = array(
'geo_login' => array(
'name' => __( 'Log into the website', 'wp-cerber' ),
'multi_set' => $set,
'multi_top' => __( 'All Users', 'wp-cerber' )
),
'geo_register' => array( 'name' => __( 'Register on the website', 'wp-cerber' ) ),
'geo_submit' => array( 'name' => __( 'Submit forms', 'wp-cerber' ) ),
'geo_comment' => array( 'name' => __( 'Post comments', 'wp-cerber' ) ),
'geo_xmlrpc' => array( 'name' => __( 'Use XML-RPC', 'wp-cerber' ) ),
'geo_restapi' => array( 'name' => __( 'Use REST API', 'wp-cerber' ) ),
);
return $rules;
}
function crb_settings_update_geo_rules( $post_fields = array() ) {
global $check, $admin_country;
if ( ! lab_lab() ) {
return;
}
$check = array_keys( CERBER_COUNTRY_NAMES );
// Prevent the admin country from being blocked
if ( nexus_is_valid_request() ) {
$admin_country = null;
}
else {
$admin_country = lab_get_country( cerber_get_remote_ip(), false );
}
$geo = array();
foreach ( cerber_geo_rule_set() as $rule_id => $rule ) {
if ( $data = crb_admin_process_geo( $post_fields, $rule_id ) ) {
$geo[ $rule_id ] = $data;
}
if ( isset( $rule['multi_set'] ) ) {
foreach ( $rule['multi_set'] as $item_id => $item_title ) {
$id = $rule_id . '_' . $item_id;
if ( $data = crb_admin_process_geo( $post_fields, $id ) ) {
$geo[ $id ] = $data;
}
}
}
}
crb_sanitize_deep( $geo );
if ( update_site_option( CERBER_GEO_RULES, $geo ) ) {
cerber_admin_message( __( 'Security rules have been updated', 'wp-cerber' ) );
}
}
function crb_admin_process_geo( $post, $rule_id ) {
global $check, $admin_country;
if ( empty( $post[ 'crb-' . $rule_id . '-list' ] ) || empty( $post[ 'crb-' . $rule_id . '-type' ] ) ) {
return false;
}
$list = array_intersect( $post[ 'crb-' . $rule_id . '-list' ], $check );
if ( $post[ 'crb-' . $rule_id . '-type' ] == 'B' ) {
$type = 'B';
if ( $admin_country && ( ( $key = array_search( $admin_country, $list ) ) !== false ) ) {
unset( $list[ $key ] );
}
}
else {
$type = 'W';
if ( $admin_country && ( ( $key = array_search( $admin_country, $list ) ) === false ) ) {
array_push( $list, $admin_country );
}
}
return array( 'list' => $list, 'type' => $type );
}
/**
* Generates HTML for showing country in UI
*
* @param null $code
* @param null $ip
* @param bool $cache_only
*
* @return string
*/
function crb_country_html( $code = null, $ip = null, $cache_only = true ) {
if ( ! lab_lab() ) {
return '';
}
if ( ! $code ) {
if ( $ip ) {
$code = lab_get_country( $ip, $cache_only );
}
else {
return '';
}
}
if ( $code ) {
$ret = crb_get_flag_html( $code, crb_get_country_name( $code ) );
}
else {
$ip_id = cerber_get_id_ip( $ip );
$ret = crb_get_ajax_placeholder( 'country', $ip_id );
}
return $ret;
}
// Traffic =====================================================================================
/**
* Generates a label for the given HTTP code
*
* @param int $http_code
*
* @return string HTML code of the label, safe in any context
*
* @since 9.6.6.4
*/
function crb_get_http_code_label( int $http_code ): string {
$http_class = 'crb-http-' . $http_code . ' ' . ( ( $http_code < 400 ) ? 'crb-http-ok' : 'crb-http-error' );
if ( $http_code == 302 || $http_code == 301 ) {
$text = 'REDIRECT ' . $http_code;
}
else {
$text = 'HTTP ' . $http_code . ' ' . get_status_header_desc( $http_code );
}
return '<span class="' . $http_class . '"> ' . $text . '</span>';
}
/**
* Marks WP Cerber's fields by transforming values into objects.
*
* @param array $fields Fields to apply conditional highlighting
* @param int $type
*
* @return void
*
* @since 9.5.3.4
*/
function crb_highlight_fields_type_two( &$fields, $type = 1 ) {
static $crb_fields;
if ( ! $crb_fields ) {
if ( ! $anti = cerber_antibot_gene() ) {
return;
}
$crb_fields[1] = array_flip( array_column( $anti[0], 0 ) );
// All cookies
$crb_cookies = array_column( $anti[1], 0 );
foreach ( $crb_cookies as &$val ) {
$val = cerber_get_cookie_prefix() . $val;
}
$crb_cookies[] = cerber_get_cookie_prefix() . CRB_GROOVE;
$groove_x = cerber_get_groove_x();
$crb_cookies[] = cerber_get_cookie_prefix() . CRB_GROOVE . '_x_' . $groove_x[0];
$crb_cookies[] = cerber_get_cookie_prefix() . 'cerber_nexus_id';
$crb_fields[2] = array_flip( $crb_cookies );
}
foreach ( $fields as $field_id => &$value ) {
if ( ! $value
|| ! is_scalar( $value ) ) {
continue;
}
if ( isset( $crb_fields[ $type ][ $field_id ] ) ) {
$value = (object) array( 'element_class' => 'crb-highlight-this', 'element_value' => $value );
}
}
}
/**
* Marks WP Cerber's fields by transforming values into objects.
*
* @param array $fields Fields to apply conditional highlighting
* @param array $target These fields having these values will be highlighted
*
* @return void
*
* @since 9.6.6.4
*/
function crb_highlight_fields_type_one( array &$fields, array $target = array() ) {
foreach ( $fields as $field_id => &$value ) {
if ( ! $value
|| ! is_scalar( $value ) ) {
continue;
}
if ( isset( $target[ $field_id ] )
&& $value == $target[ $field_id ] ) {
$value = (object) array( 'element_class' => 'crb-highlight-this', 'element_value' => $value );
}
}
}
/**
* Create a table view of an array to display it
*
* @param string $title
* @param array $fields
* @param bool $plain If true, treat $fields as a two-dimensional list
* @param bool $nested
*
* @return string An escaped table view
*
* @deprecated 9.6.9.7 Use {@see crb_ui_table_view()}
*
*/
function cerber_table_view( string $title, array $fields, $plain = false, $nested = false ) {
if ( empty( $fields ) ) {
return '';
}
$class = 'crb-fields-table ' . ( $nested ? 'crb-sub-table' : 'crb-top-table' );
$view = '<table class="' . $class . '">';
if ( $title ) {
//$view .= '<tr><td colspan="2" class="crb-monospace-bold">' . $title . '</td></tr>';
$view .= '<tr><td colspan="2">' . $title . '</td></tr>';
}
foreach ( $fields as $key => $value ) {
$row_name = $key;
$atts = '';
if ( is_object( $value ) ) {
$atts = ' class="' . crb_boring_escape( $value->element_class ) . '"';
$value = $value->element_value;
}
if ( is_array( $value ) ) {
if ( $plain
&& is_scalar( $value[0] ) && is_scalar( $value[1] ) ) { // Make sure it is a two-dimensional list
$row_name = $value[0];
$content = '<div>' . nl2br( crb_escape_html( $value[1] ) ) . '</div>';
}
else {
$content = cerber_table_view( '', $value, false, true );
}
}
else {
$content = '<div>' . nl2br( crb_escape_html( $value ) ) . '</div>';
}
$view .= '<tr><td ' . $atts . '>' . crb_escape_html( $row_name ) . '</td><td>' . $content . '</td></tr>';
}
$view .= '</table>';
return $view;
}
function cerber_get_wp_type( $wp_type ) {
$types = array(
515 => 'XML-RPC',
520 => 'REST API'
);
if ( isset( $types[ $wp_type ] ) ) {
return $types[ $wp_type ];
}
return '';
}
/**
* Returns the name of a PHP error level constant (e.g., E_WARNING) based on its numeric value.
*
* @param int $level Numeric PHP error level.
*
* @return string Name of the error level, or 'Unknown (<code>)' if not recognized.
*/
function cerber_get_err_level( $level ): string {
static $list = [
E_ERROR => 'E_ERROR',
E_WARNING => 'E_WARNING',
E_PARSE => 'E_PARSE',
E_NOTICE => 'E_NOTICE',
E_CORE_ERROR => 'E_CORE_ERROR',
E_CORE_WARNING => 'E_CORE_WARNING',
E_COMPILE_ERROR => 'E_COMPILE_ERROR',
E_COMPILE_WARNING => 'E_COMPILE_WARNING',
E_USER_ERROR => 'E_USER_ERROR',
E_USER_WARNING => 'E_USER_WARNING',
E_USER_NOTICE => 'E_USER_NOTICE',
E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR',
E_DEPRECATED => 'E_DEPRECATED',
E_USER_DEPRECATED => 'E_USER_DEPRECATED',
E_ALL => 'E_ALL',
];
return $list[ $level ] ?? 'Unknown (' . crb_absint( $level ) . ')';
}
/**
* Check if admin AJAX is permitted.
*
*/
function cerber_check_ajax_permissions( $strict = true ) {
if ( nexus_is_valid_request() ) {
/*
$nonce = crb_array_get( nexus_request_data()->get_params, 'ajax_nonce' );
if ( ! $nonce ) {
$nonce = nexus_request_data()->get_post_data( 'ajax_nonce' );
}
if ( ! wp_verify_nonce( $nonce, 'crb-ajax-admin' ) ) {
return false;
}
*/
return true;
}
check_ajax_referer( 'crb-ajax-admin', 'ajax_nonce' );
if ( ! cerber_user_can_manage() ) {
if ( $strict ) {
wp_die( 'Oops! Access denied.' );
}
return 0;
}
return true;
}
function crb_admin_allowed_ajax( $action ) {
$list = array(
'cerber_ajax',
'cerber_scan_control',
'cerber_ref_upload',
'cerber_view_file',
'cerber_scan_bulk_files'
);
return in_array( $action, $list );
}
function crb_is_task_permitted( $die = false ) {
if ( is_super_admin()
|| nexus_is_valid_request() ) {
return true;
}
if ( $die ) {
wp_die( 'Oops! Access denied.' );
}
return false;
}
/**
* Identify and render Cerber's admin page
*
* @param string $page_id
* @param string $active_tab
*/
function cerber_render_admin_page( $page_id = '', $active_tab = '' ) {
if ( nexus_get_context() ) {
nexus_show_remote_page();
return;
}
$error = '';
if ( ! $page_id ) {
$page_id = crb_admin_get_page();
}
if ( $page = cerber_get_admin_page_config( $page_id ) ) {
if ( ! empty( $page['pro_page'] ) && ! lab_lab() ) {
cerber_admin_notice( array(
__( 'These features are available in the professional version of WP Cerber.', 'wp-cerber' ),
__( 'Know more about all advantages at', 'wp-cerber' ) . ' <a href="https://wpcerber.com/pro/" target="_blank">https://wpcerber.com/pro/</a>'
) );
}
if ( ( $tab_filter = crb_array_get( $page, 'tab_filter' ) )
&& is_callable( $tab_filter ) ) {
$page['tabs'] = call_user_func( $tab_filter, $page['tabs'] );
}
cerber_show_admin_page( $page['title'], $page['tabs'], $active_tab, $page['callback'] );
}
else {
$error = 'Unknown admin page: ' . crb_escape_html( $page_id );
}
if ( $error ) {
echo '<div class="crb-generic-error">ERROR: ' . $error . '</div>';
}
}
/**
* Returns the configuration of the currently rendering WP Cerber admin page
*
* @param string $page
*
* @return array[]|false
*/
function cerber_get_admin_page_config( $page = '' ) {
if ( ! $page ) {
return false;
}
if ( $config = crb_get_admin_ui_config( $page ) ) {
return $config;
}
if ( $page == CRB_ADDON_PAGE
&& $config = CRB_Addons::get_admin_ui() ) {
return $config;
}
return false;
}
/**
* Configuration data describing UI of all WP Cerber admin pages.
* To get UI phrases translated, must be invoked on or after the 'init' hook.
*
* @param string $page
*
* @return array[]|false
*/
function crb_get_admin_ui_config( $page = '' ) {
// Note: Tab titles (and other phrases) are not translated if it's called before the 'init' hook
$admin_pages = array(
'cerber-security' => array(
'title' => 'WP Cerber Security',
'tabs' => array(
'dashboard' => array( 'bxs-dashboard', __( 'Dashboard', 'wp-cerber' ) ),
'activity' => array( 'bx-pulse', __( 'Activity', 'wp-cerber' ) ),
'sessions' => array( 'bx-group', __( 'Sessions', 'wp-cerber' ) ),
'lockouts' => array( 'bxs-shield', __( 'Lockouts', 'wp-cerber' ) ),
'main' => array( 'bx-slider', __( 'Main Settings', 'wp-cerber' ) ),
'acl' => array( 'bx-lock', __( 'Access Lists', 'wp-cerber' ) ),
'hardening' => array( 'bx-shield-alt', __( 'Hardening', 'wp-cerber' ) ),
//'users' => array( 'bx-group', __( 'Users', 'wp-cerber' ) ),
'notifications' => array( 'bx-bell', __( 'Notifications', 'wp-cerber' ) ),
),
'tab_filter' => function ( $tabs_heading ) {
crb_delete_expired_lockouts();
$lockouts_count = crb_get_locked_out_num();
$acl_entries_count = (int) cerber_db_get_var( 'SELECT COUNT(ip) FROM ' . CERBER_ACL_TABLE . ' WHERE acl_slice = 0' );
crb_sessions_delete_expired();
$active_sessions_count = crb_sessions_get_num();
if ( ! $active_sessions_count ) {
CRB_Deferred_Tasks::add( 'crb_sessions_sync_all' );
}
$open_issues_count = CRB_Issues::get_count();
if ( $open_issues_count ) {
$tabs_heading['dashboard'][1] .= ' <sup>' . $open_issues_count . '</sup>';
}
$tabs_heading['sessions'][1] .= ' <sup>' . $active_sessions_count . '</sup>';
$tabs_heading['lockouts'][1] .= ' <sup>' . $lockouts_count . '</sup>';
$tabs_heading['acl'][1] .= ' <sup>' . $acl_entries_count . '</sup>';
return $tabs_heading;
},
'callback' => function ( $tab ) {
switch ( $tab ) {
case 'acl':
crb_acl_render_management_page();
break;
case 'activity':
crb_render_activity_view();
break;
case 'sessions':
crb_show_sessions_page();
break;
case 'lockouts':
cerber_show_lockouts_page();
break;
case 'help':
cerber_render_help_page();
break;
case 'dashboard':
cerber_show_dashboard();
break;
default:
CRB_Settings_Renderer::render_form( $tab );
}
}
),
'cerber-recaptcha' => array(
'title' => __( 'Anti-spam and bot detection settings', 'wp-cerber' ),
'tabs' => array(
'antispam' => array( 'bx-chip', __( 'Anti-spam engine', 'wp-cerber' ) ),
'captcha' => array( 'bxl-google', 'reCAPTCHA' ),
),
'callback' => function ( $tab ) {
switch ( $tab ) {
case 'captcha':
$settings_screen_id = 'recaptcha';
break;
default:
$settings_screen_id = 'antispam';
}
CRB_Settings_Renderer::render_form( $settings_screen_id );
}
),
'cerber-traffic' => array(
'title' => __( 'Traffic Inspector', 'wp-cerber' ),
'tabs' => array(
'traffic' => array( 'bx-show', __( 'Live Traffic', 'wp-cerber' ) ),
'ti_settings' => array( 'bx-slider', __( 'Settings', 'wp-cerber' ) ),
),
'callback' => function ( $tab ) {
switch ( $tab ) {
case 'ti_settings':
CRB_Settings_Renderer::render_form( 'traffic' );
break;
default:
CRB_Traffic_Log_Screen::render_screen();
}
}
),
'cerber-shield' => array(
'title' => __( 'Data Shield Policies', 'wp-cerber' ),
'tabs' => array(
'user_shield' => array( 'bx-group', __( 'Accounts & Roles', 'wp-cerber' ) ),
'opt_shield' => array( 'bx-slider', __( 'Site Settings', 'wp-cerber' ) ),
),
'callback' => function ( $tab ) {
CRB_Settings_Renderer::render_form( $tab );
}
),
'cerber-users' => array(
'title' => __( 'User Policies', 'wp-cerber' ),
'tabs' => array(
'role_policies' => array( 'bx-group', __( 'Role Policies', 'wp-cerber' ) ),
'global_policies' => array( 'bx-user-detail', __( 'Global Policies', 'wp-cerber' ) ),
),
'callback' => function ( $tab ) {
switch ( $tab ) {
case 'role_policies':
crb_admin_render_role_policies();
break;
case 'global_policies':
CRB_Settings_Renderer::render_form( 'users' );
break;
default:
CRB_Settings_Renderer::render_form( $tab );
}
}
),
'cerber-rules' => array(
'pro_page' => 1,
'title' => __( 'Security Rules', 'wp-cerber' ),
'tabs' => array(
'geo' => array( 'bxs-world', __( 'Countries', 'wp-cerber' ) ),
),
'callback' => function ( $tab ) {
switch ( $tab ) {
case 'geo':
crb_admin_show_geo_rules();
break;
default:
crb_admin_show_geo_rules();
}
}
),
'cerber-integrity' => array(
'title' => __( 'Site Integrity', 'wp-cerber' ),
'tabs' => array(
'scan_main' => array( 'bx-radar', __( 'Security Scanner', 'wp-cerber' ) ),
'scan_settings' => array( 'bxs-slider-alt', __( 'Settings', 'wp-cerber' ) ),
'scan_schedule' => array( 'bx-time', __( 'Scheduling', 'wp-cerber' ) ),
'scan_policy' => array( 'bx-bolt', __( 'Cleaning up', 'wp-cerber' ) ),
'scan_ignore' => array( 'bx-hide', __( 'Ignore List', 'wp-cerber' ) ),
'scan_quarantine' => array( 'bx-trash', __( 'Quarantine', 'wp-cerber' ) ),
'scan_insights' => array( 'bx-flask', __( 'Analytics', 'wp-cerber' ) ),
),
'tab_filter' => function ( $tabs ) {
$numi = 0;
if ( $list = cerber_get_set( 'ignore-list' ) ) {
$numi = count( $list );
}
$numq = cerber_get_set( 'quarantined_total', null, false );
if ( ! is_numeric( $numq ) ) {
CRB_Deferred_Tasks::add( '_crb_qr_total_sync' );
$numq = '';
}
$tabs['scan_quarantine'][1] .= ' <sup id="crb-quarantined_total">' . $numq . '</sup>';
$tabs['scan_ignore'][1] .= ' <sup id="crb-ignore_total">' . $numi . '</sup>';
return $tabs;
},
'callback' => function ( $tab ) {
switch ( $tab ) {
case 'scan_settings':
CRB_Settings_Renderer::render_form( 'scanner' );
break;
case 'scan_schedule':
CRB_Settings_Renderer::render_form( 'schedule' );
break;
case 'scan_policy':
CRB_Settings_Renderer::render_form( 'policies' );
break;
case 'scan_quarantine':
cerber_show_quarantine();
break;
case 'scan_ignore':
cerber_show_ignore();
break;
case 'scan_insights':
cerber_scan_insights();
break;
case 'help':
cerber_render_help_page();
break;
default:
cerber_show_scanner();
}
}
),
'cerber-tools' => array(
'title' => __( 'Tools', 'wp-cerber' ),
'tabs' => array(
//'imex' => array( 'bx-layer', __( 'Export & Import', 'wp-cerber' ) ),
'imex' => array( 'bx-layer', __( 'Manage Settings', 'wp-cerber' ) ),
'diagnostic' => array( 'bx-wrench', __( 'Diagnostic', 'wp-cerber' ) ),
'diag-log' => array( 'bx-bug', __( 'Diagnostic Log', 'wp-cerber' ) ),
'changelog' => array( 'bx-collection', __( 'Changelog', 'wp-cerber' ) ),
'license' => array( 'bx-key', __( 'License', 'wp-cerber' ) ),
),
'callback' => function ( $tab ) {
switch ( $tab ) {
case 'diagnostic':
cerber_show_diag_page();
break;
case 'license':
cerber_show_lic();
break;
case 'diag-log':
cerber_show_diag_log();
break;
case 'changelog':
cerber_show_change_log();
break;
case 'help':
cerber_render_help_page();
break;
default:
if ( ! nexus_is_valid_request() ) {
cerber_show_imex();
}
else {
echo 'This admin page is not available in this mode.';
}
}
}
),
);
if ( ! $page ) {
return $admin_pages;
}
return crb_array_get( $admin_pages, $page );
}
/**
* Identifies and retrieves Page ID by the given Tab ID
*
* @param string $tab Tab ID used in the query parameters. In some cases can be the Page ID - in case of the first tab on the page.
*
* @return string Page ID to be used in URL query parameters. Safe in any context.
*
* @since 9.6.2.1
*/
function crb_determine_page( string $tab ): string {
static $map = array();
if ( ! $map ) {
$pages = crb_get_admin_ui_config();
// Create tab => page structure
foreach ( $pages as $page_id => $config ) {
if ( ! empty ( $config['tabs'] ) ) {
$map = array_merge( $map, array_fill_keys( array_keys( $config['tabs'] ), $page_id ) );
}
}
}
if ( $page = $map[ $tab ] ?? '' ) {
return $page;
}
// An exception when the first tab on an admin page is specified by using Page ID since pages and tabs are unique
if ( in_array( $tab, $map ) ) {
return $tab;
}
if ( $page = CRB_Addons::get_addon_page( $tab ) ) {
return $page;
}
$page = '';
if ( list( $prefix ) = explode( '_', $tab, 2 ) ) {
if ( $prefix == 'nexus' ) {
$page = 'cerber-nexus';
}
}
return $page;
}
/**
* Extracts specified query parameters from the request URI and returns them as object properties.
*
* @param array $fields List of query parameters to extract from the request.
* @param array $values Optional. Key-value pairs to overwrite extracted parameters.
*
* @return object Object with properties corresponding to the specified query parameters. Values are trimmed.
*/
function crb_admin_parse_query( array $fields, array $values = array() ): object {
$arr = crb_get_query_params();
$ret = array();
foreach ( $fields as $field ) {
$val = $values[ $field ] ?? crb_array_get( $arr, $field, false );
if ( is_array( $val ) ) {
$val = array_map( 'trim', $val );
}
elseif ( $val !== false ) {
$val = trim( $val );
}
$ret[ $field ] = $val;
}
return (object) $ret;
}
function crb_admin_get_page() {
if ( nexus_is_valid_request() ) {
return nexus_request_data()->page;
}
return cerber_get_get( 'page', '[A-Z0-9\_\-]+' );
}
/**
* Retrieves the ID of the curretly selected tab of the currently displaying admin page.
* The tab is identified by $_GET['tab'].
* If no active tab is detected and the $tabs (group of tabs) provided, returns the first one as the default
*
* @param array $tabs
*
* @return string
*/
function crb_admin_get_tab( $tabs = array() ): string {
if ( nexus_is_valid_request() ) {
$tab = nexus_request_data()->tab;
}
else {
$tab = cerber_get_get( 'tab', '[\w\-]+' );
}
if ( empty( $tabs ) ) {
return (string) $tab;
}
$tabs['help'] = 1; // always must be in the array
if ( ! $tab || ! isset( $tabs[ $tab ] ) ) {
reset( $tabs );
$tab = key( $tabs ); // Takes first tab from the group
}
return (string) $tab;
}
function cerber_show_tabs( $active, $tabs = array() ) {
echo '<h2 class="nav-tab-wrapper cerber-tabs">';
$args = array( 'page' => crb_admin_get_page() );
foreach ( $tabs as $tab => $data ) {
echo '<a href="' . cerber_admin_link( $tab, $args ) . '" class="nav-tab ' . ( $tab == $active ? 'nav-tab-active' : '' ) . '"><i class="crb-icon crb-icon-' . $data[0] . '"></i> ' . $data[1] . '</a>';
}
echo '<a href="' . cerber_admin_link( 'help', $args ) . '" class="nav-tab ' . ( $active == 'help' ? 'nav-tab-active' : '' ) . '"><i class="crb-icon crb-icon-bx-idea"></i> ' . __( 'Help', 'wp-cerber' ) . '</a>';
$lab = lab_indicator();
$ro = '';
if ( nexus_is_valid_request() && ! nexus_is_granted( 'submit' ) ) {
$ro = '<span style="font-weight: 600; font-size: 0.8em; color: #f00;">Read-only mode</span>';
}
echo '<div style="float: right;">' . $ro . ' ' . $lab . '</div>';
echo '</h2>';
}
/**
* Bulk action for WP_List_Table
*
* @return bool|array|string
*/
function cerber_get_bulk_action() {
// GET
if ( cerber_is_http_get() ) {
if ( ( $ac = crb_get_query_params( 'action', '[\w\-]+' ) ) && $ac != '-1' ) {
return $ac;
}
if ( ( $ac = crb_get_query_params( 'action2', '[\w\-]+' ) ) && $ac != '-1' ) {
return $ac;
}
return false;
}
// POST
if ( ( $ac = crb_get_post_fields( 'action', false, '[\w\-]+' ) ) && $ac != '-1' ) {
return $ac;
}
if ( ( $ac = crb_get_post_fields( 'action2', false, '[\w\-]+' ) ) && $ac != '-1' ) {
return $ac;
}
return false;
}
function crb_admin_cool_features() {
return
'<div class="crb-pro-req">' .
__( 'These features are available in the professional version of WP Cerber.', 'wp-cerber' ) .
'<br/><br/>' . __( 'Know more about all advantages at', 'wp-cerber' ) .
' <a href="https://wpcerber.com/pro/" target="_blank">https://wpcerber.com/pro/</a>
</div>';
}
/**
* Generates a flexible box HTML DOM element using string elements from $elements
*
* @param string[] $elements Text of child elements
* @param string $justify How to arrange child elements
*
* @return string HTML code of flexible box layout
*
* @since 9.6.5.12
*/
function crb_generate_html_flex( array $elements, $class = '', $justify = 'space-between' ): string {
$ret = '<div class="' . crb_boring_escape( $class ) . '" style="display: flex; justify-content: ' . crb_boring_escape( $justify ) . ';">';
$ret .= '<div>' . implode( '</div><div>', $elements ) . '</div>';
$ret .= '</div>';
return $ret;
}
/**
* Creates a Copy To Clipboard hypertext link.
*
* @param string $source_class The class of the elements to copy text content from.
* @param bool $plain If true the plain inner text will be copied without tags and processing, otherwise <br/> tags will be converted into new lines and other tags will be removed.
*
* @return string HTML code of the link
*
* @since 9.6.5.12
*/
function crb_copy_to_clipboard( string $source_class, $plain = true ): string {
return '<a href="#" data-plain_text="' . ( $plain ? 1 : 0 ) . '" data-copy_clipboard_class="' . crb_boring_escape( $source_class ) . '" class="crb-copy-to-clipboard">' . __( 'Copy To Clipboard', 'wp-cerber' ) . '</a>';
}
/**
* Creates an HTML link with a confirmation dialog.
*
* When the user clicks the link, a confirmation dialog is displayed with the specified message.
* If the user does not confirm, the action is cancelled, and the browser does not follow the link.
*
* @param string $url URL of the link to follow when the user confirms the action
* @param string $text Link text
* @param string $msg Optional confirmation message, if not specified the default message "Are you sure?" is used.
* @param string $class Optional class for a element
*
* @return string HTML element with escaped attributes
*/
function crb_confirmation_link( string $url, string $text, string $msg = '', string $class = '' ) {
$data = $msg ? 'data-user_message="' . crb_escape_html( $msg ) . '"' : '';
$class = $class ? crb_escape_html( $class ) : '';
return '<a href="' . crb_escape_url( $url ) . '" class="crb-confirm-action ' . $class . '" ' . $data . '>' . $text . '</a>';
}
/**
* @param array $args
* @param string $setting
*
* @return string
*
* @since 8.9.6.1
*/
function crb_test_notify_link( $args = array(), $setting = null ) {
if ( $setting
&& ! crb_get_settings( $setting ) ) {
return '';
}
if ( isset( $args['title'] ) ) {
$title = $args['title'];
unset( $args['title'] );
}
else {
$title = __( 'Click to send test', 'wp-cerber' );
}
return '<span class="crb-insetting-link">[ <a href="' .
cerber_admin_link_add( array_merge(
array(
'cerber_admin_do' => 'testnotify',
'type' => 'lockout',
), $args ) ) . '">' . $title . '</a> ]</span>';
}
// Pop-up dialogs -------------------------------------------------------------
/**
* Creates a pop-up dialog DOM element with a button that opens the dialog.
*
* @param array $fields Form fields - left side
* @param array $aside Form fields - right side, optional
* @param array $control
* @param array $atts HTML attributes for popup element
* @param string $method Form method
* @param string|null $action Form action
*
* @return string The HTML code of the pop-up prepared to add to the web page
*
* @since 8.9.5.3
*/
function crb_create_popup_form( array $fields, array $aside = array(), array $control = array(), array $atts = array(), string $method = 'get', string $action = '' ) {
static $counter = 0;
$counter ++; // In case of multiple dialogs on a page
$icon = $control['icon'] ?? '';
$label = $control['label'] ?? '';
$type = $control['type'] ?? 'button';
$icon_html = ( $icon ) ? '<i class="crb-icon ' . crb_escape_html( $icon ) . '"></i>' : '';
$space = ( $icon_html && $label ) ? ' ' : '';
if ( $type == 'button' ) {
$opener = '<a data-popup_element_id="crb-popup-dialog-' . $counter . '" class="button button-secondary cerber-button crb-popup-dialog-open" href="#" style="margin-right: 0.5em;">' . $icon_html . $space . $label . '</a>';
}
else {
$opener = $icon_html . $space . '<a data-popup_element_id="crb-popup-dialog-' . $counter . '" class="crb-popup-dialog-open" href="#">' . $label . '</a>';
}
if ( $aside ) {
$class = 'crb-table-2cols';
$td_aside = '<td><div class="crb-popup-dialog-aside">' . crb_create_dialog_form( $aside ) . '</div></td>';
$last_row = '<td></td>';
}
else {
$class = 'crb-table-1col';
$td_aside = '';
$last_row = '';
}
$html_atts = '';
if ( $atts ) {
foreach ( $atts as $att => $val ) {
$html_atts .= ' ' . crb_escape_html( $att ) . '="' . crb_escape_html( $val ) . '" ';
}
}
if ( $action ) {
$form_action = $action;
}
else {
$form_action = cerber_admin_link( crb_admin_get_tab() );
}
$dialog = '<div id="crb-popup-dialog-' . $counter . '" class="mfp-hide crb-popup-dialog" ' . $html_atts . '>';
$dialog .= '<form action="' . crb_escape_url( $form_action ) . '" method="' . crb_escape_html( $method ) . '">';
$dialog .= '<table class="' . crb_escape_html( $class ) . '">';
$dialog .= '<tr><td>' . crb_create_dialog_form( $fields ) . '</td>' . $td_aside . '</tr>';
$dialog .= '<tr>' . $last_row . '<td class="crb-dialog-controls">
<input type="submit" class="button button-primary" value="' . __( 'OK', 'wp-cerber' ) . '">
<input type="button" class="button button-secondary crb-popup-dialog-close" value="' . __( 'Cancel', 'wp-cerber' ) . '">
</td></tr>';
$dialog .= '</table></form></div>';
return $dialog . $opener;
}
/**
* Creates a basic HTML form for pop-up dialogs
*
* @param array $form_data List of form fields
*
* @return string HTML code
*
* @since 8.9.5.3
*/
function crb_create_dialog_form( $form_data ) {
$form = '';
if ( $title = $form_data['title'] ?? '' ) {
$form .= '<h3>' . $title . '</h3>';
}
$form .= $form_data['html'] ?? '';
$hidden = array_merge(
$form_data['hidden'] ?? array(), array(
'page' => crb_admin_get_page(),
'tab' => crb_admin_get_tab(),
'cerber_nonce' => crb_create_nonce(),
) );
foreach ( $hidden as $field => $value ) {
$field = crb_escape_html( $field );
if ( is_array( $value ) ) {
foreach ( $value as $val ) {
$form .= '<input type="hidden" name="' . $field . '[]" value="' . crb_escape_html( $val ) . '">' . "\n";
}
}
else {
$form .= '<input type="hidden" name="' . $field . '" value="' . crb_escape_html( $value ) . '">' . "\n";
}
}
$form .= '<table class="crb-form-fields">';
$visible = $form_data['visible'] ?? array();
foreach ( $visible as $field => $config ) {
$atts = '';
foreach ( $config['atts'] as $att => $val ) {
$atts .= ' ' . crb_escape_html( $att ) . '="' . crb_escape_html( $val ) . '" ';
}
$field = crb_escape_html( $field );
$input = '<input id="' . $field . '" name="' . $field . '" value="' . crb_escape_html( $config['value'] ) . '" ' . $atts . '>';
$label = '<label for="' . $field . '">' . $config['label'] . '</label>';
$type = $config['atts']['type'] ?? '';
$form .= '<tr><td>';
if ( $type == 'checkbox' ) {
$form .= '<div class="crb-wrap-' . $type . '"><div>' . $input . '</div><div>' . $label . '</div></div>';
}
else {
$form .= $label . '<div>' . $input . '</div>';
}
$form .= '</td></tr>';
}
$form .= '</table>';
$mess = $form_data['error_messages'] ?? array();
foreach ( $mess as $id => $msg ) {
$form .= '<p id="crb-message-' . crb_sanitize_id( $id ) . '" class="crb-error-text" style="display:none;">' . $msg . '</p>';
}
return '<div class="crb-popup-dialog-form">' . $form . '</div>';
}
add_filter( 'self_admin_url', 'crb_set_release_note_url' );
/**
* Sets the correct URL on the Dashboard -> Updates to view the page with the last version info.
* Fix broken link pointing to a non-existing wordpress.org repository plugin profile.
*
* @param string $url
*
* @return string
*
* @since 9.5.8
*/
function crb_set_release_note_url( $url ): string {
global $pagenow;
$add = '';
$replace = false;
if ( 'update-core.php' === $pagenow
// We are looking for: plugin-install.php?tab=plugin-information&plugin=wp-cerber§ion=changelog&TB_iframe=true&width=640&height=662
// The URL is hard coded in /wp-admin/update-core.php
&& strpos( $url, '/wp-admin/plugin-install.php?tab=plugin-information&plugin=wp-cerber§ion=changelog&TB_iframe=true&width=640&height=662' ) ) {
$add = '?TB_iframe=true&width=640&height=662';
$replace = true;
}
elseif ( 'plugins.php' === $pagenow
// This URL is generated in wp_plugin_update_row()
&& strpos( $url, '/wp-admin/plugin-install.php?tab=plugin-information&plugin=wp-cerber§ion=changelog' ) ) {
$replace = true;
}
if ( ! $replace ) {
return $url;
}
$plugins = get_plugin_updates();
$new_version = '';
if ( $plugin_data = $plugins[ CERBER_PLUGIN_ID ] ?? false ) {
$new_version = $plugin_data->update->new_version;
}
if ( $new_version ) {
$url = 'https://wpcerber.com/wp-cerber-security-' . str_replace( '.', '-', $new_version ) . '/' . $add;
}
return $url;
}
/**
* @param string $icon_id
*
* @return string HTML code to display a font-based icon
*
* @since 9.6.1.3
*/
function crb_get_icon( $icon_id ) {
return '<i class="crb-icon crb-icon-' . ( CRB_ADMIN_ICONS[ $icon_id ] ?? 'unknown-icon' ) . '"></i>';
}
// Setting up WordPress navigation menu editor ----------------------------------
add_action( 'admin_head-nav-menus.php', function () {
add_meta_box( 'wp_cerber_nav_menu',
'WP Cerber',
'cerber_nav_menu_box',
'nav-menus',
'side',
'low' );
} );
function cerber_nav_menu_box() {
// Warning: do not change array indexes
$list = array(
'login-url' => __( 'Log In', 'wp-cerber' ),
'logout-url' => __( 'Log Out', 'wp-cerber' ),
'reg-url' => __( 'Register', 'wp-cerber' ),
);
if ( class_exists( 'WooCommerce' ) ) {
$list['wc-login-url'] = __( 'WooCommerce Log In', 'wp-cerber' );
$list['wc-logout-url'] = __( 'WooCommerce Log Out', 'wp-cerber' );
}
?>
<div id="posttype-wp-cerber-nav" class="posttypediv">
<div id="tabs-panel-wp-cerber-nav" class="tabs-panel tabs-panel-active">
<ul id="wp-cerber-nav-checklist" class="categorychecklist form-no-clear">
<?php
$i = - 1;
foreach ( $list as $key => $value ) :
$id = 'wp-cerber-' . $key;
?>
<li>
<label class="menu-item-title">
<input type="checkbox" class="menu-item-checkbox"
name="menu-item[<?php echo esc_attr( $i ); ?>][menu-item-object-id]"
value="<?php echo esc_attr( $i ); ?>"/> <?php echo esc_html( $value ); ?>
</label>
<input type="hidden" class="menu-item-type"
name="menu-item[<?php echo esc_attr( $i ); ?>][menu-item-type]" value="custom"/>
<input type="hidden" class="menu-item-title"
name="menu-item[<?php echo esc_attr( $i ); ?>][menu-item-title]"
value="<?php echo esc_html( $value ); ?>"/>
<input type="hidden" class="menu-item-url"
name="menu-item[<?php echo esc_attr( $i ); ?>][menu-item-url]"
value="<?php echo '#will-be-generated-automatically'; ?>"/>
<input type="hidden" class="menu-item-attr-title"
name="menu-item[<?php echo esc_attr( $i ); ?>][menu-item-attr-title]"
value="<?php echo '*MENU*CERBER*|' . $id; ?>"/>
</li>
<?php
$i --;
endforeach;
?>
</ul>
</div>
<p class="button-controls">
<span class="add-to-menu">
<button type="submit" class="button-secondary submit-add-to-menu right"
value="<?php esc_attr_e( 'Add to menu' ); ?>" name="add-post-type-menu-item"
id="submit-posttype-wp-cerber-nav"><?php esc_html_e( 'Add to menu' ); ?></button>
<span class="spinner"></span>
</span>
</p>
</div>
<?php
}
/**
* Renders a popup form to display screen configuration dialog
*
* @return string HTML
*
* @since 9.6.4.5
*/
function crb_admin_screen_dialog() {
$atts = array( 'type' => 'checkbox', 'data-validation_group' => 'required_min_one' );
$widgets = CRB_Widgets::get_titles( true, false );
$field_list = array();
foreach ( $widgets as $widget_id => $widget ) {
if ( ! CRB_Widgets::is_hideable( $widget_id ) ) {
continue;
}
if ( CRB_Widgets::is_active( $widget_id ) ) {
$atts['checked'] = 1;
}
else {
unset( $atts['checked'] );
}
$field_list[ $widget_id ] = array(
'label' => $widget[0],
'value' => '1',
'atts' => $atts,
);
}
$form_fields = array(
'title' => __( 'Select widgets to display', 'wp-cerber' ),
'visible' => $field_list,
'hidden' => array(
'cerber_admin_do' => 'save_widget_list',
),
'error_messages' => array( 'required_min_one' => __( 'Please select at least one widget', 'wp-cerber' ) )
);
return crb_create_popup_form( $form_fields, [], [ 'icon' => 'crb-icon-bx-cog' ], [ 'data-narrow' => 1 ], 'post' );
}
add_action( 'heartbeat_tick', 'cerber_update_widget_data' );
/**
* Refresh expiring widgets stored in the WordPress persistent object cache
*
* @return void
*
* @since 9.6.4.3
*/
function cerber_update_widget_data() {
if ( ! CRB_Cache::checker()
|| ! is_super_admin() ) {
return;
}
cerber_widgets_init();
CRB_Widgets::update_cache();
}
Home - Capital GMC Buick Regina
Skip to content
{{ $t(category) }}
Error
{{vehicle.modelData.year}} {{vehicle.modelData.make}} {{vehicle.modelData.model}}
Starting from {{vehicle.modelData.startingPrice | moneyFormat(lang)}}
Welcome to Capital GMC BUICK – REGINA
Thank you for choosing Capital GMC Buick | Regina, your premier certified Buick and GMC dealership proudly serving drivers in Regina and the surrounding communities. Whether you’re searching for a brand-new Buick or GMC vehicle or a meticulously inspected pre-owned model, we have a diverse selection to match your needs and lifestyle.
Beyond our impressive inventory, we offer a seamless and stress-free financing experience through our well-connected finance centre, where our team of experts is dedicated to securing the best loan or lease options for you, quickly, transparently, and hassle-free.
But our commitment to you doesn’t stop at the sale. Our state-of-the-art service centre is staffed with skilled Buick and GMC technicians who use the latest equipment and genuine OEM parts to keep your vehicle running at its best. From routine maintenance to complex repairs, we’ve got you covered.
Experience top-tier customer service, quality vehicles, and expert care, all in one place. Visit Capital GMC Buick | Regina today or call us at 306-205-8072 with any questions. We’re here to help!
Ask a Question
Capital GMC Buick – Regina
Contact Us
Have a question or need assistance? Fill out the form and we will reach out to you as soon as possible.
Notice: JavaScript is required for this content.
CLOSE
Schedule a Visit
Let us know when you are coming and how we can assist you. We can ensure someone will be on hand to help you out at the desired date and time.
Notice: JavaScript is required for this content.
CLOSE
Find a Career
Have a look at our list of available positions and apply online today to join our team!
×
Opportunities to Grow
The auto industry is constantly changing and we want to continue to grow. We offer growth, leadership & mentorship programs to allow our staff to grow with us.
×
Competitive Salary
We have a significant earning potential with incentive-based pay in most roles. We also offer an employee referral bonus with paid bonuses.
×
Health & Dental
We offer a comprehensive benefits package including extended health, dental, and vision care. We also include paramedical, life insurance, paid sick leave, short & long-term disability coverages.
×
Vacation
We value our employees and want everyone to take their vacation time. We offer a minimum of 2 weeks vacation each year.
×
Training & Development
We have many opportunities for paid education and training in-house as well as training from the Manufacturer.
×
$10,000 Cash Giveaway – Terms & Conditions
All October long, stop by Capital GMC Buick Cadillac, to enter for your chance to win $10,000 cash. No purchase is required, but entries must be made in-store.
The contest is open to residents of Saskatchewan who are 18+. Dealership employees and their households are not eligible. Entries will be accepted from October 1 to October 31, 2025. A random draw will take place on November 1, 2025.
The prize is one $10,000 award, paid by cheque, and must be accepted as awarded. Winner will be contacted by phone or email and must respond within 7 days or another entry may be drawn. Odds of winning depend on the number of entries received.By entering, you agree that Capital Automotive Group may use your name and photo for winner announcements. The contest is governed by the laws of Saskatchewan.
Notice: JavaScript is required for this content.
CLOSE