Support » Plugins » Hacks » Sub Menus On Some Pages & Not Others – Using Page Templates

  • Resolved s-design

    (@s-design-1)


    I had a hard time figuring out how to place menus on only some pages and not all pages. I have now figured out a solution that I am happy with and so I am sharing it here, in case it could help others and to get feedback to help improve the idea.

    Here is what I was trying to do:
    I was trying to add six different sub menus to my site. I wanted each sub menu only accessed by clicking on “it’s parent” link in the main menu. Here is an example of a website that is using this interface menu method that I am describing. When the site I have been working on goes live it will have this method here.

    These are other features I wanted:
    • I wanted it to be easy to add any of these sub menus to any page I wanted.
    • I wanted to use a combo of the page and menu editors to make it easy to update any of the menus items in any of these sub menus in the future.
    • I wanted the default to be: not showing any of these sub menus.
    • I didn’t want to use CSS display:none; to make the menus not show up (because I have read this could slows my site down, because they are still being loaded even though they don’t show up. I don’t know if this is true or not, but I wanted to try a different solution just in case).
    • I also wanted my solution to work with the child theme I am using.

    What I had tried – but couldn’t get to work:
    I tried to figure out multi site to solve this problem but it was way too complex.
    I tried the solution on this support page http://wordpress.org/support/topic/custom-menus-on-different-pages shared by “teknohippy.” This seemed like is would work great but I couldn’t get the custom fields to work.

    What I used to solve these challenges:
    • New Page templates
    • My child theme’s functions.php file
    • The admin menu editor and
    • The admin page editors for each page

    These are the steps I followed:

    Step 1. In my functions.php file in my child theme I “registered” the six menus I wanted to add, like so:

    register_nav_menus(array(
    			'sub-menu-1'  => __('Page 1 Menu', 'catcheverest'),
    			'sub-menu-2'  => __('Page 2 Menu', 'catcheverest'),
    			'sub-menu-3'  => __('Page 3 Menu', 'catcheverest'),
    			'sub-menu-4'  => __('Page 4 Menu', 'catcheverest'),
                            'sub-menu-5'  => __('Page 5 Menu', 'catcheverest'),
    			'sub-menu-6'  => __('Page 6 Menu', 'catcheverest')
    		)
    );

    Of course these names (“sub-menu-3”, “Page 3 Menu”, etc.) can be customized to fit your needs. These menu names should now show up in the wp-admin > in Apperance > menu.

    Step 2. I created six duplicates of the default page template that was in my parent theme and put them in my child theme folder and renamed them like this (you can name them as you wish):

    page1.php
    page2.php
    page3.php
    page4.php
    page5.php
    page6.php

    Note: If you are not using a child theme you could just put these in your theme file. BUT, if you are not using a Child Theme you really should put in the time and effort to learn how to create and use a child theme. It will save you tons of time in the future when it comes time to update your site. There is tons of info about this in the codex or just search about how to do it in google.

    Step 3. I then opened each of the new page template files (that I put in my child theme) and changed the template name that I wanted to show up in the WP admin > page editors > template selector > drop-down menu > under “Page Attributes.” For example:

    I changed this comment line (at the top):

    <?php
    /**
     * The template for displaying all pages

    to be this:

    <?php
    /**
     * Template Name: sub-menu-1

    Then save them. They could easily be named something else, like “About Pages SubMenu Template” or whatever. The names you changed in these template files should now be showing up in: the WP admin > page editors > template selector > drop-down menu > under “Page Attributes.”

    Step 4. In each of the page template files I added code like this. I placed it where I wanted each menu to appear on each page template.

    <div class="extra-sub-menus">
                <?php
                    if ( has_nav_menu( 'sub-menu-1' ) ) {
                        $args = array(
                            'theme_location'  => 'sub-menu-1',
    			'fallback_cb'	  =>  '',
                            'container_class' => 'menu-header-container',
                            'items_wrap'      => '<ul class="sub-menu-list">%3$s</ul>'
                        );
                        wp_nav_menu( $args );
                    }
                ?>
    </div>

    Note: This coded is a little different depending on what menu page template I was adding this code to. Where you see “sub-menu-1” (in two spots) I switched it for each of the different sub menus that I registered in step 1 above, such as: sub-menu-2, sub-menu-3, sub-menu-4, etc.

    I added the above code into template page1.php. In page2.php I added the same code but switched “sub-menu-1” to “sub-menu-2” In page3.php I switched it to “sub-menu-3” and so on for each of the other page template files.

    Step 5. I went into the menu editor in the wp-admin (it is in Appearance > Menu) then created the six menus with the page links I wanted on each menu. Selected the location (the template) that I wanted each menu to go with. To make it easy I made the “location” a similar name to each menu. For example: I created a menu by the name of “sub-menu-1 links” and selected it’s location/template to be “sub-menu-1” etc.

    Step 6. I then styled the css of the menus using the “extra-sub-menus” and “sub-menu-list” classes (you can do a ton of styling changes with just these two classes in the code above). You could choose to do this step last once you know it is all functioning the way you want.

    Step 7. Then in the admin, I went to the page editors of each of the pages I wanted a different menus to appear on and selected the relevant “menu page template.” For example: I added four page links to “sub-menu-1” in the menu editor, so I went into page editor of each of those four pages and selected the corresponding page template for “sub-menu-1” in the Page Attributes – Template drop-down selection menu. I updated each page. Now I have “sub-menu-1” showing up on all the pages I added it to. And, this menu is not showing any where I did not add it. Awesome!

    Step 8. Repeated step 7 for each of my other sub menus.

    Done! Hope this helps someone else.

    Disclaimer: I don’t know how to create a plugin to do something like this. If anyone is aware of a plug in that does something like this, please share. I don’t know much PHP or Javascript, so I don’t know if I can answer everyone’s questions about this. If anyone has feedback to improve this method I would be happy to hear it. If I’m doing something “wrong” please share.

Viewing 14 replies - 1 through 14 (of 14 total)
  • Moderator bcworkz

    (@bcworkz)

    Thank you for sharing 🙂

    You made a nice pitch for making an effort to create a child theme, and rightly so, it’s quite easy, yes?

    FYI, Writing a Plugin can be just as easy. It is also just a matter of putting a proper header in a file and placing it in the proper folder. The header and folder used is different, but just as easy. Any code you put in a child theme functions.php will work equally well in a plugin file.

    Thread Starter s-design

    (@s-design-1)

    I do like the power of the child theme. I will look more into creating a plugin.

    Thanks bcworkz.

    Thread Starter s-design

    (@s-design-1)

    bcworkz, I have searched and searched for how to create a plugin. I have created a plugin that I am able to activate. It recognizes css and I have registered menus. I can’t figure out how to make my plugin recognize page templates.

    I have tried several different coding methods that I have found on google and none of them seems to do the trick. This method seems the most promising but, it still doesn’t show the page templates in the page editor under the page template drop-down.

    Any ideas of how to make this work?

    Moderator bcworkz

    (@bcworkz)

    Assigning a template by script is a simple matter of setting it in post meta, in which case the template can reside anywhere on the server. Normally, to show up in the page edit screen, the template needs to be in the current theme’s folder.

    Your linked reference attempts to work around this requirement by fooling WP into thinking the template is there. The fact it does not work for you is a little perplexing, the post is not that old. WP is constantly evolving, so it’s less surprising when old hacks no longer work. I would have expected this one to work.

    Even if it doesn’t work for you, the concept is valid. It’s probably worth debugging the offered script to determine why it does not work. Sometimes a simple adjustment is all it takes to get a hack functional again.

    If a PHP solution no longer appears possible, there is likely a jQuery solution that injects the needed options in the drop down. There may still need to be a PHP segment to set the selection in post meta, but that should be easily accomplished using ‘save_post’ or the like.

    Thread Starter s-design

    (@s-design-1)

    Thanks for you response bcworkz. I don’t even know what you mean when you say “simple matter of setting it in post meta.” I don’t know how to ” worth debugging the offered script to determine why it does not work.” I don’t know jQuery and very little php. I mostly just copy and paste code and try and try and try different things.

    Here is the code below that I have been trying but can’t get the page templates to show up in the Page editor > Page Attributes > Drop-down. Is there something I’m doing wrong? Thank you for any help.

    class PageTemplater {
    
    		/**
             * A Unique Identifier
             */
    		 protected $plugin_slug;
    
            /**
             * A reference to an instance of this class.
             */
            private static $instance;
    
            /**
             * The array of templates that this plugin tracks.
             */
            protected $templates;
    
            /**
             * Returns an instance of this class.
             */
            public static function get_instance() {
    
                    if( null == self::$instance ) {
                            self::$instance = new PageTemplater();
                    } 
    
                    return self::$instance;
    
            } 
    
            /**
             * Initializes the plugin by setting filters and administration functions.
             */
            private function __construct() {
    
                    $this->templates = array();
    
                    // Add a filter to the attributes metabox to inject template into the cache.
                    add_filter(
    					'page_attributes_dropdown_pages_args',
    					 array( $this, 'register_project_templates' )
    				);
    
                    // Add a filter to the save post to inject out template into the page cache
                    add_filter(
    					'wp_insert_post_data',
    					array( $this, 'register_project_templates' )
    				);
    
                    // Add a filter to the template include to determine if the page has our
    				// template assigned and return it's path
                    add_filter(
    					'template_include',
    					array( $this, 'view_project_template')
    				);
    
                    // Add your templates to this array.
                    $this->templates = array(
    			'template-example.php'     => 'Example Page Template',
    			'template-example-two.php' => 'Example Page Template II',
    			'About-Pages.php' => 'About SubMenu'
            );
    } 
    
            /**
             * Adds our template to the pages cache in order to trick WordPress
             * into thinking the template file exists where it doens't really exist.
             *
             */
    
            public function register_project_templates( $atts ) {
    
                    // Create the key used for the themes cache
                    $cache_key = 'page_templates-' . md5( get_theme_root() . '/' . get_stylesheet() );
    
                    // Retrieve the cache list.
    				// If it doesn't exist, or it's empty prepare an array
                    $templates = wp_get_theme()->get_page_templates();
                    if ( empty( $templates ) ) {
                            $templates = array();
                    } 
    
                    // New cache, therefore remove the old one
                    wp_cache_delete( $cache_key , 'themes');
    
                    // Now add our template to the list of templates by merging our templates
                    // with the existing templates array from the cache.
                    $templates = array_merge( $templates, $this->templates );
    
                    // Add the modified cache to allow WordPress to pick it up for listing
                    // available templates
                    wp_cache_add( $cache_key, $templates, 'themes', 1800 );
    
                    return $atts;
    
            } 
    
            /**
             * Checks if the template is assigned to the page
             */
            public function view_project_template( $template ) {
    
                    global $post;
    
                    if (!isset($this->templates[get_post_meta(
    					$post->ID, '_wp_page_template', true
    				)] ) ) {
    
                            return $template;
    
                    } 
    
                    $file = plugin_dir_path(__FILE__). 'templates/' .get_post_meta(
    					$post->ID, '_wp_page_template', true
    				);
    
                    // Just to be safe, we check if the file exist first
                    if( file_exists( $file ) ) {
                            return $file;
                    }
    				else { echo $file; }
    
                    return $template;
    
            } 
    
    } 
    
    add_action( 'plugins_loaded', array( 'PageTemplater', 'get_instance' ) );
    Moderator bcworkz

    (@bcworkz)

    I tested you code on my site, it works fine! 🙂 It’s actually pretty cool, I must say.

    One thing that’s not that clear if you don’t study (or understand) the code is the plugin page templates are hard coded to reside under a templates/ subfolder of the plugin folder. You can modify this if you like, it’s about 16 lines up from the bottom: $file = plugin_dir_path(__FILE__). 'templates/' .get_post_meta(

    You do also need to hardcode your templates, the script does not find them on it’s own like WP does with theme templates. That would be a nice feature for someone to add 🙂 The list starts about 58 lines down. I see you found this part, since the files you list are different than those from the linked reference. I mention this part for anyone else following along.

    If this script is still not working for you, it’s likely it’s conflicting with another of your plugins, or possibly even your theme or the remainder of the same plugin. Identify which one by making this script a stand alone plugin and disabling all but this script. Check that it now works, then re-activate plugins one by one until one causes failure.

    Finding exactly where the conflict lies is more difficult. As a class, it’s unlikely there is a name collision. The conflict is possibly through a filter or action used in common. Fixing this sort of conflict could be as simple as changing the priority of one of the hooks.

    Thread Starter s-design

    (@s-design-1)

    Well, I guess it is good to know that it is working on your site.

    I should give a shout-out to the ones who came up with this code that I found and I’m trying. Tom McFarlin put together this code here on github. Harri Bell-Thomas put together this tutorial here on WPExplorer.com with a github download here.

    I was aware that I could change: . 'templates/' . to something else if I wished.
    I also knew that I could change out these array items:

    $this->templates = array(
    			'template-example.php'     => 'Example Page Template',
    			'template-example-two.php' => 'Example Page Template II',
    			'About-Pages.php' => 'About SubMenu'
            );

    I knew this thanks to Harri Bell-Thomas’s tutorial.

    So, I have tried what you said. I have deactivated all my other plugins. The templates are still a no show :/

    I have tried searching these filter names in my theme files that I got from the code:
    page_attributes_dropdown_pages_args
    wp_insert_post_data
    template_include
    As far as I can tell none of them are showing in my theme files. I do see these names showing up in core WordPress files.

    I tried searching these names that I got from the action that is in the last line of code. I looked for them in my theme files:
    plugins_loaded
    PageTemplater
    get_instance
    Again, as far as I can tell none of them show up in my theme files. I do see a couple of them showing up in the core WordPress files.

    Not sure what to do next. I guess for now, I will keep searching more carefully for a conflict in my theme files. Do you have any more ideas? You are very kind to take the time to help me out.

    Moderator bcworkz

    (@bcworkz)

    You’ve certainly done your homework in checking for conflicts. It’s so disappointing it’s not working for you, it really should be 🙁

    You can try defining WP_DEBUG as true in wp-config.php to see any possible warnings and such, though anything major would have been noticed either way, so this probably will not help. It’s a good idea when testing anyway. In fact, you’ve probably done this.

    Assuming your WP and PHP versions are up to date, about all that’s left is to methodically step through the code and identify where things go wrong. Not only is this tedious, but requires an understanding of what each line of code is doing in order to know what to test for and recognize when the test results are wrong.

    I wish I had a better answer for you. The maddening thing is the problem can probably be easily fixed if one could only identify the problem 🙁

    Thread Starter s-design

    (@s-design-1)

    Happy news! It’s working!

    I kept checking to see if it was working by going into my list of pages in my admin, clicking on “Quick Edit” options and looking for the new page templates in the “Template” drop-down. It is not showing my newly added templates, it only shows my default template. This caused me to think it wasn’t working at all.

    But, then I went into a page editor and checked under “Page Attributes” in the “Template” drop-down. The new templates are there. I can select them and they work just I would expect. I should have checked this sooner, but I didn’t. So, It is working with the class PageTemplater code above without changing it at all.

    It often seems like the dumbest little thing I don’t check is often the answer to an issue I run into. I am so happy when I am able to get something working. Thanks for all your help with this.

    You are right about the benefits and power of using a plugin to add a feature like this. Now I can easily keep my core features only in my child theme and now a feature that I may want to use on other sites I can keep in a plugin. It is just as easy to set up a plugin as it is to create a child theme. I will recommend using this method for adding specific features to anyone.

    The templates still don’t show up in the template drop-down menu in the “Quick Edit” options. Any idea how to get them to show up there too? It is not essential to figure this part out, it would just be convenient if they showed up there too.

    Moderator bcworkz

    (@bcworkz)

    Happy news indeed!!

    I had a feeling it was something simple. It is a curious side effect that the hack doesn’t work with quick edit. The only way I know of to get it working is by using jQuery to inject the additional options. It was the only way I knew of for the regular edit form as well. it never occurred to me to hack the cache, I didn’t even know there was a cache for this!

    The cache hack is ever so much better than my jQuery approach. I suspect getting the quick edit dropdown populated can involve hacking a very similar cache. The trick is identifying how to access it. For that I don’t even know where to start looking 🙁

    I appreciate you gratitude in my help, though I haven’t been much help really, more of an encouraging bystander. Anyway, you’re welcome. If I find some time I’ll have a look around for cache related code just out of curiosity. If I find anything useful I’ll let you know. Just don’t hold your breath waiting, I’m not that hopeful I’ll find anything.

    Thread Starter s-design

    (@s-design-1)

    That would be really cool if you had time to look more into that bug, but I won’t hold my breath. So, no worries if you don’t have time.

    Encouragement goes a long ways to helping me and I’m sure many others work through a problem. So, thank you for that.

    Moderator bcworkz

    (@bcworkz)

    I got it! 🙂

    The problem was one of the filters hooked only fires for full edit pages. We just needed to find an alternate filter that fires for both full and quick edits. Fortunately, one is at hand.

    Simply replace the filter tag ‘page_attributes_dropdown_pages_args’ with ‘wp_dropdown_pages’. (the 1st add_filter() in __construct()) Incidentally, these filters actually have nothing to do with page templates, they just happen to fire just before the page template cache is called by page_template_dropdown(), a good opportunity to inject our templates into the cache.

    That was the one filter I didn’t really understand. The others were more obvious. The next filter allowed saving the template in post meta and ‘template_include’ loads the template when the page is displayed.

    Thread Starter s-design

    (@s-design-1)

    Wow! You are quick. It works! This is awesome. THANK YOU, for everything.

    Moderator bcworkz

    (@bcworkz)

    You’re most welcome.

    It was a fun puzzle for me. Kind of pathetic, I know. We gotta get entertainment where we can 🙂

Viewing 14 replies - 1 through 14 (of 14 total)
  • The topic ‘Sub Menus On Some Pages & Not Others – Using Page Templates’ is closed to new replies.