<?php

namespace ASENHA\Classes;

/**
 * Class for Admin Menu Organizer module
 *
 * @since 6.9.5
 */
class Admin_Menu_Organizer {
    
    /**
     * Add Admin Menu item under Settings menu
     * 
     * @since 7.8.5
     */
    public function add_menu_item() {
        add_submenu_page(
            'options-general.php', // Parent page/menu
            __( 'Admin Menu Settings', 'admin-site-enhancements' ), // Browser tab/window title
            __( 'Admin Menu', 'admin-site-enhancements' ), // Sube menu title
            'manage_options', // Minimal user capabililty
            'admin-menu-organizer', // Page slug. Shows up in URL.
            array( $this, 'add_admin_menu_settings_page' )
        );
    }
    
    /**
     * Create settings page for Admin Menu
     * 
     * @since 7.8.5
     */
    public function add_admin_menu_settings_page() {
        $render_field = new Settings_Fields_Render;
        ?>
        <div class="wrap admin-menu-organizer">
            <h1 class="wp-heading-inline"><?php echo __( 'Admin Menu Organizer', 'admin-site-enhancements' ); ?></h1>
            <div class="admin-menu-organizer-main">
                <div class="admin-menu-sortables-wrapper">
                    <?php $render_field->render_sortable_menu(); ?>
                </div>
                <div class="admin-menu-actions">
                    <button id="amo-save-changes" class="button button-primary button-large"><?php echo __( 'Save Changes', 'admin-site-enhancements' ); ?></button>
                    <div class="asenha-saving-changes" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="#2271b1" d="M12,1A11,11,0,1,0,23,12,11,11,0,0,0,12,1Zm0,19a8,8,0,1,1,8-8A8,8,0,0,1,12,20Z" opacity=".25"/><path fill="#2271b1" d="M12,4a8,8,0,0,1,7.89,6.7A1.53,1.53,0,0,0,21.38,12h0a1.5,1.5,0,0,0,1.48-1.75,11,11,0,0,0-21.72,0A1.5,1.5,0,0,0,2.62,12h0a1.53,1.53,0,0,0,1.49-1.3A8,8,0,0,1,12,4Z"><animateTransform attributeName="transform" dur="0.75s" repeatCount="indefinite" type="rotate" values="0 12 12;360 12 12"/></path></svg></div>
                    <div class="asenha-changes-saved" style="display:none;"><svg xmlns="http://www.w3.org/2000/svg" width="28" height="28" viewBox="0 0 24 24"><path fill="seagreen" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10s10-4.48 10-10S17.52 2 12 2zM9.29 16.29L5.7 12.7a.996.996 0 1 1 1.41-1.41L10 14.17l6.88-6.88a.996.996 0 1 1 1.41 1.41l-7.59 7.59a.996.996 0 0 1-1.41 0z"/></svg></div>
                </div>
            </div>
        </div>
        <?php
    }
    
    /**
     * Render custom menu order
     *
     * @param $menu_order array an ordered array of menu items
     * @link https://developer.wordpress.org/reference/hooks/menu_order/
     * @since 2.0.0
     */
    public function render_custom_menu_order( $menu_order ) {

        global $menu;

        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();

        // Get current menu order. We're not using the default $menu_order which uses index.php, edit.php as array values.
        $current_menu_order = array();

        foreach ( $menu as $menu_key => $menu_info ) {

            if ( false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
                $menu_item_id = $menu_info[2];
            } else {
                $menu_item_id = $menu_info[5];
            }

            $current_menu_order[] = array( $menu_item_id, $menu_info[2] );

        }

        // Get custom menu order
        $custom_menu_order = $options['custom_menu_order']; // comma separated
        $custom_menu_order = explode( ",", $custom_menu_order ); // array of menu ID, e.g. menu-dashboard

        // Return menu order for rendering

        $rendered_menu_order = array();

        // Render menu based on items saved in custom menu order

        foreach ( $custom_menu_order as $custom_menu_item_id ) {

            foreach ( $current_menu_order as $current_menu_item_id => $current_menu_item ) {

                if ( $custom_menu_item_id == $current_menu_item[0] ) {

                    $rendered_menu_order[] = $current_menu_item[1];

                }

            }

        }

        // Add items from current menu not already part of custom menu order, e.g. new plugin activated and adds new menu item

        foreach ( $current_menu_order as $current_menu_item_id => $current_menu_item ) {

            if ( ! in_array( $current_menu_item[0], $custom_menu_order ) ) {

                $rendered_menu_order[] = $current_menu_item[1];

            }

        }
        
        if ( bwasenha_fs()->can_use_premium_code__premium_only() ) {
            global $submenu;

            // Rearrange the sequence / positioning of sub-menu items
    
            $common_methods = new Common_Methods;

            // Get custom submenus order
            $custom_submenus_order = ( array_key_exists( 'custom_submenus_order', $options ) ) ? $options['custom_submenus_order'] : ''; // comma separated
            $custom_submenus_order = ( is_array( json_decode( stripslashes( $custom_submenus_order ), true ) ) ) ? json_decode( stripslashes( $custom_submenus_order ), true ) : array();
            $custom_submenus_order_transformed = array();

            foreach ( $custom_submenus_order as $submenu_id => $submenu_order ) {
                // Transform e.g. edit__php___post_type____page ==> edit.php?post_type=page
                $submenu_id = $common_methods->restore_menu_item_id( $submenu_id );
                $submenu_order = explode( ",", $submenu_order );
                $custom_submenus_order_transformed[$submenu_id] = $submenu_order;
            }
            
            // Change the order of submenu items for $submenu global
            foreach ( $submenu as $submenu_global_id => $submenu_global_items ) {

                // There's an empty $submenu_global_id for items that are 'hidden'
                // if ( empty( $submenu_global_id ) ) {
                //  $submenu_global_id = 'unassigned';
                // }

                $custom_ordered_items = array();

                foreach ( $custom_submenus_order_transformed as $submenu_custom_id => $submenu_custom_order ) {

                    if ( $submenu_global_id == $submenu_custom_id ) {
                        
                        // Assign new index / position for submenu items as saved in $submenu_custom_order

                        foreach ( $submenu_custom_order as $new_index => $item_id ) {
                            // Convert special HTML character into it's entity equivalent, e.g. '&' into '&amp;'
                            $item_id_htmlentities = htmlentities( $item_id );
                            foreach ( $submenu_global_items as $index => $info ) {
                                if ( isset( $info[2] ) ) {
                                    // Most of the time, this is true
                                    if ( $item_id_htmlentities == $info[2] ) {
                                        $custom_ordered_items[$new_index] = $info;
                                    } else {
                                        // Sometimes, $info[2] does not contain HTML entities, so they are teh same as $item_id
                                        if ( $item_id == $info[2] ) {
                                            $custom_ordered_items[$new_index] = $info;
                                        }
                                    }                                    
                                }
                            }
                        }

                        // Here we append submenu items that are newly added and not part of the saved $submenu_custom_order yet
                        
                        $existing_submenu_items_count = count( $submenu_custom_order );
                        $counter = $existing_submenu_items_count;

                        foreach( $submenu_global_items as $index => $info  ) {
                            if ( isset( $info[2] ) ) {
                                $info2 = $info[2];
                                // $info2 = str_replace( '&amp;', '&', $info2 ); // This has caused some submenu items to go missing
                                $info2 = html_entity_decode( $info2 );                          
                                if ( ! in_array( $info2, $submenu_custom_order ) ) {
                                    $custom_ordered_items[$counter] = $info;
                                    $counter++;
                                }                                
                            }
                        }

                        $submenu[$submenu_global_id] = $custom_ordered_items;
                    }
                }
            }
        }

        return $rendered_menu_order;

    }
    
    /**
     * Handle capabilities that certain plugins require to view a menu item but has not properly add to user role(s)
     * 
     * @since 6.9.12
     */
    public function handle_plugins_custom_capabilities__premium_only() {
        if ( function_exists( 'is_plugin_active' ) && is_plugin_active( 'contact-form-7/wp-contact-form-7.php' )) {
            // Contact Form 7
            // See: https://plugins.trac.wordpress.org/browser/contact-form-7/tags/5.9.4/includes/contact-form.php#L58
            $user_roles = array(
                'administrator',
                'editor',
                'author',
                'contributor',
                'subscriber'
            );

            $cf7_custom_capabilitiess = array(
                'wpcf7_read_contact_form',
                // 'wpcf7_edit_contact_form',
                // 'wpcf7_delete_contact_form',
                'wpcf7_read_contact_forms', // plural
                // 'wpcf7_edit_contact_forms', // plural
                // 'wpcf7_delete_contact_forms', // plural
            );
            
            foreach ( $user_roles as $role_slug ) {
                $role = get_role( $role_slug );
                foreach( $cf7_custom_capabilitiess as $custom_capability ) {
                    $role->add_cap( $custom_capability, true );             
                }
            }
        }
    }

    /**
     * Apply custom menu item titles
     *
     * @since 2.9.0
     */
    public function apply_custom_menu_item_titles() {

        global $menu;

        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();

        // Get custom menu item titles
        $custom_menu_titles = $options['custom_menu_titles'];
        $custom_menu_titles = explode( ',', $custom_menu_titles );

        foreach ( $menu as $menu_key => $menu_info ) {

            if ( false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
                $menu_item_id = $menu_info[2];
            } else {
                $menu_item_id = $menu_info[5];
            }

            // Get defaul/custom menu item title
            foreach ( $custom_menu_titles as $custom_menu_title ) {

                // At this point, $custom_menu_title value looks like toplevel_page_snippets__Code Snippets

                $custom_menu_title = explode( '__', $custom_menu_title );

                if ( $custom_menu_title[0] == $menu_item_id ) {
                    $menu_item_title = $custom_menu_title[1]; // e.g. Code Snippets
                    break; // stop foreach loop so $menu_item_title is not overwritten in the next iteration
                } else {
                    $menu_item_title = $menu_info[0];
                }

            }

            $menu[$menu_key][0] = $menu_item_title;

        }
    }

    /**
     * Apply custom menu item titles
     *
     * @since 7.9.7
     */
    public function apply_custom_title_for_posts_menu() {
        global $wp_post_types;
        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $admin_menu_options = ( isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array() );
        // $admin_menu_organizer = new ASENHA\Classes\Admin_Menu_Organizer();
    
        // For 'Posts' menu, if the title has been changed, try changing the labels for it everywhere
        $custom_menu_titles = explode( ',', $admin_menu_options['custom_menu_titles'] );
        foreach ( $custom_menu_titles as $custom_menu_title ) {
            if ( false !== strpos( $custom_menu_title, 'menu-posts__' ) ) {
                $custom_menu_title = explode( '__', $custom_menu_title );
                $posts_custom_title = $custom_menu_title[1];
                $posts_default_title = __( 'Posts', 'admin-site-enhancements' );
                if ( is_array( $wp_post_types ) ) {
                    if ( isset( $wp_post_types['post'] ) && property_exists( $wp_post_types['post'], 'label' ) ) {
                        $posts_default_title = $wp_post_types['post']->label;
                    }
                }
                if ( $posts_default_title != $posts_custom_title ) {
                    add_filter( 'post_type_labels_post', [ $this, 'change_post_labels' ] );
                    add_action( 'init', [ $this, 'change_post_object_label' ] );
                    add_action( 'admin_menu', [ $this, 'change_post_menu_label' ], PHP_INT_MAX );
                    add_action( 'admin_bar_menu', [ $this, 'change_wp_admin_bar' ], 80 );
                }
            }
        }
    }
        
    /**
     * Get custom title for 'Posts' menu item
     * 
     * @since 6.9.13
     */
    public function get_posts_custom_title() {
        $post_object = get_post_type_object( 'post' ); // object
        $posts_default_title = '';

        if ( is_object( $post_object ) ) {
            if ( property_exists( $post_object, 'label' ) ) {
                $posts_default_title = $post_object->label;                     
            } else {
                $posts_default_title = $post_object->labels->name;                          
            }            
        }
        
        $posts_custom_title = $posts_default_title;

        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();

        $custom_menu_titles = isset( $options['custom_menu_titles'] ) ? explode( ',', $options['custom_menu_titles'] ) : array();

        if ( ! empty( $custom_menu_titles ) ) {
            foreach ( $custom_menu_titles as $custom_menu_title ) {
                if ( false !== strpos( $custom_menu_title, 'menu-posts__' ) ) {
                    $custom_menu_title = explode( '__', $custom_menu_title );
                    $posts_custom_title = $custom_menu_title[1];
                }
            }            
        }
        
        return $posts_custom_title;
    }
    
    /**
     * For 'Posts', apply custom label
     * 
     * @link https://developer.wordpress.org/reference/hooks/post_type_labels_post_type/
     * @since 6.9.13
     */
    public function change_post_labels( $labels ) {
        $post_object = get_post_type_object( 'post' ); // object
        $posts_default_title_plural = '';
        
        if ( is_object( $post_object ) ) {
            if ( property_exists( $post_object, 'label' ) ) {
                $posts_default_title_plural = $post_object->label;
            } else {
                $posts_default_title_plural = $post_object->labels->name;
            }            

            $posts_default_title_singular = $post_object->labels->singular_name;

            $posts_custom_title = $this->get_posts_custom_title();

            foreach( $labels as $key => $label ){
                if ( null === $label ) {
                    continue;
                }
                $labels->{$key} = str_replace( [ $posts_default_title_plural, $posts_default_title_singular ], $posts_custom_title, $label );
            }
        }

        return $labels;        
    }
    
    /**
     * For 'Posts', apply custom label in post object
     * 
     * @since 6.9.12
     */
    public function change_post_object_label() {
        global $wp_post_types;
        $posts_custom_title = $this->get_posts_custom_title();

        $labels                     = &$wp_post_types['post']->labels;
        $labels->name               = $posts_custom_title;
        $labels->singular_name      = $posts_custom_title;
        $labels->all_items          = sprintf( 
                                        /* translators: %s is post type or taxonomy label */
                                        __( 'All %s', 'admin-site-enhancements' ),
                                        $posts_custom_title
                                    );
        $labels->add_new            = __( 'Add New', 'admin-site-enhancements' );
        $labels->add_new_item       = __( 'Add New', 'admin-site-enhancements' );
        $labels->edit_item          = __( 'Edit', 'admin-site-enhancements' );
        $labels->new_item           = $posts_custom_title;
        $labels->view_item          = __( 'View', 'admin-site-enhancements' );
        $labels->search_items       = sprintf( 
                                        /* translators: %s is post type or taxonomy label */
                                        __( 'Search %s', 'admin-site-enhancements' ),
                                        $posts_custom_title
                                    );
        $labels->not_found          = sprintf( 
                                        /* translators: %s is the post type label */
                                        __( 'No %s found', 'admin-site-enhancements' ),
                                        strtolower( $posts_custom_title )
                                    );
        $labels->not_found_in_trash = sprintf( 
                                        /* translators: %s is the post type label */
                                        __( 'No %s found in Trash', 'admin-site-enhancements' ),
                                        strtolower( $posts_custom_title )
                                    );
    }

    /**
     * For 'Posts', apply custom label in menu and submenu
     * 
     * @since 6.9.12
     */
    public function change_post_menu_label() {
        global $submenu;
        $posts_custom_title = $this->get_posts_custom_title();
        
        if ( ! empty( $posts_custom_title ) ) {
            $submenu['edit.php'][5][0]  = sprintf( 
                /* translators: %s is post type or taxonomy label */
                __( 'All %s', 'admin-site-enhancements' ),
                $posts_custom_title
            );
        } else {
            $submenu['edit.php'][5][0]  = sprintf( 
                /* translators: %s is post type or taxonomy label */
                __( 'All %s', 'admin-site-enhancements' ),
                $posts_default_title
            );
        }        
    }

    /**
     * For 'Posts', apply custom label in admin bar
     * 
     * @since 6.9.12
     */
    public function change_wp_admin_bar( $wp_admin_bar ) {
        $posts_custom_title = $this->get_posts_custom_title();
        
        $new_post_node = $wp_admin_bar->get_node( 'new-post' );
        if ( $new_post_node ) {
            $new_post_node->title = $posts_custom_title;
            $wp_admin_bar->add_node( $new_post_node );
        }
    }

    /**
     * Hide parent menu items by adding class(es) to hide them
     *
     * @since 2.0.0
     */
    public function hide_menu_items() {

        global $menu;

        if ( bwasenha_fs()->can_use_premium_code__premium_only() ) {
            $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
            $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();

            // Get data on parent menu items to be hidden by toggle and/or user roles
            if ( isset( $options['custom_menu_always_hidden'] ) ) {
                $menu_always_hidden = $options['custom_menu_always_hidden'];
                $menu_always_hidden = json_decode( stripslashes( $menu_always_hidden ), true );
            } else {
                $menu_always_hidden = array();
            }

            global $current_user;
            $current_user_roles = $current_user->roles; // indexed array of role slugs
        }

        $common_methods = new Common_Methods;
        $menu_hidden_by_toggle = $common_methods->get_menu_hidden_by_toggle(); // indexed array

        foreach ( $menu as $menu_key => $menu_info ) {

            if ( false !== strpos( $menu_info[4], 'wp-menu-separator' ) ) {
                $menu_item_id = $menu_info[2];
            } else {
                $menu_item_id = $menu_info[5];
            }

            // Append 'hidden' class to hide menu item until toggled
            if ( in_array( $menu_item_id, $menu_hidden_by_toggle ) ) {
                $menu[$menu_key][4] = $menu_info[4] . ' hidden asenha_hidden_menu';
            }

            if ( bwasenha_fs()->can_use_premium_code__premium_only() ) {
                // Maybe append 'always-hidden' class
                if ( is_array( $menu_always_hidden ) && ! empty( $menu_always_hidden ) ) {
                    foreach( $menu_always_hidden as $hidden_menu_item_id => $info ) {
                        $hidden_menu_item_id = $common_methods->restore_menu_item_id( $hidden_menu_item_id );
                        if ( $hidden_menu_item_id == $menu_item_id 
                            // Special case where Yoast SEO is showing Yoast SEO menu only to editors using a different $menu_item_id
                            || ( 'toplevel_page_wpseo_dashboard' == $hidden_menu_item_id && 'toplevel_page_wpseo_workouts' == $menu_item_id  )
                        ) {

                            // Append 'always-hidden' class to always hide menu item
                            if ( isset( $info['always_hide'] )
                                && $info['always_hide'] 
                                && ( $info['always_hide_for'] == 'all-roles' ) 
                                ) { // for all roles

                                $menu[$menu_key][4] = $menu[$menu_key][4] . ' always-hidden';

                            } elseif ( isset( $info['always_hide'] ) 
                                && $info['always_hide']
                                && $info['always_hide_for'] == 'all-roles-except'
                                && ! empty( $info['which_roles'] )
                                ) { // for all roles except

                                // Make this compatible with users who have multiple roles
                                // e.g. via Multiple User Roles module
                                $has_exception_role = false;
                                foreach ( $current_user_roles as $current_user_role ) {
                                    if ( in_array( $current_user_role, $info['which_roles'] ) ) {
                                        $has_exception_role = true;
                                    }
                                }

                                if ( ! $has_exception_role ) {
                                    $menu[$menu_key][4] = $menu[$menu_key][4] . ' always-hidden';
                                }
                                
                            } elseif ( isset( $info['always_hide'] )
                                && $info['always_hide'] 
                                && $info['always_hide_for'] == 'selected-roles'
                                && ! empty( $info['which_roles'] )
                                ) { // for selected roles
                                
                                foreach ( $current_user_roles as $current_user_role ) {
                                    foreach( $info['which_roles'] as $role_menu_is_hidden_for ) {
                                        if ( $current_user_role == $role_menu_is_hidden_for ) {

                                            $menu[$menu_key][4] = $menu[$menu_key][4] . ' always-hidden';
                                            
                                        }
                                    }
                                }
                                
                            }
                        }
                    }                   
                }
            }

        }

    }
    
    /**
     * Hide submenu items by adding class(es) to hide them
     * 
     * @since 6.9.13
     */
    public function hide_submenu_items__premium_only() {
        global $submenu;
        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();
        
        // Get data on submenu items to be hidden by toggle and/or user roles
        if ( isset( $options['custom_submenu_always_hidden'] ) ) {
            $submenu_always_hidden = $options['custom_submenu_always_hidden'];
            $submenu_always_hidden = json_decode( stripslashes( $submenu_always_hidden ), true );
        } else {
            $submenu_always_hidden = array();
        }

        global $current_user;
        $current_user_roles = $current_user->roles; // indexed array of role slugs

        $common_methods = new Common_Methods;
        $submenu_hidden_by_toggle = $common_methods->get_submenu_hidden_by_toggle__premium_only(); // indexed array
                
        foreach ( $submenu as $submenu_key => $submenu_infos ) {
            foreach ( $submenu_infos as $submenu_info_key => $submenu_info ) {
                if ( isset( $submenu_info[0] ) ) {
                    // Very rarely, submenu items use special characters / symbols, which breaks the JS. Let's remove it.
                    // We've removed it in ASE settings, so, we need to remove it here from the global $submenu as well, so both can match
                    $submenu_item_title = str_replace( 
                        array( 
                            '↳', 
                            '➤', // e.g. WP fail2ban
                            '⭐️', 
                            '✛',
                            '<img draggable="false" role="img" class="emoji" alt="👋" src="https://s.w.org/images/core/emoji/15.0.3/svg/1f44b.svg">', // WOW-Plugin (Float Menu plugin) sub-menu 'Hey' has this image at the end of the title.
                            '👋', // WOW-Plugin (Float Menu plugin) sub-menu 'Hey' has this image at the end of the title.
                            '→', // Independent Analytics >> Upgrade to Pro submenu item
                            '»', // wpDiscuz
                        ), 
                        '', 
                        $submenu_info[0] 
                    );
                    $sanitized_submenu_title = sanitize_title( $submenu_item_title );
                } else {
                    $sanitized_submenu_title = '';
                }

                $submenu_url_fragment = isset( $submenu_info[2] ) ? $submenu_info[2] : '';
                $submenu_url_fragment_length = strlen( $submenu_url_fragment );

                // We also need to reassemble the item ID, so it matches the one stored via ASE settings
                $submenu_item_id = $submenu_key . '_-_' . $sanitized_submenu_title .'-_-' . $submenu_url_fragment_length;
                // e.g. index.php_-_site-options-_-24

                // Append 'asenha_hidden_menu hidden' classes to hide menu item until toggled
                if ( in_array( $submenu_item_id, $submenu_hidden_by_toggle ) ) {
                    $submenu[$submenu_key][$submenu_info_key][4] = 'asenha_hidden_menu hidden';
                } else {
                    $submenu[$submenu_key][$submenu_info_key][4] = '';
                }

                // Maybe append 'always-hidden' class
                if ( is_array( $submenu_always_hidden ) && ! empty( $submenu_always_hidden ) ) {
                    foreach( $submenu_always_hidden as $hidden_menu_item_id => $info ) {
                        $hidden_menu_item_id = $common_methods->restore_menu_item_id( $hidden_menu_item_id ); 
                        // e.g. index__php_-_site-options ==> index.php_-_site-options                        
                        
                        if ( $hidden_menu_item_id == $submenu_item_id ) {

                            // Append 'always-hidden' class to always hide menu item
                            if ( isset( $info['always_hide'] )
                                && $info['always_hide'] 
                                && ( $info['always_hide_for'] == 'all-roles' ) 
                                ) { // for all roles

                                $submenu[$submenu_key][$submenu_info_key][4] = $submenu[$submenu_key][$submenu_info_key][4] . ' always-hidden';

                            } elseif ( isset( $info['always_hide'] ) 
                                && $info['always_hide']
                                && $info['always_hide_for'] == 'all-roles-except'
                                && ! empty( $info['which_roles'] )
                                ) { // for all roles except

                                // Make this compatible with users who have multiple roles
                                // e.g. via Multiple User Roles module
                                $has_exception_role = false;
                                foreach ( $current_user_roles as $current_user_role ) {
                                    if ( in_array( $current_user_role, $info['which_roles'] ) ) {
                                        $has_exception_role = true;
                                        break;
                                    }
                                }

                                if ( ! $has_exception_role ) {
                                    $submenu[$submenu_key][$submenu_info_key][4] = $submenu[$submenu_key][$submenu_info_key][4] . ' always-hidden';
                                }
                                
                            } elseif ( isset( $info['always_hide'] )
                                && $info['always_hide'] 
                                && $info['always_hide_for'] == 'selected-roles'
                                && ! empty( $info['which_roles'] )
                                ) { // for selected roles
                                
                                foreach ( $current_user_roles as $current_user_role ) {
                                    foreach( $info['which_roles'] as $role_menu_is_hidden_for ) {
                                        if ( $current_user_role == $role_menu_is_hidden_for ) {

                                            $submenu[$submenu_key][$submenu_info_key][4] = $submenu[$submenu_key][$submenu_info_key][4] . ' always-hidden';
                                            
                                        }
                                    }
                                }
                                
                            }
                        }
                    }                   
                }
            }
        }
    }

    /**
     * Add toggle to show hidden menu items
     *
     * @since 2.0.0
     */
    public function add_hidden_menu_toggle() {
        
        global $current_user;

        // Get menu items hidden by toggle
        $common_methods = new Common_Methods;       
        $menu_hidden_by_toggle = $common_methods->get_menu_hidden_by_toggle();
        if ( bwasenha_fs()->can_use_premium_code__premium_only() ) {
            $submenu_hidden_by_toggle = $common_methods->get_submenu_hidden_by_toggle__premium_only();        
        } else {
            $submenu_hidden_by_toggle = array();
        }

        // Get user capabilities the "Show All/Less" toggle should be shown for
        $user_capabilities_to_show_menu_toggle_for = $common_methods->get_user_capabilities_to_show_menu_toggle_for();

        // Get current user's capabilities from the user's role(s)
        $current_user_capabilities = '';
        $current_user_roles = $current_user->roles; // indexed array of role slugs
        foreach( $current_user_roles as $current_user_role ) {
            $current_user_role_capabilities = get_role( $current_user_role )->capabilities;
            if ( is_array( $current_user_role_capabilities ) ) {
                $current_user_role_capabilities = array_keys( $current_user_role_capabilities ); // indexed array
                $current_user_role_capabilities = implode( ",", $current_user_role_capabilities );
                $current_user_capabilities .= ',' . $current_user_role_capabilities;                
            }
        }

        // Maybe show "Show All/Less" toggle
        $show_toggle_menu = false;

        if ( ! empty( $current_user_capabilities ) ) {
            $current_user_capabilities = array_unique( explode(",", $current_user_capabilities) );

            foreach( $user_capabilities_to_show_menu_toggle_for as $user_capability_to_show_menu_toggle_for ) {
                if ( in_array( $user_capability_to_show_menu_toggle_for, $current_user_capabilities ) ) {
                    $show_toggle_menu = true;
                    break;
                }
            }
        }

        if ( ( ! empty( $menu_hidden_by_toggle ) || ! empty( $submenu_hidden_by_toggle ) ) 
            && $show_toggle_menu ) {

            add_menu_page(
                __( 'Show All', 'admin-site-enhancements' ),
                __( 'Show All', 'admin-site-enhancements' ),
                'read',
                'asenha_show_hidden_menu',
                function () {  return false;  },
                "dashicons-arrow-down-alt2",
                300 // position
            );

            add_menu_page(
                __( 'Show Less', 'admin-site-enhancements' ),
                __( 'Show Less', 'admin-site-enhancements' ),
                'read',
                'asenha_hide_hidden_menu',
                function () {  return false;  },
                "dashicons-arrow-up-alt2",
                301 // position
            );
        }

    }

    /**
     * Script to toggle hidden menu itesm
     *
     * @since 2.0.0
     */
    public function enqueue_toggle_hidden_menu_script() {

        // Get menu items hidden by toggle
        $common_methods = new Common_Methods;
        $menu_hidden_by_toggle = $common_methods->get_menu_hidden_by_toggle();
        if ( bwasenha_fs()->can_use_premium_code__premium_only() ) {
            $submenu_hidden_by_toggle = $common_methods->get_submenu_hidden_by_toggle__premium_only();        
        } else {
            $submenu_hidden_by_toggle = array();
        }

        if ( ! empty( $menu_hidden_by_toggle ) || ! empty( $submenu_hidden_by_toggle ) ) {

            // Script to set behaviour and actions of the sortable menu
            wp_enqueue_script( 'asenha-toggle-hidden-menu', ASENHA_URL . 'assets/js/toggle-hidden-menu.js', array(), ASENHA_VERSION, false );

        }

    }
    
    /**
     * Maybe restrict access to current screen based on 'hide', 'hide always', 'for all/some roles' settings for each menu item
     * 
     * @since 5.1.0
     */
    public function maybe_restrict_access_to_current_screen__premium_only() {
        global $wp, $menu, $submenu;

        // $current_screen = get_current_screen(); // WP_Screen object
        // $current_screen_id = $current_screen->id;
        // $current_screen_parent_file = $current_screen->parent_file;

        $current_user = wp_get_current_user();
        $current_user_roles = array_values( $current_user->roles ); // indexed array

        $common_methods = new Common_Methods;
        $always_hidden_menu_url_fragments = $common_methods->get_url_fragments_of_always_hidden_menu_pages__premium_only();

        // Get the page url id for the current admin page
        $current_admin_page_url_fragment = add_query_arg( $wp->query_vars ); // e.g. /wp-admin/admin.php?page=some-admin-page-slug

        if ( '/wp-admin/' == $current_admin_page_url_fragment && ! in_array( 'index.php', $always_hidden_menu_url_fragments ) ) {
            // do nothing, this is the dashboard at /wp-admin/ without index.php and it's not restricted for access
            $page_url_id = $current_admin_page_url_fragment; 
        } elseif ( '/wp-admin/' == $current_admin_page_url_fragment && in_array( 'index.php', $always_hidden_menu_url_fragments ) ) {
            // we're on the dashboard at /wp-admin/ and it (index.php) is restricted for access, so, change url ID to 'index.php'
            $page_url_id = 'index.php';
        } elseif ( false !== strpos( $current_admin_page_url_fragment, '?page=' ) ) {
            $current_admin_page_url_fragments = explode( '?page=', $current_admin_page_url_fragment );
            $page_url_id = $current_admin_page_url_fragments[1]; // e.g. some-admin-page-slug
        } elseif ( false !== strpos( $current_admin_page_url_fragment, '?' ) ) {
            $page_url_id = str_replace( '/wp-admin/', '', $current_admin_page_url_fragment ); // e.g. post-new.php?post_type=todos
        } else {
            $page_url_id = str_replace( '/wp-admin/', '', $current_admin_page_url_fragment ); // e.g. edit-comments.php or plugins.php  
        }

        $restrict_access = false;
        
        if ( in_array( $page_url_id, $always_hidden_menu_url_fragments ) ) {
            
            // Maybe restrict access for (parent) admin page

            // Get data for which roles should the current page be hidden for
            $always_hide_for_raw = $common_methods->get_always_hide_for__premium_only( $page_url_id );

            if ( isset( $always_hide_for_raw[$page_url_id]['always_hide'] ) && $always_hide_for_raw[$page_url_id]['always_hide'] ) {
                $always_hide = true;
            } else {
                $always_hide = false;           
            }
            if ( isset( $always_hide_for_raw[$page_url_id]['always_hide_for'] ) ) {
                $always_hide_for = $always_hide_for_raw[$page_url_id]['always_hide_for'];           
            } else {
                $always_hide_for = '';
            }
            if ( isset( $always_hide_for_raw[$page_url_id]['which_roles'] ) ) {
                $which_roles = $always_hide_for_raw[$page_url_id]['which_roles'];           
            } else {
                $which_roles = array();
            }

            // Check if current user's role(s) should be restricted from access
            
            if ( $always_hide && ( $always_hide_for == 'all-roles' ) ) {
                $restrict_access = true;
            } elseif ( $always_hide && ( $always_hide_for == 'all-roles-except' ) && ! empty( $which_roles ) ) { 
                $intersecting_roles = array_intersect( $current_user_roles, $which_roles );
                if ( empty( $intersecting_roles ) ) {
                    $restrict_access = true;
                }
            } elseif ( $always_hide && ( $always_hide_for == 'selected-roles' ) && ! empty( $which_roles ) ) {
                $intersecting_roles = array_intersect( $current_user_roles, $which_roles );
                if ( ! empty( $intersecting_roles ) ) {
                    $restrict_access = true;
                }               
            }

            if ( $restrict_access ) {
                wp_die( 'Sorry, you are not allowed to access this page.' );        
                die(); // in case wp_die() is not working as it should
            }
            
        } else {
            
            // Maybe restrict access for (child) admin pages whose parent has been restricted

            if ( is_array( $submenu ) ) {               
                foreach( $submenu as $menu_id => $submenu_items ) {
                    if ( in_array( $menu_id, $always_hidden_menu_url_fragments ) ) {
                        foreach( $submenu_items as $submenu_item_priority => $submenu_item_info ) {
                            if ( $page_url_id == $submenu_item_info[2] ) {

                                // Get roles for which parent menu page should be hidden for
                                $always_hide_for_raw = $common_methods->get_always_hide_for__premium_only( $menu_id );

                                if ( isset( $always_hide_for_raw[$menu_id]['always_hide'] ) && $always_hide_for_raw[$menu_id]['always_hide'] ) {
                                    $always_hide = true;
                                } else {
                                    $always_hide = false;           
                                }
                                if ( isset( $always_hide_for_raw[$menu_id]['always_hide_for'] ) ) {
                                    $always_hide_for = $always_hide_for_raw[$menu_id]['always_hide_for'];           
                                } else {
                                    $always_hide_for = '';
                                }
                                if ( isset( $always_hide_for_raw[$menu_id]['which_roles'] ) ) {
                                    $which_roles = $always_hide_for_raw[$menu_id]['which_roles'];           
                                } else {
                                    $which_roles = array();
                                }
                                
                                // Check if current user's role(s) should be restricted from access

                                if ( $always_hide && ( $always_hide_for == 'all-roles' ) ) {
                                    $restrict_access = true;
                                } elseif ( $always_hide && ( $always_hide_for == 'all-roles-except' ) && ! empty( $which_roles ) ) { 
                                    $intersecting_roles = array_intersect( $current_user_roles, $which_roles );
                                    if ( empty( $intersecting_roles ) ) {
                                        $restrict_access = true;
                                    }
                                } elseif ( $always_hide && ( $always_hide_for == 'selected-roles' ) && ! empty( $which_roles ) ) {
                                    $intersecting_roles = array_intersect( $current_user_roles, $which_roles );
                                    if ( ! empty( $intersecting_roles ) ) {
                                        $restrict_access = true;
                                    }               
                                }

                                if ( $restrict_access ) {
                                    wp_die( 'Sorry, you are not allowed to access this page.' );
                                    die(); // in case wp_die() is not working as it should
                                }
                                                        
                            }
                        }
                    }
                }
            }
            
        }
                        
    }
    
    /**
     * Register new separators added via the "Add separator" button
     * 
     * @since 6.9.13
     */
    public function add_new_separators__premium_only() {
        global $menu;
        
        $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
        $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();

        $new_separators = isset( $options['custom_menu_new_separators'] ) ? $options['custom_menu_new_separators'] : '';
        
        if ( is_string( $new_separators ) && ! empty( $new_separators ) ) {
            $new_separators = json_decode( stripslashes( $new_separators ), true );

            if ( is_array( $new_separators ) && ! empty( $new_separators ) ) {
                foreach( $new_separators as $separator_menu_item_id => $info ) {
                    $menu[] = array(
                        '',
                        'read',
                        $separator_menu_item_id,
                        '',
                        'wp-menu-separator additional-separator',
                    );
                }
            }            
        }
    }

    /**
     * Save admin menu via AJAX
     * 
     * @since 6.3.1
     */
    public function save_admin_menu() {

        if ( isset( $_REQUEST ) ) {
            if ( check_ajax_referer( 'save-menu-nonce', 'nonce', false ) ) {

                $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
                $options = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();
                
                if ( bwasenha_fs()->can_use_premium_code__premium_only() ) {
                                
                    $options['custom_menu_order'] = isset( $_REQUEST['custom_menu_order'] ) ? $_REQUEST['custom_menu_order'] : $options['custom_menu_order'];

                    $options['custom_menu_titles'] = isset( $_REQUEST['custom_menu_titles'] ) ? $_REQUEST['custom_menu_titles'] : $options['custom_menu_titles'];

                    $options['custom_submenus_order'] = isset( $_REQUEST['custom_submenus_order'] ) ? stripslashes( $_REQUEST['custom_submenus_order'] ) : $options['custom_submenus_order'];

                    $options['custom_menu_always_hidden'] = isset( $_REQUEST['custom_menu_always_hidden'] ) ? stripslashes( $_REQUEST['custom_menu_always_hidden'] ) : $options['custom_menu_always_hidden'];

                    $options['custom_submenu_always_hidden'] = isset( $_REQUEST['custom_submenu_always_hidden'] ) ? stripslashes( $_REQUEST['custom_submenu_always_hidden'] ) : $options['custom_submenu_always_hidden'];

                    $options['custom_menu_new_separators'] = isset( $_REQUEST['custom_menu_new_separators'] ) ? $_REQUEST['custom_menu_new_separators'] : $options['custom_menu_new_separators'];
                    
                } else {

                    $options['custom_menu_order'] = isset( $_REQUEST['custom_menu_order'] ) ? $_REQUEST['custom_menu_order'] : $options['custom_menu_order'];

                    $options['custom_menu_titles'] = isset( $_REQUEST['custom_menu_titles'] ) ? $_REQUEST['custom_menu_titles'] : $options['custom_menu_titles'];

                    $options['custom_menu_hidden'] = isset( $_REQUEST['custom_menu_hidden'] ) ? $_REQUEST['custom_menu_hidden'] : $options['custom_menu_hidden'];
                    
                }
                
                $options_extra['admin_menu'] = $options;
                // vi( $options_extra, '', 'save menu' );

                $updated = update_option( ASENHA_SLUG_U . '_extra', $options_extra, true );
                
                if ( $updated ) {
                    $response = array(
                        'status'    => 'success',
                    );
                } else {
                    $response = array(
                        'status'    => 'failed',
                    );                  
                }
                
                echo json_encode( $response );
            }
        }
        
    }
    
    /**
     * Reset admin menu via AJAX
     * 
     * @since 6.3.1
     */
    public function reset_admin_menu__premium_only() {

        if ( isset( $_REQUEST ) ) {
            if ( check_ajax_referer( 'reset-menu-nonce', 'nonce', false ) ) {

                $options_extra = get_option( ASENHA_SLUG_U . '_extra', array() );
                $admin_menu = isset( $options_extra['admin_menu'] ) ? $options_extra['admin_menu'] : array();
                // vi( $admin_menu, '', 'menu before reset' );
                
                if ( ! empty( $admin_menu ) ) {
                    unset( $admin_menu['custom_menu_order'] );
                    unset( $admin_menu['custom_menu_titles'] );
                    unset( $admin_menu['custom_submenus_order'] );
                    unset( $admin_menu['custom_menu_hidden'] );
                    unset( $admin_menu['custom_menu_always_hidden'] );
                    unset( $admin_menu['custom_submenu_always_hidden'] );
                    unset( $admin_menu['custom_menu_new_separators'] );
                    // vi( $admin_menu, '', 'menu after reset' );
                    
                    $options_extra['admin_menu'] = $admin_menu;
                    // vi( $options_extra, '', 'reset menu' );

                    $updated = update_option( ASENHA_SLUG_U . '_extra', $options_extra, true );
                    // vi( $updated );
                    
                    if ( $updated ) {
                        $response = array(
                            'status'    => 'success',
                        );
                    } else {
                        $response = array(
                            'status'    => 'failed',
                        );                  
                    }                    
                } else {
                    $response = array(
                        'status'    => 'success',
                    );
                }
                
                echo json_encode( $response );
            }
        }
        
    }
    
}