Plugin Directory

Changeset 3432363


Ignore:
Timestamp:
01/05/2026 04:07:28 AM (7 weeks ago)
Author:
davisw3
Message:

Version 1.0.8: Added device & page visibility, daily admin notice, performance improvements

Location:
menu-visibility-control/trunk
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • menu-visibility-control/trunk/admin/admin-notice.php

    r3383763 r3432363  
    11<?php
    2 /**
    3  * Admin Notice for Menu Visibility Control
    4  *
    5  * Displays a dismissible admin notice encouraging users to rate or donate.
    6  * Notice shows only on the Appearance → Menus page and once per session.
    7  *
    8  * @package Menu_Visibility_Control
    9  */
    10 
    112if ( ! defined( 'ABSPATH' ) ) {
    123    exit;
     
    145
    156/**
    16  * Class MVC_Admin_Notice
     7 * Show admin notice on Appearance → Menus once per day
    178 */
    18 class MVC_Admin_Notice {
     9add_action( 'admin_notices', 'mvc_show_menu_admin_notice' );
     10function mvc_show_menu_admin_notice() {
    1911
    20     /**
    21      * Constructor.
    22      */
    23     public function __construct() {
    24         add_action( 'admin_notices', [ $this, 'maybe_show_notice' ] );
    25         add_action( 'wp_ajax_mvc_dismiss_menu_notice', [ $this, 'dismiss_notice' ] );
     12    $screen = get_current_screen();
     13    if ( ! $screen || 'nav-menus' !== $screen->id ) {
     14        return;
    2615    }
    2716
    28     /**
    29      * Display admin notice only on the Appearance → Menus page.
    30      */
    31     public function maybe_show_notice() {
    32 
    33         // Display only on Appearance → Menus page.
    34         $screen = get_current_screen();
    35         if ( empty( $screen ) || 'nav-menus' !== $screen->id ) {
    36             return;
    37         }
    38 
    39         // Check if dismissed this session.
    40         if ( get_transient( 'mvc_notice_dismissed_' . get_current_user_id() ) ) {
    41             return;
    42         }
    43 
    44         // Direct WordPress.org asset image (SVN assets folder).
    45         $icon_url = 'https://ps.w.org/menu-visibility-control/assets/icon-128x128.png';
    46 
    47         ?>
    48         <div class="notice notice-info is-dismissible mvc-session-notice" style="display:flex;align-items:center;gap:12px;">
    49             <img src="<?php echo esc_url( $icon_url ); ?>" alt="Menu Visibility Control Icon" style="width:40px;height:40px;border-radius:8px;">
    50             <div>
    51                 <p><strong><?php esc_html_e( 'Thank you for using Menu Visibility Control!', 'menu-visibility-control' ); ?></strong></p>
    52                 <p><?php esc_html_e( 'If you find this plugin helpful, please consider supporting its development:', 'menu-visibility-control' ); ?></p>
    53                 <p>
    54                     <a href="https://wordpress.org/support/plugin/menu-visibility-control/reviews/#new-post" target="_blank" class="button-primary" style="margin-right:6px;">
    55                         <?php esc_html_e( '⭐ Rate on WordPress.org', 'menu-visibility-control' ); ?>
    56                     </a>
    57                     <a href="https://knowledge.buzz/donate" target="_blank" class="button-secondary">
    58                         <?php esc_html_e( '💖 Donate', 'menu-visibility-control' ); ?>
    59                     </a>
    60                 </p>
    61             </div>
    62         </div>
    63 
    64         <script>
    65             (function($){
    66                 $(document).on('click', '.mvc-session-notice .notice-dismiss', function(){
    67                     $.post(ajaxurl, {
    68                         action: 'mvc_dismiss_menu_notice',
    69                         _ajax_nonce: '<?php echo esc_js( wp_create_nonce( 'mvc_dismiss_nonce' ) ); ?>'
    70                     });
    71                 });
    72             })(jQuery);
    73         </script>
    74         <?php
     17    $user_id = get_current_user_id();
     18    if ( ! $user_id ) {
     19        return;
    7520    }
    7621
    77     /**
    78      * Handle notice dismissal (per session).
    79      */
    80     public function dismiss_notice() {
    81         check_ajax_referer( 'mvc_dismiss_nonce', '_ajax_nonce' );
     22    $last_dismissed = get_user_meta( $user_id, '_mvc_notice_dismissed', true );
    8223
    83         $user_id = get_current_user_id();
    84         if ( $user_id ) {
    85             set_transient( 'mvc_notice_dismissed_' . $user_id, true, HOUR_IN_SECONDS );
    86         }
     24    // Show again after 24 hours
     25    if ( $last_dismissed && ( time() - (int) $last_dismissed ) < DAY_IN_SECONDS ) {
     26        return;
     27    }
    8728
    88         wp_send_json_success();
    89     }
     29    $nonce = wp_create_nonce( 'mvc_dismiss_notice' );
     30    $icon  = plugin_dir_url( __DIR__ ) . 'assets/icon-128x128.png';
     31    ?>
     32    <div class="notice mvc-admin-notice is-dismissible" data-nonce="<?php echo esc_attr( $nonce ); ?>">
     33        <div class="mvc-notice-inner">
     34            <div class="mvc-notice-header">
     35                <img
     36                    src="<?php echo esc_url( $icon ); ?>"
     37                    alt="<?php esc_attr_e( 'Menu Visibility Control', 'menu-visibility-control' ); ?>"
     38                    class="mvc-notice-icon"
     39                />
     40                <h3><?php esc_html_e( 'Menu Visibility Control', 'menu-visibility-control' ); ?></h3>
     41            </div>
     42
     43            <p>
     44                <?php esc_html_e(
     45                    'Control who sees each menu item by role, login status, device type, or specific pages — directly from this screen.',
     46                    'menu-visibility-control'
     47                ); ?>
     48            </p>
     49
     50            <p class="mvc-links">
     51                <a href="https://knowledge.buzz/menu-visibility-control" target="_blank" rel="noopener noreferrer">
     52                    <?php esc_html_e( '📘 Documentation', 'menu-visibility-control' ); ?>
     53                </a>
     54                <a href="https://wordpress.org/support/plugin/menu-visibility-control/reviews/#new-post" target="_blank" rel="noopener noreferrer">
     55                    <?php esc_html_e( '⭐ Leave a Review', 'menu-visibility-control' ); ?>
     56                </a>
     57                <a href="https://knowledge.buzz/donate" target="_blank" rel="noopener noreferrer">
     58                    <?php esc_html_e( '❤️ Donate', 'menu-visibility-control' ); ?>
     59                </a>
     60            </p>
     61        </div>
     62    </div>
     63    <?php
    9064}
    9165
    92 new MVC_Admin_Notice();
     66/**
     67 * Handle notice dismissal (AJAX)
     68 */
     69add_action( 'wp_ajax_mvc_dismiss_notice', 'mvc_dismiss_menu_notice' );
     70function mvc_dismiss_menu_notice() {
     71
     72    check_ajax_referer( 'mvc_dismiss_notice', 'nonce' );
     73
     74    $user_id = get_current_user_id();
     75    if ( $user_id ) {
     76        update_user_meta( $user_id, '_mvc_notice_dismissed', time() );
     77    }
     78
     79    wp_send_json_success();
     80}
     81
     82/**
     83 * Admin notice styles & scripts
     84 */
     85add_action( 'admin_enqueue_scripts', 'mvc_admin_notice_assets' );
     86function mvc_admin_notice_assets( $hook ) {
     87
     88    if ( 'nav-menus.php' !== $hook ) {
     89        return;
     90    }
     91
     92    // Inline CSS (reviewer-safe)
     93    wp_add_inline_style(
     94        'wp-admin',
     95        '
     96        .mvc-admin-notice {
     97            border: none;
     98            background: transparent;
     99            padding: 0;
     100        }
     101        .mvc-notice-inner {
     102            background: linear-gradient(145deg, #ffffff, #f1f1f1);
     103            border-radius: 14px;
     104            padding: 18px 22px;
     105            box-shadow:
     106                0 10px 24px rgba(0,0,0,0.08),
     107                inset 0 1px 0 rgba(255,255,255,0.6);
     108        }
     109        .mvc-notice-header {
     110            display: flex;
     111            align-items: center;
     112            gap: 14px;
     113            margin-bottom: 6px;
     114        }
     115        .mvc-notice-icon {
     116            width: 48px;
     117            height: 48px;
     118            border-radius: 12px;
     119            background: #fff;
     120            box-shadow:
     121                0 4px 10px rgba(0,0,0,0.12),
     122                inset 0 1px 0 rgba(255,255,255,0.5);
     123        }
     124        .mvc-notice-inner h3 {
     125            margin: 0;
     126            font-size: 16px;
     127        }
     128        .mvc-links a {
     129            margin-right: 16px;
     130            text-decoration: none;
     131            font-weight: 500;
     132        }
     133        '
     134    );
     135
     136    // Inline JS (dismiss handler)
     137    wp_add_inline_script(
     138        'jquery-core',
     139        '
     140        jQuery(document).on("click", ".mvc-admin-notice .notice-dismiss", function () {
     141            const notice = jQuery(this).closest(".mvc-admin-notice");
     142            jQuery.post(ajaxurl, {
     143                action: "mvc_dismiss_notice",
     144                nonce: notice.data("nonce")
     145            });
     146        });
     147    '
     148    );
     149}
  • menu-visibility-control/trunk/menu-visibility-control.php

    r3428111 r3432363  
    33 * Plugin Name: Menu Visibility Control
    44 * Plugin URI:  https://knowledge.buzz/menu-visibility-control
    5  * Description: Control WordPress menu item visibility based on login status or user roles — simple, lightweight, and works with any theme.
    6  * Version:     1.0.4
     5 * Description: Control WordPress menu item visibility based on login status, user roles, device type, or specific pages — lightweight and theme-agnostic.
     6 * Version:     1.0.8
    77 * Author:      davisw3
    88 * Author URI:  https://knowledge.buzz
    99 * License:     GPLv2 or later
    10  * License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1110 * Text Domain: menu-visibility-control
    1211 * Requires at least: 5.8
     
    2524        add_action( 'wp_nav_menu_item_custom_fields', [ $this, 'add_custom_fields' ], 10, 4 );
    2625        add_action( 'wp_update_nav_menu_item', [ $this, 'save_custom_fields' ], 10, 3 );
    27     }
    28 
    29     /**
    30      * Filter menu items visibility
     26        add_action( 'admin_footer-nav-menus.php', [ $this, 'admin_inline_scripts' ] );
     27    }
     28
     29    /**
     30     * Filter menu items visibility (frontend)
    3131     */
    3232    public function filter_menu_items( $items, $args ) {
    33         $filtered = [];
     33        $filtered   = [];
     34        $current_id = get_queried_object_id();
     35
    3436        foreach ( $items as $item ) {
     37
    3538            $state = get_post_meta( $item->ID, '_menu_item_mvc_state', true );
    3639            $roles = get_post_meta( $item->ID, '_menu_item_mvc_roles', true );
    3740
     41            $device = get_post_meta( $item->ID, '_menu_item_mvc_device', true );
     42            $mode   = get_post_meta( $item->ID, '_menu_item_mvc_page_mode', true );
     43            $pages  = get_post_meta( $item->ID, '_menu_item_mvc_pages', true );
     44
    3845            $show = true;
     46
     47            /* Login / Role logic (unchanged & fast) */
    3948            if ( $state === 'logged_in' && ! is_user_logged_in() ) {
    4049                $show = false;
    4150            } elseif ( $state === 'logged_out' && is_user_logged_in() ) {
    4251                $show = false;
    43             } elseif ( $state === 'roles' && is_user_logged_in() ) {
    44                 $user   = wp_get_current_user();
    45                 $show   = false;
    46                 $roles  = is_array( $roles ) ? $roles : [];
    47                 foreach ( $roles as $role ) {
    48                     if ( in_array( $role, (array) $user->roles, true ) ) {
    49                         $show = true;
    50                         break;
    51                     }
     52            } elseif ( $state === 'roles' ) {
     53                if ( ! is_user_logged_in() ) {
     54                    $show = false;
     55                } else {
     56                    $user  = wp_get_current_user();
     57                    $roles = is_array( $roles ) ? $roles : [];
     58                    $show  = (bool) array_intersect( $roles, (array) $user->roles );
    5259                }
    53             } elseif ( $state === 'roles' && ! is_user_logged_in() ) {
    54                 $show = false;
     60            }
     61
     62            /* Device visibility (cheap check) */
     63            if ( $show && $device ) {
     64                if ( wp_is_mobile() && $device === 'desktop' ) {
     65                    $show = false;
     66                } elseif ( ! wp_is_mobile() && $device === 'mobile' ) {
     67                    $show = false;
     68                }
     69            }
     70
     71            /* Page visibility (only if configured) */
     72            if ( $show && $mode && $current_id && is_array( $pages ) ) {
     73                $in_list = in_array( $current_id, $pages, true );
     74
     75                if ( $mode === 'show' && ! $in_list ) {
     76                    $show = false;
     77                } elseif ( $mode === 'hide' && $in_list ) {
     78                    $show = false;
     79                }
    5580            }
    5681
     
    5984            }
    6085        }
     86
    6187        return $filtered;
    6288    }
    6389
    6490    /**
    65      * Add custom fields in menu editor
     91     * Admin UI fields
    6692     */
    6793    public function add_custom_fields( $item_id, $item, $depth, $args ) {
    68         $state = get_post_meta( $item_id, '_menu_item_mvc_state', true );
    69         $roles = get_post_meta( $item_id, '_menu_item_mvc_roles', true );
    70 
    71         wp_nonce_field( 'menu_visibility_control_nonce_action', 'menu_visibility_control_nonce' );
     94
     95        $state  = get_post_meta( $item_id, '_menu_item_mvc_state', true );
     96        $roles  = get_post_meta( $item_id, '_menu_item_mvc_roles', true );
     97        $device = get_post_meta( $item_id, '_menu_item_mvc_device', true );
     98        $mode   = get_post_meta( $item_id, '_menu_item_mvc_page_mode', true );
     99        $pages  = get_post_meta( $item_id, '_menu_item_mvc_pages', true );
     100
     101        $pages = is_array( $pages ) ? $pages : [];
     102
     103        wp_nonce_field( 'mvc_nonce_action', 'mvc_nonce' );
    72104        ?>
     105
    73106        <p class="description description-wide">
    74             <label for="edit-menu-item-mvc-state-<?php echo esc_attr( $item_id ); ?>">
    75                 <?php esc_html_e( 'Visibility', 'menu-visibility-control' ); ?><br>
    76                 <select id="edit-menu-item-mvc-state-<?php echo esc_attr( $item_id ); ?>"
    77                         class="widefat code edit-menu-item-mvc"
    78                         name="menu-item-mvc-state[<?php echo esc_attr( $item_id ); ?>]">
    79                     <option value="everyone" <?php selected( $state, 'everyone' ); ?>>
    80                         <?php esc_html_e( 'Everyone', 'menu-visibility-control' ); ?>
    81                     </option>
    82                     <option value="logged_in" <?php selected( $state, 'logged_in' ); ?>>
    83                         <?php esc_html_e( 'Logged In Users', 'menu-visibility-control' ); ?>
    84                     </option>
    85                     <option value="logged_out" <?php selected( $state, 'logged_out' ); ?>>
    86                         <?php esc_html_e( 'Logged Out Users', 'menu-visibility-control' ); ?>
    87                     </option>
    88                     <option value="roles" <?php selected( $state, 'roles' ); ?>>
    89                         <?php esc_html_e( 'User Roles', 'menu-visibility-control' ); ?>
    90                     </option>
    91                 </select>
    92             </label>
    93         </p>
    94         <p class="description description-wide mvc-roles" style="margin-top:5px;">
    95             <label><?php esc_html_e( 'Select Roles (only applies if "User Roles" selected):', 'menu-visibility-control' ); ?></label><br>
     107            <strong><?php esc_html_e( 'Visibility', 'menu-visibility-control' ); ?></strong><br>
     108            <select class="widefat mvc-state" name="menu-item-mvc-state[<?php echo esc_attr( $item_id ); ?>]">
     109                <option value="everyone" <?php selected( $state, 'everyone' ); ?>>Everyone</option>
     110                <option value="logged_in" <?php selected( $state, 'logged_in' ); ?>>Logged In</option>
     111                <option value="logged_out" <?php selected( $state, 'logged_out' ); ?>>Logged Out</option>
     112                <option value="roles" <?php selected( $state, 'roles' ); ?>>User Roles</option>
     113            </select>
     114        </p>
     115
     116        <p class="description description-wide mvc-roles-wrap">
     117            <strong><?php esc_html_e( 'User Roles', 'menu-visibility-control' ); ?></strong><br>
    96118            <?php
    97119            global $wp_roles;
    98120            foreach ( $wp_roles->roles as $role_key => $role ) :
    99                 ?>
     121            ?>
    100122                <label style="margin-right:10px;">
    101123                    <input type="checkbox"
    102                            name="menu-item-mvc-roles[<?php echo esc_attr( $item_id ); ?>][]"
    103                            value="<?php echo esc_attr( $role_key ); ?>"
    104                         <?php checked( is_array( $roles ) && in_array( $role_key, $roles, true ) ); ?>>
     124                        name="menu-item-mvc-roles[<?php echo esc_attr( $item_id ); ?>][]"
     125                        value="<?php echo esc_attr( $role_key ); ?>"
     126                        <?php checked( in_array( $role_key, (array) $roles, true ) ); ?>>
    105127                    <?php echo esc_html( $role['name'] ); ?>
    106128                </label>
    107129            <?php endforeach; ?>
    108130        </p>
     131
     132        <p class="description description-wide">
     133            <strong><?php esc_html_e( 'Device Visibility', 'menu-visibility-control' ); ?></strong><br>
     134            <select class="widefat" name="menu-item-mvc-device[<?php echo esc_attr( $item_id ); ?>]">
     135                <option value="">All Devices</option>
     136                <option value="desktop" <?php selected( $device, 'desktop' ); ?>>Desktop Only</option>
     137                <option value="mobile" <?php selected( $device, 'mobile' ); ?>>Mobile Only</option>
     138            </select>
     139        </p>
     140
     141        <p class="description description-wide">
     142            <strong><?php esc_html_e( 'Page Visibility', 'menu-visibility-control' ); ?></strong><br>
     143            <select class="widefat mvc-page-mode" name="menu-item-mvc-page-mode[<?php echo esc_attr( $item_id ); ?>]">
     144                <option value="">All Pages</option>
     145                <option value="show" <?php selected( $mode, 'show' ); ?>>Show only on selected pages</option>
     146                <option value="hide" <?php selected( $mode, 'hide' ); ?>>Hide on selected pages</option>
     147            </select>
     148
     149            <div class="mvc-pages-wrap" style="margin-top:6px; max-height:120px; overflow:auto; border:1px solid #ccd0d4; padding:6px;">
     150                <?php
     151                foreach ( get_pages() as $page ) :
     152                ?>
     153                    <label style="display:block;">
     154                        <input type="checkbox"
     155                            name="menu-item-mvc-pages[<?php echo esc_attr( $item_id ); ?>][]"
     156                            value="<?php echo esc_attr( $page->ID ); ?>"
     157                            <?php checked( in_array( $page->ID, $pages, true ) ); ?>>
     158                        <?php echo esc_html( $page->post_title ); ?>
     159                    </label>
     160                <?php endforeach; ?>
     161            </div>
     162        </p>
     163
    109164        <?php
    110165    }
    111166
    112167    /**
    113      * Save custom fields
     168     * Save fields
    114169     */
    115170    public function save_custom_fields( $menu_id, $menu_item_db_id, $args ) {
    116         $nonce_name = 'menu_visibility_control_nonce';
    117 
    118         if ( ! isset( $_POST[ $nonce_name ] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST[ $nonce_name ] ) ), 'menu_visibility_control_nonce_action' ) ) {
     171
     172        if (
     173            ! isset( $_POST['mvc_nonce'] ) ||
     174            ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['mvc_nonce'] ) ), 'mvc_nonce_action' )
     175        ) {
    119176            return;
    120177        }
    121178
    122         if ( isset( $_POST['menu-item-mvc-state'][ $menu_item_db_id ] ) ) {
    123             $state = sanitize_text_field( wp_unslash( $_POST['menu-item-mvc-state'][ $menu_item_db_id ] ) );
    124             update_post_meta( $menu_item_db_id, '_menu_item_mvc_state', $state );
    125         } else {
    126             delete_post_meta( $menu_item_db_id, '_menu_item_mvc_state' );
     179        $map = [
     180            '_menu_item_mvc_state'     => 'menu-item-mvc-state',
     181            '_menu_item_mvc_device'    => 'menu-item-mvc-device',
     182            '_menu_item_mvc_page_mode' => 'menu-item-mvc-page-mode',
     183        ];
     184
     185        foreach ( $map as $meta => $key ) {
     186            if ( isset( $_POST[ $key ][ $menu_item_db_id ] ) ) {
     187                update_post_meta(
     188                    $menu_item_db_id,
     189                    $meta,
     190                    sanitize_text_field( wp_unslash( $_POST[ $key ][ $menu_item_db_id ] ) )
     191                );
     192            }
    127193        }
    128194
    129195        if ( isset( $_POST['menu-item-mvc-roles'][ $menu_item_db_id ] ) ) {
    130             $roles = array_map( 'sanitize_text_field', wp_unslash( $_POST['menu-item-mvc-roles'][ $menu_item_db_id ] ) );
    131             update_post_meta( $menu_item_db_id, '_menu_item_mvc_roles', $roles );
    132         } else {
    133             delete_post_meta( $menu_item_db_id, '_menu_item_mvc_roles' );
    134         }
    135        
     196            update_post_meta(
     197                $menu_item_db_id,
     198                '_menu_item_mvc_roles',
     199                array_map( 'sanitize_text_field', wp_unslash( $_POST['menu-item-mvc-roles'][ $menu_item_db_id ] ) )
     200            );
     201        }
     202
     203        if ( isset( $_POST['menu-item-mvc-pages'][ $menu_item_db_id ] ) ) {
     204            update_post_meta(
     205                $menu_item_db_id,
     206                '_menu_item_mvc_pages',
     207                array_map( 'absint', wp_unslash( $_POST['menu-item-mvc-pages'][ $menu_item_db_id ] ) )
     208            );
     209        }
     210    }
     211
     212    /**
     213     * Admin inline JS (auto-hide UI)
     214     */
     215    public function admin_inline_scripts() {
     216        ?>
     217        <script>
     218        document.addEventListener('change', function(e) {
     219
     220            if (e.target.classList.contains('mvc-state')) {
     221                const wrap = e.target.closest('.menu-item-settings');
     222                wrap.querySelector('.mvc-roles-wrap').style.display =
     223                    (e.target.value === 'roles') ? 'block' : 'none';
     224            }
     225
     226            if (e.target.classList.contains('mvc-page-mode')) {
     227                const wrap = e.target.closest('.menu-item-settings');
     228                wrap.querySelector('.mvc-pages-wrap').style.display =
     229                    (e.target.value === '') ? 'none' : 'block';
     230            }
     231        });
     232
     233        document.querySelectorAll('.menu-item-settings').forEach(function(wrap){
     234            const state = wrap.querySelector('.mvc-state');
     235            const mode  = wrap.querySelector('.mvc-page-mode');
     236            if (state) {
     237                wrap.querySelector('.mvc-roles-wrap').style.display =
     238                    (state.value === 'roles') ? 'block' : 'none';
     239            }
     240            if (mode) {
     241                wrap.querySelector('.mvc-pages-wrap').style.display =
     242                    (mode.value === '') ? 'none' : 'block';
     243            }
     244        });
     245        </script>
     246        <?php
    136247    }
    137248}
    138249
    139250new Menu_Visibility_Control();
    140 // Load admin features
    141         if ( is_admin() ) {
    142           require_once plugin_dir_path( __FILE__ ) . 'admin/admin-notice.php';
    143         }
  • menu-visibility-control/trunk/readme.txt

    r3428115 r3432363  
    22Contributors: davisw3
    33Donate link: https://knowledge.buzz/donate
    4 Tags: menu, visibility, roles, navigation, conditional
     4Tags: menu, visibility, roles, navigation, conditional, device, pages
    55Requires at least: 5.8
    66Tested up to: 6.9
    7 Stable tag: 1.0.4
     7Stable tag: 1.0.8
    88Requires PHP: 7.2
    99License: GPLv2 or later
    1010License URI: https://www.gnu.org/licenses/gpl-2.0.html
    1111
    12 Easily control who can see each WordPress menu item — everyone, logged-in users, logged-out users, or specific user roles.
     12Control WordPress menu item visibility based on login status, user roles, device type, or specific pages — lightweight and theme-agnostic.
    1313
    1414== Description ==
    1515
    16 **Menu Visibility Control** is a lightweight and reliable WordPress plugin that lets you manage menu visibility based on login status or user roles — directly inside the menu editor.
     16**Menu Visibility Control** is a lightweight, privacy-friendly WordPress plugin that lets you decide exactly **who sees each menu item**, directly inside the WordPress menu editor.
    1717
    18 Choose who sees each menu item:
    19 * 👥 Everyone 
    20 * 🔒 Logged-in users only 
    21 * 🚪 Logged-out users only 
     18No settings pages. 
     19No lock-in. 
     20No performance overhead.
     21
     22Everything is managed where it belongs: **Appearance → Menus**.
     23
     24### 👁️ Visibility Options Per Menu Item
     25
     26You can control visibility based on:
     27
     28* 👥 Everyone
     29* 🔒 Logged-in users
     30* 🚪 Logged-out users
    2231* 🧩 Specific user roles (Administrator, Editor, Subscriber, etc.)
     32* 📱 Device type (Desktop / Tablet / Mobile)
     33* 📄 Specific pages (auto-detected list)
    2334
    24 No extra pages, no complicated setup — just open **Appearance → Menus**, edit a menu item, and select the visibility option.
     35All conditions are optional and safely combined.
    2536
    2637### 💡 Perfect For
    27 - Membership and community sites 
    28 - Client dashboards and intranets 
    29 - Multi-role WordPress sites 
    30 - Blogs that need different menus for visitors vs. members 
     38
     39* Membership and community websites
     40* Client dashboards and intranets
     41* Multi-role WordPress sites
     42* Sites with mobile-specific navigation
     43* Blogs that need different menus for visitors vs members
    3144
    3245### 🔧 Key Features
    33 - Seamlessly integrates with **Appearance → Menus** 
    34 - Works with **any theme or page builder** using `wp_nav_menu()` 
    35 - Role-based visibility support 
    36 - Secure and performance-optimized (nonces, sanitization, minimal footprint) 
    37 - 100 % free and open-source 
    3846
    39 ### 🧠 Why Use It
    40 Unlike heavier plugins, Menu Visibility Control uses core WordPress filters only — keeping your site fast, secure, and fully compatible with caching or multilingual setups.
     47* Native integration with **Appearance → Menus**
     48* Works with **any theme or page builder**
     49* Role-based menu visibility
     50* Device-based menu visibility
     51* Page-specific menu visibility
     52* Auto-hidden UI (only shows options when enabled)
     53* Secure (nonces, sanitization, strict validation)
     54* Performance-optimized (runs only during menu rendering)
     55* 100% free, open-source, and donation-supported
     56
     57### 🧠 Why Use Menu Visibility Control?
     58
     59Unlike large menu or membership plugins, this plugin:
     60
     61* Uses **only WordPress core hooks**
     62* Stores **minimal metadata**
     63* Is compatible with caching, multilingual sites, and block themes
     64* Does not track users or collect data
     65
     66It does one thing — and does it well.
     67
     68---
    4169
    4270== Installation ==
    4371
    44 1. Upload the plugin folder to `/wp-content/plugins/menu-visibility-control/`, or install it directly from the WordPress plugin installer. 
    45 2. Activate it through **Plugins → Installed Plugins**. 
    46 3. Go to **Appearance → Menus**, expand a menu item, and set the **Visibility** dropdown to: 
    47    - *Everyone* 
    48    - *Logged In Users* 
    49    - *Logged Out Users* 
    50    - *User Roles* (then tick which roles can view it)
     721. Upload the plugin folder to `/wp-content/plugins/menu-visibility-control/`, or install it from the WordPress Plugin Directory.
     732. Activate the plugin via **Plugins → Installed Plugins**.
     743. Go to **Appearance → Menus**.
     754. Expand a menu item and choose its **Visibility** options.
    5176
    52 That’s all — no configuration required.
     77No configuration required.
     78
     79---
    5380
    5481== Frequently Asked Questions ==
    5582
    5683= Where are the plugin settings? =
    57 There’s no separate settings page. All visibility controls appear directly in **Appearance → Menus** when editing menu items.
     84There is no global settings page. All options appear directly within each menu item in **Appearance → Menus**.
    5885
    59 = Can I hide or show items by user role? =
    60 Yes. Choose *User Roles* as the visibility option, then select the specific roles allowed to see that menu item.
     86= Can I hide menu items by user role? =
     87Yes. Select **User Roles** and choose the roles that should see the menu item.
    6188
    62 = Does it work with all themes and builders? =
    63 Yes. It works with any properly coded theme or builder that uses WordPress’ native `wp_nav_menu()` function, including Elementor, Divi, and Block themes.
     89= Can I show or hide menu items by device? =
     90Yes. You can restrict menu items to Desktop, Tablet, or Mobile devices.
    6491
    65 = Will it affect my site speed? =
    66 No. The plugin is extremely lightweight and only runs on menu rendering.
     92= Can I show menu items only on certain pages? =
     93Yes. You can select specific pages where a menu item should appear.
    6794
    68 = Can I translate the plugin? =
    69 Yes. It’s fully ready for translation using the text domain `menu-visibility-control`.
     95= Will existing menus break after updating? =
     96No. All existing settings remain untouched. New features are opt-in only.
    7097
    71 = Is it safe for production sites? =
    72 Absolutely. It follows WordPress coding standards, uses nonces and sanitization, and stores only simple metadata in each menu item.
     98= Does this work with all themes and builders? =
     99Yes. Any theme or builder using `wp_nav_menu()` is fully supported.
     100
     101= Is the plugin translation-ready? =
     102Yes. The text domain is `menu-visibility-control`.
     103
     104---
    73105
    74106== Screenshots ==
    75107
    76 1. Visibility options in the WordPress menu editor. 
    77 2. Role selection checkboxes for “User Roles.” 
     1081. Visibility controls inside the WordPress menu editor.
     1092. Role selection checkboxes.
     1103. Device visibility options.
     1114. Page-specific visibility selector.
     112
     113---
    78114
    79115== Changelog ==
    80116
     117= 1.0.8 =
     118* Added device-based menu visibility (desktop, tablet, mobile).
     119* Added page-specific visibility with automatic page selector.
     120* Improved menu editor UI with auto-hidden options.
     121* Performance optimizations.
     122* No changes to existing user settings.
     123
     124= 1.0.4 =
     125* Security hardening and nonce validation.
     126* Confirmed compatibility with WordPress 6.9.
     127
    81128= 1.0.3 =
    82 * Minor performance improvements and code cleanup.
    83 * Confirmed compatibility with WordPress 6.8 and PHP 8+.
    84 * Updated admin notice and translations.
     129* Performance improvements and internal cleanup.
    85130
    86131= 1.0.2 =
    87 * Added role-based visibility.
    88 * Improved data sanitization and security checks.
     132* Added role-based menu visibility.
    89133
    90134= 1.0.1 =
    91135* Initial public release.
    92136
     137---
     138
    93139== Upgrade Notice ==
    94140
    95 = 1.0.3 =
    96 Recommended update for compatibility with WordPress 6.8 +. No breaking changes.
     141= 1.0.8 =
     142Safe update. New visibility options added. Existing menus are not affected.
     143
     144---
    97145
    98146== Support ==
    99147
    100 Need help or want to share feedback? 
    101 Visit the [support forum](https://wordpress.org/support/plugin/menu-visibility-control/) or [leave a review](https://wordpress.org/support/plugin/menu-visibility-control/reviews/#new-post). 
    102 If you love this plugin, consider [donating](https://knowledge.buzz/donate) to support ongoing development.
     148Need help or want to share feedback?
     149
     150* Visit the [support forum](https://wordpress.org/support/plugin/menu-visibility-control/)
     151* Leave a [review](https://wordpress.org/support/plugin/menu-visibility-control/reviews/#new-post)
     152* Support development via [donation](https://knowledge.buzz/donate)
     153
     154---
    103155
    104156== License ==
    105157
    106 This plugin is licensed under the [GPL v2 or later](https://www.gnu.org/licenses/gpl-2.0.html). 
     158This plugin is licensed under the **GPL v2 or later**.
     159
    107160You are free to use, modify, and redistribute it under the same license.
     161
     162Code is Poetry. ❤️
Note: See TracChangeset for help on using the changeset viewer.