Woocommerce have a div with a class "woocommmerce" I want to add another class or remove the class. Which file is that?
<div class="woocommerce"></div>
Although there isn't any supported method provided by WooCommerce for achieving that, you could "hack" on the function which builds the wrapper directly.
The problem
<div class="woocommerce"></div>
"The master wrapper". Almost all things WooCommerce lives within it.
WooCommerce plugin kind of "protects" its main wrapper as it depends on it for doing all kinds of stuff (styling, js functionality) etc. For that reason, the plugin hasn't a filter available so one could hook to and override it.
By the way, it is not recommend to remove it, one would rather add additional css classes to it, which is possible.
There's even a Github issue which seems to state that WooCommerce "Won't fix" it (at least for now).
Use cases
Amongst all possible use cases that might be out there, mine was to apply additional css classes to the wrapper <div class="woocommerce"></div> to fit my theme's CSS framework, (Bootstrap 4) specifically.
I simply wanted it to become <div class="woocommerce container-fluid container-application"></div>
BUT
How to safely change it?
Inspecting it further
Looking at WooCommerce's class-wc-shortcodes.php under the includes/ directory, let's go ahead and dissect it. If you jump to this line you can have a glimpse at the shortcode_wrapper() function, which builds that "annoying" wrapper. Jump here to see an array of woocommerce shortcode slugs, which will have their contents wrapped within the <div class="woocommerce"></div>.
Or according to my own use case, on this specific line, My Account page shortcode is returned within the shortcode_wrapper() function, which again results in all the My Account pages' contents living within the <div class="woocommerce"></div>.
That is also true for other shortcodes used by WooCommerce, so go ahead to the solution part and you might be able to change the wrapper while on other WooCommerce pages other than the My Account.
The Solution (!)
"shut that whole thing down"
We're going to hack on the function which builds the <div class="woocommerce"></div> directly.
We have to create a new shortcode by calling the WC_Shortcodes() class. It will kind of "redirect" all the contents from a specific WooCommerce shortcode to our newly created one.
Now, the following function specifically targets the My Account pages, but it could be easily adapted to conditionally target other pages containing the WooCommerce shortcodes.
So, the default WooCommerce pages as most of you might be aware of, are nothing more than ordinary WordPress pages you can manage under the Admin dashboard. However, those pages do also display the contents of the default WooCommerce shortcodes such as the [woocommerce_my_account], which is the one we'll replace later on.
Place the function bellow on your functions.php, save & upload it.
/**
* WooCommerce My Account
* Returns custom html / css class for WooCommerce default wrapper on My Account pages
* #see https://github.com/woocommerce/woocommerce/blob/857c5cbc5edc0451cf965b19788e3993804d4131/includes/class-wc-shortcodes.php#L59
*
**/
if ( class_exists( 'woocommerce' ) ) {
function wp_wc_my_account_shortcode_handler( $atts ) {
$whichClass = new WC_Shortcodes();
$wrapper = array(
'class' => 'woocommerce container-fluid container-application',
'before' => null,
'after' => null
);
return $whichClass->shortcode_wrapper( array( 'WC_Shortcode_My_Account', 'output' ), $atts , $wrapper );
}
add_shortcode( 'new_woocommerce_my_account', 'wp_wc_my_account_shortcode_handler' );
}
------------------// ------------------
Now, let's head to the My Account Page on the browser and inspect the html, you'll notice nothing has changed. That's because we now have to go to Admin >> pages >> My Account, and then replace the default WooCommerce [woocommerce_my_account] shortcode with the [new_woocommerce_my_account].
Update/Save the My Account page under the Admin Dashboard and now all the My Account pages contents will be wrapped within our new <div class="woocommerce container-fluid container-application"></div>.
Bonus
Constructing custom html for the wrapper
In case you wanted a custom html tag for the wrapper, simply passing the tag along with your css class/classes will do the job. Change the following part of the function above to:
$wrapper = array(
'class' => '',
'before' => '<section class="woocommerce container-fluid container-application>',
'after' => '</section>'
);
And now instead of a <div></div>, our wrapper will be a <section></section>.
Simply follow (enhance) the logic and you'll be able to replace the wrapper on almost all WooCommerce pages such as products, product, product categories, cart, checkout, my account and so on.
there is no ready-made filter or anything like that, that lets you do it, but you could filter the_content, to get it done.
function so33675604_add_class_to_checkout( $content ) {
//condition to check for the proper page
if ( is_checkout() ) :
//disable error reporting for falsy formatted html code
libxml_use_internal_errors(true);
//parse the $content html (treat content as UTF-8, don't add any doctype or other html wrappers)
$html = new DOMDocument();
$html->loadHTML( mb_convert_encoding($content, 'HTML-ENTITIES', 'UTF-8'), LIBXML_HTML_NOIMPLIED | LIBXML_HTML_NODEFDTD );
//search for the very first div
$container = $html->getElementsByTagName('div')->item(0);
//add classes (remember to put woocommerce, since thats been used by some js functions)
$container->setAttribute('class', 'woocommerce oink');
//return the result
return $html->saveHTML();
endif;
return $content;
}
//add the filter (priority must be high, so it runs later, after the shortcodes have been processed)
add_filter( 'the_content', 'so33675604_add_class_to_checkout', 100 );
please be aware, that this function uses conditionals and these might not work in wp-ajax calls / you would have find another way to check, if checkout (or else), probably via global wp_query.
I'm not sure if this is possible or advisable, but I'd like to have a top-level menu item on the sidebar in the admin dashboard that links to a specific page within wordpress for editing.
Maybe there's a better way of doing this... here's the functionality I'm after:
I have a page called "Upgrade Contents" where my client can edit the contents of their upgrade package sitewide. I'd like them to be able to edit this page directly from the admin dashboard, like a setting page. Problem is, I don't know how to add a link to edit this page to the admin AND I already have everything set up with ACF using this page.
Is adding a link easy to do or should I just scrap it and make a settings page for my theme and add THAT to the admin?
To add a link at the top level of the menu you can add an item to the global $menu array, like this:
function link_to_user_settings() {
global $menu;
$title = 'User Settings';
$url = 'URL_OF_YOUR_PAGE';
$position = 73;
$permission = 'read';
$icon = 'dashicons-admin-links';
$menu[$position] = array( $title, $permission, $url, '', 'menu-top', '', $icon );
}
add_action( 'admin_menu', 'link_to_user_settings' );
Change the variables accordingly to your needs.
The $position var is where you want the link to appear based on the default positions you can find at the add_menu_page() documentation (in my example 73 means after the Users menu item).
I'm trying to get Wordpress to give me a menu item to go to "latest posts." They come up on the frontpage, but once I navigate away, I want a menu item to get back there. It seems so obvious, but several hours later, the best I could do was create a custom menu with a link to "uncategorised" as a workaround. There MUST be a better way! And this way, I get a box saying "Archive of posts filed under the Uncategorized category. " Not wanted!
Create a custom page in your template directory (http://codex.wordpress.org/Pages#Page_Templates) with a custom query (check at http://codex.wordpress.org/Class_Reference/WP_Query, http://codex.wordpress.org/Function_Reference/query_posts or http://codex.wordpress.org/Template_Tags/get_posts).
Create a page in your admin and select the template you created.
Add a link to this page in your menu and you're done.
Maybe this will help: http://www.viper007bond.com/2011/09/20/code-snippet-add-a-link-to-latest-post-to-wordpress-nav-menu/
It's a filter that will 'search and replace' placeholder anchors such as '#latestpost1' with the actual url of the latest post, and thus dynamically modify the menu before it's rendered.
I'm not sure how this is for SEO, but it's a clever solution.
Give all your posts a category name. Use something generic like "News", "Articles" or "Blogs". Then, choose the category with the name you picked from the menu page under categories. Add this category link to your menu. Rename the link whatever you wish - "Blog" - for example. And, viola - all your posts will appear when people click on that link.
Try this plugin: https://de.wordpress.org/plugins/dynamic-latest-post-in-nav-menu/ works really well and code is open sourced here: https://github.com/hijiriworld/dynamic-latest-post-in-nav-menu
simple solution:
I took this guy's code: http://www.viper007bond.com/2011/09/20/code-snippet-add-a-link-to-latest-post-to-wordpress-nav-menu/
Basically what he wrote is for the menu item to link to the latest post, not posts (plural), so I just modified it and it's working:
<?php
if ( ! is_admin() ) {
// Hook in early to modify the menu
// This is before the CSS "selected" classes are calculated
add_filter( 'wp_get_nav_menu_items', 'replace_placeholder_nav_menu_item_with_latest_post', 10, 3 );
}
// Replaces a custom URL placeholder with the URL to the latest post
function replace_placeholder_nav_menu_item_with_latest_post( $items, $menu, $args ) {
// Loop through the menu items looking for placeholder(s)
foreach ( $items as $item ) {
// Is this the placeholder we're looking for?
if (!strpos(($item->url), 'latestpost'))
continue;
// if ( 'latestpost' != $item->url )
// continue;
// Get the latest post
$latestpost = get_posts( array(
'numberposts' => 1,
) );
if ( empty( $latestpost ) )
continue;
// Replace the placeholder with the real URL
$new_link = $item->url;
$new_link = substr($new_link, 0, strlen($new_link) - 12);
$item->url = $new_link;
}
// Return the modified (or maybe unmodified) menu items array
return $items;
}
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.