reset custom field when navigation menu is deleted in WordPress - php

I have a custom field associated with all posts and pages. It's a dropdown of all the navigation menus.
here is how I'm populating the custom field drop-down: (the field name is custom_menu...)
function acf_load_menu_field_choices( $field ) {
// reset choices
$field['choices'] = array();
$menus = get_terms( 'nav_menu', array( 'hide_empty' => true ) );
$blank_list = json_encode(array( "name" => "Select Menu", "slug" => ""));
$blank_list = json_decode($blank_list);
array_unshift($menus, $blank_list);
foreach ( $menus as $val ) {
$value = $val->slug;
$label = $val->name;
$field['choices'][ $value ] = $label;
}
// return the field
return $field;
}
add_filter('acf/load_field/name=custom_menu', 'acf_load_menu_field_choices');
Here is a common menu location that I'm using on each page:
function register_custom_menu() { //function to register new menu
register_nav_menu('custom-menu',__( 'Custom Menu' ));
}
add_action( 'init', 'register_custom_menu' );
And then I'm assigning a menu dynamically to the location custom-menu based on the custom field menu on each page.
and here is the function the fires on each page when it's loaded:
add_action("wp_ajax_load_custom_menu", "load_custom_menu");
add_action("wp_ajax_nopriv_load_custom_menu", "load_custom_menu");
function load_custom_menu(){
$post_id = $_POST['page_id'];
$page_custom_menu = get_field('custom_menu', $post_id);
if(empty($page_custom_menu) || $page_custom_menu == "primary") return;
$locations = get_theme_mod( 'nav_menu_locations' );
if(!empty($locations)) {
foreach($locations as $locationId => $menuValue) {
if($locationId == "custom-menu") $menu = get_term_by('slug', $page_custom_menu, 'nav_menu');
if(isset($menu)) { $locations[$locationId] = $menu->term_id; }
}
}
set_theme_mod('nav_menu_locations', $locations);
wp_nav_menu( array(
'theme_location' => 'custom-menu',
'menu_id' => 'primary-menu',
'menu_class' => 'main-nav underline-decoration l-to-r-line level-arrows-on outside-item-remove-margin',
'container' => false
)
);
wp_die();
}
This ajax function fires on ready event and recieves a POST value called page_id. This function checks the custom field value of the given page_id for custom-menu and assign that menu to the menu location called custom-menu.
Here's my JavaScript in case you need to have a look on it:
jQuery(document).ready( function($){
let customMenu;
let page_id = script_vars.postID;
$.post(dtLocal.ajaxurl, { action: "load_custom_menu", page_id: page_id }, resp => customMenu = resp);
$(window).scroll(() => {
if(customMenu !== "0"){
$("#phantom .main-nav").remove();
$("#phantom .menu-box").html(customMenu);
}
})
})
This is how my code is working, And everything works fine up to this point.
Now here is the problem
Whenever I delete a Navigation menu from Appearence>Menus>Delete Menu My custom Menu starts behaving unexpectedly because the custom field value is still pointing to the deleted Menu.
What I want to do here is, I want to Delete the Custom Field whenever a menu is deleted. First I want to get the slug of a deleted menu and then find the custom field value with that slug and then finally, delete or reset that custom field.

Menus in WordPress are just terms in a taxonomy. So you can use all of the term hooks.
In particular the pre_delete_term hook.
add_action( 'pre_delete_term', 'my_func' );
function my_func($term, $taxonomy){
// Check the taxonomy is really 'menu', then check term
}

You are looking for action wp_delete_nav_menu that will take one argument - menu id. The problem that at that point your nav menu will be deleted, so you won't be able to take it slug.
If it is possible that you can store nav menu ID instead of slug, that would simplify things greatly.
In acf_load_menu_field_choices replace:
foreach ( $menus as $val ) {
$value = $val->term_id;
$label = $val->name;
$field['choices'][ $value ] = $label;
}
And in load_custom_menu:
$page_custom_menu = get_field('custom_menu', $post_id);
if(empty($page_custom_menu) || $page_custom_menu == /* Should be your primary menu ID */) return;
$locations = get_theme_mod( 'nav_menu_locations' );
if(!empty($locations)) {
foreach($locations as $locationId => $menuValue) {
if($locationId == "custom-menu") $menu = get_term_by('id', $page_custom_menu, 'nav_menu');
if(isset($menu)) { $locations[$locationId] = $menu->term_id; }
}
}
After those changes you will need to rewrite custom_menu in all posts\pages.
After that, following function will remove all related fields when you delete menu. It has been tested only on typical ACF field (string) instead of custom, but should work just fine with your code as well.
add_action('wp_delete_nav_menu', 'delete_all_acf_fields_with_menu', 10, 1);
function delete_all_acf_fields_with_menu($term_id) {
$args = array(
'post_type' => array('post', 'page'),
'posts_per_page' => -1,
'meta_key' => 'custom_menu',
'meta_value' => $term_id,
);
$posts = get_posts($args);
if( $posts ) {
foreach( $posts as $p ) {
delete_field('custom_menu', $p->ID);
}
}
}

Related

Add metabox if there is at least one post available

So I am building a WordPress Dashboard widget which will showcase all posts & pages where a Gutenberg Block is active.
The below code does it's job and pulls in an array based on get_posts().
Here is what I'm attempting to do:
Is there a way that I can invoke the add_action and the pardot_dashboard_widget() function ONLY if there is at least one or more posts in get_posts()? If it's an empty array, don't even bother creating the metabox.
Here is the code:
/**
* Pardot Widget for Dashboard
*/
function pardot_dashboard_widget()
{
add_meta_box(
'pardot_dashboard_meta_box',
esc_html__( 'Pardot Form Locations', 'wporg' ),
'pardot_dashboard_stats',
'dashboard',
'side', 'high'
);
}
add_action('wp_dashboard_setup', 'pardot_dashboard_widget');
function pardot_dashboard_stats()
{
$args = [
's' => '<!-- wp:acf/pardot-form ',
'sentence' => 1,
'post_type' => [
'post',
'page'
],
];
$pardot_form_query = get_posts($args);
if (!$pardot_form_query) {
echo 'There are no active pardot forms available.';
}
}
You can try assigning the meta to a variable, and using that in the if statement.
For example:
<?php
$price_list = get_post_meta( $post->ID, 'price_list_category1', true );
?>
And then...
<?php
if ($price_list == '') {
echo ' ';
} else {
echo '<h2>' . add your meta box conditions here . '</h2>';
}
?>

Hide wordpress page completely

I develop my first plugin, which creates some pages programatically by this method:
$page_id = wp_insert_post(
array(
'comment_status' => 'close',
'ping_status' => 'close',
'post_author' => 1,
'post_title' => 'HSN Form',
'post_name' => strtolower(str_replace(' ', '-', trim('hsn-form'))),
'post_status' => 'publish',
'post_type' => 'page',
)
);
And I set template file for it:
add_filter( 'page_template', 'hsn_service_form_page_template', 10, 1 );
function hsn_service_form_page_template( $page_template ){
if ( is_page( 'hsn-form' ) ) {
$page_template = plugin_dir_path(__DIR__) . 'service-form/form-template.php';
}
return $page_template;
}
After I would like to hide it completely from wordpress dashboard, but those have to available following way:
wwww.example.com/hsn-form.
I can hide it following code from Pages menu:
add_filter( 'parse_query', 'ts_hide_pages_in_wp_admin' );
function ts_hide_pages_in_wp_admin($query) {
global $pagenow,$post_type;
$page = get_page_by_path('hsn-form');
if (is_admin() && $pagenow=='edit.php' && $post_type =='page') {
$query->query_vars['post__not_in'] = array($page->ID);
}
}
It's ok, but it's still available in Appereance->Menu, where you can create nav menus.
I searched for it, but I can't find complete solution for my problem:
My plugin has to create some pages which are available this way: www.example.com/hsn-form
I need to hide this pages completely from admin dashboard
I would like to apply templates for these pages to add some custom php code.
So If somebody should know a complete solution for it, that should be great.
Thank you in advance!
change parse_query to pre_get_posts
In order to remove a page from the admin nav menu, you can hook into the admin_menu action, then manipulate the global $submenu variable.
add_action('admin_menu', function(){
global $submenu;
array_walk($submenu, function(&$child, $parent){
if( $parent != 'edit.php' )
return;
foreach($child as $key=>$submenu){
if( $submenu[2] == 'post-new.php' ) {
unset($child[$key]);
break;
}
}
});
});
In that example, we are looking for a subpage with the slug of post-new.php
underneath the top level page with the slug edit.php. If found, it gets altogether removed from the navigation menu.
The $submenu[2] part is looking for the slug element in the array. If you want to match by the submenu name instead, you can replace it with $submenu[0].

wordpress custom menu for different page

I am trying to use a custom menu on my landingpage.
I used this code:
<?php
function change_wp_nav_menu_args($args = ''){
$pageID = get_the_id();
if($pageID == '63') //custom menu for site with id 63
{
$args['menu'] = 'homepage';
}
return $args;
}
add_filter( 'wp_nav_menu_args', 'change_wp_nav_menu_args' );
?>
It works fine, but not only the main menu changes.
The footer menu changes also.
But the footer menu should be the same on every page.
How can I affect this?
This code fixed my problem:
<?php
add_filter( 'wp_nav_menu_args', 'bb_wp_nav_menu_args' );
function bb_wp_nav_menu_args( $args = '' ) {
// change the menu in the Header menu position
if( $args['theme_location'] == 'primary' && is_page('63') ) {
$args['menu'] = '6'; // 32 is the ID of the menu we want to use here
}
return $args;
}
?>
Its great that you find your answer, but it's a suggestion to make the code dynamic rather set the menu value as static [ $args['menu']='6' ].
Suggestion :
Create a Meta Box [ Dropdown with list of menu ] for Page with the Label Menu. And use the Menu Id for the wp_nav_menu .
For Dropdown [ listing Menus ]
function your_menus()
{
$menu_arr=NULL;
$menus=get_terms( 'nav_menu', array( 'hide_empty' => true ) );
$menu_arr['your-nomenu']='Default';
foreach ( $menus as $menu ){
$menu_arr[$menu->slug]=$menu->name;
}
return $menu_arr;
}//end of function
For Nav Menus : [ _your_page_menu : meta name ]. You can place the code inside a function and called it in Header or you can place this code directly into the header.
$page_menu_name=get_post_meta(get_the_ID(),'_your_page_menu',true)==''?'your-nomenu':get_post_meta(get_the_ID(),'_your_page_menu',true);
if($page_menu_name==='your-nomenu')
{
wp_nav_menu(array('theme_location' => 'primary','menu_id'=> 'main-menu','container'=>false,'fallback_cb'=>'','menu_class'=>'main-navigation'));
}
else
{
wp_nav_menu(array('menu_id'=> 'main-menu' , 'container'=>false, 'menu'=>$page_
menu_name,'fallback_cb'=>'','menu_class'=>'main-navigation'));
}
Hope it helps you.

PHP WooCommerce Simple Check (if contains) $product inside custom tab element?

I've been bouncing my head of a brick wall trying to hide a custom tab depending on grouped_products->child_products attributes so I was wondering is there a simple way to check if my custom tab contains a $product element? if it does not contain a $product then hide the tab?
I can't hide the tab if empty as my custom PHP file (grouped.php) spits out the table even if there is no content (products) with in it.
Currently the function I'm using is not working and the tab shows either way for some reason. This function is supposed to check the child products of the group and then determine if an attribute is present 'PAYG' if so return the custom tab if not do not return the custom tab but it is showing on pages it's not supposed to be at the minute so it is not working.
add_filter( 'woocommerce_product_tabs', 'woo_simfree_product_tab' );
function woo_simfree_product_tab( $tabs ) {
global $post;
if (function_exists( 'get_product' )) {
$product = get_product( $post->ID );
if ($product->is_type( 'grouped' )) {
$PAYG = false;
foreach ($product->get_children() as $child_id) {
$child = get_product($child_id);
$attr = $child->get_attribute('contract-type');
if ($attr == 'PAYG') {
$PAYG = true;
}
}
if ($PAYG = true) {
$tabs['simfree-plans'] = array( 'title' => __( 'SIM Free', 'woocommerce' ), 'priority' => 20, 'callback' => 'woo_simfree_product_tab_content' );
} else {
return $tabs;
}
} else {
return $tabs;
}
}
}
Is there anyway to just check weather the tab contains a $grouped_product that would be so much easier to code then if there is no product there just hide the tab.
My tab contents are calling a custom grouped.php..
// Function below creates an add to cart function which fetches a custom template from child theme. To add more custom add to cart templates just copy and change the path.
function woocommerce_grouped_simfree() {
global $product;
wc_get_template( 'single-product/add-to-cart/grouped-simfree.php', array(
'grouped_product' => $product,
'grouped_products' => $product->get_children(),
'quantites_required' => false
) );
}
// Tab content for custom tab
function woo_simfree_product_tab_content() {
woocommerce_grouped_simfree();
}
Please help thanks
try this
function woo_simfree_product_tab( $tabs ) {
global $post;
$PAYG = false;
if (function_exists( 'get_product' )) {
$product = get_product( $post->ID );
if ($product->is_type( 'grouped' )) {
foreach ($product->get_children() as $child_id) {
$child = get_product($child_id);
$attr = $child->get_attribute('contract-type');
if ($attr == 'PAYG') {
$PAYG = true;
break;
}
}
}
}
if ($PAYG == true) {
$tabs['simfree-plans'] = array( 'title' => __( 'SIM Free', 'woocommerce' ), 'priority' => 20, 'callback' => 'woo_simfree_product_tab_content' );
}
return $tabs;
}
also, make sure $attr = $child->get_attribute('contract-type'); is getting the right value... try checking it.

Custom Post Type active link - Wordpress

I have a custom post type named 'schools' on my wordpress site. When you hover over the schools tab a list of schools are shown in the submenu. These are pages I created with different school names. Now when you click on one of the school pages I have a sidebar with all the schools so they can naviate through the different schools from the sidebar rather than using the menu.
I have the sidebar being populated using the following snippet.
while( $shools_loop->have_posts() ) : $schools_loop->the_post();
$content .= '<li class="schools-list">';
$content .= ''.get_the_title().'';
$content .= '</li>';
endwhile;
This works perfectly and I can navigate through all the schools without a problem from my sidebar. I'm trying to find a way to when I view a school via the sidebar or nav that when I'm on the active page I create some css styling to the li of the ACTIVE page. I've already figured out how to do this with the nav menu. But need help on the sidebar menu. Since the sidebar list menu is being populated I'm unsure how to check if the custom post type link is active and corresponds to the /schools/get-title page.
I found something like this online and I've tried editing it, but I'm not sure if this only works for nav menu
add_action( 'init', 'create_post_type' );
function create_post_type() {
register_post_type( 'services',
array(
'labels' => array(
'name' => __( 'Services' ),
'singular_name' => __( 'Services' )
),
'public' => true,
'has_archive' => true,
'rewrite' => array('slug' => 'services'),
)
);
}
// highlight active custom post page in nav
add_filter( 'nav_menu_css_class', 'namespace_menu_classes', 10, 2 );
function namespace_menu_classes( $classes , $item ){
if ( get_post_type() == 'services' ) {
// remove unwanted classes if found
$classes = str_replace( 'current_page_parent', '', $classes );
// find the url you want and add the class you want
if ( $item->url == 'services/physical-therapy-services/' ) {
$classes = str_replace( 'menu-item', 'menu-item current_page_parent', $classes );
}
}
return $classes;
}
Basically need to find a way to check whether the custom post type is active or not.
You can set a global variable in the main template page with the ID of the current post and then in your sidebar loop, you can retrieve that global variable and compare it with current post's ID by using get_the_ID() function and then perform the necessary actions.
Example:
single-cpt.php
// Inside the loop
global $post_id = get_the_ID();
sidebar-cpt.php
$post_id = isset($_GLOBALS['post_id']) ? $_GLOBALS['post_id'] : 0;
while( $shools_loop->have_posts() ) : $schools_loop->the_post();
if($post_id == get_the_ID()){
// This is the active link
$content .= '<li class="schools-list selected">';
} else {
$content .= '<li class="schools-list">';
}
$content .= ''.get_the_title().'';
$content .= '</li>';
endwhile;

Categories