WordPress menu navigation by user role plugins

Development

Evaluating plugins

A client has the requirement to customize menu options by user role. Before writing the functionality myself I try to find and evaluate plugins in the WordPress repository which might fit my need.

Guidelines

So, which guidelines do I use to evaluate plugins:

  • Clean code
  • UI implementation
  • Recently updated, or at least bumped version compatibility
  • No stripped plugins in favour of “Pro” versions
  • No Freemius implementation

Available plugins

After digging around I came across the following plugins:

  • User Menus
  • Nav Menu Roles
  • If Menu
  • Menu By User Roles

Let’ s evaluate these plugins based on above guidelines like codebase, UI, functionality and distribution model.

Plugin: User Menus

Pros

  • Ability to add {text} tags to menu

Cons

  • Freemius intergration
  • Not updated for over a year
  • Bulky codebase
  • UI when many roles available

Link to WordPress Plugin

Plugin: Nav Menu Roles

Pros

  • Does what it is designed for
  • Donate based model

Cons

  • Bulky codebase
  • UI when many roles available

Link to WordPress Plugin

Plugin: If Menu

Pros

  • Minimalist UI, but it stands out due to its background and colors choices

Cons

  • Not updated for over 6 months
  • “Pro” version available
  • Bulky codebase (composer packages)
  • Lot of custom javascript

Link to WordPress Plugin

Plugin: Menu By User Roles

Pros

  • Very nice minimalist UI
  • Extemely lean codebase

Cons

  • None!

Link to WordPress Plugin.

Conclusion

Clearly the winner is Menu By User Roles plugin! It is ahead by miles in each category. Kudos to the developer by keeping his eye on the functionality required.

What strikes me is that all the other plugins use a form of menu walkers to achieve the goal. While the Menu By User Roles plugin uses the wp_nav_menu_objects filter. Said filter is much more efficient and elegant.

Code example

I can not stress enough how lean the codebase is. Only four tiny hooks and filters are used to provide the functionality we require! Because the code is so clean I will list it below.

/**
 * Enqueue Select2 scripts and styles for the menu.
 */
function menuby_user_roles_enqueue_select2() {
	$screen = get_current_screen();
	if ( 'nav-menus' === $screen->id ) {
		wp_enqueue_style( 'menuby-user-roles-select2-style', plugins_url( 'assets/css/select2.min.css', __FILE__ ), array(), MBUR_PLUGIN_VERSION );
		wp_enqueue_script( 'menuby-user-roles-select2-script', plugins_url( 'assets/js/select2.min.js', __FILE__ ), array( 'jquery' ), MBUR_PLUGIN_VERSION, true );
		wp_enqueue_script( 'menuby-user-roles-main-script', plugins_url( 'assets/js/main.js', __FILE__ ), array( 'jquery' ), MBUR_PLUGIN_VERSION, true );
	}
}
add_action( 'admin_enqueue_scripts', 'menuby_user_roles_enqueue_select2' );


/**
 * Render a custom user role selection field for a menu item.
 *
 * @param int $item_id Menu item ID.
 */
function menuby_user_roles_wp_menu_item_user_role_section( $item_id ) {

	$selected_roles = get_post_meta( $item_id, '_wp_menu_item_user_roles', true );
	$roles          = get_editable_roles();

	echo '<p class="field-wp-user-roles description description-wide">';
	echo '<label for="edit-menu-item-user-role-' . esc_attr( $item_id ) . '">';
	echo 'Choose User Roles <br/>';
	echo '<select style="width: 100%" multiple="multiple" class="widefat menuby-user-roles-dropdown" name="menuby_user_roles_menu_item_roles[' . esc_attr( $item_id ) . '][]" id="wp-mbur-menu-item-roles-' . esc_attr( $item_id ) . '">';

	// Predefined options.
	echo '<option value="all" ' . ( empty( $selected_roles ) || ( is_array( $selected_roles ) && in_array( 'all', $selected_roles, true ) ) ? 'selected' : '' ) . '>All</option>';
	echo '<option value="unauthenticated" ' . ( is_array( $selected_roles ) && in_array( 'unauthenticated', $selected_roles, true ) ? 'selected' : '' ) . '>Unauthenticated</option>';

	// User roles.
	foreach ( $roles as $role_key => $role ) {
		$selected = ( is_array( $selected_roles ) && in_array( $role_key, $selected_roles, true ) ) ? 'selected' : '';
		echo '<option value="' . esc_attr( $role_key ) . '" ' . esc_attr( $selected ) . '>' . esc_html( $role['name'] ) . '</option>';
	}

	echo '</select>';

	// Add nonce field to the form.
	wp_nonce_field( 'menuby_user_roles_nonce_action', 'menuby_user_roles_nonce' );

	echo '</label></p>';
}

add_action( 'wp_nav_menu_item_custom_fields', 'menuby_user_roles_wp_menu_item_user_role_section', 10, 2 );

/**
 * Save user role data for a menu item.
 *
 * @param int $menu_id         Menu ID.
 * @param int $menu_item_db_id Menu item ID.
 */
function menuby_user_roles_save_menu_item_user_role_data( $menu_id, $menu_item_db_id ) {
	if ( ! isset( $_POST['menuby_user_roles_nonce'] ) || ! wp_verify_nonce( sanitize_text_field( wp_unslash( $_POST['menuby_user_roles_nonce'] ) ), 'menuby_user_roles_nonce_action' ) ) {
		return;
	}

	$selected_roles = isset( $_POST['menuby_user_roles_menu_item_roles'][ $menu_item_db_id ] )
		? array_filter( array_unique( array_map( 'sanitize_text_field', wp_unslash( $_POST['menuby_user_roles_menu_item_roles'][ $menu_item_db_id ] ) ) ) )
		: '';

	update_post_meta( $menu_item_db_id, '_wp_menu_item_user_roles', $selected_roles );
}
add_action( 'wp_update_nav_menu_item', 'menuby_user_roles_save_menu_item_user_role_data', 10, 2 );


/**
 * Filter menu items for display on the front end based on user roles.
 *
 * @param array $items Menu items.
 * @return array Filtered menu items.
 */
function menuby_user_roles_filter_menu_items( $items ) {
	$user          = wp_get_current_user();
	$allowed_items = array();

	foreach ( $items as $item ) {
		$item_id        = $item->ID;
		$selected_roles = get_post_meta( $item_id, '_wp_menu_item_user_roles', true );

		if (
			! is_array( $selected_roles ) ||
			( in_array( 'all', $selected_roles, true ) ) ||
			( in_array( 'unauthenticated', $selected_roles, true ) && ! is_user_logged_in() ) ||
			( is_user_logged_in() && array_intersect( $selected_roles, $user->roles ) )
		) {
			$allowed_items[] = $item;
		}
	}

	return $allowed_items;
}
add_filter( 'wp_nav_menu_objects', 'menuby_user_roles_filter_menu_items' );

Thanks for reading! Please like this post if it was of any use to you.

Leave a Comment