add_menu_page() add_submenu_page() | passing a $variable with called function - wordpress

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

Related

WordPress publish_{$post_type} hook works only for posts and not for custom post types or pages

I'm trying to send push notification when any of posts, custom post types or pages is published. I'm getting enabled post types from the plugin settings and adding action via foreach loop in my class __construct method. The problem is that it only works for posts and not for any of custom post types or pages. Here is my function and action:
foreach ((array)get_option('PushPostTypes') as $postType) {
add_action("publish_{$postType}", array($this, 'doNewPostPush'), 10, 2);
}
public function doNewPostPush($id, $post) {
$pushData = array(
'title' => $post->post_title,
'body' => strip_tags($post->post_content),
'data' => array(
'url' => trailingslashit(get_permalink($id)),
),
);
if (has_post_thumbnail($id)) {
$pushData['image'] = get_the_post_thumbnail_url($id);
}
$this->sendNotification($pushData);
}
get_option('PushPostTypes') is an array of post types that user choose, for example: array('post', 'page', 'custom_post');
Any idea why it only works for post and not for pages or custom post types?
Your code worked for me, assuming your get_option('PushPostTypes') is working as intended (Obviously I had to mock that).
Try a different approach that does not rely on get_option('PushPostTypes') to see if you get the same result;
add_action('transition_post_status', function ($new_status, $old_status, $post) {
if ($new_status !== 'publish') {
return;
}
// do something
}, 10, 3);
Try the 'transition_post_status' hook that works for all posts without specifically defining them. Put that somewhere just to see if it runs. Do whatever debugging statement that suits you. Then if that works, then move it into Class code and see if that works. I'm debugging by trying to isolate where the first thing goes wrong. Keeping it very simple to get it to work, then gradually adding complexity until it breaks.

Adding a new BuddyPress menu item

add_action( 'bp_setup_nav', 'test_board', 100 );
function test_board() {
global $bp;
bp_core_new_nav_item( array(
'name' => 'Test Board',
'slug' => 'test-board',
'screen_function' => 'bpis_profile',
'position' => 10
)
);
}
function bpis_profile () {
echo do_shortcode('[bpis_tags]');
echo do_shortcode('[bpis_images]');
}
The issue is that when I click on this link in the BP nav bar, it outputs the shortcodes (as per the bpis_profile function) outside of any divs (meaning it just appears at the top of the website, outside of the theme). In addition, the nav bar disappears and I receive the “About” page of the user I am currently viewing (which shows subscribed forum topics, favorite forum topics, etc).
Is there any workaround for this? Ideally, I’d like my shortcodes to output in the body region, beneath the BP nav bar (which has currently disappeared).
Thank you!
Try doing this:
function bpis_profile() {
bp_core_load_template( 'buddypress/members/single/posts' );
}
Then you can create a new file under buddypress/members/single/ called posts.php. Inside that file you can use get_header and get_footer to output your whole page. Of course, be sure to include your shortcodes too.

Changing tag cloud link to location

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 :)

How do you add a WordPress admin page without adding it to the menu?

I'm building a WordPress plugin and I'd like to have an edit-item page that can't be reached via the submenu (because then the item wouldn't be specified).
This resource (http://codex.wordpress.org/Adding_Administration_Menus) shows how to associate an admin page with a function, but not how to do so without adding it as a menu item.
Can this be done?
Thanks!
Best solution here http://wordpress.org/support/topic/add-backend-page-without-menu-item
use add_submenu_page with parent slug = null
I have finally discovered a way to do this that isn't an ugly hack, doesn't require JS to highlight the desired menu item (and submenu item), and works for regular menus registered by plugins (#Josh's answer only works for custom post types).
Essentially, you just need to register your submenu normally, but then hook into the 'submenu_file' filter to deregister it and optionally also set another submenu item to highlight instead.
function so3902760_wp_admin_menu() {
// Register the parent menu.
add_menu_page(
__( 'Parent title', 'textdomain' )
, __( 'Parent', 'textdomain' )
, 'manage_options'
, 'my_parent_slug'
, 'display_my_menu'
);
// Register the hidden submenu.
add_submenu_page(
'my_parent_slug' // Use the parent slug as usual.
, __( 'Page title', 'textdomain' )
, ''
, 'manage_options'
, 'my_hidden_submenu'
, 'display_my_submenu'
);
}
add_action( 'admin_menu', 'so3902760_wp_admin_menu' );
function so3902760_wp_admin_submenu_filter( $submenu_file ) {
global $plugin_page;
$hidden_submenus = array(
'my_hidden_submenu' => true,
);
// Select another submenu item to highlight (optional).
if ( $plugin_page && isset( $hidden_submenus[ $plugin_page ] ) ) {
$submenu_file = 'submenu_to_highlight';
}
// Hide the submenu.
foreach ( $hidden_submenus as $submenu => $unused ) {
remove_submenu_page( 'my_parent_slug', $submenu );
}
return $submenu_file;
}
add_filter( 'submenu_file', 'so3902760_wp_admin_submenu_filter' );
Yes, this can be done (well, technically, it would be more like registering the whole thing and then removing the menu item later), but It would just be easiest (I think) to check for parameters in the $_GET super-global to indicate that the user wishes to edit a specific item.
For example, you could have a page that lists items to edit, and clicking 'edit' only adds the item's ID to the current URL(query-string).
In the function that displays this page, if ID is defined, give them the page to edit that item.
Otherwise, give them the list view. That's how posts, pages, and other custom post types do it.
add_submenu_page with parent slug = null
OR
add_submenu_page with menu title = null
use this code for creating new page without adding in menu
add_action( 'admin_menu', 'register_newpage' );
function register_newpage(){
add_menu_page($appname, $appname, 'administrator','custompage', 'custom');
remove_menu_page('custom');
}
function custom()
{
echo "hai";
}
Note: This solution doesn't automatically set the current menu and submenu item. If you want to highlight a particular menu as current when the hidden page is viewed, see my other answer.
From the answers that come before me, you can see that there are many ways to do this. However, there is another way that I think may be the best.
Loading the page differently based on the value of a $_GET query var is one option, but it may not be what some people are looking for.
The suggestions regarding add_submenu_page() are on the right track, but each of the previous suggestions have problems. Setting $menu_title to null doesn't keep the menu item from being displayed, it just makes it so the link doesn't have any text. The link still takes up some room in the menu though, so it looks funny. Setting the $parent_slug to null doesn't have this problem, but I noticed that the page's HTML title doesn't display the $page_title text.
My solution was to set $parent_slug to a fake menu slug, like 'i_dont_exist'. The menu item won't be displayed, and when viewing the admin screen the page title will be filled out properly.
add_submenu_page(
'_doesnt_exist'
,__( 'Page title', 'textdomain' )
,''
,'manage_options'
,'menu_slug'
,'display_my_menu'
);
Yes. It is very possible to make a page cannot be reach via submenu, or even the main menu in the WP admin panel. See the code snippet below.
function myplugin_render_edit_page() {
// Code contains the UI for edit page.
}
/**
* Manage menu items and pages.
*/
function myplugin_register_admin_page() {
global $_registered_pages;
$menu_slug = plugin_basename('myplugin.php');
$hookname = get_plugin_page_hookname($menu_slug,'');
if (!empty($hookname)) {
add_action($hookname, 'myplugin_render_edit_page');
}
$_registered_pages[$hookname] = true;
}
add_action('admin_menu', 'myplugin_register_admin_page');
Hopefully, this will help.
Create sub menu page and parent slug leave it empty like this:
// Create page were you can add new users.
public function add_create_user_menu() {
add_submenu_page(
'',
'Create User',
'Create User',
'manage_options',
'create-user',
array( $this, 'add_create_user_page' )
);
}
You can access it like this:
Add New
I've tried all of the suggestions here but with various issues associated with each.
The WordPress codex for add_submenu_page now gives the correct answer, which is to use options.php as your parent slug. I tried the trick of using a made up name but that gives permissions errors, equally use of null at various locations either causes the menu text to simply be missing (but still clickable) or for the browser title to go missing.
Using options.php worked and I've not seen any issues as a result of its use.
Using add_submenu_page with a parent of NULL definitely works, however if you want to keep the non-linked page associated with a particular menu (say a custom post type menu), you have to use a variation of #Boopathi's answer:
function my_hidden_submenu_page(){
//add the submenu page the usual way
add_submenu_page('edit.php?post_type=custom-type', 'My Page Title', 'My Page Title', 'manage_options', 'my-page-slug', 'my_page_callback');
//then remove it
remove_submenu_page('edit.php?post_type=custom-type','my-page-slug');
}
add_action('admin_menu', 'my_hidden_submenu_page');
It looks as though the two actions would cancel each other out, however remove_submenu_page does not unregister the callback function; it merely removes the link.
This way when someone is viewing your non-linked page, the correct navigation menu (our custom post type menu in this example) will still show as active.
One of the problems I found with merely adding null as the parent slug for a sub menu item is that if you're currently viewing that specific page the submenu itself won't display (at least it didn't for me (along with the page title not showing).
What I did was add an empty span element inside the menu title and use jquery to traverse the parent elements and hide it.
It seems this need is still valid for nowadays version.
I am using WordPress 5.3.2 and I used the following methods to remove the parent named menu from the submenu.
add_action( 'admin_menu', 'submenus' );
function submenus()
{
global $submenu;
$parent_slug = 'parent_slug_name';
// remove parent named menu from submenu because it is always the first one in the submenu array, so the offset is 0 and remove just 1
array_splice( $submenu[$parent_slug], 0, 1 ); // with reindex
// or unset( $submenu[$parent_slug][0] ); // without reindex
}
Hope it helps others who want to achieve the same goal.
About php array_splice()
this method could be found in the source of WP function remove_submenu_page() which is available since WP 3.1.0
Edit / Added
Apart from submenu, parent menu could also be updated in similar way.
For parent menu, the global variable is $menu.
example for reference:
add_action( 'admin_menu', array( $this, 'modify_menu_title' ) );
function modify_menu_title() {
global $menu;
$page = 'some-page-slug';
$new_menu_title = 'New Title Name';
foreach( $menu as $key => $value ) {
if( $menu[$key][2] === $page ) {
$menu[$key][0] = $new_menu_title;
}
}
}
I find you can do it by reusing the insert id, like so:
add_menu_page( 'User AS Packages', 'User AS', 'manage_options', 'myplugin/editaspackages.php', 'UserASPackages', '', 8);
add_menu_page( 'User ARP Packages', 'User ARP', 'manage_options', 'myplugin/editarppackages.php', 'UserARPPackages', '', 8);
add_menu_page( 'AS Packages', 'AS Packages', 'manage_options', 'myplugin/ars-s2.php', 'ARPPackages', '', 8);
The last 3 using position 8 and the last one overrides the two before so the two before do not appear.

How to alter a MENU_LOCAL_TASK tab menu in Drupal

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.

Resources