Changeset 3432363
- Timestamp:
- 01/05/2026 04:07:28 AM (7 weeks ago)
- Location:
- menu-visibility-control/trunk
- Files:
-
- 3 edited
-
admin/admin-notice.php (modified) (2 diffs)
-
menu-visibility-control.php (modified) (3 diffs)
-
readme.txt (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
menu-visibility-control/trunk/admin/admin-notice.php
r3383763 r3432363 1 1 <?php 2 /**3 * Admin Notice for Menu Visibility Control4 *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_Control9 */10 11 2 if ( ! defined( 'ABSPATH' ) ) { 12 3 exit; … … 14 5 15 6 /** 16 * Class MVC_Admin_Notice7 * Show admin notice on Appearance → Menus once per day 17 8 */ 18 class MVC_Admin_Notice { 9 add_action( 'admin_notices', 'mvc_show_menu_admin_notice' ); 10 function mvc_show_menu_admin_notice() { 19 11 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; 26 15 } 27 16 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; 75 20 } 76 21 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 ); 82 23 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 } 87 28 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 90 64 } 91 65 92 new MVC_Admin_Notice(); 66 /** 67 * Handle notice dismissal (AJAX) 68 */ 69 add_action( 'wp_ajax_mvc_dismiss_notice', 'mvc_dismiss_menu_notice' ); 70 function 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 */ 85 add_action( 'admin_enqueue_scripts', 'mvc_admin_notice_assets' ); 86 function 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 3 3 * Plugin Name: Menu Visibility Control 4 4 * 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. 45 * 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 7 7 * Author: davisw3 8 8 * Author URI: https://knowledge.buzz 9 9 * License: GPLv2 or later 10 * License URI: https://www.gnu.org/licenses/gpl-2.0.html11 10 * Text Domain: menu-visibility-control 12 11 * Requires at least: 5.8 … … 25 24 add_action( 'wp_nav_menu_item_custom_fields', [ $this, 'add_custom_fields' ], 10, 4 ); 26 25 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) 31 31 */ 32 32 public function filter_menu_items( $items, $args ) { 33 $filtered = []; 33 $filtered = []; 34 $current_id = get_queried_object_id(); 35 34 36 foreach ( $items as $item ) { 37 35 38 $state = get_post_meta( $item->ID, '_menu_item_mvc_state', true ); 36 39 $roles = get_post_meta( $item->ID, '_menu_item_mvc_roles', true ); 37 40 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 38 45 $show = true; 46 47 /* Login / Role logic (unchanged & fast) */ 39 48 if ( $state === 'logged_in' && ! is_user_logged_in() ) { 40 49 $show = false; 41 50 } elseif ( $state === 'logged_out' && is_user_logged_in() ) { 42 51 $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 ); 52 59 } 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 } 55 80 } 56 81 … … 59 84 } 60 85 } 86 61 87 return $filtered; 62 88 } 63 89 64 90 /** 65 * Ad d custom fields in menu editor91 * Admin UI fields 66 92 */ 67 93 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' ); 72 104 ?> 105 73 106 <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> 96 118 <?php 97 119 global $wp_roles; 98 120 foreach ( $wp_roles->roles as $role_key => $role ) : 99 ?>121 ?> 100 122 <label style="margin-right:10px;"> 101 123 <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( i s_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 ) ); ?>> 105 127 <?php echo esc_html( $role['name'] ); ?> 106 128 </label> 107 129 <?php endforeach; ?> 108 130 </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 109 164 <?php 110 165 } 111 166 112 167 /** 113 * Save customfields168 * Save fields 114 169 */ 115 170 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 ) { 119 176 return; 120 177 } 121 178 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 } 127 193 } 128 194 129 195 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 136 247 } 137 248 } 138 249 139 250 new Menu_Visibility_Control(); 140 // Load admin features141 if ( is_admin() ) {142 require_once plugin_dir_path( __FILE__ ) . 'admin/admin-notice.php';143 } -
menu-visibility-control/trunk/readme.txt
r3428115 r3432363 2 2 Contributors: davisw3 3 3 Donate link: https://knowledge.buzz/donate 4 Tags: menu, visibility, roles, navigation, conditional 4 Tags: menu, visibility, roles, navigation, conditional, device, pages 5 5 Requires at least: 5.8 6 6 Tested up to: 6.9 7 Stable tag: 1.0. 47 Stable tag: 1.0.8 8 8 Requires PHP: 7.2 9 9 License: GPLv2 or later 10 10 License URI: https://www.gnu.org/licenses/gpl-2.0.html 11 11 12 Easily control who can see each WordPress menu item — everyone, logged-in users, logged-out users, or specific user roles.12 Control WordPress menu item visibility based on login status, user roles, device type, or specific pages — lightweight and theme-agnostic. 13 13 14 14 == Description == 15 15 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 themenu 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. 17 17 18 Choose who sees each menu item: 19 * 👥 Everyone 20 * 🔒 Logged-in users only 21 * 🚪 Logged-out users only 18 No settings pages. 19 No lock-in. 20 No performance overhead. 21 22 Everything is managed where it belongs: **Appearance → Menus**. 23 24 ### 👁️ Visibility Options Per Menu Item 25 26 You can control visibility based on: 27 28 * 👥 Everyone 29 * 🔒 Logged-in users 30 * 🚪 Logged-out users 22 31 * 🧩 Specific user roles (Administrator, Editor, Subscriber, etc.) 32 * 📱 Device type (Desktop / Tablet / Mobile) 33 * 📄 Specific pages (auto-detected list) 23 34 24 No extra pages, no complicated setup — just open **Appearance → Menus**, edit a menu item, and select the visibility option.35 All conditions are optional and safely combined. 25 36 26 37 ### 💡 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 31 44 32 45 ### 🔧 Key Features 33 - Seamlessly integrates with **Appearance → Menus**34 - Works with **any theme or page builder** using `wp_nav_menu()`35 - Role-based visibility support36 - Secure and performance-optimized (nonces, sanitization, minimal footprint)37 - 100 % free and open-source38 46 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 59 Unlike 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 66 It does one thing — and does it well. 67 68 --- 41 69 42 70 == Installation == 43 71 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) 72 1. Upload the plugin folder to `/wp-content/plugins/menu-visibility-control/`, or install it from the WordPress Plugin Directory. 73 2. Activate the plugin via **Plugins → Installed Plugins**. 74 3. Go to **Appearance → Menus**. 75 4. Expand a menu item and choose its **Visibility** options. 51 76 52 That’s all — no configuration required. 77 No configuration required. 78 79 --- 53 80 54 81 == Frequently Asked Questions == 55 82 56 83 = Where are the plugin settings? = 57 There ’s no separate settings page. All visibility controls appear directly in **Appearance → Menus** when editing menu items.84 There is no global settings page. All options appear directly within each menu item in **Appearance → Menus**. 58 85 59 = Can I hide or showitems by user role? =60 Yes. Choose *User Roles* as the visibility option, then select the specific roles allowed to see thatmenu item.86 = Can I hide menu items by user role? = 87 Yes. Select **User Roles** and choose the roles that should see the menu item. 61 88 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? = 90 Yes. You can restrict menu items to Desktop, Tablet, or Mobile devices. 64 91 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? = 93 Yes. You can select specific pages where a menu item should appear. 67 94 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? = 96 No. All existing settings remain untouched. New features are opt-in only. 70 97 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? = 99 Yes. Any theme or builder using `wp_nav_menu()` is fully supported. 100 101 = Is the plugin translation-ready? = 102 Yes. The text domain is `menu-visibility-control`. 103 104 --- 73 105 74 106 == Screenshots == 75 107 76 1. Visibility options in the WordPress menu editor. 77 2. Role selection checkboxes for “User Roles.” 108 1. Visibility controls inside the WordPress menu editor. 109 2. Role selection checkboxes. 110 3. Device visibility options. 111 4. Page-specific visibility selector. 112 113 --- 78 114 79 115 == Changelog == 80 116 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 81 128 = 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. 85 130 86 131 = 1.0.2 = 87 * Added role-based visibility. 88 * Improved data sanitization and security checks. 132 * Added role-based menu visibility. 89 133 90 134 = 1.0.1 = 91 135 * Initial public release. 92 136 137 --- 138 93 139 == Upgrade Notice == 94 140 95 = 1.0.3 = 96 Recommended update for compatibility with WordPress 6.8 +. No breaking changes. 141 = 1.0.8 = 142 Safe update. New visibility options added. Existing menus are not affected. 143 144 --- 97 145 98 146 == Support == 99 147 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. 148 Need 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 --- 103 155 104 156 == License == 105 157 106 This plugin is licensed under the [GPL v2 or later](https://www.gnu.org/licenses/gpl-2.0.html). 158 This plugin is licensed under the **GPL v2 or later**. 159 107 160 You are free to use, modify, and redistribute it under the same license. 161 162 Code is Poetry. ❤️
Note: See TracChangeset
for help on using the changeset viewer.