true, 'core.settings.ui.error_notifications' => true, 'core.settings.multi_access_levels' => false, 'core.settings.ui.render_access_metabox' => false, 'core.settings.xmlrpc_enabled' => true, 'core.settings.restful_enabled' => true, 'core.settings.multisite.members_only' => false, 'core.settings.multisite.sync' => false, 'core.settings.merge.preference' => 'deny', 'core.settings.multisite.ignore_resources' => [ AAM_Framework_Type_Resource::POST, AAM_Framework_Type_Resource::TERM ] ]; /** * Constructor * * @access protected * @return void * * @version 7.0.10 */ protected function __construct() { add_filter('aam_get_config_filter', function($result, $key) { if (empty($result) && array_key_exists($key, self::DEFAULT_CONFIG)) { $result = self::DEFAULT_CONFIG[$key]; } return $result; }, 10, 2); // Hook into AAM config initialization and enrich it with ConfigPress // settings add_filter('aam_init_config_filter', function($configs) { return $this->_aam_initialize_config($configs); }); // Control user's status add_action('set_current_user', function() { $this->_control_user_account(); }); // Bootstrap RESTful API AAM_Restful_Mu::bootstrap(); AAM_Restful_Roles::bootstrap(); AAM_Restful_Users::bootstrap(); AAM_Restful_Configs::bootstrap(); AAM_Restful_Settings::bootstrap(); AAM_Restful_BackwardCompatibility::bootstrap(); add_action('init', function() { $this->initialize_hooks(); }, PHP_INT_MAX); if (is_multisite()) { // Control access controls & configurations sync $this->_control_multisite_settings_sync(); } } /** * Initialize service hooks * * @return void * @access protected * * @version 7.0.4 */ protected function initialize_hooks() { // Ensuring safely of transactions add_filter('gettext_advanced-access-manager', function($translation) { return esc_html($translation); }); add_action('aam_initialize_ui_action', function () { AAM_Backend_Feature_Settings_Service::register(); AAM_Backend_Feature_Settings_Core::register(); AAM_Backend_Feature_Settings_Content::register(); }, 1); if (is_multisite()) { add_action('aam_initialize_ui_action', function () { AAM_Backend_Feature_Settings_Multisite::register(); }); } if (is_admin()) { $metabox_enabled = AAM::api()->config->get( 'core.settings.ui.render_access_metabox' ); if ($metabox_enabled) { add_action('edit_user_profile', function($user) { if (current_user_can('aam_manager') && current_user_can('aam_manage_users') && current_user_can('edit_user', $user->ID)) { $this->_render_access_widget($user); } }); add_action('admin_enqueue_scripts', function($hook) { if ($hook === 'user-edit.php' && current_user_can('aam_manager') && current_user_can('aam_manage_users')) { $path = AAM_BASEDIR . '/media/css/user-access-link.css'; wp_enqueue_style( 'aam-user-access-link', plugins_url('media/css/user-access-link.css', AAM_BASEDIR . '/aam.php'), [], filemtime($path) ?: AAM_VERSION ); } }); } } // Allow third-party plugins to use AAM user IP detection add_filter('aam_get_user_ip_address_filter', function() { $whip = new Whip(); return $whip->getValidIpAddress(); }); // Handle access denied add_action('aam_deny_access_action', function() { AAM::api()->redirect->do_access_denied_redirect(); }); // Add toolbar "Manage Access" item add_action('admin_bar_menu', function($wp_admin_bar) { if (current_user_can('aam_manager')) { $wp_admin_bar->add_menu( array( 'parent' => 'site-name', 'id' => 'aam', 'title' => __('Manager Access', 'advanced-access-manager'), 'href' => admin_url('admin.php?page=aam'), ) ); } }, 999); // Add "Manage Access" to all sites if multisite network if (is_multisite()) { add_action('admin_bar_menu', function($wp_admin_bar) { $blog_count = 0; if (is_object($wp_admin_bar->user)) { if (is_iterable($wp_admin_bar->user->blogs)) { $blog_count = count($wp_admin_bar->user->blogs); } } if ($blog_count > 0 || current_user_can('manage_network')) { foreach((array) $wp_admin_bar->user->blogs as $blog) { switch_to_blog($blog->userblog_id); $menu_id = 'blog-' . $blog->userblog_id; if (current_user_can('aam_manager')) { $wp_admin_bar->add_menu( array( 'parent' => $menu_id, 'id' => $menu_id . '-aam', 'title' => __('Manage Access', 'advanced-access-manager'), 'href' => admin_url('admin.php?page=aam'), ) ); } restore_current_blog(); } } }, 999); add_action('wp', function() { $this->_control_multisite_members_only(); }, 999); // Control access controls & configurations sync $this->_control_multisite_settings_sync(); } // Check if user has ability to perform certain task based on provided // capability and meta data add_filter('map_meta_cap', function($caps, $_, $user_id) { return $this->_map_meta_cap($caps, $user_id); }, 999, 3); // User authentication control add_filter('wp_authenticate_user', function($result) { return $this->_authenticate_user($result); }, 1); // Disable XML-RPC if needed add_filter('xmlrpc_enabled', function($enabled) { if (AAM::api()->config->get( 'core.settings.xmlrpc_enabled') === false ) { $enabled = false; } return $enabled; }, PHP_INT_MAX); // Disable RESTful API if needed add_filter('rest_authentication_errors', function ($response) { if (!current_user_can('aam_manager') && !is_wp_error($response) && !AAM::api()->config->get('core.settings.restful_enabled') ) { $response = new WP_Error( 'rest_access_disabled', __('RESTful API is disabled', 'advanced-access-manager'), array('status' => 403) ); } return $response; }, PHP_INT_MAX); // Control admin notifications add_action( 'admin_notices', function() { $this->_control_admin_notices(); }, -1 ); add_action( 'network_admin_notices', function() { $this->_control_admin_notices(); }, -1 ); add_action( 'user_admin_notices', function() { $this->_control_admin_notices(); }, -1 ); // Screen options & contextual help hooks add_filter('screen_options_show_screen', function() { return $this->_control_screen_options(); }); add_action('in_admin_header', function() { $this->_control_help_tabs(); }); // Permalink manager add_filter('get_sample_permalink_html', function ($html) { return $this->_control_permalink_html($html); }); // Control access to the backend area $this->_control_admin_area_access(); $this->_control_admin_toolbar(); // Run upgrades if available AAM_Core_Migration::run(); } /** * Export all AAM settings & configurations * * @return array * @access public * * @version 7.0.0 */ public function export() { $db = AAM::api()->db; $result = [ 'version' => AAM_VERSION, 'plugin' => 'advanced-access-manager', 'timestamp' => (new DateTime('now', new DateTimeZone('UTC')))->format('U'), 'dataset' => [ 'settings' => $db->read(AAM_Framework_Service_Settings::DB_OPTION, []), 'configs' => $db->read(AAM_Framework_Utility_Config::DB_OPTION, []), 'configpress' => $db->read(self::DB_OPTION, ''), 'roles' => $db->read(wp_roles()->role_key, []), 'policies' => [] ] ]; // Get list of all JSON policies $policies = get_posts([ 'post_status' => [ 'publish', 'draft', 'pending', 'private' ], 'nopaging' => true, 'suppress_filters' => true, 'post_type' => AAM_Framework_Service_Policies::CPT ]); foreach($policies as $policy) { array_push($result['dataset']['policies'], [ 'id' => $policy->ID, 'slug' => $policy->post_name, 'content' => $policy->post_content, 'status' => $policy->post_status, 'title' => $policy->post_title, 'excerpt' => $policy->post_excerpt ]); } return $result; } /** * Import settings * * @param array $dataset * * @return bool * @access public * * @version 7.0.6 */ public function import($dataset) { // Import data in very specific order $policy_map = []; if (array_key_exists('policies', $dataset)) { foreach($dataset['policies'] as $policy) { $insert = true; $existing = null; if (!empty($policy['slug'])) { $existing = AAM::api()->misc->get_post_by_slug( $policy['slug'], AAM_Framework_Service_Policies::CPT ); $insert = is_a($existing, WP_Post::class) ? false : true; } if ($insert) { $policy_map[$policy['id']] = wp_insert_post([ 'post_content' => $policy['content'], 'post_type' => AAM_Framework_Service_Policies::CPT, 'post_status' => $policy['status'], 'post_title' => $policy['title'], 'post_excerpt' => $policy['excerpt'] ]); } elseif (is_a($existing, WP_Post::class)) { $policy_map[$policy['id']] = $existing->ID; // Update existing policy with new imported content wp_update_post([ 'ID' => $existing->ID, 'post_content' => $policy['content'], 'post_title' => $policy['title'], 'post_excerpt' => $policy['excerpt'] ]); } } } // Import raw data sets $db = AAM::api()->db; if (array_key_exists('roles', $dataset)) { $db->write(wp_roles()->role_key, $dataset['roles']); } if (array_key_exists('configpress', $dataset)) { $db->write(self::DB_OPTION, $dataset['configpress']); } if (array_key_exists('configs', $dataset)) { $db->write(AAM_Framework_Utility_Config::DB_OPTION, $dataset['configs']); } if (array_key_exists('settings', $dataset)) { // If policy map is not empty, replace all old policy ids with new if (!empty($policy_map)) { $settings = $this->_replace_policy_ids( $dataset['settings'], $policy_map ); } else { $settings = $dataset['settings']; } $db->write(AAM_Framework_Service_Settings::DB_OPTION, $settings); } return true; } /** * Reset AAM * * @return bool * @access public * * @version 7.0.0 */ public function reset() { global $wpdb; // Clear all configurations & settings $options = array( AAM_Framework_Service_Settings::DB_OPTION, AAM_Core_Migration::DB_OPTION, AAM_Framework_Utility_Config::DB_OPTION, AAM_Framework_Utility_Cache::DB_OPTION, AAM_Service_Core::DB_OPTION, AAM_Service_SecureLogin::TRACKER_STARTED_OPTION, AAM_Service_SecurityAudit::DB_OPTION, AAM_Service_SecurityAudit::DB_SCOPE_OPTION ); foreach($options as $option) { AAM::api()->db->delete($option); } // Delete all legacy options $query = "DELETE FROM {$wpdb->options} WHERE (`option_name` LIKE %s) AND "; $query .= "(`option_name` NOT IN ('aam_addons', 'aam_migrations'))"; $wpdb->query($wpdb->prepare($query, 'aam%')); $wpdb->query($wpdb->prepare( "DELETE FROM {$wpdb->postmeta} WHERE `meta_key` LIKE %s", 'aam-%' )); $wpdb->query($wpdb->prepare( "DELETE FROM {$wpdb->usermeta} WHERE `meta_key` LIKE %s", 'aam%' )); $wpdb->query($wpdb->prepare( "DELETE FROM {$wpdb->usermeta} WHERE `meta_key` LIKE %s", $wpdb->get_blog_prefix() . 'aam%' )); // Trigger the action to inform other services to clean-up the options do_action('aam_reset_action', $options); return true; } /** * Replace old policy ids with new * * @param array $settings * @param array $policy_map * * @return array * @access private * * @version 7.0.0 */ private function _replace_policy_ids($settings, $policy_map) { foreach($settings as $key => $data) { if ($key === 'policy' && is_array($data)) { foreach($data as $policy_id => $perm) { if (isset($policy_map[$policy_id])) { $settings[$key][$policy_map[$policy_id]] = $perm; } } } elseif (is_array($data)) { $settings[$key] = $this->_replace_policy_ids($data, $policy_map); } } return $settings; } /** * Manage notifications visibility * * @return void * @access private * * @version 7.0.0 */ private function _control_admin_notices() { if (!$this->_current_user_can('aam_show_admin_notices')) { remove_all_actions('admin_notices'); remove_all_actions('network_admin_notices'); remove_all_actions('user_admin_notices'); } } /** * Control if user has access to the Screen Options * * @return bool * @access private * * @version 7.0.0 */ private function _control_screen_options() { if (is_admin() && isset($_GET['page']) && $_GET['page'] === 'aam') { $result = false; } else { $result = $this->_current_user_can('aam_show_screen_options'); } return $result; } /** * Control Help Tabs * * @return void * @access private * * @version 7.0.0 */ private function _control_help_tabs() { $screen = get_current_screen(); if (!$this->_current_user_can('aam_show_help_tabs') && is_a($screen, WP_Screen::class) ) { get_current_screen()->remove_help_tabs(); } } /** * Control permalink editing ability * * @param string $html * * @return string * @access private * * @version 7.0.0 */ private function _control_permalink_html($html) { if (!$this->_current_user_can('aam_edit_permalink')) { $html = ''; } return $html; } /** * Control access to the admin area * * @return void * @access private * * @version 7.0.1 */ private function _control_admin_area_access() { if (is_user_logged_in() && is_admin()) { // Check if user is allowed to see backend if (!$this->_current_user_can('aam_access_dashboard')) { // If this is the AJAX call, still allow it because it will break // a lot of frontend stuff that depends on it if (!defined('DOING_AJAX')) { AAM::api()->redirect->do_access_denied_redirect(); } } } } /** * Control if user can see admin toolbar * * @return void * @access private * * @version 7.0.0 */ private function _control_admin_toolbar() { if (is_user_logged_in() && !$this->_current_user_can('aam_show_toolbar')) { add_filter('show_admin_bar', '__return_false', PHP_INT_MAX); } } /** * Check if current user has a capability * * This method checks if capability is actually existing first and then check * if user has it assigned * * @param string $cap * * @return bool * @access private * * @version 7.0.0 */ private function _current_user_can($cap) { return !AAM::api()->caps->exists($cap) || current_user_can($cap); } /** * Control if user has access to current site * * @return void * @access private * * @version 7.0.0 */ private function _control_multisite_members_only() { // Check if the non-member access restriction is on if (AAM::api()->config->get('core.settings.multisite.members_only') && !is_user_member_of_blog() ) { AAM::api()->redirect->do_access_denied_redirect(); } } /** * Controlling how access controls & settings are sync * * @return void * @access private * * @version 7.0.3 */ private function _control_multisite_settings_sync() { $enabled = AAM::api()->config->get('core.settings.multisite.sync'); if ($enabled && get_current_blog_id() !== get_main_site_id()) { // Sync roles & capabilities. Note! We only sync roles & caps and do not // bother syncing user caps add_filter('pre_option_' . wp_roles()->role_key, function() { $main_site_roles = new WP_Roles(get_main_site_id()); return $main_site_roles->roles; }); add_filter('aam_init_settings_filter', function($settings) { $base = AAM::api()->db->read( AAM_Framework_Service_Settings::DB_OPTION, [], get_main_site_id() ); $response = []; // Now, let's merge main site settings with settings for a current // blog $levels = array_unique(array_merge( is_array($settings) ? array_keys($settings) : [], is_array($base) ? array_keys($base) : [] )); foreach ($levels as $level) { if (in_array($level, [ AAM_Framework_Type_AccessLevel::DEFAULT, AAM_Framework_Type_AccessLevel::VISITOR ])) { $response[$level] = $this->_merge_settings( isset($settings[$level]) ? $settings[$level] : [], isset($base[$level]) ? $base[$level] : [] ); } else { $response[$level] = $this->_merge_identity_settings( isset($settings[$level]) ? $settings[$level] : [], isset($base[$level]) ? $base[$level] : [] ); } } return $response; }); } } /** * Merge to set of settings into one * * @param array $settings * @param array $base_settings * * @return array * @access private * * @version 7.0.3 */ private function _merge_settings($settings, $base_settings) { $response = []; $ignore = AAM::api()->config->get( 'core.settings.multisite.ignore_resources' ); // Get unique list of resource types $types = array_unique(array_merge( is_array($settings) ? array_keys($settings) : [], is_array($base_settings) ? array_keys($base_settings) : [] )); foreach($types as $type) { if (!in_array($type, $ignore)) { $response[$type] = array_replace( isset($base_settings[$type]) ? $base_settings[$type] : [], isset($settings[$type]) ? $settings[$type] : [] ); } } return $response; } /** * Merge role or user settings * * @param array $settings * @param array $base_settings * * @return array * @access private * * @version 7.0.3 */ private function _merge_identity_settings($settings, $base_settings) { $response = []; // Get unique list of identity ids $ids = array_unique(array_merge( is_array($settings) ? array_keys($settings) : [], is_array($base_settings) ? array_keys($base_settings) : [] )); foreach($ids as $id) { $response[$id] = $this->_merge_settings( isset($settings[$id]) ? $settings[$id] : [], isset($base_settings[$id]) ? $base_settings[$id] : [] ); } return $response; } /** * Initialize configurations by adding ConfigPress settings * * @param array $configs * * @return array * @access private * * @version 7.0.3 */ private function _aam_initialize_config($configs) { $response = $this->_enhance_with_configpress($configs); if (is_multisite() && get_current_blog_id() !== get_main_site_id()) { $base = $this->_enhance_with_configpress(AAM::api()->db->read( AAM_Framework_Utility_Config::DB_OPTION, [], get_main_site_id() ), get_main_site_id()); $response = array_replace($base, $response); } return $response; } /** * Enhance configuration with ConfigPress * * @param array $configs * * @return array * @access private * * @version 7.0.3 */ private function _enhance_with_configpress($configs, $blog_id = null) { $configpress = AAM::api()->db->read(self::DB_OPTION, null, $blog_id); if (!empty($configpress) && is_string($configpress)) { $result = parse_ini_string($configpress, true, INI_SCANNER_TYPED); if ($result !== false) { // Clear error // If we have "aam" key, then AAM ConfigPress is properly formatted // and we take all the values from this section. // Otherwise - assume that user forgot to add the "[aam]" section if (array_key_exists('aam', $result)) { $configs = array_merge($configs, $result['aam']); } else { $configs = array_merge($configs, $result); } } } return $configs; } /** * Render "Access Manager" widget on the user/profile edit screen * * @param WP_User $user * * @return void * @access public * * @version 7.0.0 */ private function _render_access_widget($user) { echo AAM_Backend_View::get_instance()->renderUserMetabox($user); } /** * Check user capability * * This is a hack function that add additional layout on top of WordPress * core functionality. Based on the capability passed in the $args array as * "0" element, it performs additional check on user's capability to manage * post, users etc. * * @param array $caps * @param int $user_id * * @return array * @access public * * @version 7.0.0 */ private function _map_meta_cap($caps, $user_id) { // Mutate any AAM specific capability if it does not exist foreach ((array) $caps as $i => $capability) { if (is_string($capability) && (strpos($capability, 'aam_') === 0)) { if (!AAM::api()->capabilities->exists($capability)) { $caps[$i] = AAM::api()->config->get( 'page.capability', 'administrator' ); } } } return $caps; } /** * Validate user status * * Check if user is locked or not * * @param WP_Error $result * * @return WP_Error|WP_User * @access public * * @version 7.0.0 */ private function _authenticate_user($result) { // Check if user is blocked if (is_a($result, 'WP_User')) { $user = AAM::api()->users->get_user($result); // Step #1. Verify that user is active if (!$user->is_user_active()) { $result = new WP_Error( 'inactive_user', '[ERROR]: User is inactive. Contact the administrator.' ); } // Step #2. Verify that user is not expired if ($user->is_user_access_expired()) { $result = new WP_Error( 'inactive_user', '[ERROR]: User access is expired. Contact the administrator.' ); } } return $result; } /** * Verify user status and act accordingly if user is no longer active or * expired * * @return void * @access public * * @version 7.0.0 */ private function _control_user_account() { $user = AAM::api()->users->get_user(get_current_user_id()); if (!is_null($user)) { // If user status is inactive - immediately logout user if ($user->is_user_active() === false) { wp_logout(); exit; } elseif ($user->is_user_access_expired()) { // Trigger specified action switch ($user->expiration_trigger['type']) { case 'change-role': case 'change_role': $user->set_role($user->expiration_trigger['to_role']); $user->reset('expiration'); break; case 'delete': // For compatibility purposes case 'lock': $user->update(['status' => $user::STATUS_INACTIVE]); $user->reset('expiration'); // And logout immediately case 'logout': wp_logout(); exit; break; default: break; } } } } }