option_name, array( $this, 'handle_settings_update' ), 10, 2 );
// Apply some security services on init
add_action( 'init', array( $this, 'apply_security_services' ), 20 );
// Apply some security services early on plugins_loaded
add_action( 'plugins_loaded', array( $this, 'apply_early_security_services' ), 20 );
//rest api init hook for rest api disabling
add_action( 'rest_api_init', array( $this, 'apply_rest_api_disabling' ), 0 );
}
//function to disable rest api for non-authenticated users
public function apply_rest_api_disabling() {
$settings = get_option( 'ADALC_settings', [] );
if ( ! empty( $settings['disable_rest_api'] ) ) {
add_filter( 'rest_authentication_errors', function( $result ) {
if ( ! empty( $result ) ) {
return $result;
}
if ( ! is_user_logged_in() ) {
return new WP_Error( 'rest_cannot_access', __( 'REST API restricted to authenticated users only.', 'accessdoor-smart-admin-login-url-control' ), array( 'status' => 401 ) );
}
return $result;
});
}
}
//function to handle settings update for xmlrpc services
public function apply_early_security_services() {
$settings = get_option( 'ADALC_settings', [] );
// Disable File Editing in Dashboard
if ( isset( $settings['disable_file_editing'] ) && $settings['disable_file_editing'] ) {
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound
if ( ! defined( 'DISALLOW_FILE_EDIT' ) ) {
// phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedConstantFound
define( 'DISALLOW_FILE_EDIT', true );
}
}
//disable xmlrpc completely
if ( ! empty( $settings['disable_xmlrpc'] ) ) {
add_filter( 'xmlrpc_enabled', '__return_false' );
if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
status_header( 403 );
header( 'Content-Type: text/plain; charset=UTF-8' );
echo 'XML-RPC is disabled.';
exit;
}
}
// Disable XML-RPC Pingbacks
if ( ! empty( $settings['disable_xmlrpc_pingback'] ) ) {
add_filter( 'xmlrpc_methods', function( $methods ) {
unset( $methods['pingback.ping'] );
unset( $methods['pingback.extensions.getPingbacks'] );
return $methods;
});
add_action( 'xmlrpc_call', function( $method ) {
if ( strpos( $method, 'pingback' ) !== false ) {
status_header( 403 );
header( 'Content-Type: text/plain; charset=UTF-8' );
echo 'Pingbacks are disabled.';
exit;
}
});
}
}
//function handle_settings_update to check if any security-related settings were changed and apply changes immediately
public function handle_settings_update( $old_value, $new_value ) {
// print_r($old_value);
// Stop if no security settings changed
if ( $old_value === $new_value ) {
return;
}
if ( ! function_exists( 'request_filesystem_credentials' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
global $wp_filesystem;
if ( empty( $wp_filesystem ) ) {
$creds = request_filesystem_credentials( '', '', false, false, null );
if ( ! WP_Filesystem( $creds ) ) {
return;
}
}
/**
* -----------------------------
* BLOCK DIRECTORY BROWSING
* -----------------------------
*/
$htaccess_path = ABSPATH . '.htaccess';
if ( $wp_filesystem->exists( $htaccess_path ) && $wp_filesystem->is_writable( $htaccess_path ) ) {
$htaccess = $wp_filesystem->get_contents( $htaccess_path );
$block = "\n# BEGIN Accessdoor Directory Protection\nOptions -Indexes\n# END Accessdoor Directory Protection\n";
// Enable
if ( ! empty( $new_value['block_directory_browsing'] ) ) {
if ( strpos( $htaccess, 'BEGIN Accessdoor Directory Protection' ) === false ) {
$htaccess .= $block;
}
}
// Disable
else {
$htaccess = preg_replace( '/# BEGIN Accessdoor Directory Protection.*?# END Accessdoor Directory Protection\n?/s', '', $htaccess );
}
$wp_filesystem->put_contents( $htaccess_path, $htaccess, FS_CHMOD_FILE );
}
/**
* -----------------------------
* BLOCK PHP IN UPLOADS
* -----------------------------
*/
$uploads_dir = wp_get_upload_dir();
$uploads_path = trailingslashit( $uploads_dir['basedir'] );
$uploads_htaccess = $uploads_path . '.htaccess';
$php_block = "# BEGIN Accessdoor Upload Protection\n\nDeny from all\n \n# END Accessdoor Upload Protection\n";
$existing = $wp_filesystem->exists( $uploads_htaccess )
? $wp_filesystem->get_contents( $uploads_htaccess )
: '';
// Enable
if ( ! empty( $new_value['forbid_php_uploads'] ) ) {
if ( strpos( $existing, '# BEGIN Accessdoor Upload Protection' ) === false ) {
// Remove any old block first, then append the new one
$cleaned = preg_replace( '/# BEGIN Accessdoor Upload Protection.*?# END Accessdoor Upload Protection\n?/s', '', $existing );
$wp_filesystem->put_contents( $uploads_htaccess, $cleaned . $php_block, FS_CHMOD_FILE );
}
}
// Disable
else {
$cleaned = preg_replace( '/# BEGIN Accessdoor Upload Protection.*?# END Accessdoor Upload Protection\n?/s', '', $existing );
$wp_filesystem->put_contents( $uploads_htaccess, $cleaned, FS_CHMOD_FILE );
}
}
/**
* Apply selected security services based on plugin settings.
*
* @since 1.0.0
*/
public function apply_security_services() {
$settings = get_option( $this->option_name, array() );
// Disable Comments
if ( isset( $settings['disable_comments'] ) && $settings['disable_comments'] ) {
add_filter( 'comments_open', '__return_false', 20, 2 );
add_filter( 'pings_open', '__return_false', 20, 2 );
}
// Turn Off Pingbacks (XML-RPC + for all posts)
if ( isset( $settings['turn_off_pingbacks'] ) && $settings['turn_off_pingbacks'] ) {
// 1. Disable pingback & trackback XML-RPC methods
add_filter( 'xmlrpc_methods', function( $methods ) {
unset( $methods['pingback.ping'] );
unset( $methods['pingback.extensions.getPingbacks'] );
return $methods;
});
// 2. Remove X-Pingback HTTP header
add_filter( 'wp_headers', function( $headers ) {
unset( $headers['X-Pingback'] );
return $headers;
});
// 3. Remove pingback discovery links from
remove_action( 'wp_head', 'rsd_link' );
remove_action( 'wp_head', 'wlwmanifest_link' );
// 4. Disable pingbacks & trackbacks in settings (default behavior)
add_filter( 'pre_option_default_ping_status', '__return_zero' );
add_filter( 'pre_option_default_comment_status', '__return_zero' );
// 5. Disable trackback rewrite rules
add_filter( 'rewrite_rules_array', function( $rules ) {
foreach ( $rules as $rule => $rewrite ) {
if ( strpos( $rewrite, 'trackback' ) !== false ) {
unset( $rules[ $rule ] );
}
}
return $rules;
});
// 6. Force close pings on all existing posts
// add_filter( 'comments_open', function( $open, $post_id ) {
// return false;
// }, 10, 2 );
add_filter( 'pings_open', function() {
return false;
});
}
}
/**
* Add settings page to WordPress admin menu.
*/
public function add_settings_page() {
// Do nothing if multisite, so no settings page is added
if ( is_multisite() ) {
return;
}
add_options_page(
__( 'AccessDoor Smart Admin Login & Security', 'accessdoor-smart-admin-login-url-control' ),
__( 'AccessDoor Smart Admin Login & Security', 'accessdoor-smart-admin-login-url-control' ),
'manage_options',
'accessdoor-smart-admin-login-url-control',
array( $this, 'render_settings_page' )
);
}
/**
* Register plugin settings.
*/
public function register_settings() {
// Register the same option for both tabs with proper sanitization
register_setting(
'ADALC_general_group',
$this->option_name,
array( $this, 'sanitize_settings' )
);
register_setting(
'ADALC_branding_group',
$this->option_name,
array( $this, 'sanitize_settings' )
);
register_setting(
'ADALC_admin_group',
$this->option_name,
array( $this, 'sanitize_settings' )
);
// General Settings Section.
add_settings_section(
'ADALC_general_section',
__( 'Step 1 — Your private login address', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'general_section_callback' ),
'adalc-general-settings'
);
add_settings_field(
'enable_custom_login',
' '. __( 'Use a private login address', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'enable_custom_login_callback' ),
'adalc-general-settings',
'ADALC_general_section'
);
add_settings_field(
'role_login_urls',
' ' . __( 'Login Address for Each User Type', 'accessdoor-smart-admin-login-url-control' ) .
' ' .
'' . __( 'Type a short, easy-to-remember word for each user type. Leave a field empty and those users will simply use the Administrator address.', 'accessdoor-smart-admin-login-url-control' ) . ' ' .
' ',
array( $this, 'role_login_urls_callback' ),
'adalc-general-settings',
'ADALC_general_section'
);
// Redirect Settings Section.
add_settings_section(
'ADALC_redirect_section',
__( 'Step 2 — The old login page', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'redirect_section_callback' ),
'adalc-general-settings'
);
add_settings_field(
'redirect_wp_login',
' '. __( 'Send visitors somewhere else', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'redirect_wp_login_callback' ),
'adalc-general-settings',
'ADALC_redirect_section'
);
add_settings_field(
'redirect_url',
'' . __( 'Where should they land?', 'accessdoor-smart-admin-login-url-control' ) .
' ' .
'' . __( 'The page people are sent to if they try to open the old wp-login.php address. Your homepage is a safe choice.', 'accessdoor-smart-admin-login-url-control' ) . ' ' .
' ',
array( $this, 'redirect_url_callback' ),
'adalc-general-settings',
'ADALC_redirect_section'
);
// Custom Branding Section.
add_settings_section(
'ADALC_branding_section',
__( 'Login screen branding', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'branding_section_callback' ),
'adalc-branding-settings'
);
add_settings_field(
'custom_branding',
'',
array( $this, 'role_branding_callback' ),
'adalc-branding-settings',
'ADALC_branding_section'
);
// Change Admin Info.
add_settings_section(
'ADALC_admin_info_section',
__( 'Step 3 — Your administrator account', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'admin_info_section_callback' ),
'adalc-admin-settings'
);
// Move admin email field to Admin Settings tab (last/third tab)
add_settings_field(
'change_admin_email',
' '. __( 'Website contact email', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'admin_email_field_callback' ),
'adalc-admin-settings',
'ADALC_admin_info_section'
);
// User email and username remain in Admin Settings
add_settings_field(
'change_user_email',
' '. __( 'Your personal login email', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'current_user_email_field_callback' ),
'adalc-admin-settings',
'ADALC_admin_info_section'
);
add_settings_field(
'change_admin_username',
' '. __( 'Your username', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'admin_username_field_callback' ),
'adalc-admin-settings',
'ADALC_admin_info_section'
);
// Security Measures Section (new tab)
register_setting(
'ADALC_security_group',
$this->option_name,
array( $this, 'sanitize_settings' )
);
add_settings_section(
'ADALC_security_section',
__( 'Comments & spam', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'security_section_callback' ),
'adalc-security-settings'
);
add_settings_section(
'ADALC_security_remote_section',
__( 'Remote access', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'security_remote_section_callback' ),
'adalc-security-settings'
);
add_settings_section(
'ADALC_security_files_section',
__( 'Files & server', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'security_files_section_callback' ),
'adalc-security-settings'
);
// Security options fields
add_settings_field(
'disable_comments',
__( 'Turn off comments everywhere', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'disable_comments_callback' ),
'adalc-security-settings',
'ADALC_security_section'
);
// Turn Off Pingbacks
add_settings_field(
'turn_off_pingbacks',
__( 'Turn off pingbacks & trackbacks', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'turn_off_pingbacks_callback' ),
'adalc-security-settings',
'ADALC_security_section'
);
add_settings_field(
'disable_xmlrpc_pingback',
__( 'Block pingback spam attacks', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'disable_xmlrpc_pingback_callback' ),
'adalc-security-settings',
'ADALC_security_remote_section'
);
add_settings_field(
'disable_rest_api',
__( 'Hide site data from strangers', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'disable_rest_api_callback' ),
'adalc-security-settings',
'ADALC_security_remote_section'
);
add_settings_field(
'disable_xmlrpc',
__( 'Block remote publishing (XML-RPC)', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'disable_xmlrpc_callback' ),
'adalc-security-settings',
'ADALC_security_remote_section'
);
// Block Directory Browsing
add_settings_field(
'block_directory_browsing',
__( 'Hide your folder contents', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'block_directory_browsing_callback' ),
'adalc-security-settings',
'ADALC_security_files_section'
);
// Forbid PHP in Uploads
add_settings_field(
'forbid_php_uploads',
__( 'Stop code running from uploads', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'forbid_php_uploads_callback' ),
'adalc-security-settings',
'ADALC_security_files_section'
);
// Disable File Editing
add_settings_field(
'disable_file_editing',
__( 'Lock theme & plugin file editor', 'accessdoor-smart-admin-login-url-control' ),
array( $this, 'disable_file_editing_callback' ),
'adalc-security-settings',
'ADALC_security_files_section'
);
}
/**
* Get all roles that have at least one user.
*
* @return array Array of role slugs and names with user counts.
*/
public function get_roles_with_users() {
$roles_with_users = array();
$user_counts = count_users();
$all_roles = wp_roles()->get_names();
foreach ( $user_counts['avail_roles'] as $role_slug => $count ) {
if ( $count > 0 && isset( $all_roles[ $role_slug ] ) ) {
$roles_with_users[ $role_slug ] = array(
'name' => $all_roles[ $role_slug ],
'count' => $count,
);
}
}
return $roles_with_users;
}
/**
* Role-based login URLs callback.
*/
public function role_login_urls_callback() {
$settings = get_option( $this->option_name, array() );
$roles = $this->get_roles_with_users();
if ( empty( $roles ) ) {
echo '' . esc_html__( 'No user types with people assigned were found.', 'accessdoor-smart-admin-login-url-control' ) . '
';
return;
}
echo '';
echo '
';
echo '';
echo '';
echo '' . esc_html__( 'User role', 'accessdoor-smart-admin-login-url-control' ) . ' ';
echo '' . esc_html__( 'Secret word', 'accessdoor-smart-admin-login-url-control' ) . ' ';
echo '' . esc_html__( 'Login link', 'accessdoor-smart-admin-login-url-control' ) . ' ';
echo '' . esc_html__( 'Users', 'accessdoor-smart-admin-login-url-control' ) . ' ';
echo ' ';
echo ' ';
echo '';
foreach ( $roles as $role_slug => $role_data ) {
$slug_value = isset( $settings['role_slugs'][ $role_slug ] ) ? $settings['role_slugs'][ $role_slug ] : '';
$login_url = $slug_value ? home_url( $slug_value ) : __( 'Not set — uses the Administrator address', 'accessdoor-smart-admin-login-url-control' );
if($role_data['name'] == 'Administrator'){
$login_url = $slug_value ? home_url( $slug_value ) : __( 'Not set', 'accessdoor-smart-admin-login-url-control' );
}
echo '';
echo '' . esc_html( $role_data['name'] ) . ' ';
echo '';
printf(
' ',
esc_attr( $this->option_name ),
esc_attr( $role_slug ),
esc_attr( $slug_value ),
esc_attr( strtolower( str_replace( ' ', '-', $role_data['name'] ) ) . '-login' )
);
echo ' ';
$url_to_copy = is_string( $login_url ) ? $login_url : home_url( $slug_value );
echo '';
echo '';
echo '
' . esc_html( $url_to_copy ) . '';
echo '
';
echo '';
echo ' ';
echo '' . esc_html__( 'Copy', 'accessdoor-smart-admin-login-url-control' ) . ' ';
echo ' ';
echo '
';
echo '
';
echo ' ';
echo '' . esc_html( $role_data['count'] ) . ' ';
echo ' ';
}
echo ' ';
echo '
';
echo '
' . esc_html__( 'For safety, these words cannot be used as a login address:', 'accessdoor-smart-admin-login-url-control' ) . '
wp-admin, wp-login, wp-login.php, login, admin, logout, lostpassword, resetpassword, register, wp-json, xmlrpc, xmlrpc.php, my-account, cart, checkout, shop, account, profile, signin, signup, etc.
';
echo '
';
}
/**
* Sanitize settings before saving.
*
* @param array $input Settings input.
* @return array Sanitized settings.
*/
public function sanitize_settings( $input ) {
// Get existing settings to merge with new input
$existing = get_option( $this->option_name, array() );
$sanitized = is_array( $existing ) ? $existing : array();
// Determine which tab is being submitted
$is_general_tab = ! empty( $input['general_tab_submitted'] )
|| isset( $input['role_slugs'] )
|| isset( $input['enable_custom_login'] )
|| isset( $input['redirect_wp_login'] );
$is_security_tab = ! empty( $input['security_tab_submitted'] );
if ( $is_general_tab ) {
$sanitized['enable_custom_login'] = ! empty( $input['enable_custom_login'] ) ? 1 : 0;
$sanitized['redirect_wp_login'] = ! empty( $input['redirect_wp_login'] ) ? 1 : 0;
if ( isset( $input['redirect_url'] ) ) {
$sanitized['redirect_url'] = esc_url_raw( $input['redirect_url'] );
}
// Sanitize role-based slugs and block restricted/existing slugs.
if ( isset( $input['role_slugs'] ) && is_array( $input['role_slugs'] ) ) {
$sanitized['role_slugs'] = array();
$restricted_slugs = array(
'wp-admin', 'wp-login', 'wp-login.php', 'login', 'admin',
'logout', 'lostpassword', 'resetpassword', 'register',
'wp-json', 'xmlrpc', 'xmlrpc.php',
'my-account', 'cart', 'checkout', 'shop',
'account', 'profile', 'signin', 'signup',
);
foreach ( $input['role_slugs'] as $role => $slug ) {
if ( empty( $slug ) ) {
continue;
}
$slug = sanitize_title( $slug );
// Block restricted slugs
if ( in_array( $slug, $restricted_slugs, true ) ) {
add_settings_error(
$this->option_name,
'restricted_slug_' . $role,
/* translators: %s: The slug entered by the user */
sprintf( __( 'The slug "%s" is not allowed for security reasons.', 'accessdoor-smart-admin-login-url-control' ), esc_html( $slug ) ),
'error'
);
continue;
}
// Block if slug matches an existing page
if ( get_page_by_path( $slug ) ) {
add_settings_error(
$this->option_name,
'existing_page_slug_' . $role,
/* translators: %s: The slug entered by the user */
sprintf( __( 'The slug "%s" is already used by a page. Please choose another.', 'accessdoor-smart-admin-login-url-control' ), esc_html( $slug ) ),
'error'
);
continue;
}
$sanitized['role_slugs'][ $role ] = $slug;
}
}
} else {
if ( ! isset( $sanitized['enable_custom_login'] ) ) {
$sanitized['enable_custom_login'] = isset( $existing['enable_custom_login'] ) ? (int) $existing['enable_custom_login'] : 0;
}
if ( ! isset( $sanitized['redirect_wp_login'] ) ) {
$sanitized['redirect_wp_login'] = isset( $existing['redirect_wp_login'] ) ? (int) $existing['redirect_wp_login'] : 0;
}
}
// Sanitize role logo IDs.
if ( isset( $input['role_logo'] ) && is_array( $input['role_logo'] ) ) {
if ( ! isset( $sanitized['role_logo'] ) ) {
$sanitized['role_logo'] = array();
}
foreach ( $input['role_logo'] as $role => $attachment_id ) {
if ( ! empty( $attachment_id ) ) {
$sanitized['role_logo'][ $role ] = absint( $attachment_id );
} else {
// Remove if empty
unset( $sanitized['role_logo'][ $role ] );
}
}
}
// Save Security tab options, but validate .htaccess changes for relevant features
$security_fields = array(
'disable_comments',
'disable_xmlrpc_pingback',
'disable_rest_api',
'disable_xmlrpc',
'block_directory_browsing',
'forbid_php_uploads',
'turn_off_pingbacks',
'disable_file_editing',
);
if ( $is_security_tab ) {
foreach ( $security_fields as $field ) {
$sanitized[ $field ] = ! empty( $input[ $field ] ) ? 1 : 0;
}
} else {
// Preserve existing security options when saving other tabs
foreach ( $security_fields as $field ) {
if ( ! isset( $sanitized[ $field ] ) ) {
$sanitized[ $field ] = isset( $existing[ $field ] ) ? (int) $existing[ $field ] : 0;
}
}
}
unset(
$sanitized['general_tab_submitted'],
$sanitized['security_tab_submitted'],
$sanitized['branding_tab_submitted']
);
// Sanitize role background image IDs.
if ( isset( $input['role_bg_image'] ) && is_array( $input['role_bg_image'] ) ) {
if ( ! isset( $sanitized['role_bg_image'] ) ) {
$sanitized['role_bg_image'] = array();
}
foreach ( $input['role_bg_image'] as $role => $attachment_id ) {
if ( ! empty( $attachment_id ) ) {
$sanitized['role_bg_image'][ $role ] = absint( $attachment_id );
} else {
// Remove if empty
unset( $sanitized['role_bg_image'][ $role ] );
}
}
}
// Sanitize role background colors.
if ( isset( $input['role_bg_color'] ) && is_array( $input['role_bg_color'] ) ) {
if ( ! isset( $sanitized['role_bg_color'] ) ) {
$sanitized['role_bg_color'] = array();
}
foreach ( $input['role_bg_color'] as $role => $color ) {
if ( ! empty( $color ) ) {
$sanitized['role_bg_color'][ $role ] = sanitize_hex_color( $color );
} else {
// Remove if empty
unset( $sanitized['role_bg_color'][ $role ] );
}
}
}
if (
isset( $input['change_admin_email'] ) ||
isset( $input['change_user_email'] ) ||
isset( $input['change_admin_username'] )
) {
$current_user = wp_get_current_user();
$user_id = $current_user->ID;
$is_admin = user_can( $current_user, 'manage_options' );
if ( empty( $user_id ) || ! $is_admin ) {
return $sanitized;
}
// Track which fields were actually changed
$changed_fields = array();
/**
* ---------------------------------------
* 1. Update ADMIN EMAIL (Settings → General)
* No confirmation email
* ---------------------------------------
*/
if ( isset( $input['change_admin_email'] ) ) {
$new_admin_email = sanitize_email( $input['change_admin_email'] );
$current_admin_email = get_option( 'admin_email' );
if($new_admin_email != $current_admin_email){
if ( ! is_email( $new_admin_email ) ) {
add_settings_error(
$this->option_name,
'invalid_admin_email',
__( 'Please enter a valid admin email address.', 'accessdoor-smart-admin-login-url-control' ),
'error'
);
}else {
// Force update without confirmation, allow any email (even if used by another user)
update_option( 'admin_email', $new_admin_email );
delete_option( 'new_admin_email' );
remove_action( 'update_option_admin_email', 'wp_send_new_admin_email', 10 );
add_settings_error(
$this->option_name,
'admin_email_updated',
__( 'Admin email updated successfully.', 'accessdoor-smart-admin-login-url-control' ),
'updated'
);
}
$sanitized['change_admin_email'] = $new_admin_email;
}
}
/**
* ---------------------------------------
* 2. Update ADMIN USER EMAIL
* ---------------------------------------
*/
if ( isset( $input['change_user_email'] ) ) {
$new_user_email = sanitize_email( $input['change_user_email'] );
// Only process if username is changed
if ( $new_user_email !== $current_user->user_email ) {
if ( ! is_email( $new_user_email ) ) {
add_settings_error(
$this->option_name,
'invalid_user_email',
__( 'Please enter a valid user email address.', 'accessdoor-smart-admin-login-url-control' ),
'error'
);
$sanitized['change_user_email'] = $current_user->user_email;
} elseif ( email_exists( $new_user_email ) && $new_user_email !== $current_user->user_email ) {
add_settings_error(
$this->option_name,
'user_email_exists',
__( 'This email is already in use by another user.', 'accessdoor-smart-admin-login-url-control' ),
'error'
);
$sanitized['change_user_email'] = $current_user->user_email;
}else {
$result = wp_update_user( array(
'ID' => $user_id,
'user_email' => $new_user_email,
) );
if ( is_wp_error( $result ) ) {
add_settings_error(
$this->option_name,
'user_email_update_failed',
__( 'Failed to update user email: ', 'accessdoor-smart-admin-login-url-control' ) . $result->get_error_message(),
'error'
);
$sanitized['change_user_email'] = $current_user->user_email;
} else {
add_settings_error(
$this->option_name,
'user_email_updated',
__( 'User email updated successfully.', 'accessdoor-smart-admin-login-url-control' ),
'updated'
);
$sanitized['change_user_email'] = $new_user_email;
}
}
}
}
/**
* ---------------------------------------
* 3. Update ADMIN USERNAME (Advanced)
* ---------------------------------------
*/
if ( isset( $input['change_admin_username'] ) ) {
$new_username = sanitize_user( $input['change_admin_username'], true );
// Only process if username is changed
if ( $new_username !== $current_user->user_login ) {
// Block for super admins in multisite
if ( is_multisite() && is_super_admin( $user_id ) ) {
add_settings_error(
$this->option_name,
'superadmin_username_change_blocked',
__( 'Changing the username for a network (super) admin is not allowed for security reasons.', 'accessdoor-smart-admin-login-url-control' ),
'error'
);
$sanitized['change_admin_username'] = $current_user->user_login;
} elseif ( empty( $new_username ) ) {
add_settings_error(
$this->option_name,
'invalid_username',
__( 'Username cannot be empty.', 'accessdoor-smart-admin-login-url-control' ),
'error'
);
$sanitized['change_admin_username'] = $current_user->user_login;
} elseif ( username_exists( $new_username ) ) {
add_settings_error(
$this->option_name,
'username_exists',
__( 'This username already exists.', 'accessdoor-smart-admin-login-url-control' ),
'error'
);
$sanitized['change_admin_username'] = $current_user->user_login;
} else {
global $wpdb;
// phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
$updated = $wpdb->update(
$wpdb->users,
array( 'user_login' => $new_username ),
array( 'ID' => $user_id ),
array( '%s' ),
array( '%d' )
);
clean_user_cache( $user_id );
if ( false === $updated ) {
add_settings_error(
$this->option_name,
'username_update_failed',
__( 'Failed to update username.', 'accessdoor-smart-admin-login-url-control' ),
'error'
);
$sanitized['change_admin_username'] = $current_user->user_login;
} else {
// Refresh login session
wp_clear_auth_cookie();
wp_set_current_user( $user_id );
wp_set_auth_cookie( $user_id );
add_settings_error(
$this->option_name,
'username_updated',
__( 'Username updated successfully.', 'accessdoor-smart-admin-login-url-control' ),
'updated'
);
$sanitized['change_admin_username'] = $new_username;
}
}
}
}
}
// Improved: Only set transient if values actually changed (robust for unset/empty cases)
$settings_changed = false;
// Compare role_slugs
$old_slugs = isset( $existing['role_slugs'] ) ? $existing['role_slugs'] : array();
$new_slugs = isset( $sanitized['role_slugs'] ) ? $sanitized['role_slugs'] : array();
if ( $old_slugs !== $new_slugs ) {
$settings_changed = true;
}
// Compare enable_custom_login
$old_enable = isset( $existing['enable_custom_login'] ) ? (int)$existing['enable_custom_login'] : 0;
$new_enable = isset( $sanitized['enable_custom_login'] ) ? (int)$sanitized['enable_custom_login'] : 0;
if ( $old_enable !== $new_enable ) {
$settings_changed = true;
}
if ( $settings_changed ) {
set_transient( 'ADALC_permalinks_flush_needed', true, 60 );
// REMOVED: set_transient( 'ADALC_permalinks_flushed', true, 30 ); // Removed to prevent duplicate success message on settings save
}
return $sanitized;
}
/**
* REMOVED: display_admin_notices()
* Removed method display_admin_notices() to prevent duplicate success notice.
* WordPress core automatically handles settings saved notices via settings_errors().
*/
/**
* General section callback.
*/
public function general_section_callback() {
echo '' . esc_html__( 'Give your login page a private address that only your team knows.', 'accessdoor-smart-admin-login-url-control' ) . '
';
}
/**
* Redirect settings section callback.
*/
public function redirect_section_callback() {
echo '' . esc_html__( 'Choose what visitors see if they open the old wp-login.php address.', 'accessdoor-smart-admin-login-url-control' ) . '
';
}
/**
* Branding settings section callback.
*/
public function branding_section_callback() {
echo '' . esc_html__( 'Add a logo, background or colour. Changes show instantly in the live preview.', 'accessdoor-smart-admin-login-url-control' ) . '
';
}
/**
* Admin Information settings section callback.
*/
public function admin_info_section_callback() {
echo '' . esc_html__( 'Update your admin emails and username. Changing the username signs you out.', 'accessdoor-smart-admin-login-url-control' ) . '
';
}
/**
* Admin email change callback.
*/
public function admin_email_field_callback() {
$current_admin_email = get_option('admin_email');
?>
user_email : '';
?>
user_login : '';
?>
option_name, array() );
$value = isset( $settings['enable_custom_login'] ) ? $settings['enable_custom_login'] : 0;
?>
/>
option_name, array() );
$value = isset( $settings['custom_login_slug'] ) ? $settings['custom_login_slug'] : 'custom-login';
?>
' . esc_html( home_url( $value ) ) . ''
);
?>
option_name, array() );
$value = isset( $settings['redirect_wp_login'] ) ? $settings['redirect_wp_login'] : 0;
?>
/>
option_name, array() );
$value = isset( $settings['redirect_url'] ) ? $settings['redirect_url'] : home_url();
?>
option_name, array() );
$roles = $this->get_roles_with_users();
if ( empty( $roles ) ) {
echo '' . esc_html__( 'No user types with people assigned were found.', 'accessdoor-smart-admin-login-url-control' ) . '
';
return;
}
$first_role = true;
?>
$role_data ) :
$is_administrator = false;
$logo_id = isset( $settings['role_logo'][ $role_slug ] ) ? $settings['role_logo'][ $role_slug ] : '';
$bg_image_id = isset( $settings['role_bg_image'][ $role_slug ] ) ? $settings['role_bg_image'][ $role_slug ] : '';
$bg_color = isset( $settings['role_bg_color'][ $role_slug ] ) ? $settings['role_bg_color'][ $role_slug ] : '#f0f0f1';
$logo_url = $logo_id ? wp_get_attachment_url( $logo_id ) : '';
$bg_image_url = $bg_image_id ? wp_get_attachment_url( $bg_image_id ) : '';
if($role_data['name'] == 'Administrator'){
$is_administrator = true;
}
?>
roles, true ) ) {
// wp_die( esc_html__( 'You do not have permission to access this page.', 'accessdoor-smart-admin-login-url-control' ) );
// }
// }
// phpcs:ignore WordPress.Security.NonceVerification.Recommended
$active_tab = isset( $_GET['tab'] ) ? sanitize_key( wp_unslash( $_GET['tab'] ) ) : 'general';
// Login Address and Admin Account are now presented inside the General Settings tab.
if ( 'admin_settings' === $active_tab ) {
$active_tab = 'general';
}
if ( ! in_array( $active_tab, array( 'general', 'advanced', 'security' ), true ) ) {
$active_tab = 'general';
}
$current_user = wp_get_current_user();
$username = $current_user ? $current_user->user_login : '';
$tab_intros = array(
'general' => array(
'icon' => 'dashicons-admin-settings',
'title' => __( 'General Settings', 'accessdoor-smart-admin-login-url-control' ),
'text' => __( 'Set your private login address and manage your administrator account. Save the new address somewhere safe before you log out.', 'accessdoor-smart-admin-login-url-control' ),
),
'advanced' => array(
'icon' => 'dashicons-art',
'title' => __( 'Login Page Design', 'accessdoor-smart-admin-login-url-control' ),
'text' => __( 'Add your logo, background or colour to the login screen. Each user type can have its own look.', 'accessdoor-smart-admin-login-url-control' ),
),
'security' => array(
'icon' => 'dashicons-shield-alt',
'title' => __( 'Security', 'accessdoor-smart-admin-login-url-control' ),
'text' => __( 'Simple on/off switches that close the doors attackers use most. Turn on anything your site does not need.', 'accessdoor-smart-admin-login-url-control' ),
),
);
$intro = isset( $tab_intros[ $active_tab ] ) ? $tab_intros[ $active_tab ] : $tab_intros['general'];
// Presentation only: the General tab shows the same guidance as a 3-step layout.
$general_steps = array(
array(
'icon' => 'dashicons-admin-network',
'title' => __( 'Set your private login address', 'accessdoor-smart-admin-login-url-control' ),
'text' => __( 'Turn on the private login address, then type a secret word for each user type. Your login page moves to that address and the copy button gives you the full link.', 'accessdoor-smart-admin-login-url-control' ),
'hint' => __( 'You do this in Step 1 below.', 'accessdoor-smart-admin-login-url-control' ),
),
array(
'icon' => 'dashicons-randomize',
'title' => __( 'Decide what happens to the old page', 'accessdoor-smart-admin-login-url-control' ),
'text' => __( 'Choose whether anyone opening the old wp-login.php address is sent somewhere else, and pick the page they land on — your homepage is a safe choice.', 'accessdoor-smart-admin-login-url-control' ),
'hint' => __( 'You do this in Step 2 below.', 'accessdoor-smart-admin-login-url-control' ),
),
array(
'icon' => 'dashicons-admin-users',
'title' => __( 'Review your administrator account', 'accessdoor-smart-admin-login-url-control' ),
'text' => __( 'Check the website contact email, your personal login email and your username. Save your new login link somewhere safe before you sign out.', 'accessdoor-smart-admin-login-url-control' ),
'hint' => __( 'You do this in Step 3 below.', 'accessdoor-smart-admin-login-url-control' ),
),
);
?>
' . esc_html__( 'Turn comments off if your site does not need them.', 'accessdoor-smart-admin-login-url-control' ) . '';
}
/**
* Remote access security section callback.
*/
public function security_remote_section_callback() {
echo '' . esc_html__( 'Limit how outside apps and bots can reach your site. Leave an option on only if a service you use needs it.', 'accessdoor-smart-admin-login-url-control' ) . '
';
}
/**
* Files & server security section callback.
*/
public function security_files_section_callback() {
echo '' . esc_html__( 'Extra protection for server files. Some options need Apache and a writable .htaccess file.', 'accessdoor-smart-admin-login-url-control' ) . '
';
}
public function disable_comments_callback() {
$settings = get_option( $this->option_name, array() );
$status = '';
$value = !empty($settings) && isset($settings['disable_comments']) ? (int)$settings['disable_comments'] : 0;
if ( has_filter( 'comments_open', '__return_false' ) && ! $value ) {
$status = 'disabled';
}
?>
option_name, array() );
$status = '';
$value = !empty($settings) && isset($settings['disable_xmlrpc_pingback']) ? (int)$settings['disable_xmlrpc_pingback'] : 0;
if ( has_filter( 'xmlrpc_methods', '__return_false' ) && ! $value ) {
$status = 'disabled';
}
?>
option_name, array() );
$status = '';
$value = !empty($settings) && isset($settings['disable_rest_api']) ? (int)$settings['disable_rest_api'] : 0;
if ( has_filter( 'rest_authentication_errors', '__return_false' ) && ! $value ) {
$status = 'disabled';
}
?>
option_name, array() );
$status = '';
$value = !empty($settings) && isset($settings['disable_xmlrpc']) ? (int)$settings['disable_xmlrpc'] : 0;
if ( has_filter( 'xmlrpc_enabled', '__return_false' ) && ! $value ) {
$status = 'disabled';
}
?>
option_name, array() );
$value = !empty($settings) && isset($settings['block_directory_browsing']) ? (int)$settings['block_directory_browsing'] : 0;
$htaccess_path = ABSPATH . '.htaccess';
$htaccess_error = false;
if ( ! function_exists( 'request_filesystem_credentials' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
global $wp_filesystem;
if ( empty( $wp_filesystem ) ) {
$creds = request_filesystem_credentials( '', '', false, false, null );
if ( ! WP_Filesystem( $creds ) ) {
$wp_filesystem = null;
}
}
if ( ! $wp_filesystem || ! $wp_filesystem->exists( $htaccess_path ) || ! $wp_filesystem->is_writable( $htaccess_path ) ) {
$htaccess_error = true;
$value = 0; // Prevent enabling the feature
}
?>
/>
option_name, array() );
$value = !empty($settings) && isset($settings['forbid_php_uploads']) ? (int)$settings['forbid_php_uploads'] : 0;
$uploads_dir = wp_get_upload_dir();
$uploads_path = trailingslashit( $uploads_dir['basedir'] );
$uploads_htaccess = $uploads_path . '.htaccess';
$uploads_error = false;
if ( ! function_exists( 'request_filesystem_credentials' ) ) {
require_once ABSPATH . 'wp-admin/includes/file.php';
}
global $wp_filesystem;
if ( empty( $wp_filesystem ) ) {
$creds = request_filesystem_credentials( '', '', false, false, null );
if ( ! WP_Filesystem( $creds ) ) {
$wp_filesystem = null;
}
}
if ( ! $wp_filesystem || ! $wp_filesystem->exists( $uploads_htaccess ) || ! $wp_filesystem->is_writable( $uploads_htaccess ) ) {
$uploads_error = true;
$value = 0; // Prevent enabling the feature
}
?>
/>
option_name, array() );
$status = '';
$value = !empty($settings) && isset($settings['turn_off_pingbacks']) ? (int)$settings['turn_off_pingbacks'] : 0;
// Check if pingbacks are already disabled by another plugin or code
$pingback_disabled = has_filter( 'xmlrpc_methods', '__return_false' );
if ( $pingback_disabled && ! $value ) {
$status = 'disabled';
}
?>
/>
option_name, array() );
$status = '';
$value = !empty($settings) && isset($settings['disable_file_editing']) ? (int)$settings['disable_file_editing'] : 0;
// Check if DISALLOW_FILE_EDIT is already defined and true
if ( defined( 'DISALLOW_FILE_EDIT' ) && DISALLOW_FILE_EDIT && ! $value ) {
$status = 'disabled';
}
?>
/>
' . __( 'Settings', 'accessdoor-smart-admin-login-url-control' ) . '';
array_unshift( $links, $settings_link );
return $links;
}
public function change_admin_email( $new_email ) {
// 1. Validate email
if ( ! is_email( $new_email ) ) {
return new WP_Error( 'invalid_email', 'Invalid email address.' );
}
update_option('admin_email', sanitize_email( $new_email ));
update_option('new_admin_email', sanitize_email($new_email));
// Remove WordPress pending confirmation
delete_option( 'new_admin_email' );
delete_option( 'adminhash' );
return true;
}
}