add_menu([ 'id' => 'wp-extra', 'title' => __('WP EXtra', 'wp-extra'), 'href' => admin_url('admin.php?page=wp-extra'), ]); } protected $features = [ 'wp_toolbar', 'admin_site_link', 'wp_adminbar', 'wp_adminbar_auto', 'adminmenu_list', 'menu_chunk_saver', 'adminplugin_list', 'disable_widgets', 'scrolltotop', 'registration_date', 'last_login', 'application_passwords', 'profile_pw', 'profile_email', 'no_backend', 'adminmenu_extra', ]; public function scrolltotop() { if (is_admin()) { add_action('admin_footer', [$this, 'render_scrolltotop']); } } public function render_scrolltotop() { $screen = get_current_screen(); if ($screen && !empty($screen->is_block_editor)) { return; } ?> 'registered'], $columns); } public function last_login() { add_filter('manage_users_columns', [$this, 'last_login_columns']); add_filter('manage_users_custom_column', [$this, 'last_login_custom_column'], 10, 3); add_filter('manage_users_sortable_columns', [$this, 'last_login_sortable_columns']); add_action('wp_login', [$this, 'update_last_login'], 10, 2); } public function last_login_columns($columns) { $columns['last_login'] = __('Last Login', 'wp-extra'); return $columns; } public function last_login_custom_column($row_output, $column_id_attr, $user_id) { if ($column_id_attr === 'last_login') { $last_login = get_user_meta($user_id, 'last_login', true); if (!empty($last_login)) { return sprintf(__('%s ago', 'wp-extra'), human_time_diff(strtotime($last_login), current_time('timestamp'))); } return __('Never', 'wp-extra'); } return $row_output; } public function update_last_login($login, $user = null) { if (!$user) { $user = get_user_by('login', $login); } if ($user) { update_user_meta($user->ID, 'last_login', current_time('mysql')); } } public function last_login_sortable_columns($columns) { return wp_parse_args(['last_login' => 'lastlogin'], $columns); } public function wp_toolbar() { add_action('admin_bar_menu', [$this, 'remove_nodes'], 999); } public function admin_site_link() { add_action('admin_bar_menu', [$this, 'custom_site_name_link'], 99); } public function custom_site_name_link($wp_admin_bar) { if (Helper::get_option('admin_site_link')) { $node = $wp_admin_bar->get_node('site-name'); if ($node) { $node->href = admin_url(); $wp_admin_bar->add_node((array) $node); } } } public function remove_nodes($wp_admin_bar) { $toolbar_nodes = Helper::get_option('wp_toolbar'); if (is_array($toolbar_nodes)) { foreach ($toolbar_nodes as $menu_id) { $wp_admin_bar->remove_node($menu_id); } } } public function wp_adminbar() { add_filter('show_admin_bar', [$this, 'filter_show_admin_bar'], 99); } public function filter_show_admin_bar($show) { if (is_admin()) { return $show; } return false; } public function wp_adminbar_auto() { add_action('admin_enqueue_scripts', [$this, 'adminbar_auto_css']); add_action('wp_enqueue_scripts', [$this, 'adminbar_auto_css']); } public function adminbar_auto_css() { if (!is_admin_bar_showing()) { return; } $css = '@media (min-width: 783px) { html.wp-toolbar { padding-top: 0 !important; } html { margin-top: 0 !important; } body.admin-bar { margin-top: 0 !important; } #wpadminbar { top: 0 !important; transform: translateY(-100%); opacity: 0; pointer-events: none; transition: transform 0.25s cubic-bezier(0.4, 0, 0.2, 1), opacity 0.25s cubic-bezier(0.4, 0, 0.2, 1); z-index: 999999 !important; } #wpadminbar::after { content: ""; position: absolute; top: 100%; left: 0; right: 0; height: 12px; pointer-events: auto; } #wpadminbar:hover, #wpadminbar:focus-within { transform: translateY(0); opacity: 1; pointer-events: auto; } }'; wp_add_inline_style('admin-bar', Helper::minifyCSS($css)); } public function adminmenu_list() { add_action('admin_menu', [$this, 'admin_menu_roles'], 9999); } public function admin_menu_roles() { if (!is_admin()) { return; } $admin_menus = (array) Helper::get_option('adminmenu_list', []); if (isset($_GET['page']) && $_GET['page'] === 'wp-extra') { return; } foreach ($admin_menus as $menu_page) { remove_menu_page($menu_page); } } public function adminplugin_list() { add_filter('all_plugins', [$this, 'filter_hidden_plugins']); } public function filter_hidden_plugins($plugins) { $hide_plugins = (array) Helper::get_option('adminplugin_list', []); if (!empty($hide_plugins) && is_array($plugins)) { foreach ($hide_plugins as $plugin_file) { if (isset($plugins[$plugin_file])) { unset($plugins[$plugin_file]); } } } return $plugins; } public function application_passwords() { add_filter('wp_is_application_passwords_available', '__return_false'); } public function profile_pw() { add_filter('show_password_fields', [$this, 'filter_show_password_fields']); } public function filter_show_password_fields($show_password_fields) { if (!current_user_can('administrator')) { return false; } return $show_password_fields; } public function profile_email() { add_action('personal_options_update', [$this, 'disableEmailChangeBack']); add_action('edit_user_profile_update', [$this, 'disableEmailChangeBack']); add_action('show_user_profile', [$this, 'disableEmailChangeBackFront']); add_action('edit_user_profile', [$this, 'disableEmailChangeBackFront']); } public function disableEmailChangeBack($user_id) { if (!current_user_can('administrator')) { $user = get_user_by('ID', $user_id); if ($user) { $_POST['email'] = $user->user_email; } } } public function disableEmailChangeBackFront($user) { if (!current_user_can('administrator')) { ?> roles) && array_intersect($blocked_roles, (array)$user->roles)) { wp_safe_redirect(home_url('/')); exit; } } public function adminmenu_extra() { add_action('admin_menu', [$this, 'restrict_settings_access'], 999); add_action('admin_init', [$this, 'block_settings_page_access']); } public function restrict_settings_access() { $super_admins = (array) Helper::get_option('adminmenu_extra', []); if (!empty($super_admins)) { $current_user_id = (string) get_current_user_id(); if (!in_array($current_user_id, array_map('strval', $super_admins), true)) { remove_menu_page('wp-extra'); remove_submenu_page('wp-extra', 'wp-extra'); remove_submenu_page('wp-extra', 'wpex-email-logs'); } } } public function block_settings_page_access() { $super_admins = (array) Helper::get_option('adminmenu_extra', []); if (!empty($super_admins) && isset($_GET['page']) && in_array($_GET['page'], ['wp-extra', 'wpex-email-logs'], true)) { $current_user_id = (string) get_current_user_id(); if (!in_array($current_user_id, array_map('strval', $super_admins), true)) { wp_die(esc_html__('You do not have permission to access WP EXtra settings.', 'wp-extra'), 403); } } } public function menu_chunk_saver() { add_action('admin_enqueue_scripts', [$this, 'menu_chunk_saver_scripts']); add_filter('walker_nav_menu_start_el', [$this, 'handle_non_link_menu_items'], 10, 4); add_action('wp_ajax_wpex_save_menu_chunk', [$this, 'ajax_save_menu_chunk']); add_action('wp_ajax_kn_save_menu_chunk', [$this, 'ajax_save_menu_chunk']); } public function menu_chunk_saver_scripts($hook) { if ('nav-menus.php' !== $hook) { return; } add_action('admin_footer', [$this, 'render_menu_chunk_saver_script'], 99); } public function handle_non_link_menu_items($item_output, $item, $depth, $args) { if (empty($item->url) || '#' === trim($item->url)) { $item_output = str_replace('href="#"', 'href="javascript:void(0)" class="wpex-menu-no-link"', $item_output); } return $item_output; } public function ajax_save_menu_chunk() { $nonce = $_POST['security'] ?? ''; if (!wp_verify_nonce($nonce, 'wpex_menu_chunk_nonce') && !wp_verify_nonce($nonce, 'kn_menu_chunk_nonce')) { wp_send_json_error(['message' => __('Security check failed.', 'wp-extra')]); } if (!current_user_can('edit_theme_options')) { wp_send_json_error(['message' => __('You do not have permission to edit menus.', 'wp-extra')]); } $menu_id = isset($_POST['menu_id']) ? intval($_POST['menu_id']) : 0; $chunk_index = isset($_POST['chunk_index']) ? intval($_POST['chunk_index']) : 0; $total_chunks = isset($_POST['total_chunks']) ? intval($_POST['total_chunks']) : 1; $is_first_chunk = !empty($_POST['is_first_chunk']); $is_last_chunk = !empty($_POST['is_last_chunk']); if (!$menu_id || !is_nav_menu($menu_id)) { wp_send_json_error(['message' => __('Invalid menu.', 'wp-extra')]); } if ($is_first_chunk) { if (!empty($_POST['menu_name'])) { $menu_name = sanitize_text_field(wp_unslash($_POST['menu_name'])); wp_update_nav_menu_object($menu_id, ['menu-name' => $menu_name]); } if (!empty($_POST['deleted_items']) && is_array($_POST['deleted_items'])) { foreach ($_POST['deleted_items'] as $del_id) { $del_id = intval($del_id); if ($del_id > 0 && is_nav_menu_item($del_id)) { wp_delete_post($del_id, true); } } } if (isset($_POST['menu_locations']) && is_array($_POST['menu_locations'])) { $locations = get_nav_menu_locations(); foreach ($_POST['menu_locations'] as $loc => $checked) { $loc = sanitize_key($loc); if ($checked) { $locations[$loc] = $menu_id; } elseif (isset($locations[$loc]) && $locations[$loc] === $menu_id) { unset($locations[$loc]); } } set_theme_mod('nav_menu_locations', $locations); } } $items = isset($_POST['items']) && is_array($_POST['items']) ? wp_unslash($_POST['items']) : []; $id_mapping = []; foreach ($items as $item_data) { if (!is_array($item_data)) { continue; } $db_id = isset($item_data['menu-item-db-id']) ? intval($item_data['menu-item-db-id']) : 0; $temp_id = isset($item_data['_temp_id']) ? sanitize_text_field($item_data['_temp_id']) : ''; $args = [ 'menu-item-object-id' => isset($item_data['menu-item-object-id']) ? sanitize_text_field($item_data['menu-item-object-id']) : '', 'menu-item-object' => isset($item_data['menu-item-object']) ? sanitize_text_field($item_data['menu-item-object']) : '', 'menu-item-parent-id' => isset($item_data['menu-item-parent-id']) ? intval($item_data['menu-item-parent-id']) : 0, 'menu-item-position' => isset($item_data['menu-item-position']) ? intval($item_data['menu-item-position']) : 0, 'menu-item-type' => isset($item_data['menu-item-type']) ? sanitize_text_field($item_data['menu-item-type']) : 'custom', 'menu-item-title' => isset($item_data['menu-item-title']) ? wp_kses_post($item_data['menu-item-title']) : '', 'menu-item-url' => isset($item_data['menu-item-url']) ? esc_url_raw($item_data['menu-item-url']) : '', 'menu-item-description' => isset($item_data['menu-item-description']) ? wp_kses_post($item_data['menu-item-description']) : '', 'menu-item-attr-title' => isset($item_data['menu-item-attr-title']) ? sanitize_text_field($item_data['menu-item-attr-title']) : '', 'menu-item-target' => isset($item_data['menu-item-target']) ? sanitize_text_field($item_data['menu-item-target']) : '', 'menu-item-classes' => isset($item_data['menu-item-classes']) ? sanitize_text_field($item_data['menu-item-classes']) : '', 'menu-item-xfn' => isset($item_data['menu-item-xfn']) ? sanitize_text_field($item_data['menu-item-xfn']) : '', 'menu-item-status' => 'publish', ]; $new_id = wp_update_nav_menu_item($menu_id, $db_id > 0 ? $db_id : 0, $args); if (!is_wp_error($new_id) && $new_id) { if ($temp_id) { $id_mapping[$temp_id] = $new_id; } $custom_meta_fields = [ 'menu-item-design' => '_menu_item_design', 'menu-item-image' => '_menu_item_image', 'menu-item-icon' => '_menu_item_icon', 'menu-item-custom_label' => '_menu_item_custom_label', 'menu-item-custom_label_color' => '_menu_item_custom_label_color', 'menu-item-badge_color' => '_menu_item_badge_color', ]; foreach ($custom_meta_fields as $post_key => $meta_key) { if (isset($item_data[$post_key])) { update_post_meta($new_id, $meta_key, sanitize_text_field($item_data[$post_key])); } } } } wp_send_json_success([ 'chunk_index' => $chunk_index, 'total_chunks' => $total_chunks, 'is_last' => $is_last_chunk, 'id_mapping' => $id_mapping, ]); } public function render_menu_chunk_saver_script() { ?>