AlkantarClanX12
| Current Path : /www/capitalgmcbuickregina_830/public/wp-content/plugins/wp-cerber/admin/ |
| Current File : /www/capitalgmcbuickregina_830/public/wp-content/plugins/wp-cerber/admin/cerber-dashboard.php |
<?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();
}