change position of wordpress menu items using wp_nav_menu_items - wordpress

I'm adding a search box and a "Join Email List" button to the top of a wordpress main menu. The search box appears as expected, but the email button shows up within the menu's UL -- which I can compensate for with positioning, in the full menu, but it hides the button completely when the responsive mobile menu appears. Not sure how to get it out of the UL properly.
I found that if you require the email button as an external file, it will appear outside the UL -- but that also causes a "1" to appear before the main menu in the NON-mobile version. Puzzling...
Here's my code, with both the string method and require method shown (commented out).
add_filter('wp_nav_menu_items','add_search_box_to_menu', 10, 2);
function add_search_box_to_menu( $items, $args ) {
if( $args->theme_location == 'main-nav-menu' ) {
$search_form = get_search_form();
// $email_button = require_once('emailbutton.php');
$email_button = '<button id="btn_emaillist">Join Our Email List</button>';
$items = $search_form . $email_button . $items;
}
return $items;
}
You can see this live at http://example.wdev05.com/. How can I make the button appear outside the UL so that it's visible in both desktop & mobile, without any strange side effects?

wp_nav_menu_items will add content inside UL.
use wp_nav_menu filter to insert content outside ul
function _wp_nav_m_filter( $nav, $args ) {
if( $args->theme_location == 'main-nav-menu' ) {
$search_form = get_search_form( false ); // do not echo the form
$search_form .= '<button id="btn_emaillist">Join Our Email List</button>';
return $search_form . $nav;
}
return $nav;
}
add_filter('wp_nav_menu', '_wp_nav_m_filter', 10, 2);
checkout https://core.trac.wordpress.org/browser/tags/4.5/src/wp-includes/nav-menu-template.php#L451

Related

Why WP_Screeen doesn't show all options with admin_body_class

I was trying to add a specific class to the admin section. I created a new top-level menu page and now I want to add a specific class to this top-level menu and its submenu items.
What I have tried so far is that I used WP_Screen to get the data of the current screen.
If I check the submenu page and I did var_dump(get_current_screen()). It shows every detail.
But now the case is that If I use get_current_screen() with admin_body_class why it doesn't work.
As far as I know, the hook is early called before the submenu page details are loaded.
Now I want to ask, how it is possible to check if the submenu has a specific parent base,
Here is the code that I tried so far.
add_filter('admin_body_class', 'tw_admin_body_class');
function tw_admin_body_class( $classes ) {
$screen = get_current_screen();
var_dump($screen);
if ( $screen->parent_base == 'tw-top' ) {
return $classes . ' raashid';
}
}
But parent_base is set to null. Any idea how to add class to submenu pages if it is under the specific yop-level menu.
It worked by using the Id option.
Here is the working option.
add_filter( 'admin_body_class', 'my_admin_body_class' );
function my_admin_body_class( $classes ) {
$screen = get_current_screen();
$classes .= ' raashid';
if ( get_plugin_page_hook( 'tw-top', '' ) === $screen->id ) {
return $classes;
}
}

Display Elementor page to customer in Woocommerce My-Account

I have Woocommerce (Subscriptions) and Elementor. I'm trying to add a page/content within the Myaccount area of Woocommerce - new navigation menu item.
Creating the endpoint and navigation menu bit works without issue. The issue I'm facing is displaying a page created in elementor. One page (also created in elementor) works without issue while the other doesn't.
The page created in elementor is fairly simple that essentially creates 4 columns, 10 rows. Within each row there is button that uses shortcodes to get the button text and url to navigate to when pressed. This is all tested and works without issue when accessing the page directly.
If I use this code
$post = get_post(1114);
$content = apply_filters('the_content', $post->post_content);
echo $content;
on the endpoint to display the page the output is just a list of rows of text showing the table cells from left to right. This only shows the button text (no sign of the URL) and is not formatted in anyway like the page in the elementor editor is (or if accessed directly)
e.g if the table is
H1 H2 H3 H4
R1a R1b R1c R1d
R2a R2b R2d R2d
The display is
H1
H2
H3
R1a
R1b
R1c
R1d
R2a
R2b
R2c
R2d
If I use the below code
$content = \Elementor\Plugin::$instance->frontend->get_builder_content_for_display( 1119);
echo $content;
the table largely displays correctly with all formatting etc. The one thing that isn't working is the button text. Instead of displaying the text returned by shortcode it just displays the shortcode.
I'm sure I'm just missing something that needs to be processed somewhere but I have no idea what it is and the Elementor pages don't give much away unfortunately.
Any help would be appreciated.
EDIT : The correct answer is that there is a dynamic button/dropdown next to the text field that I didn't notice before. Using this you can select shortcode and enter the shortcode details and it will display correctly without having to process manually.
Not sure why it doesn't load correctly as above in the first place but to get around it I did the below. No sure this is the correct way to do this but it works.
$content = \Elementor\Plugin::$instance->frontend->get_builder_content_for_display( 1013 );
$content = process_subswitch_content_for_shortcodes( $content );
echo $content;
The below essentially searches the content returned from the get_builder_content_for_display function for '[subswitch' which is the start of the shortcodes in question (unable to just search for ] as elementor puts other [] in the content).
function process_subswitch_content_for_shortcodes( $content ) {
$keepprocessing = true;
$searchstart_pos = 0;
do {
$startchar_pos = strpos( $content, "[subswitch", $searchstart_pos );
if ( $startchar_pos == false ) {
$keepprocessing = false;
}
$endchar_pos = strpos( $content, "]", $startchar_pos );
$shortcode_request = substr( $content, $startchar_pos, $endchar_pos );
if ( $shortcode_request == false ) {
$keepprocessing = false;
}
$shortcode_content = do_shortcode( $shortcode_request );
$content = str_replace( $shortcode_request, $shortcode_content, $content );
$searchstart_pos = $endchar_pos;
} while ( $keepprocessing );
return $content;
}
I'd of course still like to know the way to load this directly so that it displays without having to process the shortcodes manually so to speak.

Wordpress add link if item has submenu

I want to add a button for a responsive dropdown in a wordpress menu, which I can trigger with javascript.
For that I need a custom link with an icon or something like that after the a-tag but inside the li-tag.
How can I check a menuitem if it has a submenu and add a custom a-tag inside?
I've tried the walker and add_filter function, but it doesn't work.
Can anybody help me?
Thanks
If you add this to your function.php file, it will add class dropdown to all the parent menus with children (submenus). Then you can target .dropdown with javascript.
function menu_set_dropdown( $sorted_menu_items, $args ) {
$last_top = 0;
foreach ( $sorted_menu_items as $key => $obj ) {
// it is a top lv item?
if ( 0 == $obj->menu_item_parent ) {
// set the key of the parent
$last_top = $key;
} else {
$sorted_menu_items[$last_top]->classes['dropdown'] = 'dropdown';
}
}
return $sorted_menu_items;
}
add_filter( 'wp_nav_menu_objects', 'menu_set_dropdown', 10, 2 );

Drupal - how to modify only one instance of a menu with them_menu_link

I'm using theme_menu_link to modify a menu, in this case main_menu. Evertything works as expected, however I'm using Menu Block to render another instance of the main menu in the sidebar on internal pages.
The problem is, I only want my modifications to show up in the instance of main_menu that I'm using for the site's main navigation, but not in the instance I'm using in the sidebar.
Details:
Here's my theme_menu_link function
/**
* Returns custom HTML for the main navigation menu.
* Adds an HTML element to top level parent <li> elements
* to serve as a drop-down menu toggle.
*
* this also sets the HTML wrapper for sub menus, to override the above
* them_menu_tree function, which is intended for top level menus only.
*
* #param $variables
* An associative array containing:
* - element: Structured array data for a menu link.
*
* #ingroup themeable
*/
function mytheme_menu_link__main_menu($variables) {
$element = $variables['element'];
$child_menu = $element['#below'];
$sub_menu = '';
// if the current top level item has child menus (sub menus)
if ($child_menu) {
// unset the sub menu wrapper set above in mytheme_menu_tree__new_main_menu()
unset($child_menu['#theme_wrappers']);
// add opening ul tag for the sub menu wrapper to $sub_menu
$sub_menu = '<ul class="menu sub-menu">';
// iterate over each sub menu item
foreach ($child_menu as $child_menu_item){
// add sub menu item link HTML to a variable
$child_menu_output = l($child_menu_item['#title'], $child_menu_item['#href']);
// check to see if item has a title, sincle $element['#below'] returns things besides menu items
if($child_menu_item['#title']) {
// output each sub menu item's link and description, wrapped in a <li> element
$sub_menu .= '<li>' . $child_menu_output .'</li>';
} // end if shild menu item has a title
} // end foreach child menu item
// add closing ul tag for the sub menu wrapper to $sub_menu
$sub_menu .= '</ul>';
}
// output a dummy link for top level items
$output = '' . $element['#title'] . '';
return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . '</li>';
}
the above function works just like I want it to work on the site's main navigation. However, I do not want this to apply to the instance of main_menu that I'm using in the sidebar with Menu Block. Is there a way to conditionally use them_menu_link based on the region or block the menu is bing rendered it?
So I figured out a fix for this one, but I'm not sure if it's the best way to go about it.
Using krumo($variables) inside the theme_menu_link function I was able to see that each menu has an array at $variables['element']['#theme'], where the first item is the block that the menu is in. So for the main navigation: $variables['element']['#theme'][0] === 'menu_link__menu_block__1', and for the sidebar navigation: $variables['element']['#theme'][0] === 'menu_link__menu_block__2'.
Once I saw that it wasn't hard to create an if/else in the mytheme_menu_link__main_menu() function, where we do the customizations for the main menu (outputting a href="#" for top level links, etc), but not for the sidebar menu.
So the new and improved theme_menu_link function looks like this:
function mytheme_menu_link__main_menu($variables) {
$element = $variables['element'];
$sub_menu = '';
// Only customize the menu instance if its the main navigation, not sidebar navigation, etc.
// We need to check which block the menu is in, otherwise all instances of main_menu will get this treatment.
if($element['#theme'][0] === 'menu_link__menu_block__1'){
$child_menu = $element['#below'];
// if the current top level item has child menus (sub menus)
if ($child_menu) {
// unset the sub menu wrapper set above in mytheme_menu_tree__new_main_menu()
unset($child_menu['#theme_wrappers']);
// add opening ul tag for the sub menu wrapper to $sub_menu
$sub_menu = '<ul class="menu sub-menu">';
// iterate over each sub menu item
foreach ($child_menu as $child_menu_item){
// add sub menu item link HTML to a variable
$child_menu_output = l($child_menu_item['#title'], $child_menu_item['#href']);
// check to see if item has a title, sincle $element['#below'] returns things besides menu items
if($child_menu_item['#title']) {
// output each sub menu item's link and description, wrapped in a <li> element
$sub_menu .= '<li>' . $child_menu_output .'</li>';
} // end if shild menu item has a title
} // end foreach child menu item
// add closing ul tag for the sub menu wrapper to $sub_menu
$sub_menu .= '</ul>';
}
// output a dummy link for top level items
$output = '' . $element['#title'] . '';
return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . '</li>';
}
// Otherwise if we're not in the main navigation block, output a normal menu
else {
if ($element['#below']) {
// unset the sub-menu wrapper set above in mytheme_menu_tree__main_menu()
unset($element['#below']['#theme_wrappers']);
// Add a custom wrapper for sub-menus for CSS sanity and profit
$sub_menu = '<ul class="menu sub-menu">' . drupal_render($element['#below']) . '</ul>';
}
$output = l($element['#title'], $element['#href'], $element['#localized_options']);
return '<li' . drupal_attributes($element['#attributes']) . '>' . $output . $sub_menu . '</li>';
}
}

Is it possible to *optionally* override a theme in Drupal 6?

I want to override the theming of only one (custom) menu. I can do this with phptemplate_menu_tree() but - of course - it overrides the rendering of all menus.
I've tried returning FALSE (an obvious technique IMO) if the menu is not the specific one I want to override - but this doesn't cause the overridden theme function to be called.
My only alternative (when the menu is anything other than the specific one) is to call the overridden function from within phptemplate_menu_tree() - but this seems to defeat the whole point of the override system, since the default rendering function will be hard-coded therein.
I hope the explanation is clear, and any help is greatly appreciated - tks.
UPDATE
For the sake of future reference, I'll explain how I solved this.
First off, the menu rendering starts with this function in menu.module:
function menu_block($op = 'list', $delta = 0) {
$menus = menu_get_menus();
// The Navigation menu is handled by the user module.
unset($menus['navigation']);
if ($op == 'list') {
$blocks = array();
foreach ($menus as $name => $title) {
// Default "Navigation" block is handled by user.module.
$blocks[$name]['info'] = check_plain($title);
// Menu blocks can't be cached because each menu item can have
// a custom access callback. menu.inc manages its own caching.
$blocks[$name]['cache'] = BLOCK_NO_CACHE;
}
return $blocks;
}
else if ($op == 'view') {
$data['subject'] = check_plain($menus[$delta]);
$data['content'] = menu_tree($delta);
return $data;
}
}
If you only want to override how individual item (links) are rendered then you can use the theme system (there are loads of references on how do this) - but if you want complete control on how the entire menu tree is rendered (for example, wrapping the output in nested DIVs so it can be centred on the page) then there is no way to override menu_block().
Therefore, I removed the menu I wanted to render differently from the administer blocks page (site building->blocks) and rendered the menu directly in my page.tpl.php using code something like this: (angle brackets removed)
$m = menu_tree_page_data('my-menu-id');
$o = "DIV";
foreach($m as $k => $v){
$o .= "SPAN {$v['link']['title']} /SPAN";
}
$o .= "/DIV";
echo $o;
I hope this helps.
I've had mixed success doing template.php menu overrides to force CSS classes and ids or HTML into the output.
You could make use of Block Theme when enabling the menu as a block, but I've never tried it.
http://drupal.org/project/blocktheme
If you want to tackle the template way, here are the zen menu override funcitons...
function zen_menu_item_link($link) {
if (empty($link['localized_options'])) {
$link['localized_options'] = array();
}
// If an item is a LOCAL TASK, render it as a tab
if ($link['type'] & MENU_IS_LOCAL_TASK) {
$link['title'] = '<span class="tab">' . check_plain($link['title']) . '</span>';
$link['localized_options']['html'] = TRUE;
}
return l($link['title'], $link['href'], $link['localized_options']);
}
function zen_menu_local_tasks() {
$output = '';
if ($primary = menu_primary_local_tasks()) {
$output .= '<ul class="tabs primary clear-block">' . $primary . '</ul>';
}
if ($secondary = menu_secondary_local_tasks()) {
$output .= '<ul class="tabs secondary clear-block">' . $secondary . '</ul>';
}
return $output;
}
You could use sections module, or look at how it switches theme for certain menu-items.
what I did was register a new theme function in my template.php called primary_links (because I wanted to only customize this menu in certain way) created the function mytheme_primary_links() in my template.php refreshed the cache so Drupal would add my theme function to the system then changed theme function on primary_links from links to my custom theme function primary_links - this allows me to customize only this 1 menu - could you do this and hook into where ever to change the theme function being called for your links?
Chris

Resources