Instantly share code, notes, and snippets.
Created
January 18, 2020 19:49
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save tharsheblows/47b0c72bff01841d2082d8e55196aa46 to your computer and use it in GitHub Desktop.
Adding conditional menu items to WordPress menus with custom type labels.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Conditional menu items and sane ways to add them to menus in the admin area. | |
* | |
* @package mjj_menu | |
*/ | |
class MJJ_Menu_Functions { | |
/** | |
* Our new menu types | |
* | |
* @var array | |
*/ | |
public $mjj_menu_types; | |
/** | |
* Sets up common variable and adds in hooks. | |
* | |
* @return void | |
*/ | |
public function init() { | |
// Use mjj-logged-in- as the prefix to check for logged in users. | |
// Use mjj-subs- as the prefix to check for subscribers. | |
// The last bit will tell you how to get the permalink from the id. | |
$this->mjj_menu_types = [ | |
'mjj-logged-in-page' => 'Shows for logged in users', | |
'mjj-subs-cat' => 'Shows for subscribers', | |
'mjj-subs-page' => 'Shows for subscribers', | |
]; | |
// This is what filters the menu items shown on the public facing site. | |
add_filter( 'wp_nav_menu_objects', array( $this, 'dynamic_menu_items' ), 10, 2 ); | |
// This adds a new metabox in the admin section from which you can choose menu items to add to your menu. | |
add_action( 'admin_head-nav-menus.php', array( $this, 'add_menu_metabox' ) ); | |
// This handles the new label | |
add_filter( 'wp_setup_nav_menu_item', array( $this, 'nav_type_labels' ) ); | |
// Re-add the url to the menu item. | |
add_action( 'wp_update_nav_menu_item', array( $this, 'add_back_url' ), 10, 3 ); | |
} | |
/** | |
* Filter out conditional menu items if the user should not see them. | |
* | |
* @param array $menu_items The menu items, sorted by each menu item's menu order. | |
* @param object $menu_args The menu object. | |
* @return array The final html for the menu. | |
*/ | |
public function dynamic_menu_items( $menu_items, $menu_args ) { | |
foreach ( $menu_items as $key => $item ) { | |
$type = $item->type; | |
// If a user is not logged in, unset the "mjj-logged-in" type menu items so they don't show in the menu. | |
if ( strpos( $type, 'mjj-logged-in' ) === 0 ) { | |
$is_logged_in = is_user_logged_in(); | |
if ( ! $is_logged_in ) { | |
unset( $menu_items[ $key ] ); // If the user is not logged in, remove the link. | |
continue; | |
} | |
} | |
// If a user has not bought a subscription, unset the "mjj-subscription-member" type menu items so they don't show in the menu. | |
if ( strpos( $type, 'mjj-subs-' ) === 0 ) { | |
$user_id = get_current_user_id(); | |
$is_subscription_user = mjj_user_has_subscription( $user_id ); // Made up function for the purpose of this post. | |
if ( ! $is_subscription_user ) { | |
unset( $menu_items[ $key ] ); | |
continue; | |
} | |
} | |
} | |
return $menu_items; | |
} | |
/** | |
* This takes the object id and object type and returns the correct link. | |
* | |
* @param string|int $object_id Will be used as an int. The id of the object the menu item is for. | |
* @param string $object_type The type of the object the menu item is for. | |
* @return string The url for the menu item. | |
*/ | |
private function get_link( $object_id, $object_type ) { | |
$url = ''; | |
switch ( $object_type ) { | |
case 'term': | |
$url = get_term_link( (int) $object_id ); | |
break; | |
// A note on get_term_link: if you want to use an id, it *must* be an int, otherwise it treats it as the slug. | |
// See: https://developer.wordpress.org/reference/functions/get_term_link/ . | |
case 'post': | |
$url = get_permalink( $object_id ); | |
break; | |
} | |
return $url; | |
} | |
/** | |
* Adds the metabox to the nav menu list. | |
* | |
* @return void | |
*/ | |
public function add_menu_metabox() { | |
add_meta_box( 'mjj_course_conditional_nav_links', __( 'MJJ Conditionals', 'mjjmenu' ), array( $this, 'menu_metabox' ), 'nav-menus', 'side', 'low' ); | |
} | |
/** | |
* Output menu metabox. | |
* | |
* @return void | |
*/ | |
public function menu_metabox() { | |
// A contact page for subscribers only. | |
$member_contact_page = get_page_by_path( 'member-contact-us', OBJECT ); | |
$one_off_things_to_add = [ | |
'home_on_checkout' => [ | |
'label' => 'Conditional home button for logged in users', // Only logged in people get the home button. Ha. | |
// Label is used in the checklist. | |
'title' => 'Home', | |
'class' => 'mjj-show-logged-in', | |
'id' => get_option( 'page_on_front' ), // id is the id of the page. | |
'type' => 'mjj-logged-in-page', | |
'object-type' => 'post', | |
// The type. 'mjj-logged-in' tells me it's for logged in users, 'page' tells me how to get the url. | |
// We'll use id and the type to get the link with get_permalink(). | |
], | |
'back_to_your_course' => [ | |
'label' => 'Member contact page (shows only for paid subscribers)', | |
'title' => 'Contact us', | |
'class' => 'mjj-subscription-members', | |
'id' => $member_contact_page->ID, // id is the id of the page. | |
'type' => 'mjj-subs-page', | |
'object-type' => 'post', | |
// The type. 'mjj-subs' is the prefix which tells me to limit to subscribers. 'page' tells me how to get the url. | |
// We'll use id and the type to get the link with get_permalink(). | |
], | |
]; | |
// This gets a list of categories which are only available to subscribers. | |
$something = new Something(); | |
$subscription_only_categories = $something->get_protected_categories; // Made up function for illustrative purposes. | |
// Start with an empty array of them. | |
$add_sub_cats = []; | |
foreach ( $subscription_only_categories as $index => $row ) { | |
$term = get_term( $row->category_id ); | |
$add_sub_cats[ 'cat_id_' . (int) $row->category_id ] = [ | |
'label' => esc_html( $term->name . ' (shows only for paid subscribers)' ), | |
'title' => esc_html( $term->name ), | |
'class' => 'mjj-subscription-members', | |
'id' => $term->term_id, // id is the id of the term. | |
'type' => 'mjj-subs-cat', | |
'object-type' => 'term', | |
// The type. 'mjj-subs' is the prefix which tells me to limit to subscribers. 'cat' tells me how to get the url. | |
// We'll use id and the type to get the link with get_term_link(). | |
]; | |
} | |
if ( ! empty( $add_sub_cats ) ) { | |
// We'll build the menu checklist from the merged arrays. | |
$things_to_add = array_merge( $one_off_things_to_add, $add_sub_cats ); | |
} | |
?> | |
<div id="mjj-conditional-menu-items" class="posttypediv"> | |
<div id="tabs-panel-mjj-conditionals" class="tabs-panel tabs-panel-active"> | |
<ul id="mjj-conditional-checklist" class="categorychecklist form-no-clear"> | |
<?php | |
$i = -100; // I think this needs to be unique? Fingers crossed no one else is using -100 as a starting point. | |
foreach ( $things_to_add as $item => $details ) : | |
?> | |
<li> | |
<!-- The label in the checklist *only* --> | |
<label class="menu-item-title"> | |
<input type="checkbox" class="menu-item-checkbox" name="menu-item[<?php echo esc_attr( $i ); ?>][menu-item-object-id]" value="<?php echo (int) $details['id']; ?>" /> <?php echo esc_html( $details['label'] ); ?> | |
</label> | |
<!-- Adds the type to the menu item --> | |
<input type="hidden" class="item-type" name="menu-item[<?php echo esc_attr( $i ); ?>][menu-item-type]" value="<?php echo esc_attr( $details['type'] ); ?>" /> | |
<!-- Adds the default title to the menu item --> | |
<input type="hidden" class="menu-item-title" name="menu-item[<?php echo esc_attr( $i ); ?>][menu-item-title]" value="<?php echo esc_html( $details['title'] ); ?>" /> | |
<!-- Adds classes to the menu item --> | |
<input type="hidden" class="menu-item-classes" name="menu-item[<?php echo esc_attr( $i ); ?>][menu-item-classes]" value="<?php echo esc_html( $details['class'] ); ?>" /> | |
<!-- Adds the object type (*not* custom post type but the type which indicates which table it's in) --> | |
<input type="hidden" class="menu-item-classes" name="menu-item[<?php echo esc_attr( $i ); ?>][menu-item-object]" value="<?php echo esc_attr( $details['object-type'] ); ?>" /> | |
</li> | |
<?php | |
$i--; | |
endforeach; | |
?> | |
</ul> | |
</div> | |
<p class="button-controls"> | |
<span class="add-to-menu"> | |
<button type="submit" class="button-secondary submit-add-to-menu right" value="<?php esc_attr_e( 'Add to menu', 'mjjmenu' ); ?>" name="add-post-type-menu-item" id="submit-mjj-conditional-menu-items"><?php esc_html_e( 'Add to menu', 'mjjmenu' ); ?></button> | |
<span class="spinner"></span> | |
</span> | |
</p> | |
</div> | |
<?php | |
} | |
/** | |
* This filters the label on the nav item in the list so it says something other than the default "Custom link" for certain item types. | |
* | |
* @param object $item The nav menu item object. | |
* @return object The nav menu item object. | |
*/ | |
public function nav_type_labels( $item ) { | |
$types = $this->mjj_menu_types; | |
if ( ! empty( $types[ $item->type ] ) ) { | |
$item->type_label = $types[ $item->type ]; | |
} | |
return $item; | |
} | |
/** | |
* Updates the menu item's url to the correct one. | |
* | |
* @param int $menu_id The id of the menu. | |
* @param int $menu_item_db_id The id of the menu item. | |
* @param array $args The values which were saved as postmeta and in the menu item's posts entry. | |
* @return void | |
*/ | |
public function add_back_url( $menu_id, $menu_item_db_id, $args ) { | |
$mjj_types = $this->mjj_menu_types; | |
$item_type = $args['menu-item-type']; | |
if ( ! empty( $mjj_types[ $item_type ] ) ) { | |
$object_id = $args['menu-item-object-id']; | |
$object = $args['menu-item-object']; | |
$url = $this->get_link( $object_id, $object ); // Private function which gets the correct link depending on the object type and object id. | |
update_post_meta( $menu_item_db_id, '_menu_item_url', esc_url_raw( $url ) ); | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment