roles[ $role ] ) ) { $valid_roles[] = $role; } } update_post_meta( $menu_item_id, self::META_VISIBILITY_ROLES, $valid_roles ); } else { delete_post_meta( $menu_item_id, self::META_VISIBILITY_ROLES ); } } /** * Copy visibility settings from one menu item to another. * * @param int $from_item_id Source menu item ID. * @param int $to_item_id Target menu item ID. */ public static function copy_visibility( $from_item_id, $to_item_id ) { self::save_visibility( $to_item_id, self::get_visibility( $from_item_id ), self::get_visibility_roles( $from_item_id ) ); } /** * Attach visibility data to menu item objects before revision storage. * * @param array $menu_items Menu item objects. * @return array */ public static function attach_visibility_to_menu_items( $menu_items ) { if ( empty( $menu_items ) || ! is_array( $menu_items ) ) { return $menu_items; } foreach ( $menu_items as $index => $item ) { $item_id = isset( $item->db_id ) ? (int) $item->db_id : 0; if ( $item_id <= 0 ) { continue; } $menu_items[ $index ]->amm_visibility = self::get_visibility( $item_id ); $menu_items[ $index ]->amm_visibility_roles = self::get_visibility_roles( $item_id ); } return $menu_items; } /** * Restore visibility settings from a revision menu item object. * * @param int $menu_item_id Menu item post ID. * @param object $item Menu item object from revision. */ public static function restore_visibility_from_item_object( $menu_item_id, $item ) { $visibility = self::VISIBILITY_EVERYONE; $roles = array(); if ( isset( $item->amm_visibility ) && ! empty( $item->amm_visibility ) ) { $visibility = $item->amm_visibility; } if ( isset( $item->amm_visibility_roles ) && is_array( $item->amm_visibility_roles ) ) { $roles = $item->amm_visibility_roles; } self::save_visibility( $menu_item_id, $visibility, $roles ); } /** * Save visibility from POST request for a menu item. * * @param int $menu_item_id Menu item post ID. */ public static function save_from_request( $menu_item_id ) { $menu_item_id = (int) $menu_item_id; // phpcs:disable WordPress.Security.NonceVerification.Missing -- Nonce already verified in caller dsamm_save_existing_menu(). if ( $menu_item_id <= 0 || empty( $_POST['menu-item-visibility'] ) || ! is_array( $_POST['menu-item-visibility'] ) ) { return; } $visibility_values = wp_unslash( $_POST['menu-item-visibility'] ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotSanitized if ( ! isset( $visibility_values[ $menu_item_id ] ) ) { return; } $visibility = sanitize_text_field( $visibility_values[ $menu_item_id ] ); $roles = array(); if ( self::VISIBILITY_LOGGED_IN === $visibility && ! empty( $_POST['menu-item-visibility-roles'][ $menu_item_id ] ) && is_array( $_POST['menu-item-visibility-roles'][ $menu_item_id ] ) ) { $roles = array_map( 'sanitize_key', wp_unslash( $_POST['menu-item-visibility-roles'][ $menu_item_id ] ) ); } // phpcs:enable WordPress.Security.NonceVerification.Missing self::save_visibility( $menu_item_id, $visibility, $roles ); } /** * Determine if the current visitor can see a menu item. * * @param string $visibility Visibility mode. * @param array $roles Allowed role slugs. * @return bool */ public static function user_can_see_item( $visibility, $roles = array() ) { if ( self::VISIBILITY_EVERYONE === $visibility ) { return true; } if ( self::VISIBILITY_LOGGED_OUT === $visibility ) { return ! is_user_logged_in(); } if ( self::VISIBILITY_LOGGED_IN === $visibility ) { if ( ! is_user_logged_in() ) { return false; } if ( empty( $roles ) ) { return true; } $user = wp_get_current_user(); foreach ( $roles as $role ) { if ( in_array( $role, (array) $user->roles, true ) ) { return true; } } return false; } return true; } /** * Filter nav menu items on the frontend. * * @param array $items Menu items. * @param object $args Menu arguments. * @return array */ public static function filter_nav_menu_items( $items, $args ) { if ( empty( $items ) || ! is_array( $items ) ) { return $items; } if ( is_admin() && ! wp_doing_ajax() ) { return $items; } $hidden_ids = array(); foreach ( $items as $item ) { if ( in_array( (int) $item->menu_item_parent, $hidden_ids, true ) ) { $hidden_ids[] = (int) $item->ID; continue; } $visibility = self::get_visibility( $item->ID ); $roles = self::get_visibility_roles( $item->ID ); if ( ! self::user_can_see_item( $visibility, $roles ) ) { $hidden_ids[] = (int) $item->ID; } } $changed = true; while ( $changed ) { $changed = false; foreach ( $items as $item ) { $item_id = (int) $item->ID; $parent_id = (int) $item->menu_item_parent; if ( ! in_array( $item_id, $hidden_ids, true ) && in_array( $parent_id, $hidden_ids, true ) ) { $hidden_ids[] = $item_id; $changed = true; } } } $hidden_ids = array_unique( $hidden_ids ); return array_values( array_filter( $items, function( $item ) use ( $hidden_ids ) { return ! in_array( (int) $item->ID, $hidden_ids, true ); } ) ); } /** * Render visibility fields for a menu item. * * @param int $menu_item_id Menu item db/post ID. * @param array $args Field arguments. */ public static function render_fields( $menu_item_id, $args = array() ) { $defaults = array( 'readonly' => false, 'locked_attr' => '', ); $args = wp_parse_args( $args, $defaults ); $menu_item_id = (int) $menu_item_id; $visibility = self::get_visibility( $menu_item_id ); $roles = self::get_visibility_roles( $menu_item_id ); $wp_roles = wp_roles(); $role_names = is_object( $wp_roles ) ? $wp_roles->get_names() : array(); $show_roles = ( self::VISIBILITY_LOGGED_IN === $visibility ); $readonly = ! empty( $args['readonly'] ); $locked_attr = ! empty( $args['locked_attr'] ) ? $args['locked_attr'] : ''; $disabled = ( $readonly || ! empty( $locked_attr ) ) ? 'disabled="disabled"' : ''; ?>