Jump to content

Excluding pages from the Foundation menu by using a checkbox field


Zahari M.
 Share

Recommended Posts

Hi Guys. First Post!

Greetings from Malaysia.

I'm trying to move over from WordPress and am absolutely delighted to have found ProcessWire!
As I'm a really slow learner, it's been taking me quite some time to
understand the ProcessWire conventions. But bit by bit I'm getting there...

OK...
as the question asks, I would be really grateful if someone could help
me out by telling me how we can go about excluding pages from the
Foundation menu?

I have downloaded and installed the Foundation template files for the standard Processwire site install.

I have spent a long time trying to understand it and believe that the solution lies in editing renderTopNav that is located in  _nav.php.

function renderTopNav(PageArray $items, array $options = array(), $level = 0) {

    $defaults = array(
        'tree' => 2, // number of levels it should recurse into the tree
        'dividers' => true,
        'repeat' => false, // whether to repeat items with children as first item in their children nav
        );

    $options = array_merge($defaults, $options);
    $divider = $options['dividers'] ? "<li class='divider'></li>" : "";
    $page = wire('page');
    $out = '';

    foreach($items as $item) {

        $numChildren = $item->numChildren(true);
        if($level+1 > $options['tree'] || $item->id == 1) $numChildren = 0;
        $class = '';
        if($numChildren) $class .= "has-dropdown ";
        if($page->id == $item->id) $class .= "current ";
        if(($item->id > 1 && $page->parents->has($item)) || $page->id == $item->id) $class .= "active ";
        if($class) $class = " class='" . trim($class) . "'";

        $out .= "$divider<li$class><a href='$item->url'>$item->title</a>";

        if($numChildren) {
            $out .= "<ul class='dropdown'>";
            if($options['repeat']) $out .= "$divider<li><a href='$item->url'>$item->title</a></li>";
            $out .= renderTopNav($item->children, $options, $level+1);
            $out .= "</ul>";
        }

        $out .= "</li>";
    }

    return $out;
}

During a search, there was a thread here that discussed the various ways on how to exclude pages from the navigation.

The only problem is that due to my limited skill set, it's just a bit beyond my abilities to adapt this into the function above!

Could
anyone help me out with how to modify the function so that we can
exclude pages from the Foundation menu by way creating a checkbox field?

Many thanks!

Zahari

Link to comment
Share on other sites

Hi Zahari,

welcome to the forums. Great you have found PW ;-)

 ...

Could anyone help me out with how to modify the function so that we can exclude pages from the Foundation menu by way creating a checkbox field?

I assume you know how to add a checkbox-field to the templates (if not, please ask here again!)

For example you may call this field "excludeFromTopnav" and after that you have to check in the foreach-loop in the function if the current item (what comes from an PageArray, so it is a $page) have this field checked or not: (Line 16)

function renderTopNav(PageArray $items, array $options = array(), $level = 0) {

    $defaults = array(
        'tree' => 2, // number of levels it should recurse into the tree
        'dividers' => true,
        'repeat' => false, // whether to repeat items with children as first item in their children nav
        );

    $options = array_merge($defaults, $options);
    $divider = $options['dividers'] ? "<li class='divider'></li>" : "";
    $page = wire('page');
    $out = '';

    foreach($items as $item) {

        if($item->excludeFromTopnav == 1) {
            continue; // should be excluded, so we skip and went to the next 
        }

        $numChildren = $item->numChildren(true);
        if($level+1 > $options['tree'] || $item->id == 1) $numChildren = 0;
        $class = '';
        if($numChildren) $class .= "has-dropdown ";
        if($page->id == $item->id) $class .= "current ";
        if(($item->id > 1 && $page->parents->has($item)) || $page->id == $item->id) $class .= "active ";
        if($class) $class = " class='" . trim($class) . "'";

        $out .= "$divider<li$class><a href='$item->url'>$item->title</a>";

        if($numChildren) {
            $out .= "<ul class='dropdown'>";
            if($options['repeat']) $out .= "$divider<li><a href='$item->url'>$item->title</a></li>";
            $out .= renderTopNav($item->children, $options, $level+1);
            $out .= "</ul>";
        }

        $out .= "</li>";
    }

    return $out;
}

So, not tested and I also haven't worked with the foundation profile, but it should work. Otherwise please come back here.

Link to comment
Share on other sites

Hi Horst!

Thank you so much for the welcome. And even more so for helping me out!

Thanks so much Horst for pointing me to the area that needed looking at.

Your code works great! But it's brought up 2 slight issues as a result....

But first, for the benefit of other beginners like me looking at this..... I did change it ever so slightly. I really wanted the default behaviour to be the opposite and that is by default pages are not added.... so I ended up calling my checkbox field addtonavbar and did this:

if($item->addtonavbar == 0) {
            continue; // should be excluded, so we skip and went to the next 
        }

Of course Horst you would've inverted it this way too I'm most certain :) !

The two "catches" or issues that I discovered when testing this that you have to be mindful of....


First is that if you "hide" a parent page, you will automatically lose any wanted child pages in the nav bar, even if you had "checked" some of these child pages to be displayed using our approach above.


Second, and a minor one at that, is that if you only wanted to display a particular parent, and have all it's children hidden, then our nav bar confusingly still renders a dropdown triangle...

I'm happy with the progress thus far and can live with this as the nav bar wont be flooded with a huge number of items!!

But if these two "catches" could be resolved, then we all would have an uber "complete & refined" Foundation top nav bar solution :) !

Thanks again so much Horst!

Zahari

Link to comment
Share on other sites

Hi Zahari,
 
great there is some progress. Lets's look to the two issues now. And I once again want to say that I haven't used that profile, and I don't know it, - so if there is someone reading who knows it, come in and shed some light :)

second one first:

...
Second, and a minor one at that, is that if you only wanted to display a particular parent, and have all it's children hidden, then our nav bar confusingly still renders a dropdown triangle...

As the original code is written to render the navbar fast and straight forward, we have to hack in and rearrange parts and also first have to buffer the (optional) childrens output. This very fast can become cumbersome. Here is a version that you may try. I haven't tried it and I don't know if it works. If I understand the original code and assume it's relations right, it should work, but I don't know.

function renderTopNav(PageArray $items, array $options = array(), $level = 0) {

	$defaults = array(
		'tree' => 2, // number of levels it should recurse into the tree
		'dividers' => true,
		'repeat' => false, // whether to repeat items with children as first item in their children nav
	);

	$options = array_merge($defaults, $options);
	$divider = $options['dividers'] ? "<li class='divider'></li>" : "";
	$page = wire('page');
	$out = '';

	foreach($items as $item) {

		if($item->addtonavbar == 0) {
			continue; // should be excluded, so we skip and went to the next
		}

		$numChildren = $item->numChildren(true);
		if($level+1 > $options['tree'] || $item->id == 1) $numChildren = 0;

		$buffer = ''; // we need to buffer all children output
		if($numChildren) {
			$buffer2 = renderTopNav($item->children, $options, $level+1);
			if(strlen(trim($buffer2))>0) {
				$buffer = "<ul class='dropdown'>";
				if($options['repeat']) $buffer .= "$divider<li><a href='$item->url'>$item->title</a></li>";
				$buffer .= $buffer2;
				$buffer .= "</ul>";
			}
		}

		$class = '';
		if($numChildren && strlen($buffer)>0) $class .= "has-dropdown "; // parent gets dropdown-triangle if it has numchildren and the childrens-buffer isn't empty
		if($page->id == $item->id) $class .= "current ";
		if(($item->id > 1 && $page->parents->has($item)) || $page->id == $item->id) $class .= "active ";
		if($class) $class = " class='" . trim($class) . "'";

		$out .= "$divider<li$class><a href='$item->url'>$item->title</a>";
		$out .= $buffer;  // buffer is an empty string or contains complete ul-list of children
		$out .= "</li>";
	}

	return $out;
}

...
First is that if you "hide" a parent page, you will automatically lose any wanted child pages in the nav bar, even if you had "checked" some of these child pages to be displayed using our approach above.

I think it is logical if you don't show a parent, you cannot show it's children. They belong together.

Link to comment
Share on other sites

Also, don't forget about the "hidden" checkbox on every page's "settings" menu. That by itself would enable you to hide items from the Foundation menu. This is the method that is used natively in the profile to hide things like the search page from appearing in the menu.

  • Like 2
Link to comment
Share on other sites

Also, don't forget about the "hidden" checkbox on every page's "settings" menu. That by itself would enable you to hide items from the Foundation menu. This is the method that is used natively in the profile to hide things like the search page from appearing in the menu.

Thank's Ryan!

I assumed that the pages only should excluded from the menu, but not from searches or maybe other lists (e.g. sitemap). But I only assumed that. I better first should ask for it, - what could save some coding, depending on the answer. :lol:

Link to comment
Share on other sites

@Horst, Thank you for greatly for working on this.  This solved an issue where I had many tags showing up in the top navigation menu.

Also, don't forget about the "hidden" checkbox on every page's "settings" menu. That by itself would enable you to hide items from the Foundation menu. This is the method that is used natively in the profile to hide things like the search page from appearing in the menu.

@Ryan, that works under most situations, but it does not allow you to later search for those items.  It would be nice to have a status of Hidden: Not excluded from lists and searches.  Hopefully that makes sense to you. 

  • Like 1
Link to comment
Share on other sites

@Ryan, that works under most situations, but it does not allow you to later search for those items.  It would be nice to have a status of Hidden: Not excluded from lists and searches.  Hopefully that makes sense to you. 

@cstevensjr

I get what you are saying about different situations but of course you can still search for hidden and unpublished items by using "include=hidden" or "include=all" in your selectors. You can also use $pages->get to get one page, hidden or not. :). I suppose it's about using what best suits the situation..

@Horst - the above code was definitely not a waste of time! ;)

  • Like 2
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...