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() {
?>