I've been stuck on how to manipulate in the template file what a menu block outputs in it's html. The regular < ul class="menu" > with li links is fine and I don't need to completely gut this html that drupal creates for all menus but I want to inject classes 'inline' and 'links' like the system main menu (usually) already has under it's ul element. I could print the menu directly in the theme skipping blocks altogether but it would be more helpful in the long run to learn injecting class names into the output of menu blocks that are generated.
So far from googling around I've only been able to find a module that can enter ID's and Class names on the individual li's but not the ul wrapping them and I've been unable to get any similar template file snippets I've come across to work.
There is a way to use a hook function to do this isn't there?
Why don't you add the classes you want via javascript?!
Example:
jQuery("#MY_MENU_WRAPPER ul.menu").addClass("inline");
If that's the case, try the following code in your theme's template.php file
function return_menu_markup($menu_name, $attributes)
{
$items = array();
$menu_tree = menu_tree_all_data($menu_name);
$menu_tree_output = menu_tree_output($menu_tree);
foreach($menu_tree_output as $item_id => $item_data)
{
if(is_numeric($item_id) && is_array($item_data))
{
$items[] = l('<span>' . $item_data['#title'] . '</span>', $item_data['#href'], array(
'attributes' => $item_data['#attributes'],
'html' => TRUE,
)
);
}
}
return theme('item_list', array('items' => $items, 'type' => 'ul'));
}
Then anywhere in the template, simply do the following:
$attributes = array();
$attributes['id'] = "MY_MENU_ID";
attributes['class'] = array('MY_CLASS_1', 'MY_CLASS_2', 'MY_CLASS_3');
return_menu_markup("main-menu", $attributes);
Hope you find what needed :)
-Muhammad.
You can use template.php in your theme folder, use hook:
function THEMENAME_menu_tree__menu_main_navigation($variables){
return "<ul class=\"inline\">\n" . $variables['tree'] ."</ul>\n";
}
also note this menu_main_navigation is menu URL path, other values are always same. Do some cache delete few times, maybe it want work immediately.
Related
Problem: I'm looking to expand our WordPress theme(s)/websites to make it easier for our communications team to create posts and pages by adding in Bootstrap rows and columns.
My idea is to create A custom Field type with a selection to add in rows and columns to fit our themes pages.
Has anyone done something like this? We are using Divi Builder but that is a bit complicated for average users and we are not in arush to create a whole new theme for our 30+ websites.
Or would I need to create a custom plugin for that? I'd rather not use a third party plugin for security reasons
So are you wanting output bootstrap markup in your default wordpress editor to make rows and columns etc?
Short answer is no, the wysiwyg editor is only really built for paragraphs, headings, quotes, horizontal rules, lists, images, inline formatting etc...
In order integrate bootstraps block layout markup would require some extra level of builder addon. Elementor definitely could do the job with some super customisation, even ACF flexible content would do it, and i think even WP Gutenberg can do this natively... but would require customisation to use bootstrap markup.
But if you wanted to attempt and utilise the standard wordpress editor (not Gutenberg editor) using the element dropdown (paragraph, headings etc by default).
You could try this, however I recommend loading the same front end bootstrap css into the wysiwyg editor so you can instantly see changes.
Please note this method below is risky as the user would need to understand how bootstraps row/column structure should be formatted. The probability of html markup going wrong is highly likely. Hence using an actual block element builder (like Elementor or ACF) would be a safer choice.
Anyway, see below example of how to customise standard wordpress wysiwyg editor dropdown for wrapping html tags around content.
First load the same front end bootstrap css into the wysiwyg editor...
// editor stylesheet (containing the front end css for the post content)
add_editor_style('dist/css/editor.css');
Now lets add the styleselect dropdown to our wysiwyg editor...
// add the style select dropdown to our wysiwyg editor
add_filter('mce_buttons_2', 'formatting_dropdown');
/**
* #link https://developer.wordpress.org/reference/hooks/mce_buttons_2
* #param $buttons
* #return mixed
*/
function formatting_dropdown($buttons) {
// global post object
global $post;
// if no post return param
if(!$post) return $buttons;
// switch case for post types or type to run this on (page/post)
switch ($post->post_type) {
case 'page':
case 'post';
// reveal the hidden “Styles” dropdown in the advanced toolbar
array_unshift($buttons,'styleselect');
return $buttons;
default;
return $buttons;
}
}
And then add your bootstrap formatting settings using this filter below...
// init filter for handling the style select options
add_filter('tiny_mce_before_init', 'formatting_dropdown_options');
/**
* #link https://developer.wordpress.org/reference/hooks/tiny_mce_before_init/
* #param $mceInit
* #return mixed
*/
function formatting_dropdown_options($mceInit) {
// global post
global $post;
// if no post return param
if(!$post) return $mceInit;
// switch case for post type (page/post)
switch ($post->post_type) {
case 'page':
case 'post';
// new style array
$style_formats = [
[
'title' => 'row',
'block' => 'div',
'classes' => 'row'
],
[
'title' => 'col',
'block' => 'div',
'classes' => 'col'
]
];
// bs5 breakpoints
$breakpoints = ['xs','sm','md','lg','xl','xxl'];
// bs5 column grid
$grid = 12;
// add bs5 column classes to $style_formats array
foreach ($breakpoints as $breakpoint) {
foreach (range(1, $grid) as $column) {
$style_formats[] = [
'title' => $breakpoint === 'xs' ? 'col-' . $column : 'col-' . $breakpoint . '-' . $column,
'block' => 'div',
'classes' => $breakpoint === 'xs' ? 'col-' . $column : 'col-' . $breakpoint . '-' . $column
]
}
}
// override style format with new style formats
$mceInit['style_formats'] = json_encode($style_formats);
// init array
return $mceInit;
break;
default;
// init array
return $mceInit;
}
}
I have added a menu page on my WordPress backend with some submenu items.
A snippet of the code i use is:
// Add to admin_menu function
add_menu_page(__('New Menu'), __('New Menu Title'), 'edit_themes', 'new_menu_item', 'functiontocallonclick', '', 3.5);
// Add to secondlevel menu
add_submenu_page('new_menu_item', __('New |Sub Menu item'), __('New Menu Title item'), 'edit_themes', 'new_menu_sub_item', 'subfunctiontocallonclick',');
As you can see above it is calling the function functiontocallonclick when you go to the New menu item in the backend.
What i am wondering now:
I would like to pass a variable with the function.
functiontocallonclick($value);
Ofcourse it can't be done that way, so what is the good way?
I use this:
switch($_GET['page']){
case 'suppliers': $type='c';
break;
case 'contractors': $type='s';
break;
default: $type='';
break;
}
but I try to find some better solution.
This answer might seem late but I'll share how I work around this because it looks like quite a few people are looking for an answer to this question.
My answer is going to be passing variables to a nested function within a class-based context so if you're using functional programming then disregard the $this.
The Main hook action:
function mainSetup(){
add_action('admin_menu', array($this, 'mainMenuSetup'));
add_action('admin_menu', array($this, 'subMenuSetup'));
}
Menu Setup:
function mainMenuSetup(){
add_menu_page('DashBoard',
'DashBoard',
'manage_options',
'[yoursitesname]-admin-menu',
function(){ $this->pageSelect("admin"); },
'',
200
);
}
Within the example of the Main menu navigation option, we are passed a string in the nested function that will be passed as an argument to the method(function) 'pageSelect', but the string is hardcoded and can't be changed easily. We'll address this problem when setting up the submenu(s).
TODO: You should replace [yoursitename].
SubMenu Setup:
For the submenu let's say we want to make it easier to go back and add new submenus and change who has access to which submenus. For this, I'm going to make an associative array called 'subMenuObjects' where the array's keys are the submenu objects and their values are the page's permissions.
function subMenuSetup(){
$subMenuObjects = array(
'Settings' => 'manage_options',
'Services' => 'manage_options'
);
foreach ($subMenuObjects as $subMenu => $value) {
add_submenu_page('[yoursitesname]-admin-menu',
$subMenu . 'Page',
$subMenu,
$value,
'[yoursitesname]' . strtolower($subMenu) . '-menu',
function() use ($subMenu){ $this->pageSelect($subMenu); }
);
}
}
Here we use the 'use' to pass the $subMenu to the nested function.
Page select:
function pageSelect($page){
switch ($page) {
case 'admin':
echo '<div>Welcome to the Admin page</div>';
break;
case 'Settings':
echo '<div>Welcome to the Settings page</div>';
break;
case 'Services':
echo '<div>Welcome to the Service page</div>';
break;
default:
echo '<div>something happened, contact dev</div>';
break;
}
}
Yes, the way you're doing it is the way WordPress does it itself. To re-use admin screens, you have to pass some query var in the URL and then show/hide elements based on that.
You can also create invisible admin screens: How to enable additional page in WordPress custom plugin?. And this may be useful too: Redirect from add_menu_page
I'm having some problems defining my question properly in the title, but here is what I'm looking for:
I have defined some menu items in the menu function in my module, and somehow I would like to retrieve this data in the template files and use it to build a menu.
Is there any good way to do this, or am I approaching the issue in the wrong way?
The simplest way would be to call the menu function directly and build up a list of links:
$items = array();
foreach (mymodule_menu() as $path => $item) {
$items[] = l($item['title'], $path);
}
// For Drupal 6
$rendered_menu = theme('item_list', $items);
// Or for Drupal 7
$rendered_menu = theme('item_list', array('items' => $items));
I am learning Drupal as I go and I am wondering if I can change a link coming from out of a tag cloud.
The link coming from the tag cloud goes to ...category/articles/locations/kittys
I would like it to go to the node tag at ...content/kittys
Any thoughts?
The assummed module you used for this, tagadelic, uses default taxonomy-paths for that.
So the answer is "yes" it can be changed. e.g. Forums (which are terms/tags in a taxonomy too) in a tagcloud will link to the forum home, not to the forum overview. This works because tagadelic uses taxonomy_term_path().
However, your question is a bit unclear about what (and why) you want to achieve this. What is "content/kitties"? Your question makes me believe you want to link to a node? Why? Tag-clouds represent tags, where the tag links to the list of posts within that tag.
That said, the easy way to change outgoing links is in the theme_function: to override the theme function.
/**
* theme function that renders the HTML for the tags
* #ingroup themable
*/
function my_custom_chees_puff_theme_tagadelic_weighted($terms) {
$output = '';
foreach ($terms as $term) {
$output .= l($term->name, "/link/to/anywere", array(
'attributes' => array(
'class' => "tagadelic level$term->weight",
'rel' => 'tag',
'title' => $term->description,
)
)
) ." \n";
}
return $output;
}
The other option is to override the general "where should a tag-link-link-to" Drupalwide. As forementioned forum.module does, trough hook_term_path():
function my_cheesy_puffs_kitten_module_term_path($term) {
return 'links/to/kittens/' . $term->tid;
}
Success! Bèr Kessels - Author and maintainer of Tagadelic :)
I have built a tabbed menu in my custom Drupal 6 module. I want to position an html dropdown list to the right of the tabbed menu at the top of my module page. The list will fire some ajax events on change e.g. changing the LIMIT clause on SQL query by specifying 10,20,50,100 results. How can I achieve this in Drupal without hacking templates?
Thanks,
You could do this by overriding theme_menu_local_tasks() within your theme:
function yourTheme_menu_local_tasks() {
// Prepare empty dropdown to allow for unconditional addition to output below
$dropdown = '';
// Check if the dropdown should be added to this menu
$inject_dropdown = TRUE; // TODO: Add checking logic according to your needs, e.g. by inspecting the path via arg()
// Injection wanted?
if ($inject_dropdown) {
// Yes, build the dropdown using Forms API
$select = array(
'#type' => 'select',
'#title' => t('Number of results:'),
'#options' => array('10', '20', '50', '100'),
);
// Wrap rendered select in <li> tag to fit within the rest of the tabs list
$dropdown = '<li>' . drupal_render($select) . '</li>';
}
// NOTE: The following is just a copy of the default theme_menu_local_tasks(),
// with the addition of the (possibly empty) $dropdown variable output
$output = '';
if ($primary = menu_primary_local_tasks()) {
$output .= "<ul class=\"tabs primary\">\n". $primary . $dropdown . "</ul>\n";
}
if ($secondary = menu_secondary_local_tasks()) {
$output .= "<ul class=\"tabs secondary\">\n". $secondary ."</ul>\n";
}
return $output;
}
(NOTE: Untested code - potential typos)
As you are referring to code to put in a module, then the module should implement hook_theme_registry_alter(), which would allow the module to override the function theme_menu_local_tasks(). The module should store the value of the previous callback, so that it could still call it in the case the page it not one that it should change.
Implementing a hook in the module allows you to have the normal menu tabs, once the module has been disabled; altering the current theme would require you to change it back when you want the functionality anymore, and if you are using a theme made from another person you should change the theme all time you download a new version. If you are using more than one theme, then you should make the change to each used theme.
In general, a modification to a theme that is required from a module should be done inside a module.